Merge pull request #2347 from posjodin/PR9

Driver for Plantower PMS 5003 dust sensors.
This commit is contained in:
Nicolas Tsiftes 2017-10-11 15:47:35 +02:00 committed by GitHub
commit 80dbe5c17e
9 changed files with 976 additions and 0 deletions

View File

@ -0,0 +1,21 @@
Contiki implementation for Plantower PMS 5003 series
dust sensors.
The PMS sensors support serial communication through UART and
I2C. This implementation supports both modes, and they can be
individually turned on/off through config options. Both can be
enabled at the same time.
In UART mode, the operation is asynchronous; the sensor periodically
outputs a frame of data, which is assembled by the sensor driver
through a UART callback function.
In I2C mode, the I2C bus is probed and if the sensor is found,
the frame is read. This is synchronous, triggered by a configurable
timer.
The sensor is duty-cycled through the SET input signal. The duty-cycling
intervals are configurable.
This implementation has been tested against the PMS 5003i (I2C) and
PMS 3003 (UART) sensors.

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2017, Peter Sjodin, KTH Royal Institute of Technology
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
* Author : Peter Sjodin, KTH Royal Institute of Technology
* Created : 2017-04-21
*/
#include <stdlib.h>
#include "contiki.h"
#include "lib/sensors.h"
#include "pms5003.h"
#include "pms5003-sensor.h"
const struct sensors_sensor pms5003_sensor;
enum {
ON, OFF
};
static uint8_t state = OFF;
/*---------------------------------------------------------------------------*/
PROCESS(pms5003_sensor_process, "PMS5003 sensor process");
/*---------------------------------------------------------------------------*/
static int
value(int type)
{
switch(type) {
case PMS5003_SENSOR_PM1:
return pms5003_pm1();
case PMS5003_SENSOR_PM2_5:
return pms5003_pm2_5();
case PMS5003_SENSOR_PM10:
return pms5003_pm10();
case PMS5003_SENSOR_PM1_ATM:
return pms5003_pm1_atm();
case PMS5003_SENSOR_PM2_5_ATM:
return pms5003_pm2_5_atm();
case PMS5003_SENSOR_PM10_ATM:
return pms5003_pm10_atm();
case PMS5003_SENSOR_DB0_3:
return pms5003_db0_3();
case PMS5003_SENSOR_DB0_5:
return pms5003_db0_5();
case PMS5003_SENSOR_DB1:
return pms5003_db1();
case PMS5003_SENSOR_DB2_5:
return pms5003_db2_5();
case PMS5003_SENSOR_DB5:
return pms5003_db5();
case PMS5003_SENSOR_DB10:
return pms5003_db10();
case PMS5003_SENSOR_TIMESTAMP:
return pms5003_timestamp();
}
return 0;
}
/*---------------------------------------------------------------------------*/
static int
status(int type)
{
switch(type) {
case SENSORS_ACTIVE:
case SENSORS_READY:
return state == ON;
}
return 0;
}
/*---------------------------------------------------------------------------*/
static int
configure(int type, int c)
{
switch(type) {
case SENSORS_ACTIVE:
if(c) {
if(!status(SENSORS_ACTIVE)) {
pms5003_init();
process_start(&pms5003_sensor_process, NULL);
state = ON;
}
} else {
pms5003_off();
state = OFF;
}
}
return 0;
}
/*---------------------------------------------------------------------------*/
SENSORS_SENSOR(pms5003_sensor, "pms5003",
value, configure, status);
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(pms5003_sensor_process, ev, data)
{
PROCESS_BEGIN();
while(1) {
do {
PROCESS_WAIT_EVENT();
} while(ev != pms5003_event);
sensors_changed(&pms5003_sensor);
}
PROCESS_END();
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2017, Peter Sjodin, KTH Royal Institute of Technology
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*
*
* -----------------------------------------------------------------
*
* Author : Peter Sjodin, KTH Royal Institute of Technology
* Created : 2017-04-21
*/
#ifndef PMS5003_SENSOR_H_
#define PMS5003_SENSOR_H_
#include "lib/sensors.h"
extern const struct sensors_sensor pms5003_sensor;
#define PMS5003_SENSOR_PM1 0
#define PMS5003_SENSOR_PM2_5 1
#define PMS5003_SENSOR_PM10 2
#define PMS5003_SENSOR_PM1_ATM 3
#define PMS5003_SENSOR_PM2_5_ATM 4
#define PMS5003_SENSOR_PM10_ATM 5
#define PMS5003_SENSOR_DB0_3 6
#define PMS5003_SENSOR_DB0_5 7
#define PMS5003_SENSOR_DB1 8
#define PMS5003_SENSOR_DB2_5 9
#define PMS5003_SENSOR_DB5 10
#define PMS5003_SENSOR_DB10 11
#define PMS5003_SENSOR_TIMESTAMP 12
#endif /* PMS5003_SENSOR_H_ */

507
dev/pms5003/pms5003.c Normal file
View File

@ -0,0 +1,507 @@
/*
* Copyright (c) 2017, Peter Sjodin, KTH Royal Institute of Technology
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
* Author : Peter Sjodin, KTH Royal Institute of Technology
* Created : 2017-04-21
*/
/*
* \file
* Driver for Planttower PMSX003 dust sensors
*/
#include "contiki.h"
#include "sys/etimer.h"
#include "sys/pt.h"
#include <stdio.h>
#include "i2c.h"
#include "watchdog.h"
#include "dev/leds.h"
#include "dev/rs232.h"
#include "dev/pms5003-arch.h"
#include "pms5003.h"
#include "lib/ringbuf.h"
/*
* Definitions for frames from PMSX003 sensors
*/
/* Two preamble bytes */
#define PRE1 0x42
#define PRE2 0x4d
/* Valid values for body length field */
#define PMSMINBODYLEN 20
#define PMSMAXBODYLEN 28
/* Buffer holds frame body plus preamble (two bytes)
* and length field (two bytes) */
#define PMSBUFFER (PMSMAXBODYLEN + 4)
/* Frame assembly statistics */
static uint32_t invalid_frames, valid_frames;
/* Sensor configured on? */
static uint8_t configured_on = 0;
/* Last readings of sensor data */
static uint16_t PM1, PM2_5, PM10;
static uint16_t PM1_ATM, PM2_5_ATM, PM10_ATM;
static uint16_t DB0_3, DB0_5, DB1, DB2_5, DB5, DB10;
/* Time when last sensor data was read, in clock_seconds()*/
static unsigned long timestamp = 0;
#if PMS_SERIAL_UART
#if (PMS_BUFSIZE & (PMS_BUFSIZE - 1)) != 0
#error PMS5003_CONF_UART_BUFSIZE must be a power of two (i.e., 1, 2, 4, 8, 16, 32, 64, ...).
#endif /* PMS_BUFSIZE */
/* Ring buffer for storing input from uart */
static struct ringbuf rxbuf;
static uint8_t rxbuf_data[PMS_BUFSIZE];
static int uart_input_byte(unsigned char);
#endif /* PMS_SERIAL_UART */
static struct pms_config {
unsigned sample_period; /* Time between samples (sec) */
unsigned warmup_interval; /* Warmup time (sec) */
} pms_config;
/*---------------------------------------------------------------------------*/
#if PMS_SERIAL_UART
PROCESS(pms5003_uart_process, "PMS5003/UART dust sensor process");
#endif /* PMS_SERIAL_UART */
PROCESS(pms5003_timer_process, "PMS5003 periodic dust sensor process");
/*---------------------------------------------------------------------------*/
/**
* Initialize. Create event, and start timer-driven process.
* If UART enabled, also install UART callback function and
* start PMS frame assembly process.
*/
void
pms5003_init()
{
pms5003_config_sample_period(PMS_SAMPLE_PERIOD);
pms5003_config_warmup_interval(PMS_WARMUP_INTERVAL);
pms5003_event = process_alloc_event();
process_start(&pms5003_timer_process, NULL);
#if PMS_SERIAL_UART
ringbuf_init(&rxbuf, rxbuf_data, sizeof(rxbuf_data));
rs232_set_input(PMS_UART_PORT, uart_input_byte);
process_start(&pms5003_uart_process, NULL);
#endif /* PMS_SERIAL_UART */
configured_on = 1;
#ifdef DEBUG
printf("PMS5003: UART %d, I2C %d, sample period %d, warmup interval %d\n",
PMS_SERIAL_UART, PMS_SERIAL_I2C,
pms_config.sample_period, pms_config.warmup_interval);
#endif /* DEBUG */
}
/*---------------------------------------------------------------------------*/
/**
* Sensor API for PMS5003
*/
void
pms5003_off()
{
pms5003_set_standby_mode(STANDBY_MODE_ON);
configured_on = 0;
}
uint16_t
pms5003_pm1()
{
return PM1;
}
uint16_t
pms5003_pm2_5()
{
return PM2_5;
}
uint16_t
pms5003_pm10()
{
return PM10;
}
uint16_t
pms5003_pm1_atm()
{
return PM1_ATM;
}
uint16_t
pms5003_pm2_5_atm()
{
return PM2_5_ATM;
}
uint16_t
pms5003_pm10_atm()
{
return PM10_ATM;
}
uint16_t
pms5003_db0_3()
{
return DB0_3;
}
uint16_t
pms5003_db0_5()
{
return DB0_5;
}
uint16_t
pms5003_db1()
{
return DB1;
}
uint16_t
pms5003_db2_5()
{
return DB2_5;
}
uint16_t
pms5003_db5()
{
return DB5;
}
uint16_t
pms5003_db10()
{
return DB10;
}
uint32_t
pms5003_timestamp()
{
return timestamp;
}
uint32_t
pms5003_valid_frames()
{
return valid_frames;
}
uint32_t
pms5003_invalid_frames()
{
return invalid_frames;
}
void
pms5003_config_sample_period(unsigned int sample_period) {
pms_config.sample_period = sample_period;
}
void
pms5003_config_warmup_interval(unsigned int warmup_interval) {
pms_config.warmup_interval = warmup_interval;
}
unsigned
pms5003_get_sample_period(void) {
return pms_config.sample_period;
}
unsigned
pms5003_get_warmup_interval(void) {
return pms_config.warmup_interval;
}
/*---------------------------------------------------------------------------*/
/**
* Check if it is time to put sensor in standby mode.
* The condition is that the time from when last sample was taken until the
* next is due, is greater than the time it takes to wake up the sensor.
*/
static int
timetosleep(void) {
return (timestamp + pms_config.sample_period >
clock_seconds() + pms_config.warmup_interval);
}
/*---------------------------------------------------------------------------*/
/**
* Check if it is time to get the next sample.
*/
static int
timetoread(void) {
return (clock_seconds() - timestamp >= pms_config.sample_period);
}
/*---------------------------------------------------------------------------*/
/**
* Validate frame by checking preamble, length field and checksum.
* Return 0 if invalid frame, otherwise 1.
*/
static int
check_pmsframe(uint8_t *buf)
{
int sum, pmssum;
int i;
int len;
if(buf[0] != PRE1 || buf[1] != PRE2) {
return 0;
}
/* len is length of frame not including preamble and checksum */
len = (buf[2] << 8) + buf[3];
if(len < PMSMINBODYLEN || len > PMSMAXBODYLEN) {
return 0;
}
/* Sum data bytewise, including preamble but excluding checksum */
sum = 0;
for(i = 0; i < len + 2; i++) {
sum += buf[i];
}
/* Compare with received checksum last in frame*/
pmssum = (buf[len + 2] << 8) + buf[len + 3];
return pmssum == sum;
}
/*---------------------------------------------------------------------------*/
#ifdef DEBUG
static void
printpm()
{
printf("PMS frames: valid %lu, invalid %lu\n",
valid_frames, invalid_frames);
printf("PM1 = %04d, PM2.5 = %04d, PM10 = %04d\n", PM1, PM2_5, PM10);
printf("PM1_ATM = %04d, PM2.5_ATM = %04d, PM10_ATM = %04d\n",
PM1_ATM, PM2_5_ATM, PM10_ATM);
printf(" DB0_3 = %04d, DB0_5 = %04d, DB1 = %04d, DB2_5 = %04d, DB5 = %04d, DB10 = %04d\n",
DB0_3, DB0_5, DB1, DB2_5, DB5, DB10);
}
#endif /* DEBUG */
/*---------------------------------------------------------------------------*/
/**
* Frame received from PMS sensor. Validate and update sensor data.
* Return 1 if valid frame, otherwise 0
*/
static int
pmsframe(uint8_t *buf)
{
int len;
if(check_pmsframe(buf)) {
timestamp = clock_seconds();
valid_frames++;
/* Update sensor readings */
PM1 = (buf[4] << 8) | buf[5];
PM2_5 = (buf[6] << 8) | buf[7];
PM10 = (buf[8] << 8) | buf[9];
PM1_ATM = (buf[10] << 8) | buf[11];
PM2_5_ATM = (buf[12] << 8) | buf[13];
PM10_ATM = (buf[14] << 8) | buf[15];
/* Not all Plantower sensors report dust size bins.
* PMS3003 (frame length 20) doesn't.
* PMS5003 (frame length 28) does.
* Use length field to detect if the frame has size bins.
*/
len = (buf[2] << 8) + buf[3];
if(len == 28) {
DB0_3 = (buf[16] << 8) | buf[17];
DB0_5 = (buf[18] << 8) | buf[19];
DB1 = (buf[20] << 8) | buf[21];
DB2_5 = (buf[22] << 8) | buf[23];
DB5 = (buf[24] << 8) | buf[25];
DB10 = (buf[26] << 8) | buf[27];
}
#ifdef DEBUG
printpm();
#endif /* DEBUG */
return 1;
} else {
invalid_frames++;
#ifdef DEBUG
printpm();
#endif /* DEBUG */
return 0;
}
}
/*---------------------------------------------------------------------------*/
#if PMS_SERIAL_UART
/**
* State machine for assembling PMS5003 frames
* from uart. Use protothread for state machine.
*/
static
PT_THREAD(pms5003_uart_fsm_pt(struct pt *pt, uint8_t data)) {
static uint8_t buf[PMSBUFFER], *bufp;
static int remain;
PT_BEGIN(pt);
bufp = buf;
if(data != PRE1) {
PT_RESTART(pt);
}
*bufp++ = data;
PT_YIELD(pt);
if(data != PRE2) {
PT_RESTART(pt);
}
*bufp++ = data;
/* Found preamble. Then get length (two bytes) */
PT_YIELD(pt);
*bufp++ = data;
PT_YIELD(pt);
*bufp++ = data;
/* Get body length -- no of bytes that remain */
remain = (buf[2] << 8) + buf[3];
if(remain < PMSMINBODYLEN || remain > PMSMAXBODYLEN) {
invalid_frames++;
} else {
while(remain--) {
PT_YIELD(pt);
*bufp++ = data;
}
/* We have a frame! */
/* Is it time for next reading? Otherwise ignore. */
if(timetoread()) {
/* Check frame and update sensor readings */
if(pmsframe(buf)) {
/* Tell other processes there is new data */
(void)process_post(PROCESS_BROADCAST, pms5003_event, NULL);
/* Put sensor in standby mode? */
if(timetosleep()) {
pms5003_set_standby_mode(STANDBY_MODE_ON);
}
}
}
}
PT_RESTART(pt);
PT_END(pt);
}
/*---------------------------------------------------------------------------*/
/**
* UART callback function.
*/
static int
uart_input_byte(unsigned char c)
{
/* Add char to buffer. Unlike serial line input, ignore buffer overflow */
(void)ringbuf_put(&rxbuf, c);
/* Wake up consumer process */
process_poll(&pms5003_uart_process);
return 1;
}
/*---------------------------------------------------------------------------*/
static struct pt uart_pt;
/**
* Consumer thread for UART process. Pick up data from input buffer and
* dispatch to FSM for frame assembly.
*/
PROCESS_THREAD(pms5003_uart_process, ev, data)
{
PROCESS_BEGIN();
PT_INIT(&uart_pt);
while(1) {
int c = ringbuf_get(&rxbuf);
if(c == -1) {
PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL);
} else if(configured_on) {
pms5003_uart_fsm_pt(&uart_pt, c);
}
}
PROCESS_END();
}
#endif /* PMS_SERIAL_UART */
/*---------------------------------------------------------------------------*/
/**
* Timer thread: duty-cycle sensor. Toggle between idle and active mode.
* For I2C, also read data when it is due.
*/
PROCESS_THREAD(pms5003_timer_process, ev, data)
{
static struct etimer pmstimer;
static uint8_t standbymode;
PROCESS_BEGIN();
etimer_set(&pmstimer, CLOCK_SECOND * PMS_PROCESS_PERIOD);
/* Pretend there is a fresh reading, to postpone the first
* reading for one cycle
*/
timestamp = clock_seconds();
/* Put sensor in standby, if there is enough time */
if(timetosleep())
pms5003_set_standby_mode(STANDBY_MODE_ON);
else
pms5003_set_standby_mode(STANDBY_MODE_OFF);
pms5003_event = process_alloc_event();
/* Main loop */
while(1) {
PROCESS_YIELD();
if(!configured_on) {
continue;
}
if((ev == PROCESS_EVENT_TIMER) && (data == &pmstimer)) {
standbymode = pms5003_get_standby_mode();
if(standbymode == STANDBY_MODE_OFF) {
#if PMS_SERIAL_I2C
static uint8_t buf[PMSBUFFER];
/* Read data over I2C if it is time */
if(timetoread()) {
if(pms5003_i2c_probe()) {
leds_on(LEDS_RED);
i2c_read_mem(I2C_PMS5003_ADDR, 0, buf, PMSBUFFER);
/* Check frame and update sensor readings */
if(pmsframe(buf)) {
/* Tell other processes there is new data */
if(process_post(PROCESS_BROADCAST, pms5003_event, NULL) == PROCESS_ERR_OK) {
PROCESS_WAIT_EVENT_UNTIL(ev == pms5003_event);
}
/* Put sensor in standby, if there is enough time */
if(timetosleep()) {
pms5003_set_standby_mode(STANDBY_MODE_ON);
}
}
}
}
#endif /* PMS_SERIAL_I2C */
} else if(standbymode == STANDBY_MODE_ON) {
/* Time to warm up sensor for next reading? */
if(!timetosleep()) {
pms5003_set_standby_mode(STANDBY_MODE_OFF);
}
}
etimer_reset(&pmstimer);
}
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/

116
dev/pms5003/pms5003.h Normal file
View File

@ -0,0 +1,116 @@
/*
* Copyright (c) 2017, Peter Sjodin, KTH Royal Institute of Technology
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*
* Author : Peter Sjodin, KTH Royal Institute of Technology
* Created : 2017-04-21
*/
#ifndef PMS5003_H
#define PMS5003_H
/* How often sensor process runs (sec) -- defines resolution
* of warmup time and sample period
*/
#ifdef PMS5003_CONF_PROCESS_PERIOD
#define PMS_PROCESS_PERIOD PMS5003_CONF_PROCESS_PERIOD
#else
#define PMS_PROCESS_PERIOD 5
#endif /* PMS5003_CONF_PROCESS_PERIOD */
/* Default sample period - how often sensor data is collected (sec) */
#ifdef PMS5003_CONF_SAMPLE_PERIOD
#define PMS_SAMPLE_PERIOD PMS5003_CONF_SAMPLE_PERIOD
#else
#define PMS_SAMPLE_PERIOD 60
#endif /* PMS5003_CONF_SAMPLE_PERIOD */
/* Default warmup time before sensor data can be read (sec) */
#ifdef PMS5003_CONF_WARMUP_INTERVAL
#define PMS_WARMUP_INTERVAL PMS5003_CONF_WARMUP_INTERVAL
#else
#define PMS_WARMUP_INTERVAL 30
#endif /* PMS5003_WARMUP_INTERVAL */
/* Use I2C interface? */
#ifdef PMS5003_CONF_SERIAL_I2C
#define PMS_SERIAL_I2C PMS5003_CONF_SERIAL_I2C
#else
#define PMS_SERIAL_I2C 1
#endif /* PMS_CONF_SERIAL_I2C */
/* Use UART interface? */
#ifdef PMS5003_CONF_SERIAL_UART
#define PMS_SERIAL_UART PMS5003_CONF_SERIAL_UART
#else
#define PMS_SERIAL_UART 1
#endif /* PMS_CONF_SERIAL_UART */
#if PMS_SERIAL_UART
/* What buffer size to use */
#ifdef PMS5003_CONF_UART_BUFSIZE
#define PMS_BUFSIZE PMS5003_CONF_UART_BUFSIZE
#else /* PMS5003_CONF_UART_BUFSIZE */
#define PMS_BUFSIZE 128
#endif /* PMS5003_CONF_UART_BUFSIZE */
/* What UART port to use */
#ifdef PMS5003_CONF_UART_PORT
#define PMS_UART_PORT PMS5003_CONF_UART_PORT
#else
#define PMS_UART_PORT RS232_PORT_0
#endif /* PMS_CONF_UART_RS232_PORT */
#endif /* PMS_SERIAL_UART */
/* Event to signal presence of new sensor data */
process_event_t pms5003_event;
void pms5003_init();
void pms5003_off();
uint16_t pms5003_pm1();
uint16_t pms5003_pm2_5();
uint16_t pms5003_pm10();
uint16_t pms5003_pm1_atm();
uint16_t pms5003_pm2_5_atm();
uint16_t pms5003_pm10_atm();
uint16_t pms5003_db0_3();
uint16_t pms5003_db0_5();
uint16_t pms5003_db1();
uint16_t pms5003_db2_5();
uint16_t pms5003_db5();
uint16_t pms5003_db10();
uint32_t pms5003_timestamp();
void pms5003_config_sample_period(unsigned int);
void pms5003_config_warmup_interval(unsigned int);
unsigned pms5003_get_sample_period(void);
unsigned pms5003_get_warmup_interval(void);
#endif /* PMS5003_H */

View File

@ -230,5 +230,11 @@ i2c_probe(void)
probed |= I2C_BME280;
print_delim(p++, "BME280", del);
}
watchdog_periodic();
if(!i2c_start(I2C_PMS5003_ADDR)) {
i2c_stop();
probed |= I2C_PMS5003;
print_delim(p++, "PMS5003", del);
}
return probed;
}

View File

@ -41,12 +41,14 @@
#define I2C_AT24MAC_ADDR 0xB0 /* EUI64 ADDR */
#define I2C_SHT2X_ADDR (0x40 << 1) /* SHT2X ADDR */
#define I2C_BME280_ADDR (0x77 << 1) /* Alternative 0x76 */
#define I2C_PMS5003_ADDR (0x12 << 1) /* PM sensor */
/* Here we define a enumration for devices */
#define I2C_AT24MAC (1<<0)
#define I2C_SHT2X (1<<1)
#define I2C_CO2SA (1<<2) /* Sense-Air CO2 */
#define I2C_BME280 (1<<3)
#define I2C_PMS5003 (1<<4)
#define I2C_READ 1
#define I2C_WRITE 0

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2017, Peter Sjodin, KTH Royal Institute of Technology
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*
* Author : Peter Sjodin psj@kth.se
* Created : 2017-01-06
*/
#include "contiki.h"
#include "i2c.h"
#include "watchdog.h"
#include "dev/pms5003/pms5003.h"
#include "pms5003-arch.h"
static uint8_t standbymode;
/*---------------------------------------------------------------------------*/
/*
* Configure low power standby mode (PIN3, SET)
*/
void
pms5003_set_standby_mode(uint8_t mode) {
SET_PMS_DDR |= (1 << PMS_SET);
if (mode == STANDBY_MODE_OFF)
SET_PMS_PORT |= (1 << PMS_SET);
else if (mode == STANDBY_MODE_ON)
SET_PMS_PORT &= ~(1 << PMS_SET);
standbymode = mode;
}
/*---------------------------------------------------------------------------*/
/*
* Return current standby mode
*/
uint8_t
pms5003_get_standby_mode(void) {
return standbymode;
}
/*---------------------------------------------------------------------------*/
/*
* Probe I2C bus for PMS5003 device
*/
uint8_t
pms5003_i2c_probe(void) {
watchdog_periodic();
if(!i2c_start(I2C_PMS5003_ADDR)) {
i2c_stop();
i2c_probed |= I2C_PMS5003;
return 1;
}
i2c_probed &= ~I2C_PMS5003;
return 0;
}
/*---------------------------------------------------------------------------*/

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2017, Peter Sjodin, KTH Royal Institute of Technology
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*
* Author : Peter Sjodin psj@kth.se
* Created : 2017-01-06
*/
#ifndef PMS5003_ARCH_H
#define PMS5003_ARCH_H
/**
* \file
* Architecture-specific definitions for the Plantower PMS X003 dust sensors for avr-rss2
*/
/* AVR configuration for controlling dust sensor */
#define SET_PMS_DDR DDRB /* Data Direction Register: Port B */
#define SET_PMS_PORT PORTB /* Serial Peripheral Interface */
#define PMS_SET 2 /* PD1: OW2_PIN, Chip Select */
/* Duty cycle mode -- STANDBY_MODE_OFF means device is active, etc. */
#define STANDBY_MODE_OFF 0
#define STANDBY_MODE_ON 1
extern void pms5003_set_standby_mode(uint8_t mode);
extern uint8_t pms5003_get_standby_mode(void);
extern uint8_t pms5003_i2c_probe(void);
#endif /* PMS5003_ARCH_H */