Skip to content

Commit

Permalink
driver/pir: add pir-based occupancy sensing
Browse files Browse the repository at this point in the history
  • Loading branch information
Hyungsin committed Jun 26, 2018
1 parent 9c612d4 commit 0c5c3d9
Show file tree
Hide file tree
Showing 13 changed files with 345 additions and 30 deletions.
5 changes: 5 additions & 0 deletions drivers/Makefile.dep
Expand Up @@ -279,6 +279,11 @@ ifneq (,$(filter pcd8544,$(USEMODULE)))
USEMODULE += xtimer
endif

ifneq (,$(filter pir,$(USEMODULE)))
FEATURES_REQUIRED += periph_gpio
USEMODULE += xtimer
endif

ifneq (,$(filter rgbled,$(USEMODULE)))
USEMODULE += color
endif
Expand Down
4 changes: 4 additions & 0 deletions drivers/Makefile.include
Expand Up @@ -186,6 +186,10 @@ ifneq (,$(filter pcd8544,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/pcd8544/include
endif

ifneq (,$(filter pir,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/pir/include
endif

ifneq (,$(filter pulse_counter,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/pulse_counter/include
endif
Expand Down
50 changes: 44 additions & 6 deletions drivers/include/pir.h
Expand Up @@ -16,24 +16,49 @@
* @brief Device driver interface for the PIR motion sensor
*
* @author Ludwig Knüpfer <ludwig.knuepfer@fu-berlin.de>
* @author Hyung-Sin Kim <hs.kim@cs.berkeley.edu>
*/

#ifndef PIR_H
#define PIR_H

#include "kernel_types.h"
#include "periph/gpio.h"
#include "stdbool.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief PIR specific return values
*/
enum {
PIR_OK = 0, /**< everything went as expected */
PIR_NOGPIO = -1, /**< errors while initializing the GPIO */
PIR_NOTHREAD = -2, /**< errors while registering the thread */
PIR_TIMEERR = -3, /**< errors while getting the time information */
};

/**
* @brief Parameters needed for device initialization
*/
typedef struct {
gpio_t gpio; /**< GPIO device which is used */
bool active_high; /**< Active when GPIO pin is high or not */
} pir_params_t;


/**
* @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 */
uint64_t start_active_time; /**< Time when PIR starts to be active */
uint64_t accum_active_time; /**< Accumulated active time */
uint64_t last_read_time; /**< Last time when PIR status is read */
kernel_pid_t msg_thread_pid; /**< thread to msg on irq */
bool active; /**< Indicate PIR is active or not */
pir_params_t p; /**< Configuration parameters */
} pir_t;

/**
Expand All @@ -47,8 +72,8 @@ typedef struct {
* @brief event type for a PIR sensor
*/
typedef enum {
PIR_STATUS_HI = PIR_MSG_T_STATUS_START, /**< motion was detected */
PIR_STATUS_LO, /**< no motion is detected */
PIR_STATUS_ACTIVE = PIR_MSG_T_STATUS_START, /**< motion was detected */
PIR_STATUS_INACTIVE, /**< no motion is detected */
} pir_event_t;

/**
Expand All @@ -62,12 +87,12 @@ typedef enum {
* measurements can be made.
*
* @param[out] dev device descriptor of an PIR sensor
* @param[in] gpio the GPIO device the sensor is connected to
* @param[in] params parameters of the PIR sensor
*
* @return 0 on success
* @return -1 on error
*/
int pir_init(pir_t *dev, gpio_t gpio);
int pir_init(pir_t *dev, const pir_params_t* params);

/**
* @brief Read the current status of the motion sensor
Expand All @@ -78,6 +103,19 @@ int pir_init(pir_t *dev, gpio_t gpio);
*/
pir_event_t pir_get_status(const pir_t *dev);

/**
* @brief Read OCCUPANCY value
*
* @param[in] dev device descriptor of the PIR motion sensor to read from
* @param[out] occup occupancy ratio [in 100 * percentage]
* The value is renewed when it is read. So it is percentage
* of occupancy since the last read.
*
* @return 0 on success,
* @return -1 on errors,
*/
int pir_get_occupancy(pir_t *dev, int16_t *occup);

/**
* @brief Register a thread for notification whan state changes on the
* motion sensor.
Expand Down
1 change: 1 addition & 0 deletions drivers/include/saul.h
Expand Up @@ -96,6 +96,7 @@ enum {
SAUL_SENSE_DISTANCE = 0x8e, /**< sensor: distance */
SAUL_SENSE_CO2 = 0x8f, /**< sensor: CO2 Gas */
SAUL_SENSE_TVOC = 0x90, /**< sensor: TVOC Gas */
SAUL_SENSE_OCCUP = 0x91, /**< sensor: occupancy */
SAUL_CLASS_ANY = 0xff /**< any device - wildcard */
/* extend this list as needed... */
};
Expand Down
71 changes: 71 additions & 0 deletions drivers/pir/include/pir_params.h
@@ -0,0 +1,71 @@
/*
* Copyright (C) 2018 UC Berkeley
*
* 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 drivers_pir
*
* @{
* @file
* @brief Default configuration for PIR devices
*
* @author Hyung-Sin Kim <hs.kim@cs.berkeley.edu>
*/

#ifndef PIR_PARAMS_H
#define PIR_PARAMS_H

#include "board.h"
#include "pir.h"
#include "saul_reg.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Set default configuration parameters for the PIR driver
* @{
*/
#ifndef PIR_PARAM_GPIO
#define PIR_PARAM_GPIO GPIO_PIN(0, 6)
#endif
#ifndef PIR_PARAM_ACTIVE_HIGH
#define PIR_PARAM_ACTIVE_HIGH 1
#endif

#ifndef PIR_PARAMS
#define PIR_PARAMS { .gpio = PIR_PARAM_GPIO, \
.active_high = PIR_PARAM_ACTIVE_HIGH }
#endif
#ifndef PIR_SAUL_INFO
#define PIR_SAUL_INFO { .name = "pir" }
#endif
/**@}*/

/**
* @brief PIR configuration
*/
static const pir_params_t pir_params[] =
{
PIR_PARAMS
};

/**
* @brief Additional meta information to keep in the SAUL registry
*/
static const saul_reg_info_t pir_saul_info[] =
{
PIR_SAUL_INFO
};

#ifdef __cplusplus
}
#endif

#endif /* PIR_PARAMS_H */
/** @} */
90 changes: 81 additions & 9 deletions drivers/pir/pir.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 Freie Universität Berlin
* Copyright (C) 2018 UC Berkeley
*
* 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
Expand All @@ -14,13 +15,16 @@
* @brief Device driver implementation for the PIR motion sensor
*
* @author Ludwig Knüpfer <ludwig.knuepfer@fu-berlin.de>
* @author Hyung-Sin Kim <hs.kim@cs.berkeley.edu>
*
* @}
*/

#include "pir.h"
#include "irq.h"
#include "thread.h"
#include "msg.h"
#include "xtimer.h"

#define ENABLE_DEBUG (0)
#include "debug.h"
Expand All @@ -37,37 +41,77 @@ static void pir_send_msg(pir_t *dev, pir_event_t event);
* public API implementation
**********************************************************************/

int pir_init(pir_t *dev, gpio_t gpio)
int pir_init(pir_t *dev, const pir_params_t *params)
{
dev->gpio_dev = gpio;
dev->p.gpio = params->gpio;
dev->p.active_high = params->active_high;
dev->msg_thread_pid = KERNEL_PID_UNDEF;
return gpio_init(dev->gpio_dev, GPIO_IN);

dev->active = false;
dev->accum_active_time = 0;
dev->start_active_time = 0;
dev->last_read_time = xtimer_now_usec64();

gpio_mode_t gpio_mode;
if (dev->p.active_high) {
gpio_mode = GPIO_IN_PD;
}
else {
gpio_mode = GPIO_IN_PU;
}

if (gpio_init_int(dev->p.gpio, gpio_mode, GPIO_BOTH, pir_callback, dev)) {
return PIR_NOGPIO;
}
return PIR_OK;
}

pir_event_t pir_get_status(const pir_t *dev)
{
return ((gpio_read(dev->gpio_dev) == 0) ? PIR_STATUS_LO : PIR_STATUS_HI);
return (((gpio_read(dev->p.gpio) > 0) == dev->p.active_high) ?
PIR_STATUS_ACTIVE : PIR_STATUS_INACTIVE);
}

int pir_get_occupancy(pir_t *dev, int16_t *occup) {
int irq_state = irq_disable();
uint64_t now = xtimer_now_usec64();
uint64_t total_time = now - dev->last_read_time;
if (total_time == 0) {
irq_restore(irq_state);
return PIR_TIMEERR;
}

/* We were busy counting */
if (dev->active) {
dev->accum_active_time += (now - dev->start_active_time);
dev->start_active_time = now;
}
*occup = (int16_t)((dev->accum_active_time * 10000) / total_time);
dev->last_read_time = now;
dev->accum_active_time = 0;
irq_restore(irq_state);
return PIR_OK;
}

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;
return PIR_NOTHREAD;
}
}
else {
DEBUG("pir_register_thread: activating interrupt for %p..\n", (void *)dev);
if (pir_activate_int(dev) != 0) {
if (pir_activate_int(dev) != PIR_OK) {
DEBUG("\tfailed\n");
return -1;
return PIR_NOGPIO;
}
DEBUG("\tsuccess\n");
}
dev->msg_thread_pid = thread_getpid();

return 0;
return PIR_OK;
}

/**********************************************************************
Expand Down Expand Up @@ -100,12 +144,40 @@ static void pir_callback(void *arg)
{
DEBUG("pir_callback: %p\n", arg);
pir_t *dev = (pir_t*) arg;
bool pin_now = gpio_read(dev->p.gpio);
uint64_t now = xtimer_now_usec64();

/* We were busy counting */
if (dev->active) {
/* Add into accumulation */
dev->accum_active_time += (now - dev->start_active_time);
}
/* Pin is rising */
if (pin_now == dev->p.active_high) {
dev->start_active_time = now;
dev->active = true;
/* Pin is falling */
} else {
dev->active = false;
}

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_IN, GPIO_BOTH, pir_callback, dev);
gpio_mode_t gpio_mode;
if (dev->p.active_high) {
gpio_mode = GPIO_IN_PD;
}
else {
gpio_mode = GPIO_IN_PU;
}

if (gpio_init_int(dev->p.gpio, gpio_mode, GPIO_BOTH, pir_callback, dev)) {
return PIR_NOGPIO;
}
return PIR_OK;
}
42 changes: 42 additions & 0 deletions drivers/pir/pir_saul.c
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2018 UC Berkeley
*
* 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 drivers_pir
* @{
*
* @file
* @brief PIR adaption to the RIOT actuator/sensor interface
*
* @author Hyung-Sin Kim <hs.kim@cs.berkeley.edu>
*
* @}
*/

#include <string.h>

#include "saul.h"
#include "pir.h"

static int read_occup(const void *dev, phydat_t *res) {
pir_t *d = (pir_t *)dev;
if (pir_get_occupancy(d, &(res->val[0]))) {
/* Read failure */
return -ECANCELED;
}
memset(&(res->val[1]), 0, 2 * sizeof(int16_t));
res->unit = UNIT_PERCENT;
res->scale = -2;
return 1;
}

const saul_driver_t pir_saul_occup_driver = {
.read = read_occup,
.write = saul_notsup,
.type = SAUL_SENSE_OCCUP,
};

0 comments on commit 0c5c3d9

Please sign in to comment.