Merge pull request #10458 from MichelRottleuthner/pr_driver_sds011
add driver for SDS011 active laser dust sensor
This commit is contained in:
commit
805efb69c5
@ -394,6 +394,10 @@ ifneq (,$(filter sdcard_spi,$(USEMODULE)))
|
||||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter sds011,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_uart
|
||||
endif
|
||||
|
||||
ifneq (,$(filter servo,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_pwm
|
||||
endif
|
||||
|
||||
@ -285,3 +285,7 @@ endif
|
||||
ifneq (,$(filter xbee,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/xbee/include
|
||||
endif
|
||||
|
||||
ifneq (,$(filter sds011,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/sds011/include
|
||||
endif
|
||||
|
||||
@ -101,6 +101,7 @@ enum {
|
||||
SAUL_SENSE_RSSI = 0x93, /**< sensor: RSSI */
|
||||
SAUL_SENSE_CHARGE = 0x94, /**< sensor: coulomb counter */
|
||||
SAUL_SENSE_CURRENT = 0x95, /**< sensor: ammeter */
|
||||
SAUL_SENSE_PM = 0x96, /**< sensor: particulate matter */
|
||||
SAUL_CLASS_ANY = 0xff /**< any device - wildcard */
|
||||
/* extend this list as needed... */
|
||||
};
|
||||
|
||||
317
drivers/include/sds011.h
Normal file
317
drivers/include/sds011.h
Normal file
@ -0,0 +1,317 @@
|
||||
/*
|
||||
* Copyright (C) 2018 HAW-Hamburg
|
||||
*
|
||||
* 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 drivers_sds011 SDS011 Laser Dust Sensor
|
||||
* @ingroup drivers_sensors
|
||||
* @brief Driver SDS011 Laser Dust Sensor
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Interface for controlling SDS011 Laser Dust Sensor
|
||||
*
|
||||
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
|
||||
*/
|
||||
|
||||
#ifndef SDS011_H
|
||||
#define SDS011_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "periph/gpio.h"
|
||||
#include "periph/uart.h"
|
||||
#include "mutex.h"
|
||||
#include "sds011_internal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SDS011 wildcard address to address all devices
|
||||
*/
|
||||
#define SDS011_DEVID_WILDCARD (0xFFFF)
|
||||
|
||||
/**
|
||||
* @brief Named return values
|
||||
*/
|
||||
enum {
|
||||
SDS011_OK, /**< all good */
|
||||
SDS011_INVALID_RESPONSE, /**< invalid response */
|
||||
SDS011_INVALID_CHKSUM, /**< invalid checksum */
|
||||
SDS011_ERROR, /**< internal error */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Report mode of the SDS011 sensor
|
||||
*/
|
||||
typedef enum sds011_reporting_mode {
|
||||
SDS011_RMODE_ACTIVE = 0, /**< continuously reporting values */
|
||||
SDS011_RMODE_QUERY = 1, /**< sensor needs to be queried */
|
||||
} sds011_reporting_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Work/sleep mode of the SDS011 sensor
|
||||
*/
|
||||
typedef enum sds011_working_mode {
|
||||
SDS011_WMODE_SLEEP = 0, /**< laser & fan are disabled */
|
||||
SDS011_WMODE_WORK = 1, /**< laser & fan are enabled */
|
||||
} sds011_working_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Configuration parameters for SDS011 Laser Dust Sensor
|
||||
*/
|
||||
typedef struct {
|
||||
uart_t uart; /**< UART device the sensor is connected to */
|
||||
gpio_t pwr_pin; /**< GPIO pin for disabling supply voltage for the sensor */
|
||||
uint16_t dev_id; /**< Unique sensor device ID */
|
||||
bool pwr_ah; /**< Logic level of the power pin (true for active high) */
|
||||
} sds011_params_t;
|
||||
|
||||
/**
|
||||
* @brief Data type for storing SDS011 sensor readings
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t pm_2_5; /**< Particulate Matter 2.5 concentration [0.1µg/m^3] */
|
||||
uint16_t pm_10; /**< Particulate Matter 10 concentration [0.1µg/m^3] */
|
||||
} sds011_data_t;
|
||||
|
||||
/**
|
||||
* @brief callback for measurements actively reported by the SDS011 sensor
|
||||
*/
|
||||
typedef void (*sds011_callback_t)(sds011_data_t *data, void *ctx);
|
||||
|
||||
/**
|
||||
* @brief Device descriptor definition for SDS011 Laser Dust Sensor
|
||||
*/
|
||||
typedef struct {
|
||||
sds011_params_t params; /**< parameters for SDS011 device */
|
||||
mutex_t dev_lock; /**< mutex to synchronize device access */
|
||||
mutex_t cb_lock; /**< mutex to synchronize callbacks */
|
||||
sds011_callback_t cb; /**< callback deliver values async */
|
||||
void *cbctx; /**< user context for the callback */
|
||||
uint16_t checksum; /**< iteratively calculated checksum */
|
||||
uint8_t rx_mem[SDS011_FRAME_RECV_LEN]; /**< receive buffer */
|
||||
uint8_t pos; /**< receive buffer position counter */
|
||||
} sds011_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize SDS011 Laser Dust Sensor
|
||||
*
|
||||
* @param[out] dev device descriptor
|
||||
* @param[in] params device configuration
|
||||
*
|
||||
* @pre @p dev != NULL
|
||||
* @pre @p params != NULL
|
||||
*
|
||||
* @return SDS011_OK on success
|
||||
* @return SDS011_ERROR on error
|
||||
*/
|
||||
int sds011_init(sds011_t *dev, const sds011_params_t *params);
|
||||
|
||||
/**
|
||||
* @brief Enable power supply of SDS011 laser dust sensor
|
||||
*
|
||||
* @param[in] dev device descriptor
|
||||
*
|
||||
* @pre @p dev != NULL
|
||||
*/
|
||||
void sds011_power_on(const sds011_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Disable power supply of SDS011 laser dust sensor
|
||||
*
|
||||
* @param[in] dev device descriptor
|
||||
*
|
||||
* @pre @p dev != NULL
|
||||
*/
|
||||
void sds011_power_off(const sds011_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Read measurement values from SDS011 laser dust sensor
|
||||
*
|
||||
* @param[in] dev device descriptor
|
||||
* @param[out] data pointer for storing the values
|
||||
*
|
||||
* @return SDS011_OK on success
|
||||
* @return SDS011_INVALID_RESPONSE when response doesn't match the request
|
||||
* @return SDS011_INVALID_CHKSUM when response checksum is invalid
|
||||
* @return SDS011_ERROR when other error occured
|
||||
*
|
||||
* @pre @p dev != NULL
|
||||
* @pre @p data != NULL
|
||||
*/
|
||||
int sds011_read(sds011_t *dev, sds011_data_t *data);
|
||||
|
||||
/**
|
||||
* @brief Register measurement callback
|
||||
*
|
||||
* The registered callback is executed when new measurements were
|
||||
* received by the sensor. This function should be used together with
|
||||
* active reporting mode of the sensor that automatically sends new
|
||||
* measurements periodically (factory default setting of the sensor).
|
||||
*
|
||||
* @param[in] dev device descriptor
|
||||
* @param[in] cb function to be called for new values (NULL for disable)
|
||||
* @param[in] ctx context pointer that will be handed to the callback
|
||||
*
|
||||
* @return SDS011_OK on success
|
||||
* @return SDS011_ERROR when error occured
|
||||
*
|
||||
* @pre @p dev != NULL
|
||||
*/
|
||||
int sds011_register_callback(sds011_t *dev, sds011_callback_t cb, void *ctx);
|
||||
|
||||
/**
|
||||
* @brief Get the current reporting mode of the sensor
|
||||
*
|
||||
* @param[in] dev device descriptor
|
||||
* @param[out] mode SDS011_RMODE_ACTIVE: continuously report new values
|
||||
* SDS011_RMODE_QUERY: new values need to be requested
|
||||
*
|
||||
* @return SDS011_OK on success
|
||||
* @return SDS011_INVALID_RESPONSE when response doesn't match the request
|
||||
* @return SDS011_INVALID_CHKSUM when response checksum is invalid
|
||||
* @return SDS011_ERROR when other error occured
|
||||
*
|
||||
* @pre @p dev != NULL
|
||||
*/
|
||||
int sds011_get_reporting_mode(sds011_t *dev, sds011_reporting_mode_t *mode);
|
||||
|
||||
/**
|
||||
* @brief Set the reporting mode of the sensor
|
||||
*
|
||||
* @param[in] dev device descriptor
|
||||
* @param[in] mode SDS011_RMODE_ACTIVE: continuously report new values
|
||||
* SDS011_RMODE_QUERY: new values need to be requested
|
||||
*
|
||||
* @note This setting is persistent even after a full power-cycle!
|
||||
* Factory default is SDS011_RMODE_ACTIVE
|
||||
*
|
||||
* @return SDS011_OK on success
|
||||
* @return SDS011_INVALID_RESPONSE when response doesn't match the request
|
||||
* @return SDS011_INVALID_CHKSUM when response checksum is invalid
|
||||
* @return SDS011_ERROR when other error occured
|
||||
*
|
||||
* @pre @p dev != NULL
|
||||
*/
|
||||
int sds011_set_reporting_mode(sds011_t *dev, sds011_reporting_mode_t mode);
|
||||
|
||||
/**
|
||||
* @brief Get current working mode of the sensor
|
||||
*
|
||||
* @param[in] dev device descriptor
|
||||
* @param[out] mode SDS011_WMODE_SLEEP: sensor is in sleep mode (~3 mA)
|
||||
* SDS011_WMODE_WORK: sensor is in working mode (~65 mA)
|
||||
*
|
||||
* @return SDS011_OK on success
|
||||
* @return SDS011_INVALID_RESPONSE when response doesn't match the request
|
||||
* @return SDS011_INVALID_CHKSUM when response checksum is invalid
|
||||
* @return SDS011_ERROR when other error occured
|
||||
*
|
||||
* @pre @p dev != NULL
|
||||
*/
|
||||
int sds011_get_working_mode(sds011_t *dev, sds011_working_mode_t *mode);
|
||||
|
||||
/**
|
||||
* @brief Set working mode of the sensor
|
||||
*
|
||||
* @param[in] dev device descriptor
|
||||
* @param[in] mode SDS011_WMODE_SLEEP: put to sleep mode (~3 mA)
|
||||
* SDS011_WMODE_WORK: put to working mode (~65 mA)
|
||||
*
|
||||
* @return SDS011_OK on success
|
||||
* @return SDS011_INVALID_RESPONSE when response doesn't match the request
|
||||
* @return SDS011_INVALID_CHKSUM when response checksum is invalid
|
||||
* @return SDS011_ERROR when other error occured
|
||||
*
|
||||
* @pre @p dev != NULL
|
||||
*/
|
||||
int sds011_set_working_mode(sds011_t *dev, sds011_working_mode_t mode);
|
||||
|
||||
/**
|
||||
* @brief Get current working period of the sensor
|
||||
*
|
||||
* @param[in] dev device descriptor
|
||||
* @param[out] minutes working period of the sensor in minutes
|
||||
*
|
||||
* @return SDS011_OK on success
|
||||
* @return SDS011_INVALID_RESPONSE when response doesn't match the request
|
||||
* @return SDS011_INVALID_CHKSUM when response checksum is invalid
|
||||
* @return SDS011_ERROR when other error occured
|
||||
*
|
||||
* @pre @p dev != NULL
|
||||
*/
|
||||
int sds011_get_working_period(sds011_t *dev, uint8_t *minutes);
|
||||
|
||||
/**
|
||||
* @brief Set working period of the sensor
|
||||
*
|
||||
* @param[in] dev device descriptor
|
||||
* @param[in] minutes 0 - 30 new working period of the sensor in minutes
|
||||
* 0 for coninuous reporting mode
|
||||
* 1 - 30 for a period of @p minutes
|
||||
*
|
||||
* @note For values greater than 0, the active duration (fan, laser enabled)
|
||||
* is always fixed to 30 seconds, while the sleep duration is adjusted
|
||||
* to give an overall period of @p minutes.
|
||||
*
|
||||
* @return SDS011_OK on success
|
||||
* @return SDS011_INVALID_RESPONSE when response doesn't match the request
|
||||
* @return SDS011_INVALID_CHKSUM when response checksum is invalid
|
||||
* @return SDS011_ERROR when other error occured
|
||||
*
|
||||
* @pre @p dev != NULL
|
||||
*/
|
||||
int sds011_set_working_period(sds011_t *dev, uint8_t minutes);
|
||||
|
||||
/**
|
||||
* @brief Get firmware version of the sensor
|
||||
*
|
||||
* @param[in] dev device descriptor
|
||||
* @param[out] year year of the firmware version
|
||||
* @param[out] mon month of the firmware version
|
||||
* @param[out] day day of the firmware version
|
||||
*
|
||||
* @return SDS011_OK on success
|
||||
* @return SDS011_INVALID_RESPONSE when response doesn't match the request
|
||||
* @return SDS011_INVALID_CHKSUM when response checksum is invalid
|
||||
* @return SDS011_ERROR when other error occured
|
||||
*
|
||||
* @pre @p dev != NULL
|
||||
* @pre @p year != NULL
|
||||
* @pre @p mon != NULL
|
||||
* @pre @p day != NULL
|
||||
*/
|
||||
int sds011_get_fw_version(sds011_t *dev, uint8_t *year, uint8_t *mon, uint8_t *day);
|
||||
|
||||
/**
|
||||
* @brief Set device ID of the sensor
|
||||
*
|
||||
* @param[in] dev device descriptor
|
||||
* @param[in] sens_dev_id ID as one number (ID byte 1 MSB, ID byte 2 LSB)
|
||||
*
|
||||
* @note This setting is persistent even after a full power-cycle!
|
||||
* Factory default is an individual ID which is printed next to the
|
||||
* serial number barcode. For the number xxxx-abab the ID is 0xabab.
|
||||
*
|
||||
* @return SDS011_OK on success
|
||||
* @return SDS011_INVALID_RESPONSE when response doesn't match the request
|
||||
* @return SDS011_INVALID_CHKSUM when response checksum is invalid
|
||||
* @return SDS011_ERROR when other error occured
|
||||
*
|
||||
* @pre @p dev != NULL
|
||||
*/
|
||||
int sds011_set_dev_id(sds011_t *dev, uint16_t sens_dev_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SDS011_H */
|
||||
/** @} */
|
||||
@ -57,8 +57,9 @@ const char *saul_class_to_str(const uint8_t class_id)
|
||||
case SAUL_SENSE_RSSI: return "SENSE_RSSI";
|
||||
case SAUL_SENSE_CHARGE: return "SENSE_CHARGE";
|
||||
case SAUL_SENSE_CURRENT: return "SENSE_CURRENT";
|
||||
case SAUL_CLASS_ANY: return "CLASS_ANY";
|
||||
case SAUL_SENSE_OCCUP: return "SENSE_OCCUP";
|
||||
case SAUL_SENSE_PM: return "SENSE_PM";
|
||||
case SAUL_CLASS_ANY: return "CLASS_ANY";
|
||||
default: return "CLASS_UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
1
drivers/sds011/Makefile
Normal file
1
drivers/sds011/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
100
drivers/sds011/include/sds011_internal.h
Normal file
100
drivers/sds011/include/sds011_internal.h
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2018 HAW-Hamburg
|
||||
*
|
||||
* 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_sds011
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Internal constants etc. for the SDS011 laser dust sensor
|
||||
*
|
||||
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
|
||||
* @}
|
||||
*/
|
||||
#ifndef SDS011_INTERNAL_H
|
||||
#define SDS011_INTERNAL_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SDS011 baud rate
|
||||
*/
|
||||
#define SDS011_UART_BAUDRATE (9600U)
|
||||
|
||||
/**
|
||||
* @name SDS011 frame lengths
|
||||
* @{
|
||||
*/
|
||||
#define SDS011_FRAME_SEND_LEN (19U)
|
||||
#define SDS011_FRAME_RECV_LEN (10U)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name SDS011 command values
|
||||
* @{
|
||||
*/
|
||||
#define SDS011_CMD_DB1_SET_DR_MODE (2U)
|
||||
#define SDS011_CMD_DB1_QUERY_DATA (4U)
|
||||
#define SDS011_CMD_DB1_SET_DEV_ID (5U)
|
||||
#define SDS011_CMD_DB1_SET_SLEEP_WORK (6U)
|
||||
#define SDS011_CMD_DB1_CHECK_FIRMWARE (7U)
|
||||
#define SDS011_CMD_DB1_SET_WORK_PERIOD (8U)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name SDS011 command option values
|
||||
* @{
|
||||
*/
|
||||
#define SDS011_CMD_OPT_QUERY (0U)
|
||||
#define SDS011_CMD_OPT_SET (1U)
|
||||
#define SDS011_CMD_OPT_REPORT_ACTIVE (0U)
|
||||
#define SDS011_CMD_OPT_REPORT_QUERY (1U)
|
||||
#define SDS011_CMD_OPT_SLEEP (0U)
|
||||
#define SDS011_CMD_OPT_WORK (1U)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name SDS011 frame constants
|
||||
* @{
|
||||
*/
|
||||
#define SDS011_CMDID_QUERY (0xB4)
|
||||
#define SDS011_RCMDID_REPLY (0xC5)
|
||||
#define SDS011_RCMDID_DATA (0xC0)
|
||||
#define SDS011_FRAME_TAIL (0xAB)
|
||||
#define SDS011_FRAME_HEAD (0xAA)
|
||||
#define SDS011_FRAME_CSUM_MSK (0xFF)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name SDS011 frame value indexes
|
||||
* @{
|
||||
*/
|
||||
#define SDS011_FRAME_HEAD_IDX (0U)
|
||||
#define SDS011_CMDID_IDX (1U)
|
||||
#define SDS011_DB1_IDX (2U)
|
||||
#define SDS011_DB2_IDX (3U)
|
||||
#define SDS011_DB3_IDX (4U)
|
||||
#define SDS011_DB4_IDX (5U)
|
||||
#define SDS011_DB5_IDX (6U)
|
||||
#define SDS011_DB6_IDX (7U)
|
||||
#define SDS011_DEVID1_IDX (15U)
|
||||
#define SDS011_DEVID2_IDX (16U)
|
||||
#define SDS011_FRAME_SEND_TAIL_IDX (SDS011_FRAME_SEND_LEN - 1)
|
||||
#define SDS011_FRAME_RECV_TAIL_IDX (SDS011_FRAME_RECV_LEN - 1)
|
||||
#define SDS011_FRAME_SEND_CSUM_IDX (SDS011_FRAME_SEND_LEN - 2)
|
||||
#define SDS011_FRAME_RECV_CSUM_IDX (SDS011_FRAME_RECV_LEN - 2)
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SDS011_INTERNAL_H */
|
||||
/** @} */
|
||||
78
drivers/sds011/include/sds011_params.h
Normal file
78
drivers/sds011/include/sds011_params.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2018 HAW-Hamburg
|
||||
*
|
||||
* 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_sds011
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief SDS011 sensor specific configuration
|
||||
*
|
||||
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
|
||||
*/
|
||||
|
||||
#ifndef SDS011_PARAMS_H
|
||||
#define SDS011_PARAMS_H
|
||||
|
||||
#include "board.h"
|
||||
#include "periph/uart.h"
|
||||
#include "saul_reg.h"
|
||||
#include "sds011.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Set default configuration parameters for the SDS011 driver
|
||||
* @{
|
||||
*/
|
||||
#ifndef SDS011_PARAM_UART_DEV
|
||||
#define SDS011_PARAM_UART_DEV (UART_DEV(1))
|
||||
#endif
|
||||
#ifndef SDS011_PARAM_PWR_PIN
|
||||
#define SDS011_PARAM_PWR_PIN (GPIO_PIN(0, 0))
|
||||
#endif
|
||||
#ifndef SDS011_PARAM_PWR_PIN_AH
|
||||
#define SDS011_PARAM_PWR_PIN_AH (true)
|
||||
#endif
|
||||
|
||||
#ifndef SDS011_PARAMS
|
||||
#define SDS011_PARAMS { .uart = SDS011_PARAM_UART_DEV, \
|
||||
.pwr_pin = SDS011_PARAM_PWR_PIN, \
|
||||
.pwr_ah = SDS011_PARAM_PWR_PIN_AH, \
|
||||
.dev_id = SDS011_DEVID_WILDCARD }
|
||||
#endif
|
||||
|
||||
#ifndef SDS011_SAUL_INFO
|
||||
#define SDS011_SAUL_INFO { .name = "SDS011" }
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief SDS011 configuration
|
||||
*/
|
||||
static const sds011_params_t sds011_params[] =
|
||||
{
|
||||
SDS011_PARAMS
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Allocate and configure entries to the SAUL registry
|
||||
*/
|
||||
saul_reg_info_t sds011_saul_info[] =
|
||||
{
|
||||
SDS011_SAUL_INFO
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SDS011_PARAMS_H */
|
||||
/** @} */
|
||||
384
drivers/sds011/sds011.c
Normal file
384
drivers/sds011/sds011.c
Normal file
@ -0,0 +1,384 @@
|
||||
/*
|
||||
* Copyright (C) 2018 HAW-Hamburg
|
||||
*
|
||||
* 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_sds011
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief SDS011 Laser Dust Sensor driver implementation
|
||||
*
|
||||
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "sds011.h"
|
||||
#include "periph/uart.h"
|
||||
|
||||
/**
|
||||
* @brief UART receive callback
|
||||
*
|
||||
* @param[in] arg Context value previously handed to the uart_init call
|
||||
* @param[in] data single byte received over UART
|
||||
*/
|
||||
static void _rx_cb(void *arg, uint8_t data)
|
||||
{
|
||||
sds011_t *dev = (sds011_t*)arg;
|
||||
|
||||
/* frame MUST start with HEAD byte and the buffer must be cleared
|
||||
before writing to it again */
|
||||
if (((dev->pos == 0) && (data != SDS011_FRAME_HEAD)) ||
|
||||
(dev->pos == SDS011_FRAME_RECV_LEN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dev->rx_mem[dev->pos] = data;
|
||||
|
||||
if ((dev->pos >= SDS011_DB1_IDX) &&
|
||||
(dev->pos < SDS011_FRAME_RECV_CSUM_IDX)) {
|
||||
dev->checksum += data;
|
||||
}
|
||||
else if ((dev->pos == SDS011_FRAME_RECV_LEN - 1) &&
|
||||
(dev->rx_mem[SDS011_FRAME_HEAD_IDX] == SDS011_FRAME_HEAD) &&
|
||||
(dev->rx_mem[SDS011_FRAME_RECV_TAIL_IDX] == SDS011_FRAME_TAIL)) {
|
||||
|
||||
dev->checksum &= SDS011_FRAME_CSUM_MSK;
|
||||
if (dev->rx_mem[SDS011_FRAME_RECV_CSUM_IDX] == dev->checksum) {
|
||||
|
||||
if ((dev->cb != NULL) &&
|
||||
(dev->rx_mem[SDS011_CMDID_IDX] == SDS011_RCMDID_DATA)) {
|
||||
sds011_data_t measure;
|
||||
measure.pm_2_5 = dev->rx_mem[SDS011_DB1_IDX] |
|
||||
(dev->rx_mem[SDS011_DB2_IDX] << 8);
|
||||
measure.pm_10 = dev->rx_mem[SDS011_DB3_IDX] |
|
||||
(dev->rx_mem[SDS011_DB4_IDX] << 8);
|
||||
dev->cb(&measure, dev->cbctx);
|
||||
dev->pos = -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dev->pos = -1;
|
||||
}
|
||||
|
||||
dev->checksum = 0;
|
||||
|
||||
/* unlock the mutex for the calling function */
|
||||
mutex_unlock(&dev->cb_lock);
|
||||
}
|
||||
|
||||
dev->pos++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief send command and wait for first replied message
|
||||
*
|
||||
* @param[in] dev SDS011 device the command is sent to
|
||||
* @param[in] data_bytes data bytes to send within the command
|
||||
* @param[in] len number of data bytes
|
||||
* @param[out] recv_frm pointer where the received frame will be stored
|
||||
* must at least provide SDS011_FRAME_RECV_LEN bytes
|
||||
*/
|
||||
int _send_recv_cmd(sds011_t *dev, uint8_t *data_bytes, size_t len, uint8_t *recv_frm)
|
||||
{
|
||||
uint8_t cmd[SDS011_FRAME_SEND_LEN] = {0};
|
||||
int checksum = 0;
|
||||
int res = SDS011_ERROR;
|
||||
|
||||
cmd[SDS011_FRAME_HEAD_IDX] = SDS011_FRAME_HEAD;
|
||||
cmd[SDS011_CMDID_IDX] = SDS011_CMDID_QUERY;
|
||||
|
||||
for (unsigned i = 0; i < len; i++) {
|
||||
cmd[SDS011_DB1_IDX + i] = data_bytes[i];
|
||||
checksum += data_bytes[i];
|
||||
}
|
||||
|
||||
cmd[SDS011_DEVID1_IDX] = (dev->params.dev_id >> 8) & 0xFF;
|
||||
checksum += cmd[SDS011_DEVID1_IDX];
|
||||
cmd[SDS011_DEVID2_IDX] = dev->params.dev_id & 0xFF;
|
||||
checksum += cmd[SDS011_DEVID2_IDX];
|
||||
|
||||
cmd[SDS011_FRAME_SEND_LEN - 2] = checksum & SDS011_FRAME_CSUM_MSK;
|
||||
cmd[SDS011_FRAME_SEND_TAIL_IDX] = SDS011_FRAME_TAIL;
|
||||
|
||||
mutex_lock(&dev->dev_lock);
|
||||
|
||||
dev->pos = 0;
|
||||
dev->checksum = 0;
|
||||
|
||||
mutex_lock(&dev->cb_lock);
|
||||
|
||||
/* if no active reporting callback is registered, UART must be enabled first */
|
||||
if((dev->cb == NULL) &&
|
||||
(uart_init(dev->params.uart, SDS011_UART_BAUDRATE, _rx_cb, dev) != 0)) {
|
||||
mutex_unlock(&dev->cb_lock);
|
||||
mutex_unlock(&dev->dev_lock);
|
||||
return SDS011_ERROR;
|
||||
}
|
||||
|
||||
uart_write(dev->params.uart, cmd, SDS011_FRAME_SEND_LEN);
|
||||
|
||||
/* wait for the isr callback to unlock the mutex */
|
||||
mutex_lock(&dev->cb_lock);
|
||||
|
||||
/* only copy data when checksum was valid */
|
||||
if (dev->pos != 0) {
|
||||
memcpy(recv_frm, dev->rx_mem, SDS011_FRAME_RECV_LEN);
|
||||
/* mark the recv buffer as free */
|
||||
dev->pos = 0;
|
||||
dev->checksum = 0;
|
||||
|
||||
/* check if we received a valid response for the cmd sent*/
|
||||
if(((recv_frm[SDS011_CMDID_IDX] == SDS011_RCMDID_REPLY) &&
|
||||
(cmd[SDS011_DB1_IDX] == recv_frm[SDS011_DB1_IDX]))
|
||||
|| ((recv_frm[SDS011_CMDID_IDX] == SDS011_RCMDID_DATA)
|
||||
&& (cmd[SDS011_DB1_IDX] == SDS011_CMD_DB1_QUERY_DATA))) {
|
||||
res = SDS011_OK;
|
||||
}
|
||||
else {
|
||||
res = SDS011_INVALID_RESPONSE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
res = SDS011_INVALID_CHKSUM;
|
||||
}
|
||||
|
||||
/* reset mutex state */
|
||||
mutex_unlock(&dev->cb_lock);
|
||||
|
||||
/* if no active reporting callback is registered, UART can be disabled */
|
||||
if((dev->cb == NULL) &&
|
||||
(uart_init(dev->params.uart, SDS011_UART_BAUDRATE, NULL, NULL) != 0)) {
|
||||
res = SDS011_ERROR;
|
||||
}
|
||||
|
||||
/* release device */
|
||||
mutex_unlock(&dev->dev_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief shorthand to get a single byte property with _send_recv_cmd
|
||||
*
|
||||
* @param[in] dev SDS011 device the command is sent to
|
||||
* @param[in] data_bytes data bytes to send within the command
|
||||
* @param[in] len number of data bytes
|
||||
* @param[out] p pointer for storing single data byte of the reply
|
||||
* @param[out] p_idx index of data byte we want to read
|
||||
*/
|
||||
static int _get_property(sds011_t *dev, uint8_t *data_bytes, size_t len,
|
||||
uint8_t *p, uint8_t p_idx)
|
||||
{
|
||||
uint8_t recv[SDS011_FRAME_RECV_LEN];
|
||||
int res = _send_recv_cmd(dev, data_bytes, len, recv);
|
||||
|
||||
if (res == SDS011_OK) {
|
||||
*p = recv[p_idx];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sds011_init(sds011_t *dev, const sds011_params_t *params)
|
||||
{
|
||||
assert((dev != NULL) && (params != NULL) && (params->uart < UART_NUMOF));
|
||||
|
||||
if ((params->pwr_pin != GPIO_UNDEF) &&
|
||||
(gpio_init(params->pwr_pin, GPIO_OUT) != 0)) {
|
||||
return SDS011_ERROR;
|
||||
}
|
||||
|
||||
memcpy(&dev->params, params, sizeof(sds011_params_t));
|
||||
|
||||
mutex_init(&dev->dev_lock);
|
||||
mutex_init(&dev->cb_lock);
|
||||
|
||||
dev->cb = NULL;
|
||||
|
||||
sds011_power_on(dev);
|
||||
|
||||
return SDS011_OK;
|
||||
}
|
||||
|
||||
int sds011_register_callback(sds011_t *dev, sds011_callback_t cb, void *ctx)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
mutex_lock(&dev->dev_lock);
|
||||
dev->cbctx = ctx;
|
||||
dev->cb = cb;
|
||||
|
||||
/* either register un unregister the uart callback */
|
||||
if (uart_init(dev->params.uart, SDS011_UART_BAUDRATE,
|
||||
cb == NULL ? NULL : _rx_cb,
|
||||
cb == NULL ? NULL : dev) != 0) {
|
||||
mutex_unlock(&dev->dev_lock);
|
||||
return SDS011_ERROR;
|
||||
}
|
||||
mutex_unlock(&dev->dev_lock);
|
||||
return SDS011_OK;
|
||||
}
|
||||
|
||||
void sds011_power_on(const sds011_t *dev)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
if(dev->params.pwr_pin != GPIO_UNDEF) {
|
||||
gpio_write(dev->params.pwr_pin, dev->params.pwr_ah);
|
||||
}
|
||||
}
|
||||
|
||||
void sds011_power_off(const sds011_t *dev)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
if(dev->params.pwr_pin != GPIO_UNDEF) {
|
||||
gpio_write(dev->params.pwr_pin, !dev->params.pwr_ah);
|
||||
}
|
||||
}
|
||||
|
||||
int sds011_get_reporting_mode(sds011_t *dev, sds011_reporting_mode_t *mode)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
uint8_t cmd[] = {SDS011_CMD_DB1_SET_DR_MODE, SDS011_CMD_OPT_QUERY};
|
||||
uint8_t prop = 0;
|
||||
int res = _get_property(dev, cmd, sizeof(cmd), &prop, SDS011_DB3_IDX);
|
||||
*mode = ((prop == 0) ? SDS011_RMODE_ACTIVE : SDS011_RMODE_QUERY);
|
||||
return res;
|
||||
}
|
||||
|
||||
int sds011_set_reporting_mode(sds011_t *dev, sds011_reporting_mode_t mode)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
uint8_t cmd[] = {SDS011_CMD_DB1_SET_DR_MODE, SDS011_CMD_OPT_SET, mode};
|
||||
|
||||
uint8_t recv[SDS011_FRAME_RECV_LEN];
|
||||
int res = _send_recv_cmd(dev, cmd, sizeof(cmd), recv);
|
||||
|
||||
if (res == SDS011_OK) {
|
||||
if ((recv[SDS011_DB2_IDX] == SDS011_CMD_OPT_SET) &&
|
||||
(recv[SDS011_DB3_IDX] == mode)) {
|
||||
return SDS011_OK;
|
||||
}
|
||||
|
||||
return SDS011_ERROR;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sds011_read(sds011_t *dev, sds011_data_t *data)
|
||||
{
|
||||
assert((dev != NULL) && (data != NULL));
|
||||
uint8_t cmd[] = {SDS011_CMD_DB1_QUERY_DATA};
|
||||
|
||||
uint8_t recv[SDS011_FRAME_RECV_LEN];
|
||||
int res = _send_recv_cmd(dev, cmd, sizeof(cmd), recv);
|
||||
|
||||
if (res == SDS011_OK) {
|
||||
data->pm_2_5 = recv[SDS011_DB1_IDX] | (recv[SDS011_DB2_IDX] << 8);
|
||||
data->pm_10 = recv[SDS011_DB3_IDX] | (recv[SDS011_DB4_IDX] << 8);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sds011_set_dev_id(sds011_t *dev, uint16_t sens_dev_id)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
uint8_t cmd[13] = {0};
|
||||
cmd[0] = SDS011_CMD_DB1_SET_DEV_ID;
|
||||
cmd[11] = (sens_dev_id >> 8) & 0xFF;
|
||||
cmd[12] = sens_dev_id & 0xFF;
|
||||
|
||||
uint8_t recv[SDS011_FRAME_RECV_LEN];
|
||||
int res = _send_recv_cmd(dev, cmd, sizeof(cmd), recv);
|
||||
|
||||
if ((res == SDS011_OK) &&
|
||||
(recv[SDS011_DB5_IDX] == cmd[11]) &&
|
||||
(recv[SDS011_DB6_IDX] == cmd[12])) {
|
||||
return SDS011_OK;
|
||||
}
|
||||
|
||||
return SDS011_ERROR;
|
||||
}
|
||||
|
||||
int sds011_get_working_mode(sds011_t *dev, sds011_working_mode_t *mode)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
uint8_t cmd[] = {SDS011_CMD_DB1_SET_SLEEP_WORK, SDS011_CMD_OPT_QUERY};
|
||||
uint8_t prop = 0;
|
||||
int res = _get_property(dev, cmd, sizeof(cmd), &prop, SDS011_DB3_IDX);
|
||||
*mode = ((prop == 0) ? SDS011_WMODE_SLEEP : SDS011_WMODE_WORK);
|
||||
return res;
|
||||
}
|
||||
|
||||
int sds011_set_working_mode(sds011_t *dev, sds011_working_mode_t mode)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
uint8_t cmd[] = {SDS011_CMD_DB1_SET_SLEEP_WORK, SDS011_CMD_OPT_SET, mode};
|
||||
|
||||
uint8_t recv[SDS011_FRAME_RECV_LEN];
|
||||
int res = _send_recv_cmd(dev, cmd, sizeof(cmd), recv);
|
||||
|
||||
if (res == SDS011_OK) {
|
||||
if ((recv[SDS011_DB2_IDX] == SDS011_CMD_OPT_SET) &&
|
||||
(recv[SDS011_DB3_IDX] == mode)) {
|
||||
return SDS011_OK;
|
||||
}
|
||||
|
||||
return SDS011_ERROR;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sds011_get_working_period(sds011_t *dev, uint8_t *minutes)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
uint8_t cmd[] = {SDS011_CMD_DB1_SET_WORK_PERIOD, SDS011_CMD_OPT_QUERY};
|
||||
return _get_property(dev, cmd, sizeof(cmd), minutes, SDS011_DB3_IDX);
|
||||
}
|
||||
|
||||
int sds011_set_working_period(sds011_t *dev, uint8_t minutes)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
uint8_t cmd[] = {SDS011_CMD_DB1_SET_WORK_PERIOD, SDS011_CMD_OPT_SET, minutes};
|
||||
|
||||
uint8_t recv[SDS011_FRAME_RECV_LEN];
|
||||
int res = _send_recv_cmd(dev, cmd, sizeof(cmd), recv);
|
||||
|
||||
if (res == SDS011_OK) {
|
||||
if ((recv[SDS011_DB2_IDX] == SDS011_CMD_OPT_SET) &&
|
||||
(recv[SDS011_DB3_IDX] == minutes)) {
|
||||
return SDS011_OK;
|
||||
}
|
||||
|
||||
return SDS011_ERROR;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sds011_get_fw_version(sds011_t *dev, uint8_t *year, uint8_t *mon, uint8_t *day)
|
||||
{
|
||||
assert((dev != NULL) && (year != NULL) && (mon != NULL) && (day != NULL));
|
||||
uint8_t cmd[] = {SDS011_CMD_DB1_CHECK_FIRMWARE};
|
||||
|
||||
uint8_t recv[SDS011_FRAME_RECV_LEN];
|
||||
int res = _send_recv_cmd(dev, cmd, sizeof(cmd), recv);
|
||||
|
||||
if (res == SDS011_OK) {
|
||||
*year = recv[SDS011_DB2_IDX];
|
||||
*mon = recv[SDS011_DB3_IDX];
|
||||
*day = recv[SDS011_DB4_IDX];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
46
drivers/sds011/sds011_saul.c
Normal file
46
drivers/sds011/sds011_saul.c
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2018 HAW-Hamburg
|
||||
*
|
||||
* 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_sds011
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief SAUL adaption for SDS011 sensor
|
||||
*
|
||||
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "saul.h"
|
||||
#include "sds011.h"
|
||||
#include "xtimer.h"
|
||||
|
||||
static int _read(const void *dev, phydat_t *res)
|
||||
{
|
||||
sds011_data_t data;
|
||||
|
||||
if (sds011_read((sds011_t *)dev, &data) == SDS011_OK) {
|
||||
res->val[0] = data.pm_2_5;
|
||||
res->val[1] = data.pm_10;
|
||||
res->unit = UNIT_GPM3;
|
||||
res->scale = -7;
|
||||
return 2;
|
||||
}
|
||||
return ECANCELED;
|
||||
}
|
||||
|
||||
const saul_driver_t sds011_saul_driver = {
|
||||
.read = _read,
|
||||
.write = saul_notsup,
|
||||
.type = SAUL_SENSE_PM
|
||||
};
|
||||
@ -449,6 +449,10 @@ void auto_init(void)
|
||||
extern void auto_init_sht3x(void);
|
||||
auto_init_sht3x();
|
||||
#endif
|
||||
#ifdef MODULE_SDS011
|
||||
extern void auto_init_sds011(void);
|
||||
auto_init_sds011();
|
||||
#endif
|
||||
#ifdef MODULE_SI114X
|
||||
extern void auto_init_si114x(void);
|
||||
auto_init_si114x();
|
||||
|
||||
88
sys/auto_init/saul/auto_init_sds011.c
Normal file
88
sys/auto_init/saul/auto_init_sds011.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2018 HAW-Hamburg
|
||||
*
|
||||
* 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 sys_auto_init_saul
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Auto initialization for SDS011 particulate matter sensor
|
||||
*
|
||||
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef MODULE_SDS011
|
||||
|
||||
#include "assert.h"
|
||||
#include "log.h"
|
||||
#include "saul_reg.h"
|
||||
#include "sds011.h"
|
||||
#include "sds011_params.h"
|
||||
|
||||
/**
|
||||
* @brief Define the number of configured sensors
|
||||
*/
|
||||
#define SDS011_NUM (sizeof(sds011_params) / sizeof(sds011_params[0]))
|
||||
|
||||
/**
|
||||
* @brief Allocate memory for the device descriptors
|
||||
*/
|
||||
static sds011_t sds011_devs[SDS011_NUM];
|
||||
|
||||
/**
|
||||
* @brief Memory for the SAUL registry entries
|
||||
*/
|
||||
static saul_reg_t saul_entries[SDS011_NUM];
|
||||
|
||||
/**
|
||||
* @brief Define the number of saul info
|
||||
*/
|
||||
#define SDS011_INFO_NUM (sizeof(sds011_saul_info) / sizeof(sds011_saul_info[0]))
|
||||
|
||||
/**
|
||||
* @name Import SAUL endpoint
|
||||
* @{
|
||||
*/
|
||||
extern const saul_driver_t sds011_saul_driver;
|
||||
/** @} */
|
||||
|
||||
void auto_init_sds011(void)
|
||||
{
|
||||
assert(SDS011_INFO_NUM == SDS011_NUM);
|
||||
|
||||
for (unsigned int i = 0; i < SDS011_NUM; i++) {
|
||||
LOG_DEBUG("[auto_init_saul] initializing sds011 #%u\n", i);
|
||||
|
||||
if (sds011_init(&sds011_devs[i], &sds011_params[i]) != SDS011_OK) {
|
||||
LOG_ERROR("[auto_init_saul] error initializing sds011 #%u\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
int retries = 0;
|
||||
|
||||
/* sensor must be set to query mode for manual reading by saul */
|
||||
while (sds011_set_reporting_mode(&sds011_devs[i], SDS011_RMODE_QUERY) != SDS011_OK) {
|
||||
if (retries++ >= 3) {
|
||||
LOG_ERROR("[auto_init_saul] error setting sds011 to query mode #%u\n", i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
saul_entries[i].dev = &(sds011_devs[i]);
|
||||
saul_entries[i].name = sds011_saul_info[i].name;
|
||||
saul_entries[i].driver = &sds011_saul_driver;
|
||||
saul_reg_add(&saul_entries[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
typedef int dont_be_pedantic;
|
||||
#endif /* MODULE_SDS011 */
|
||||
@ -110,7 +110,9 @@ enum {
|
||||
UNIT_PPB, /**< part per billion */
|
||||
/* aggregate values */
|
||||
UNIT_TIME, /**< the three dimensions contain sec, min, and hours */
|
||||
UNIT_DATE /**< the 3 dimensions contain days, months and years */
|
||||
UNIT_DATE, /**< the 3 dimensions contain days, months and years */
|
||||
/* mass concentration */
|
||||
UNIT_GPM3 /**< grams per cubic meters */
|
||||
/* extend this list as needed */
|
||||
};
|
||||
|
||||
|
||||
@ -102,6 +102,7 @@ const char *phydat_unit_to_str(uint8_t unit)
|
||||
case UNIT_PERCENT: return "%";
|
||||
case UNIT_CTS: return "cts";
|
||||
case UNIT_COULOMB: return "C";
|
||||
case UNIT_GPM3: return "g/m^3";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
6
tests/driver_sds011/Makefile
Normal file
6
tests/driver_sds011/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
USEMODULE += sds011
|
||||
USEMODULE += xtimer
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
236
tests/driver_sds011/main.c
Normal file
236
tests/driver_sds011/main.c
Normal file
@ -0,0 +1,236 @@
|
||||
/**
|
||||
* Copyright (C) 2018 HAW-Hamburg
|
||||
*
|
||||
* 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 SDS011 Laser Dust Sensor driver
|
||||
*
|
||||
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "xtimer.h"
|
||||
|
||||
#include "sds011.h"
|
||||
#include "sds011_params.h"
|
||||
#include "msg.h"
|
||||
#include "thread.h"
|
||||
|
||||
#define ACTIVE_REPORTING_TEST_CNT (20U)
|
||||
#define PUT_TO_QUERY_MODE_RETRIES (3U)
|
||||
#define PUT_TO_QUERY_MODE_RETRY_TIMEOUT_MS (100U)
|
||||
#define MANUAL_QUERY_CNT (10U)
|
||||
#define WORKING_PERIOD (0U)
|
||||
|
||||
/**
|
||||
* @brief Allocate the device descriptor
|
||||
*/
|
||||
static sds011_t dev;
|
||||
|
||||
static char* _rmode_str(sds011_reporting_mode_t rmode)
|
||||
{
|
||||
switch (rmode) {
|
||||
case SDS011_RMODE_ACTIVE:
|
||||
return "ACTIVE";
|
||||
case SDS011_RMODE_QUERY:
|
||||
return "QUERY";
|
||||
default:
|
||||
return "INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
static char* _wmode_str(sds011_working_mode_t wmode)
|
||||
{
|
||||
switch (wmode) {
|
||||
case SDS011_WMODE_WORK:
|
||||
return "WORK";
|
||||
case SDS011_WMODE_SLEEP:
|
||||
return "SLEEP";
|
||||
default:
|
||||
return "INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
static void _print_measurement(sds011_data_t *data)
|
||||
{
|
||||
uint16_t pm10_ug_int = data->pm_10 / 10;
|
||||
uint16_t pm10_ug_dec = data->pm_10 - 10 * pm10_ug_int;
|
||||
uint16_t pm2_5_ug_int = data->pm_2_5 / 10;
|
||||
uint16_t pm2_5_ug_dec = data->pm_2_5 - 10 * pm2_5_ug_int;
|
||||
printf("==> PM2.5: %d.%0d ug/m^3 | PM10: %d.%0d ug/m^3\n",
|
||||
pm2_5_ug_int, pm2_5_ug_dec, pm10_ug_int, pm10_ug_dec);
|
||||
}
|
||||
|
||||
void measure_cb(sds011_data_t *data, void *ctx)
|
||||
{
|
||||
msg_t msg = { .content.value = (((uint32_t)data->pm_10) << 16 | data->pm_2_5) };
|
||||
kernel_pid_t target_pid = (int)ctx;
|
||||
msg_send(&msg, target_pid);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
unsigned retry_cnt = 0;
|
||||
uint8_t year;
|
||||
uint8_t month;
|
||||
uint8_t day;
|
||||
sds011_reporting_mode_t rmode;
|
||||
sds011_working_mode_t wmode;
|
||||
uint8_t minutes;
|
||||
sds011_data_t data;
|
||||
|
||||
puts("SDS011 test application");
|
||||
|
||||
/* initialize the driver */
|
||||
if (sds011_init(&dev, &sds011_params[0]) == SDS011_OK) {
|
||||
puts("init [OK]");
|
||||
}
|
||||
else {
|
||||
puts("initalization [ERROR]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("setting reporting mode to '%s'...\n", _rmode_str(SDS011_RMODE_QUERY));
|
||||
|
||||
/* set the sensor to query mode to disable active reporting messages
|
||||
-> to work correctly, this step may need to be repeated if the automatic
|
||||
output is incoming while the reply is expected */
|
||||
while (sds011_set_reporting_mode(&dev, SDS011_RMODE_QUERY) != SDS011_OK) {
|
||||
if (retry_cnt++ >= PUT_TO_QUERY_MODE_RETRIES) {
|
||||
puts("[ERROR]");
|
||||
return -1;
|
||||
}
|
||||
xtimer_usleep(PUT_TO_QUERY_MODE_RETRY_TIMEOUT_MS * 1000);
|
||||
puts("[RETRY]");
|
||||
}
|
||||
|
||||
puts("[OK]");
|
||||
puts("getting reporting mode from device...");
|
||||
|
||||
if (sds011_get_reporting_mode(&dev, &rmode) == SDS011_OK) {
|
||||
printf("[OK] => mode: %s\n", _rmode_str(rmode));
|
||||
if (rmode != SDS011_RMODE_QUERY) {
|
||||
puts("mismatch! [ERROR]");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
puts("[ERROR]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
puts("getting firmware version...");
|
||||
|
||||
if (sds011_get_fw_version(&dev, &year, &month, &day) == SDS011_OK) {
|
||||
printf("[OK] => %d.%d.%d\n", year, month, day);
|
||||
}
|
||||
else {
|
||||
puts("[ERROR]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("setting working mode to '%s'...\n", _wmode_str(SDS011_WMODE_WORK));
|
||||
|
||||
if (sds011_set_working_mode(&dev, SDS011_WMODE_WORK) == SDS011_OK) {
|
||||
puts("[OK]");
|
||||
}
|
||||
else {
|
||||
puts("[ERROR]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
puts("getting working mode from device...");
|
||||
|
||||
if (sds011_get_working_mode(&dev, &wmode) == SDS011_OK) {
|
||||
printf("[OK] => mode: %s\n", _wmode_str(wmode));
|
||||
if (wmode != SDS011_WMODE_WORK) {
|
||||
puts("mismatch! [ERROR]");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
puts("[ERROR]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("setting working period to %u...\n", WORKING_PERIOD);
|
||||
|
||||
if (sds011_set_working_period(&dev, WORKING_PERIOD) == SDS011_OK) {
|
||||
puts("[OK]");
|
||||
}
|
||||
else {
|
||||
puts("[ERROR]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (sds011_get_working_period(&dev, &minutes) == SDS011_OK) {
|
||||
printf("[OK] => working period: %u\n", minutes);
|
||||
if (minutes != WORKING_PERIOD) {
|
||||
puts("mismatch! [ERROR]");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
puts("[ERROR]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < MANUAL_QUERY_CNT; i++) {
|
||||
if (sds011_read(&dev, &data) == SDS011_OK) {
|
||||
printf("manual query %2u/%u [OK]: ", i + 1, MANUAL_QUERY_CNT);
|
||||
_print_measurement(&data);
|
||||
}
|
||||
}
|
||||
|
||||
sds011_register_callback(&dev, measure_cb, (void*)(int)thread_getpid());
|
||||
|
||||
printf("switching to active reporting mode for %u measurements...\n",
|
||||
ACTIVE_REPORTING_TEST_CNT);
|
||||
|
||||
if (sds011_set_reporting_mode(&dev, SDS011_RMODE_ACTIVE) == SDS011_OK) {
|
||||
puts("[OK]");
|
||||
}
|
||||
else {
|
||||
puts("[ERROR]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* wait a little bit so the callback gets executed a few times */
|
||||
msg_t msg;
|
||||
for(unsigned msg_cnt = 0; msg_cnt < ACTIVE_REPORTING_TEST_CNT; msg_cnt++){
|
||||
msg_receive(&msg);
|
||||
sds011_data_t data;
|
||||
data.pm_10 = msg.content.value >> 16;
|
||||
data.pm_2_5 = msg.content.value & 0xFFFF;
|
||||
printf("msg from callback: ");
|
||||
_print_measurement(&data);
|
||||
}
|
||||
|
||||
/* unregister callback */
|
||||
sds011_register_callback(&dev, NULL, NULL);
|
||||
|
||||
puts("switching to sleep mode...");
|
||||
|
||||
if (sds011_set_working_mode(&dev, SDS011_WMODE_SLEEP) == SDS011_OK) {
|
||||
puts("[OK]");
|
||||
}
|
||||
else {
|
||||
puts("[ERROR]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
puts("[SUCCESS]");
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user