Skip to content

Commit

Permalink
drivers: import PIR motion sensor driver
Browse files Browse the repository at this point in the history
Includes an application for manual testing.
  • Loading branch information
LudwigKnuepfer committed Sep 26, 2014
1 parent 99760f3 commit e75dd40
Show file tree
Hide file tree
Showing 7 changed files with 380 additions and 0 deletions.
3 changes: 3 additions & 0 deletions drivers/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,8 @@ endif
ifneq (,$(filter servo,$(USEMODULE)))
DIRS += servo
endif
ifneq (,$(filter pir,$(USEMODULE)))
DIRS += pir
endif

include $(RIOTBASE)/Makefile.base
88 changes: 88 additions & 0 deletions drivers/include/pir.h
Original file line number Diff line number Diff line change
@@ -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 <ludwig.ortmann@fu-berlin.de>
*/

#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 */
/** @} */
3 changes: 3 additions & 0 deletions drivers/pir/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
MODULE = pir

include $(RIOTBASE)/Makefile.base
110 changes: 110 additions & 0 deletions drivers/pir/pir.c
Original file line number Diff line number Diff line change
@@ -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 <ludwig.ortmann@fu-berlin.de>
*
* @}
*/

#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);
}
29 changes: 29 additions & 0 deletions tests/driver_pir/Makefile
Original file line number Diff line number Diff line change
@@ -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
60 changes: 60 additions & 0 deletions tests/driver_pir/README.md
Original file line number Diff line number Diff line change
@@ -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
...
87 changes: 87 additions & 0 deletions tests/driver_pir/main.c
Original file line number Diff line number Diff line change
@@ -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 <ludwig.ortmann@fu-berlin.de>
*
* @}
*/

#ifndef PIR_GPIO
#error "PIR_GPIO not defined"
#endif

#include <stdio.h>

#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;
}

0 comments on commit e75dd40

Please sign in to comment.