diff --git a/drivers/dht/dht.c b/drivers/dht/dht.c index c27f3a78d1..6c29899949 100644 --- a/drivers/dht/dht.c +++ b/drivers/dht/dht.c @@ -38,10 +38,47 @@ #define ENABLE_DEBUG (0) #include "debug.h" +/* Every pulse send by the DHT longer than 40µs is interpreted as 1 */ #define PULSE_WIDTH_THRESHOLD (40U) +/* If an expected pulse is not detected within 1000µs, something is wrong */ +#define TIMEOUT (1000U) +/* The DHT sensor cannot measure more than once a second */ +#define DATA_HOLD_TIME (US_PER_SEC) +/* The start signal by pulling data low for at least 18ms and then up for + * 20-40µs*/ +#define START_LOW_TIME (20U * US_PER_MS) +#define START_HIGH_TIME (40U) -static uint16_t read(gpio_t pin, int bits) + +static inline void _reset(dht_t *dev) { + gpio_init(dev->params.pin, GPIO_OUT); + gpio_set(dev->params.pin); +} + +/** + * @brief Wait until the pin @p pin has level @p expect + * + * @param pin GPIO pin to wait for + * @param expect Wait until @p pin has this logic level + * @param timeout Timeout in µs + * + * @retval 0 Success + * @retval -1 Timeout occurred before level was reached + */ +static inline int _wait_for_level(gpio_t pin, bool expect, unsigned timeout) +{ + while (((gpio_read(pin) > 0) != expect) && timeout) { + xtimer_usleep(1); + timeout--; + } + + return (timeout > 0) ? 0 : -1; +} + +static int _read(uint16_t *dest, gpio_t pin, int bits) +{ + DEBUG("read\n"); uint16_t res = 0; for (int i = 0; i < bits; i++) { @@ -49,16 +86,24 @@ static uint16_t read(gpio_t pin, int bits) res <<= 1; /* measure the length between the next rising and falling flanks (the * time the pin is high - smoke up :-) */ - while (!gpio_read(pin)) {} + if (_wait_for_level(pin, 1, TIMEOUT)) { + return -1; + } start = xtimer_now_usec(); - while (gpio_read(pin)) {} + + if (_wait_for_level(pin, 0, TIMEOUT)) { + return -1; + } end = xtimer_now_usec(); + /* if the high phase was more than 40us, we got a 1 */ if ((end - start) > PULSE_WIDTH_THRESHOLD) { res |= 0x0001; } } - return res; + + *dest = res; + return 0; } int dht_init(dht_t *dev, const dht_params_t *params) @@ -66,13 +111,13 @@ int dht_init(dht_t *dev, const dht_params_t *params) DEBUG("dht_init\n"); /* check parameters and configuration */ - assert(dev && params && - ((dev->type == DHT11) || (dev->type == DHT22) || (dev->type == DHT21))); + assert(dev && params); + assert((params->type == DHT11) || (params->type == DHT22) || (params->type == DHT21)); - *dev = *params; + memset(dev, 0, sizeof(dht_t)); + dev->params = *params; - gpio_init(dev->pin, GPIO_OUT); - gpio_set(dev->pin); + _reset(dev); xtimer_usleep(2000 * US_PER_MS); @@ -80,66 +125,99 @@ int dht_init(dht_t *dev, const dht_params_t *params) return DHT_OK; } -int dht_read(const dht_t *dev, int16_t *temp, int16_t *hum) +int dht_read(dht_t *dev, int16_t *temp, int16_t *hum) { - uint8_t csum, sum; + uint16_t csum; uint16_t raw_hum, raw_temp; - assert(dev && temp && hum); + assert(dev); - /* send init signal to device */ - gpio_clear(dev->pin); - xtimer_usleep(20 * US_PER_MS); - gpio_set(dev->pin); - xtimer_usleep(40); + uint32_t now_us = xtimer_now_usec(); + if ((now_us - dev->last_read_us) > DATA_HOLD_TIME) { + /* send init signal to device */ + gpio_clear(dev->params.pin); + xtimer_usleep(START_LOW_TIME); + gpio_set(dev->params.pin); + xtimer_usleep(START_HIGH_TIME); - /* sync on device */ - gpio_init(dev->pin, dev->in_mode); - while (!gpio_read(dev->pin)) {} - while (gpio_read(dev->pin)) {} + /* sync on device */ + gpio_init(dev->params.pin, dev->params.in_mode); + if (_wait_for_level(dev->params.pin, 1, TIMEOUT)) { + _reset(dev); + return DHT_TIMEOUT; + } - /* - * data is read in sequentially, highest bit first: - * 40 .. 24 23 .. 8 7 .. 0 - * [humidity][temperature][checksum] - */ + if (_wait_for_level(dev->params.pin, 0, TIMEOUT)) { + _reset(dev); + return DHT_TIMEOUT; + } - /* read the humidity, temperature, and checksum bits */ - raw_hum = read(dev->pin, 16); - raw_temp = read(dev->pin, 16); - csum = (uint8_t)read(dev->pin, 8); + /* + * data is read in sequentially, highest bit first: + * 40 .. 24 23 .. 8 7 .. 0 + * [humidity][temperature][checksum] + */ - /* set pin high again - so we can trigger the next reading by pulling it low - * again */ - gpio_init(dev->pin, GPIO_OUT); - gpio_set(dev->pin); + /* read the humidity, temperature, and checksum bits */ + if (_read(&raw_hum, dev->params.pin, 16)) { + _reset(dev); + return DHT_TIMEOUT; + } - /* validate the checksum */ - sum = (raw_temp >> 8) + (raw_temp & 0xff) + (raw_hum >> 8) + (raw_hum & 0xff); - if ((sum != csum) || (csum == 0)) { - DEBUG("error: checksum invalid\n"); - return DHT_NOCSUM; + if (_read(&raw_temp, dev->params.pin, 16)) { + _reset(dev); + return DHT_TIMEOUT; + } + + if (_read(&csum, dev->params.pin, 8)) { + _reset(dev); + return DHT_TIMEOUT; + } + + /* Bring device back to defined state - so we can trigger the next reading + * by pulling the data pin low again */ + _reset(dev); + + /* validate the checksum */ + uint8_t sum = (raw_temp >> 8) + (raw_temp & 0xff) + (raw_hum >> 8) + + (raw_hum & 0xff); + if (sum != csum) { + DEBUG("error: checksum doesn't match\n"); + return DHT_NOCSUM; + } + + /* parse the RAW values */ + DEBUG("RAW values: temp: %7i hum: %7i\n", (int)raw_temp, (int)raw_hum); + switch (dev->params.type) { + case DHT11: + dev->last_val.temperature = (int16_t)((raw_temp >> 8) * 10); + dev->last_val.humidity = (int16_t)((raw_hum >> 8) * 10); + break; + /* DHT21 == DHT22 (same value in enum), so both are handled here */ + case DHT22: + dev->last_val.humidity = (int16_t)raw_hum; + /* if the high-bit is set, the value is negative */ + if (raw_temp & 0x8000) { + dev->last_val.temperature = (int16_t)((raw_temp & ~0x8000) * -1); + } + else { + dev->last_val.temperature = (int16_t)raw_temp; + } + break; + default: + return DHT_NODEV; /* this should never be reached */ + } + + /* update time of last measurement */ + dev->last_read_us = now_us; } - /* parse the RAW values */ - DEBUG("RAW values: temp: %7i hum: %7i\n", (int)raw_temp, (int)raw_hum); - switch (dev->type) { - case DHT11: - *temp = (int16_t)((raw_temp >> 8) * 10); - *hum = (int16_t)((raw_hum >> 8) * 10); - break; - case DHT22: - *hum = (int16_t)raw_hum; - /* if the high-bit is set, the value is negative */ - if (raw_temp & 0x8000) { - *temp = (int16_t)((raw_temp & ~0x8000) * -1); - } - else { - *temp = (int16_t)raw_temp; - } - break; - default: - return DHT_NODEV; /* this should never be reached */ + if (temp) { + *temp = dev->last_val.temperature; + } + + if (hum) { + *hum = dev->last_val.humidity; } return DHT_OK; diff --git a/drivers/dht/dht_saul.c b/drivers/dht/dht_saul.c index 3a05601e0c..c5ff1c2e3d 100644 --- a/drivers/dht/dht_saul.c +++ b/drivers/dht/dht_saul.c @@ -30,38 +30,25 @@ #include "saul.h" #include "dht.h" -#include "xtimer.h" -#define DHT_SAUL_HOLD_TIME (1000 * 1000U) /* 1s */ - -static int16_t temp, hum; -static uint32_t last = 0; - -static int check_and_read(const void *dev, phydat_t *res, int16_t *val, uint8_t unit) +static int read_temp(const void *dev, phydat_t *res) { - uint32_t now = xtimer_now_usec(); - - if ((now - last) > DHT_SAUL_HOLD_TIME) { - dht_read((const dht_t *)dev, &temp, &hum); - last = now; + if (dht_read((dht_t *)dev, &res->val[0], NULL)) { + return -ECANCELED; } - - res->val[0] = *val; - res->val[1] = 0; - res->val[2] = 0; - res->unit = unit; + res->unit = UNIT_TEMP_C; res->scale = -1; return 1; } -static int read_temp(const void *dev, phydat_t *res) -{ - return check_and_read(dev, res, &temp, UNIT_TEMP_C); -} - static int read_hum(const void *dev, phydat_t *res) { - return check_and_read(dev, res, &hum, UNIT_PERCENT); + if (dht_read((dht_t *)dev, NULL, &res->val[0])) { + return -ECANCELED; + } + res->unit = UNIT_PERCENT; + res->scale = -1; + return 1; } const saul_driver_t dht_temp_saul_driver = { diff --git a/drivers/include/dht.h b/drivers/include/dht.h index 37c7df7e4e..973453c5b9 100644 --- a/drivers/include/dht.h +++ b/drivers/include/dht.h @@ -45,7 +45,8 @@ extern "C" { enum { DHT_OK = 0, /**< all good */ DHT_NOCSUM = -1, /**< checksum error */ - DHT_NODEV = -2 /**< device type not defined */ + DHT_TIMEOUT = -2, /**< communication timed out */ + DHT_NODEV = -3 /**< device type not defined */ }; /** @@ -66,19 +67,24 @@ typedef enum { } dht_type_t; /** - * @brief Device descriptor for DHT sensor devices + * @brief Configuration parameters for DHT devices */ typedef struct { gpio_t pin; /**< GPIO pin of the device's data pin */ dht_type_t type; /**< type of the DHT device */ gpio_mode_t in_mode; /**< input pin configuration, with or without pull * resistor */ -} dht_t; + +} dht_params_t; /** - * @brief Configuration parameters for DHT devices + * @brief Device descriptor for DHT sensor devices */ -typedef dht_t dht_params_t; +typedef struct { + dht_params_t params; /**< Device parameters */ + dht_data_t last_val; /**< Values of the last measurement */ + uint32_t last_read_us; /**< Time of the last measurement */ +} dht_t; /** * @brief Initialize a new DHT device @@ -101,11 +107,12 @@ int dht_init(dht_t *dev, const dht_params_t *params); * @param[out] temp temperature value [in °C * 10^-1] * @param[out] hum relative humidity value [in percent * 10^-1] * - * @return 0 on success - * @return -1 on checksum error - * @return -2 on parsing error + * @retval `DHT_OK` Success + * @retval `DHT_NOCSUM` Checksum error + * @retval `DHT_TIMEOUT` Reading data timed out (check wires!) + * @retval `DHT_NODEV` Unsupported device type specified */ -int dht_read(const dht_t *dev, int16_t *temp, int16_t *hum); +int dht_read(dht_t *dev, int16_t *temp, int16_t *hum); #ifdef __cplusplus }