diff --git a/drivers/Makefile b/drivers/Makefile index fbe4236691..51d10128b5 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -37,5 +37,8 @@ endif ifneq (,$(filter servo,$(USEMODULE))) DIRS += servo endif +ifneq (,$(filter pir,$(USEMODULE))) + DIRS += pir +endif include $(RIOTBASE)/Makefile.base diff --git a/drivers/include/pir.h b/drivers/include/pir.h new file mode 100644 index 0000000000..a08b56817e --- /dev/null +++ b/drivers/include/pir.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup driver_pir PIR Motion Sensor + * @ingroup drivers + * @brief Device driver interface for the PIR motion sensor + * @{ + * + * @file + * @brief Device driver interface for the PIR motion sensor + * + * @author Ludwig Ortmann + */ + +#ifndef __PIR_H +#define __PIR_H + +#include "kernel_types.h" +#include "periph/gpio.h" + +/** + * @brief device descriptor for a PIR sensor + */ +typedef struct { + gpio_t gpio_dev; /**< GPIO device which is used */ + kernel_pid_t msg_thread_pid; /**< thread to msg on irq */ +} pir_t; + +#ifndef PIR_MSG_T_STATUS_START +#define PIR_MSG_T_STATUS_START 150 +#endif + +typedef enum { + PIR_STATUS_HI = PIR_MSG_T_STATUS_START, /**< motion was detected */ + PIR_STATUS_LO, /**< no motion is detected */ +} pir_event_t; + +/** + * @brief Initialize a PIR motion sensor + * + * The PIR motion sensor is interfaced by a single GPIO pin, specified by + * `gpio`. + * + * @note + * The sensor needs up to a minute to settle down before meaningful + * measurements can be made. + * + * @param[out] dev device descriptor of an PIR sensor + * @param[in] gpio the GPIO device the sensor is connected to + * + * @return 0 on success + * @return -1 on error + */ +int pir_init(pir_t *dev, gpio_t gpio); + +/** + * @brief Read the current status of the motion sensor + * + * @param[in] dev device descriptor of the PIR motion sensor to read from + * + * @return 1 if motion is detected, 0 otherwise + */ +pir_event_t pir_get_status(pir_t *dev); + +/** + * @brief Register a thread for notification whan state changes on the + * motion sensor. + * + * @note + * This configures the gpio device for interrupt driven operation. + * + * @param[in] dev device descriptor of the PIR motion sensor to + * register for + * + * @return 0 on succuess, + * @return -1 on internal errors, + * @return -2 if another thread is registered already + */ +int pir_register_thread(pir_t *dev); + +#endif /* __PIR_H */ +/** @} */ diff --git a/drivers/pir/Makefile b/drivers/pir/Makefile new file mode 100644 index 0000000000..b2984a2c6b --- /dev/null +++ b/drivers/pir/Makefile @@ -0,0 +1,3 @@ +MODULE = pir + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/pir/pir.c b/drivers/pir/pir.c new file mode 100644 index 0000000000..2a802b20c6 --- /dev/null +++ b/drivers/pir/pir.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup driver_pir + * @{ + * + * @file + * @brief Device driver implementation for the PIR motion sensor + * + * @author Ludwig Ortmann + * + * @} + */ + +#include "pir.h" +#include "thread.h" +#include "msg.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/********************************************************************** + * internal API declaration + **********************************************************************/ + +static int pir_activate_int(pir_t *dev); +static void pir_callback(void *dev); +static void pir_send_msg(pir_t *dev, pir_event_t event); + +/********************************************************************** + * public API implementation + **********************************************************************/ + +int pir_init(pir_t *dev, gpio_t gpio) +{ + dev->gpio_dev = gpio; + dev->msg_thread_pid = KERNEL_PID_UNDEF; + return gpio_init_in(dev->gpio_dev, GPIO_NOPULL); +} + +pir_event_t pir_get_status(pir_t *dev) +{ + return ((gpio_read(dev->gpio_dev) == 0) ? PIR_STATUS_LO : PIR_STATUS_HI); +} + +int pir_register_thread(pir_t *dev) +{ + if (dev->msg_thread_pid != KERNEL_PID_UNDEF) { + if (dev->msg_thread_pid != thread_getpid()) { + DEBUG("pir_register_thread: already registered to another thread\n"); + return -2; + } + } + else { + DEBUG("pir_register_thread: activating interrupt for %p..\n", dev); + if (pir_activate_int(dev) != 0) { + DEBUG("\tfailed\n"); + return -1; + } + DEBUG("\tsuccess\n"); + } + dev->msg_thread_pid = thread_getpid(); + + return 0; +} + +/********************************************************************** + * internal API implementation + **********************************************************************/ + +static void pir_send_msg(pir_t *dev, pir_event_t event) +{ + DEBUG("pir_send_msg\n"); + msg_t m = { .type = event, .content.ptr = (char*) dev, }; + + int ret = msg_send_int(&m, dev->msg_thread_pid); + DEBUG("pir_send_msg: msg_send_int: %i\n", ret); + switch (ret) { + case 0: + DEBUG("pir_send_msg: msg_thread_pid not receptive, event is lost"); + break; + case 1: + DEBUG("pir_send_msg: OK"); + break; + case -1: + DEBUG("pir_send_msg: msg_thread_pid is gone, clearing it"); + dev->msg_thread_pid = KERNEL_PID_UNDEF; + break; + } +} + +static void pir_callback(void *arg) +{ + DEBUG("pir_callback: %p\n", arg); + pir_t *dev = (pir_t*) arg; + if (dev->msg_thread_pid != KERNEL_PID_UNDEF) { + pir_send_msg(dev, pir_get_status(dev)); + } +} + +static int pir_activate_int(pir_t *dev) +{ + return gpio_init_int(dev->gpio_dev, GPIO_NOPULL, GPIO_BOTH, pir_callback, dev); +} diff --git a/tests/driver_pir/Makefile b/tests/driver_pir/Makefile new file mode 100644 index 0000000000..8fd60f5994 --- /dev/null +++ b/tests/driver_pir/Makefile @@ -0,0 +1,29 @@ +APPLICATION = driver_pir +include ../Makefile.tests_common + +# These boards do not suport periph/gpio.h at the time of this writing: +BOARD_BLACKLIST = native arduino-mega2560 avsextrem chronos mbed_lpc1768 msb-430 msb-430h msba2 pttu qemu-i386 redbee-econotag telosb wsn430-v1_3b wsn430-v1_4 z1 + +# Define default pin mappings for some boards: +BOARD_WHITELIST = stm32f4discovery arduino-due +ifneq (,$(filter stm32f4discovery,$(BOARD))) + export PIR_GPIO ?= GPIO_8 +endif +ifneq (,$(filter arduino-due,$(BOARD))) + export PIR_GPIO ?= GPIO_10 +endif + +USEMODULE += pir +USEMODULE += vtimer + +ifneq (,$(PIR_GPIO)) + CFLAGS += -DPIR_GPIO=$(PIR_GPIO) +endif + +include $(RIOTBASE)/Makefile.include + +all-polling: CFLAGS += -DTEST_PIR_POLLING + +all-polling: all + +all-interrupt: all diff --git a/tests/driver_pir/README.md b/tests/driver_pir/README.md new file mode 100644 index 0000000000..108a479c59 --- /dev/null +++ b/tests/driver_pir/README.md @@ -0,0 +1,60 @@ +# About +This is a manual test application for the PIR motion sensor driver. + +In order to build this application, you need to add the board to the +Makefile's `WHITELIST` first and define a pin mapping (see below). + + +# Usage +There are two ways to test this. You can either actively poll the sensor +state, or you can register a thread which receives messages for state +changes. + +## Interrupt driven +Connect the sensor's "out" pin to a GPIO of your board that can be +configured to create interrupts. +Compile and flash this test application like: + + export BOARD=your_board + export PIR_GPIO=name_of_your_pin + make clean + make all-interrupt + make flash + +The output should look like: + + kernel_init(): jumping into first task... + + PIR motion sensor test application + + Initializing PIR sensor at GPIO_8... [OK] + + Registering PIR handler thread... [OK] + PIR handler got a message: the movement has ceased. + PIR handler got a message: something started moving. + PIR handler got a message: the movement has ceased. + + +## Polling Mode +Connect the sensor's "out" pin to any GPIO pin of you board. +Compile and flash this test application like: + + export BOARD=your_board + export PIR_GPIO=name_of_your_pin + make clean + make all-polling + make flash + +The output should look like this: + + kernel_init(): jumping into first task... + PIR motion sensor test application + + Initializing PIR sensor at GPIO_10... [OK] + + Printing sensor state every second. + Status: lo + ... + Status: lo + Status: hi + ... diff --git a/tests/driver_pir/main.c b/tests/driver_pir/main.c new file mode 100644 index 0000000000..0d80931ebf --- /dev/null +++ b/tests/driver_pir/main.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for the PIR motion sensor driver + * + * @author Ludwig Ortmann + * + * @} + */ + +#ifndef PIR_GPIO +#error "PIR_GPIO not defined" +#endif + +#include + +#include "thread.h" +#include "vtimer.h" +#include "pir.h" + +char pir_handler_stack[KERNEL_CONF_STACKSIZE_MAIN]; +pir_t dev; + +void* pir_handler(void *arg) +{ + msg_t msg_q[1]; + msg_init_queue(msg_q, 1); + + printf("Registering PIR handler thread... %s\n", + pir_register_thread(&dev) == 0 ? "[OK]" : "[Failed]"); + + msg_t m; + while (msg_receive(&m)) { + printf("PIR handler got a message: "); + switch (m.type) { + case PIR_STATUS_HI: + puts("something started moving."); + break; + case PIR_STATUS_LO: + puts("the movement has ceased."); + break; + default: + puts("stray message."); + break; + } + } + puts("PIR handler: this should not have happened!"); + + return NULL; +} + +int main(void) +{ + puts("PIR motion sensor test application\n"); + printf("Initializing PIR sensor at GPIO_%i... ", PIR_GPIO); + if (pir_init(&dev, PIR_GPIO) == 0) { + puts("[OK]\n"); + } + else { + puts("[Failed]"); + return 1; + } + +#if TEST_PIR_POLLING + puts("Printing sensor state every second."); + while (1) { + printf("Status: %s\n", pir_get_status(&dev) == PIR_STATUS_LO ? "lo" : "hi"); + vtimer_usleep(1000 * 1000); + } +#else + thread_create( + pir_handler_stack, sizeof(pir_handler_stack), PRIORITY_MAIN - 1, + CREATE_WOUT_YIELD | CREATE_STACKTEST, + pir_handler, NULL, "pir_handler"); +#endif + return 0; +}