diff --git a/boards/common/msb-430/Makefile b/boards/common/msb-430/Makefile index baa55d873151f46154b83eaf906cf985093e8d3b..4695a0f76122f2071ad699558c56d5db8c4cc60a 100644 --- a/boards/common/msb-430/Makefile +++ b/boards/common/msb-430/Makefile @@ -1,5 +1,3 @@ MODULE = boards_common_msb-430 -DIRS = drivers - include $(RIOTBASE)/Makefile.base diff --git a/boards/common/msb-430/Makefile.include b/boards/common/msb-430/Makefile.include index 3125ba07156dde89ca3ac1024abab859dc231b86..5c054c4c66a56b51993fd9b50f34c582d588156a 100644 --- a/boards/common/msb-430/Makefile.include +++ b/boards/common/msb-430/Makefile.include @@ -25,6 +25,3 @@ export DEBUGGER_FLAGS = --tui --ex="target remote localhost:2000" --ex "monitor # export common msb-430 includes export INCLUDES += -I$(RIOTBOARD)/common/msb-430/include -export INCLUDES += -I$(RIOTBOARD)/common/msb-430/drivers/include - -USEMODULE += boards_common_msb-430-drivers diff --git a/boards/common/msb-430/drivers/Makefile b/boards/common/msb-430/drivers/Makefile deleted file mode 100644 index ed11f0ae340a4b762ad012b7380fdc0f9e5aaecf..0000000000000000000000000000000000000000 --- a/boards/common/msb-430/drivers/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -MODULE = boards_common_msb-430-drivers - -include $(RIOTBOARD)/$(BOARD)/Makefile.include - -include $(RIOTBASE)/Makefile.base diff --git a/boards/common/msb-430/drivers/include/sht11-board.h b/boards/common/msb-430/drivers/include/sht11-board.h deleted file mode 100644 index a79fe22b4d9414da141707d52c31e11428a4b661..0000000000000000000000000000000000000000 --- a/boards/common/msb-430/drivers/include/sht11-board.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2013, Freie Universitaet Berlin (FUB). All rights reserved. - * - * 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. - */ - -#ifndef SHT11_BOARD_H -#define SHT11_BOARD_H - -/** - * @ingroup boards_common_msb-430 - * @{ - */ - -/** - * @file - * @brief SHT11 Device Driver Configuration For MSB-430 Platform - * - * @author Freie Universität Berlin, Computer Systems & Telematics, RIOT - * - */ -#include <msp430x16x.h> -#include "bitarithm.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* SCK = P3B5 - * DATA = P3B4 - */ - -#define SHT11_SCK_LOW P3OUT &= ~(BIT5); /**< serial clock line low */ -#define SHT11_SCK_HIGH P3OUT |= BIT5; /**< serial clock line high */ -#define SHT11_DATA (P3IN & BIT5) /**< read serial I/O */ -#define SHT11_DATA_LOW P3OUT &= ~(BIT5); /**< serial I/O line low */ -#define SHT11_DATA_HIGH P3OUT |= BIT5; /**< serial I/O line high */ -#define SHT11_DATA_IN P3DIR &= ~(BIT5); /**< serial I/O as input */ -#define SHT11_DATA_OUT P3DIR |= BIT5; /**< serial I/O as output */ -#define SHT11_INIT P3DIR |= BIT5; /* FIO1DIR |= BIT25; PINSEL3 &= ~(BIT14|BIT15 | BIT16|BIT17); */ - -#ifdef __cplusplus -} -#endif - -/** @} */ -#endif /* SHT11_BOARD_H */ diff --git a/boards/common/msba2/Makefile b/boards/common/msba2/Makefile index a8c0997289fe79230595a9ce38d8fe672455deb8..ccf58a08443576884bb286094d185f5f49707ff5 100644 --- a/boards/common/msba2/Makefile +++ b/boards/common/msba2/Makefile @@ -1,5 +1,3 @@ MODULE = boards_common_msba2 -DIRS = drivers - include $(RIOTBASE)/Makefile.base diff --git a/boards/common/msba2/Makefile.include b/boards/common/msba2/Makefile.include index 5ba3b3d01cf3a541777712afb7d7229d2298ef75..00260c06232bfe698b32669e1ecf753e1c35a622 100644 --- a/boards/common/msba2/Makefile.include +++ b/boards/common/msba2/Makefile.include @@ -27,8 +27,5 @@ endif export FFLAGS = $(PORT) $(HEXFILE) INCLUDES += -I$(RIOTBOARD)/common/msba2/include -INCLUDES += -I$(RIOTBOARD)/common/msba2/drivers/include export UNDEF += $(BINDIR)/cpu/startup.o - -USEMODULE += boards_common_msba2-drivers diff --git a/boards/common/msba2/drivers/Makefile b/boards/common/msba2/drivers/Makefile deleted file mode 100644 index 26dd1f4f5da09469500439c62212c5d5f9aa905b..0000000000000000000000000000000000000000 --- a/boards/common/msba2/drivers/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -MODULE = boards_common_msba2-drivers - -include $(RIOTBASE)/Makefile.base diff --git a/boards/common/msba2/drivers/include/sht11-board.h b/boards/common/msba2/drivers/include/sht11-board.h deleted file mode 100644 index 7ee0009965815089280778938f45db381b052104..0000000000000000000000000000000000000000 --- a/boards/common/msba2/drivers/include/sht11-board.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2009, Freie Universitaet Berlin (FUB). All rights reserved. - * - * 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. - */ - -#ifndef SHT11_BOARD_H -#define SHT11_BOARD_H - -/** - * @ingroup boards_common_msba2 - * @{ - */ - -/** - * @file - * @brief LPC2387 SHT11 Device Driver - * - * @author Freie Universität Berlin, Computer Systems & Telematics, FeuerWhere project - * @version $Revision$ - * - * @note $Id$ - */ - -#include "cpu.h" -#include "board.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* serial clock line low */ -#define SHT11_SCK_LOW FIO1CLR = BIT25; -/* serial clock line high */ -#define SHT11_SCK_HIGH FIO1SET = BIT25; -/* read serial I/O */ -#define SHT11_DATA ((FIO1PIN & BIT26) != 0) -/* serial I/O line low */ -#define SHT11_DATA_LOW (FIO1CLR = BIT26); -/* serial I/O line high */ -#define SHT11_DATA_HIGH (FIO1SET = BIT26); -/* serial I/O as input */ -#define SHT11_DATA_IN (FIO1DIR &= ~BIT26) -/* serial I/O as output */ -#define SHT11_DATA_OUT (FIO1DIR |= BIT26) - -#define SHT11_INIT FIO1DIR |= BIT25; PINSEL3 &= ~(BIT14|BIT15 | BIT16|BIT17); - -#ifdef __cplusplus -} -#endif - -/** @} */ -#endif /* SHT11_BOARD_H */ diff --git a/boards/msb-430/Makefile.dep b/boards/msb-430/Makefile.dep new file mode 100644 index 0000000000000000000000000000000000000000..f5124575543f30d05365553fb31a71cf64b4ab33 --- /dev/null +++ b/boards/msb-430/Makefile.dep @@ -0,0 +1,3 @@ +ifneq (,$(filter saul_default,$(USEMODULE))) + USEMODULE += sht11 +endif diff --git a/boards/msb-430/include/board.h b/boards/msb-430/include/board.h index a053c327f338d41cdb072833b4845f9a31e44820..7baa71f1aa0a16d481f5c8df2cc139a2c1a06732 100644 --- a/boards/msb-430/include/board.h +++ b/boards/msb-430/include/board.h @@ -59,6 +59,14 @@ extern "C" { #define MSP430_HAS_EXTERNAL_CRYSTAL 0 /** @} */ +/** + * @name Configure on-board SHT11 device + * @{ + */ +#define SHT1X_PARAM_CLK (GPIO_PIN(3, 5)) +#define SHT1X_PARAM_DATA (GPIO_PIN(3, 4)) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/boards/msb-430h/Makefile.dep b/boards/msb-430h/Makefile.dep new file mode 100644 index 0000000000000000000000000000000000000000..f5124575543f30d05365553fb31a71cf64b4ab33 --- /dev/null +++ b/boards/msb-430h/Makefile.dep @@ -0,0 +1,3 @@ +ifneq (,$(filter saul_default,$(USEMODULE))) + USEMODULE += sht11 +endif diff --git a/boards/msb-430h/include/board.h b/boards/msb-430h/include/board.h index 8fc284faa1f3e9e1463c13ccc82d6f55edd99058..ae744f1b5f0432a32c9a636daea553f06a5ba5d3 100644 --- a/boards/msb-430h/include/board.h +++ b/boards/msb-430h/include/board.h @@ -47,6 +47,14 @@ extern "C" { #define MSP430_HAS_EXTERNAL_CRYSTAL 1 /** @} */ +/** + * @name Configure on-board SHT11 device + * @{ + */ +#define SHT1X_PARAM_CLK (GPIO_PIN(3, 5)) +#define SHT1X_PARAM_DATA (GPIO_PIN(3, 4)) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/boards/msba2/Makefile.dep b/boards/msba2/Makefile.dep index e5d94f2d25850c062976f10d5a38ff3fca618243..d7230f86c2db4c22efab874deb9b8f70a1c0c7f2 100644 --- a/boards/msba2/Makefile.dep +++ b/boards/msba2/Makefile.dep @@ -3,3 +3,7 @@ include $(RIOTBOARD)/common/msba2/Makefile.dep ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE))) USEMODULE += cc110x endif + +ifneq (,$(filter saul_default,$(USEMODULE))) + USEMODULE += sht11 +endif diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 48f58ce29532725f7abffe93aaa0447fdcfbb490..776b16540929f08b059d5b0849323ec3bc723ab7 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -307,7 +307,9 @@ ifneq (,$(filter servo,$(USEMODULE))) FEATURES_REQUIRED += periph_pwm endif -ifneq (,$(filter sht11,$(USEMODULE))) +ifneq (,$(filter sht1%,$(USEMODULE))) + USEMODULE += sht1x + FEATURES_REQUIRED += periph_gpio USEMODULE += xtimer endif diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 7bf99640856ac668b4eafdf5ad4883ab61e1f0a1..e37c2b67522e8cc622c548341242141e8bf1861e 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -50,6 +50,10 @@ ifneq (,$(filter dht,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/dht/include endif +ifneq (,$(filter sht1x,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/sht1x/include +endif + ifneq (,$(filter ds1307,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/ds1307/include endif diff --git a/drivers/include/sht11.h b/drivers/include/sht11.h deleted file mode 100644 index 2378ca8c1b83245ec00a8af111af5bae7845c506..0000000000000000000000000000000000000000 --- a/drivers/include/sht11.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2009, Freie Universitaet Berlin (FUB). All rights reserved. - * - * 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_sht11 SHT11 Humidity and Temperature Sensor - * @ingroup drivers_sensors - * @brief Driver for Sensirion SHT11 Humidity and Temperature Sensor - * @{ - * - * @file - * @brief SHT11 Device Driver - * - * @author Freie Universität Berlin, Computer Systems & Telematics - */ - -#ifndef SHT11_H -#define SHT11_H - -#include <stdint.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#define SHT11_NO_ACK (0) /**< don't ack read in `read_byte` */ -#define SHT11_ACK (1) /**< do acknowledge read in `read_byte` */ -/* adr command r/w */ -#define SHT11_STATUS_REG_W (0x06) /**< will write to status register */ -#define SHT11_STATUS_REG_R (0x07) /**< will read from status register */ -#define SHT11_MEASURE_TEMP (0x03) /**< tell sensor to measure temperature */ -#define SHT11_MEASURE_HUMI (0x05) /**< tell sensor to measure humidity */ -#define SHT11_RESET (0x1E) /**< reset the sensor */ - -/** time to wait after toggling the data line */ -#define SHT11_DATA_WAIT (1) -/** time to wait after toggling the clock line */ -#define SHT11_CLK_WAIT (1) - -/** set measurement timeout to 1 second */ -#define SHT11_MEASURE_TIMEOUT (1000) - -/** - * @brief sht11 measureable data - */ -typedef struct { - float temperature; /**< temperature value */ - float relhum; /**< linear relative humidity */ - float relhum_temp; /**< temperature compensated relative humidity */ -} sht11_val_t; - -/** - * @brief SHT11 modes that can be measured - */ -typedef enum { - TEMPERATURE = 1, - HUMIDITY = 2 -} sht11_mode_t; - -/** - * @brief Initialize SHT11 ports - */ -void sht11_init(void); - -/** - * @brief Read sensor - * - * @param value The struct to be filled with measured values - * @param mode Specifies type of data to be read - * - * @return 1 on success, 0 otherwise - * - * Example: - * \code sht11_val sht11; - * sht11_read_sensor(&sht11, HUMIDITY|TEMPERATURE); - * printf("%-6.2f °C %5.2f %% %5.2f %%\n", sht11.temperature, sht11.relhum, sht11.relhum_temp); \endcode - */ -uint8_t sht11_read_sensor(sht11_val_t *value, sht11_mode_t mode); - -/** - * @brief Write status register - * - * @param p_value The value to write - * - * @return 1 on success, 0 otherwise - */ -uint8_t sht11_write_status(uint8_t *p_value); - -/** - * @brief Read status register with checksum - * - * @param p_value The read value - * @param p_checksum The received checksum - * - * return 1 on success, 0 otherwise - */ -uint8_t sht11_read_status(uint8_t *p_value, uint8_t *p_checksum); - -#ifdef __cplusplus -} -#endif - -#endif /* SHT11_H */ -/** @} */ diff --git a/drivers/include/sht1x.h b/drivers/include/sht1x.h new file mode 100644 index 0000000000000000000000000000000000000000..b8c7bd87197323e29e0f5728f1d48490e744e6e2 --- /dev/null +++ b/drivers/include/sht1x.h @@ -0,0 +1,188 @@ +/* + * Copyright 2009 Freie Universitaet Berlin (FUB) + * 2018 Otto-von-Guericke-Universität Magdeburg + * + * 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_sht1x SHT10/SHT11/SHT15 Humidity and Temperature Sensor + * @ingroup drivers_sensors + * @brief Driver for Sensirion SHT10/SHT11/SHT15 Humidity and Temperature + Sensor + * @{ + * + * @file + * @brief SHT10/SHT11/SHT15 Device Driver + * + * @author Marian Buschsieweke <marian.buschsieweke@ovgu.de> + */ + +#ifndef SHT1X_H +#define SHT1X_H + +#include <stdint.h> +#include <periph/gpio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Possible configuration (=status byte) values of the SHT10/11/15 + * + * These values can be or'ed together to get the configuration. + */ +typedef enum { + /** Use 8/12 bit resolution instead of 12/14 bit for temp/hum */ + SHT1X_CONF_LOW_RESOLUTION = 0x01, + /** Don't upload calibration data to register to safe 10 millisec */ + SHT1X_CONF_SKIP_CALIBRATION = 0x02, + /** Waste 8mA at 5V to increase the sensor temperature up to 10°C */ + SHT1X_CONF_ENABLE_HEATER = 0x04, + /** Skip the CRC check (and reading the CRC byte) to safe time */ + SHT1X_CONF_SKIP_CRC = 0x08, +} sht1x_conf_t; + +/** + * @brief Possible values for Vdd (measured temperature depends on it) + */ +typedef enum { + SHT1X_VDD_5_0V = 0, + SHT1X_VDD_4_0V = 1, + SHT1X_VDD_3_5V = 2, + SHT1X_VDD_3_0V = 3, + SHT1X_VDD_2_5V = 4, +} sht1x_vdd_t; + +/** + * @brief SHT10/11/15 temperature humidity sensor + */ +typedef struct { + gpio_t clk; /**< GPIO connected to the clock pin of the SHT1X */ + gpio_t data; /**< GPIO connected to the data pin of the SHT1X */ + int16_t temp_off; /**< Offset to add to the measured temperature */ + int16_t hum_off; /**< Offset to add to the measured humidity */ + uint8_t conf; /**< Status byte (containing configuration) of the SHT1X */ + uint8_t vdd; /**< Supply voltage of the SHT1X (as sht1x_vdd_t) */ +} sht1x_dev_t; + +/** + * @brief Parameters required to set up the SHT10/11/15 device driver + */ +typedef struct { + gpio_t clk; /**< GPIO connected to the clock pin of the SHT1X */ + gpio_t data; /**< GPIO connected to the data pin of the SHT1X */ + sht1x_vdd_t vdd; /**< The supply voltage of the SHT1X */ +} sht1x_params_t; + +/** + * @brief Initialize the SHT10/11/15 sensor + * + * @param dev SHT1X sensor to initialize + * @param params Information on how the SHT1X is connected to the board + * + * @retval 0 Success + * @retval -EIO IO failure (`gpio_init()` failed) + * @retval -EPROTO Sensor did not acknowledge reset command + */ +int sht1x_init(sht1x_dev_t *dev, const sht1x_params_t *params); + +/** + * @brief Calculate the temperature from the raw input + * @note This internal function is exposed for unit tests + * + * @param dev Device from which the raw value was received + * @param raw The raw (unprocessed) temperature value + * + * @return The correct temperature in E-02 °C + * @retval INT16_MIN Passed `NULL` for parameter `dev` or `dev->vdd` + */ +int16_t sht1x_temperature(const sht1x_dev_t *dev, uint16_t raw); + +/** + * @brief Calculate the relative humidity from the raw input + * @note This internal function is exposed for unit tests + * + * @param dev Device from which the raw value was received + * @param raw The raw (unprocessed) temperature value + * @param temp The temperature at which the humidity was measure in + * E-02 °C + * + * @return The correct temperature in E-02 % + * @retval -1 Passed `NULL` for parameter `dev` + */ +int16_t sht1x_humidity(const sht1x_dev_t *dev, uint16_t raw, int16_t temp); + +/** + * @brief Read the current temperature + * + * @param dev SHT1X sensor to read + * @param temp Store the measured temperature in E-02 °C here + * @param hum Store the measured relative humidity in E-02 % here + * + * @retval 0 Success + * @retval -EIO IO failure (`gpio_init()` failed) + * @retval -EBADMSG CRC-8 checksum didn't match (--> Retry) + * @retval -EINVAL Passed `NULL` for dev or for both `temp` and `hum` + * @retval -EBADMSG CRC checksum didn't match + * @retval -ECANCELED Measurement timed out + * @retval -EPROTO Sensor did not acknowledge command + * + * For either `temp` or `hum` `NULL` can be passed, if only one value is of + * interest. Passing `NULL` for `hum` speeds up the communication, but + * passing `NULL` for `temp` does not. The temperature value is required to + * calculate the relative humidity from the raw input. So the temperature is + * measured in any case, it is just not returned if `temp` is `NULL`. + */ +int sht1x_read(const sht1x_dev_t *dev, int16_t *temp, int16_t *hum); + +/** + * @brief Apply the given configuration (= status byte) to + * + * @param dev SHT1X device to configure + * @param conf Configuration to apply + * + * @retval 0 Configuration applied + * @retval -EINVAL Called with `dev == NULL` + * @retval -EIO I/O error (`gpio_init()` failed) + * @retval -EPROTO Sensor did not acknowledge command + * @retval -ECANCELED Sensor did not apply configuration + * @retval -EBADMSG CRC checksum error while verifying uploaded configuration + */ +int sht1x_configure(sht1x_dev_t *dev, sht1x_conf_t conf); + +/** + * @brief Read the status byte of an SHT1X sensor + * + * @param dev SHT1X device to receive the status from + * @param status Store the received status byte here + * + * @retval 0 Configuration applied + * @retval -EINVAL Called with `dev == NULL` + * @retval -EIO I/O error (`gpio_init()` failed) + * @retval -EPROTO Sensor did not acknowledge command + * @retval -EBADMSG CRC checksum didn't match + */ +int sht1x_read_status(sht1x_dev_t *dev, uint8_t *status); + +/** + * @brief Reset the sensor's configuration to default values + * + * @param dev SHT1X device to reset + * + * @retval 0 Reset successful + * @retval -EINVAL Called with `dev == NULL` + * @retval -EIO I/O error (`gpio_init()` failed) + * @retval -EPROTO Sensor did not acknowledge reset command + */ +int sht1x_reset(sht1x_dev_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* SHT1X_H */ +/** @} */ diff --git a/drivers/sht11/sht11.c b/drivers/sht11/sht11.c deleted file mode 100644 index e92b15013b7f6b605d4170dafe7f7b9f9f89f0d7..0000000000000000000000000000000000000000 --- a/drivers/sht11/sht11.c +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright 2009, Freie Universitaet Berlin (FUB). All rights reserved. - * - * 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_sht11 - * @brief Driver for the Sensirion SHT11 humidity and temperature sensor - * @{ - * - * @file - * @brief SHT11 Device Driver - * - * @version $Revision: 2396 $ - * - * @note $Id: sht11.c 2396 2010-07-06 15:12:35Z ziegert $ - * @} - */ - -#include <stdio.h> -#include <stdint.h> - -#include "xtimer.h" -#include "mutex.h" -#include "sht11.h" -#include "sht11-board.h" -#include "bitarithm.h" - -float sht11_temperature_offset; - -/** - * @brief Perform measurement - * - * @param p_value Measured value (14 or 12 bit -> 2 bytes) - * @param p_checksum Checksum of measurement - * @param mode The requestested measurement mode: temperature or humidity - * - * @return 1 on success, 0 otherwise - */ -static uint8_t measure(uint8_t *p_value, uint8_t *p_checksum, uint8_t mode); - -/** - * @brief Write one byte - * - * @param value The value to write - * - * @return 1 for acknowledged write, 0 otherwise - */ -static uint8_t write_byte(uint8_t value); - -/** - * @brief Read ony byte - * - * @param ack Set if the data read should be acknowledged - * - * @return The read byte - */ -static uint8_t read_byte(uint8_t ack); - -/** - * @brief Communication reset - */ -static void connection_reset(void); - -/** - * @brief Send start of transmision sequence - */ -static void transmission_start(void); - -/** - * @brief Toggle the clock line - */ -static inline void clk_signal(void); - -/* mutex for exclusive measurement operation */ -mutex_t sht11_mutex = MUTEX_INIT; - -/*---------------------------------------------------------------------------*/ -static inline void clk_signal(void) -{ - SHT11_SCK_HIGH; - xtimer_usleep(SHT11_CLK_WAIT); - SHT11_SCK_LOW; - xtimer_usleep(SHT11_CLK_WAIT); -} - -/*---------------------------------------------------------------------------*/ -static uint8_t write_byte(uint8_t value) -{ - uint8_t i; - uint8_t ack; - - SHT11_DATA_OUT; - - /* send value bit by bit to sht11 */ - for (i = 0; i < 8; i++) { - if (value & BIT7) { - SHT11_DATA_HIGH; - xtimer_usleep(SHT11_DATA_WAIT); - } - else { - SHT11_DATA_LOW; - xtimer_usleep(SHT11_DATA_WAIT); - } - - /* trigger clock signal */ - clk_signal(); - - /* shift value to write next bit */ - value = value << 1; - } - - /* wait for ack */ - SHT11_DATA_IN; - xtimer_usleep(SHT11_CLK_WAIT); - ack = SHT11_DATA; - - clk_signal(); - - return ack; -} -/*---------------------------------------------------------------------------*/ -static uint8_t read_byte(uint8_t ack) -{ - uint8_t i; - uint8_t value = 0; - - SHT11_DATA_IN; - xtimer_usleep(SHT11_DATA_WAIT); - - /* read value bit by bit */ - for (i = 0; i < 8; i++) { - value = value << 1; - SHT11_SCK_HIGH; - xtimer_usleep(SHT11_CLK_WAIT); - - if (SHT11_DATA) { - /* increase data by one when DATA is high */ - value++; - } - - SHT11_SCK_LOW; - xtimer_usleep(SHT11_CLK_WAIT); - } - - /* send ack if necessary */ - SHT11_DATA_OUT; - - if (ack) { - SHT11_DATA_LOW; - xtimer_usleep(SHT11_DATA_WAIT); - } - else { - SHT11_DATA_HIGH; - xtimer_usleep(SHT11_DATA_WAIT); - } - - clk_signal(); - - /* release data line */ - SHT11_DATA_IN; - - return value; -} -/*---------------------------------------------------------------------------*/ -static void transmission_start(void) -{ - /* _____ ________ - DATA: |_______| - ___ ___ - SCK : ___| |___| |______ - */ - SHT11_DATA_OUT; - - /* set initial state */ - SHT11_DATA_HIGH; - xtimer_usleep(SHT11_DATA_WAIT); - SHT11_SCK_LOW; - xtimer_usleep(SHT11_CLK_WAIT); - - SHT11_SCK_HIGH; - xtimer_usleep(SHT11_CLK_WAIT); - - SHT11_DATA_LOW; - xtimer_usleep(SHT11_DATA_WAIT); - - SHT11_SCK_LOW; - xtimer_usleep(SHT11_CLK_WAIT); - - SHT11_SCK_HIGH; - xtimer_usleep(SHT11_CLK_WAIT); - - SHT11_DATA_HIGH; - xtimer_usleep(SHT11_DATA_WAIT); - - SHT11_SCK_LOW; - xtimer_usleep(SHT11_CLK_WAIT); -} -/*---------------------------------------------------------------------------*/ -static void connection_reset(void) -{ - /* _____________________________________________________ ____ - DATA: |_______| - _ _ _ _ _ _ _ _ _ ___ ___ - SCK : __| |__| |__| |__| |__| |__| |__| |__| |__| |______| |___| |__ - */ - uint8_t i; - SHT11_DATA_HIGH; - xtimer_usleep(SHT11_DATA_WAIT); - SHT11_SCK_LOW; - xtimer_usleep(SHT11_CLK_WAIT); - - for (i = 0; i < 9; i++) { - clk_signal(); - } - - transmission_start(); -} -/*---------------------------------------------------------------------------*/ -static uint8_t measure(uint8_t *p_value, uint8_t *p_checksum, uint8_t mode) -{ - uint8_t error = 0; - uint8_t ack = 1; - uint16_t i; - - transmission_start(); - error = write_byte(mode); - - xtimer_usleep(1000); - - /* wait untile sensor has finished measurement or timeout */ - for (i = 0; (i < SHT11_MEASURE_TIMEOUT) && (!error); i++) { - ack = SHT11_DATA; - - if (!ack) { - break; - } - - xtimer_usleep(1000); - } - - error += ack; - - /* read MSB */ - *(p_value + 1) = read_byte(SHT11_ACK); - /* read LSB */ - *(p_value) = read_byte(SHT11_ACK); - /* read checksum */ - *p_checksum = read_byte(SHT11_NO_ACK); - - return (!error); -} -/*---------------------------------------------------------------------------*/ -void sht11_init(void) -{ - sht11_temperature_offset = 0; - SHT11_INIT; - xtimer_usleep(11 * 1000); -} -/*---------------------------------------------------------------------------*/ -uint8_t sht11_read_status(uint8_t *p_value, uint8_t *p_checksum) -{ - uint8_t error = 0; - - transmission_start(); - error |= write_byte(SHT11_STATUS_REG_R); - *p_value = read_byte(SHT11_ACK); - *p_checksum = read_byte(SHT11_NO_ACK); - return (!error); -} -/*---------------------------------------------------------------------------*/ -uint8_t sht11_write_status(uint8_t *p_value) -{ - uint8_t error = 0; - - transmission_start(); - error += write_byte(SHT11_STATUS_REG_W); - error += write_byte(*p_value); - return (!error); -} -/*---------------------------------------------------------------------------*/ -uint8_t sht11_read_sensor(sht11_val_t *value, sht11_mode_t mode) -{ - uint8_t error = 0; - uint8_t checksum; - uint16_t humi_int, temp_int; - - /* Temperature arithmetic where S0(T) is read value - * T = D1 + D2 * S0(T) */ - const float D1 = -39.6; - const float D2 = 0.01; - - /* Arithmetic for linear humdity where S0(RH) is read value - * HL = C1 + C2 * S0(RH) + C3 * SO(RH)^2 */ - const float C1 = -4.0; - const float C2 = +0.0405; - const float C3 = -0.0000028; - - /* Arithmetic for temperature compesated relative humdity - * HT = (T-25) * ( T1 + T2 * SO(RH) ) + HL */ - const float T1 = +0.01; - const float T2 = +0.00008; - - /* check for valid buffer */ - if (value == NULL) { - return 0; - } - - value->temperature = 0; - value->relhum = 0; - value->relhum_temp = 0; - - mutex_lock(&sht11_mutex); - connection_reset(); - - /* measure humidity */ - if (mode & HUMIDITY) { - error += (!measure((uint8_t *) &humi_int, &checksum, SHT11_MEASURE_HUMI)); - } - - /* measure temperature */ - if (mode & TEMPERATURE) { - error += (!measure((uint8_t *) &temp_int, &checksum, SHT11_MEASURE_TEMP)); - } - - /* break on error */ - if (error != 0) { - connection_reset(); - mutex_unlock(&sht11_mutex); - return 0; - } - - if (mode & TEMPERATURE) { - value->temperature = D1 + (D2 * ((float) temp_int)) + sht11_temperature_offset; - } - - if (mode & HUMIDITY) { - value->relhum = C1 + (C2 * ((float) humi_int)) + (C3 * ((float) humi_int) * ((float) humi_int)); - - if (mode & TEMPERATURE) { - value->relhum_temp = (value->temperature - 25) * (T1 + (T2 * (float) humi_int)) + value->relhum; - } - } - - mutex_unlock(&sht11_mutex); - return 1; -} diff --git a/drivers/sht11/Makefile b/drivers/sht1x/Makefile similarity index 100% rename from drivers/sht11/Makefile rename to drivers/sht1x/Makefile diff --git a/drivers/sht1x/include/sht1x_defines.h b/drivers/sht1x/include/sht1x_defines.h new file mode 100644 index 0000000000000000000000000000000000000000..0327387ab08d78cedf0c80f87eb46ea52b5c8514 --- /dev/null +++ b/drivers/sht1x/include/sht1x_defines.h @@ -0,0 +1,72 @@ +/* + * Copyright 2009 Freie Universitaet Berlin (FUB) + * 2018 Otto-von-Guericke-Universität Magdeburg + * + * 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_sht1x + * @{ + * + * @file + * @brief Internal defines required by the SHT10/SHT11/SHT15 driver + * + * @author Marian Buschsieweke <marian.buschsieweke@ovgu.de> + */ + +#ifndef SHT1X_DEFINES_H +#define SHT1X_DEFINES_H + +#include <stdint.h> +#include <periph/gpio.h> +#include <mutex.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Possible values to pass as `ack` parameter to `write_byte` + * @see write_byte + * @{ + */ +#define SHT1X_NO_ACK (1) /**< don't ack read in `read_byte` */ +#define SHT1X_ACK (0) /**< do acknowledge read in `read_byte` */ +/** @} */ + +/** + * @name Commands that can be sent to the SHT1X driver + * @{ + */ +#define SHT1X_STATUS_REG_W (0x06) /**< will write to status register */ +#define SHT1X_STATUS_REG_R (0x07) /**< will read from status register */ +#define SHT1X_MEASURE_TEMP (0x03) /**< tell sensor to measure temperature */ +#define SHT1X_MEASURE_HUM (0x05) /**< tell sensor to measure humidity */ +#define SHT1X_RESET (0x1E) /**< reset the sensor */ +/** @} */ + +/** + * @name Timing parameters for the SHT10/SHT1X/SHT15 + * @{ + */ +#define SHT1X_HALF_CLOCK (1) /**< Half clock length in µsec */ +#define SHT1X_MEASURE_TIMEOUT (1000) /**< Timeout for the SHT1x to complete + the measurement (in millisec) */ +#define SHT1X_RESET_WAIT (11000) /**< Wait 11ms after soft reset */ +/** @} */ + +#define SHT1X_CONF_MASK (0x07) /**< Bitmask to get writable bits of the + status byte */ +#define SHT1X_SAUL_RETRIES (3) /**< How often reading the sensor should + be retried in case of communication + failures */ + +#ifdef __cplusplus +} +#endif + +#endif /* SHT1X_DEFINES_H */ +/** @} */ diff --git a/drivers/sht1x/include/sht1x_params.h b/drivers/sht1x/include/sht1x_params.h new file mode 100644 index 0000000000000000000000000000000000000000..02b94c784de41ed8e5b0eaaedf1caed0debb7668 --- /dev/null +++ b/drivers/sht1x/include/sht1x_params.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2018 Otto-von-Guericke-Universität Magdeburg + * + * 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_sht1x + * + * @{ + * @file + * @brief Default configuration for SHT10/SHT11/SHT15 devices + * + * @author Marian Buschsieweke <marian.buschsieweke@ovgu.de> + */ + +#ifndef SHT1X_PARAMS_H +#define SHT1X_PARAMS_H + +#include "board.h" +#include "sht1x.h" +#include "saul_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Set default configuration parameters for the SHT1X devices + * @{ + */ +#ifndef SHT1X_PARAM_CLK +#define SHT1X_PARAM_CLK (GPIO_PIN(1, 25)) +#endif +#ifndef SHT1X_PARAM_DATA +#define SHT1X_PARAM_DATA (GPIO_PIN(1, 26)) +#endif +#ifndef SHT1X_PARAM_VDD +#define SHT1X_PARAM_VDD (SHT1X_VDD_3_5V) +#endif +#ifndef SHT1X_PARAMS +#define SHT1X_PARAMS { .clk = SHT1X_PARAM_CLK, \ + .data = SHT1X_PARAM_DATA, \ + .vdd = SHT1X_PARAM_VDD } +#endif +/**@}*/ + +/** + * @name Set default SAUL info text depending on used pseudo module + * @{ + */ +#ifndef SHT1X_SAULINFO +#ifdef MODULE_SHT15 +#define SHT1X_SAULINFO { .name = "SHT15 temperature" }, \ + { .name = "SHT15 humidity" } +#else +#ifdef MODULE_SHT10 +#define SHT1X_SAULINFO { .name = "SHT10 temperature" }, \ + { .name = "SHT10 humidity" } +#else +/* SHT11 is the most commonly used, so use that as default */ +#define SHT1X_SAULINFO { .name = "SHT11 temperature" }, \ + { .name = "SHT11 humidity" } +#endif /* MODULE_SHT10 */ +#endif /* MODULE_SHT15 */ +#endif /* SHT1X_SAULINFO */ + +/**@}*/ + +/** + * @brief Configure SHT1X devices + */ +static const sht1x_params_t sht1x_params[] = +{ + SHT1X_PARAMS +}; + +/** + * @brief Allocate and configure entries to the SAUL registry + */ +static const saul_reg_info_t sht1x_saul_info[] = +{ + SHT1X_SAULINFO +}; + +#ifdef __cplusplus +} +#endif + +#endif /* SHT1X_PARAMS_H */ +/** @} */ diff --git a/drivers/sht1x/sht1x.c b/drivers/sht1x/sht1x.c new file mode 100644 index 0000000000000000000000000000000000000000..f2a55cc0139e168001c18a47e374fca56ad69558 --- /dev/null +++ b/drivers/sht1x/sht1x.c @@ -0,0 +1,694 @@ +/* + * Copyright 2009 Freie Universitaet Berlin (FUB) + * 2018 Otto-von-Guericke-Universität Magdeburg + * + * 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_sht1x + * @brief Driver for the Sensirion SHT10/SHT11/SHT15 humidity and + * temperature sensor + * @{ + * + * @file + * @brief SHT10/SHT11/SHT15 Device Driver + * @author Marian Buschsieweke <marian.buschsieweke@ovgu.de> + * + * @} + */ +#include <errno.h> +#include <stdint.h> + +#include "xtimer.h" +#include "sht1x.h" +#include "sht1x_defines.h" +#include "bitarithm.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief Perform measurement + * + * @param dev SHT1X device to use + * @param value Measured value + * @param mode The requested measurement mode: temperature or humidity + * + * @retval 0 Success + * @retval -EIO I/O failure (`gpio_init()` failed) + * @retval -EBADMSG CRC-8 checksum didn't match + * @retval -EPROTO SHT1x did not acknowledge command + * @retval -ECANCELED Measurement timed out + */ +static int measure(const sht1x_dev_t *dev, uint16_t *value, uint8_t mode); + +/** + * @brief Write one byte + * + * @param dev SHT1X device to send the byte to + * @param value The value to write + * + * @retval 1 Write was acknowledged + * @retval 0 Write was *NOT* acknowledged (communication failure) + * @retval -EIO I/O failure (`gpio_init()` failed) + */ +static int write_byte(const sht1x_dev_t *dev, uint8_t value); + +/** + * @brief Read one byte + * + * @param dev SHT1X device to receive the byte from + * @param dest Store the received byte here + * @param ack `SHT1X_ACK` to acknowledge byte, `SHT1X_NO_ACK` otherwise + * + * @retval 0 Success + * @retval -EIO I/O failure (`gpio_init()` failed) + */ +static int read_byte(const sht1x_dev_t *dev, uint8_t *dest, int ack); + +/** + * @brief Communication reset + * + * @param dev SHT1X device to reset the connection to + * + * @retval 0 Success + * @retval -EIO I/O failure (`gpio_init()` failed) + */ +static int connection_reset(const sht1x_dev_t *dev); + +/** + * @brief Send start of transmission sequence + * + * @param dev SHT1X device to send the transmission start sequence to + * + * @retval 0 Success + * @retval -EIO I/O failure (`gpio_init()` failed) + */ +static int transmission_start(const sht1x_dev_t *dev); + +/** + * @brief Toggle the clock line + * + * @param dev SHT1X device to send one clock signal to + */ +static inline void clk_signal(const sht1x_dev_t *dev); + +/** + * @brief Calculate the initial value of the CRC-8 checksum + * + * @param status The current sensor status + * + * @return The initial value of the CRC-8 checksum + */ +static inline uint8_t crc_initial_value(uint8_t status); + +/** + * @brief Reverse the order of bits in a byte (needed for CRC) + * + * @param value The byte to reverse the bits of + * + * @return The reversed input + */ +static inline uint8_t reverse_byte(uint8_t value); + +/** + * @brief Look up table required for CRC-8 calculation + * + * Values taken from the Application Note PDF of Sensirion (December 2011) + */ +static const uint8_t crc_lookup_table[] = { + 0x00, 0x31, 0x62, 0x53, 0xc4, 0xf5, 0xa6, 0x97, + 0xb9, 0x88, 0xdb, 0xea, 0x7d, 0x4c, 0x1f, 0x2e, + 0x43, 0x72, 0x21, 0x10, 0x87, 0xb6, 0xe5, 0xd4, + 0xfa, 0xcb, 0x98, 0xa9, 0x3e, 0x0f, 0x5c, 0x6d, + 0x86, 0xb7, 0xe4, 0xd5, 0x42, 0x73, 0x20, 0x11, + 0x3f, 0x0e, 0x5d, 0x6c, 0xfb, 0xca, 0x99, 0xa8, + 0xc5, 0xf4, 0xa7, 0x96, 0x01, 0x30, 0x63, 0x52, + 0x7c, 0x4d, 0x1e, 0x2f, 0xb8, 0x89, 0xda, 0xeb, + 0x3d, 0x0c, 0x5f, 0x6e, 0xf9, 0xc8, 0x9b, 0xaa, + 0x84, 0xb5, 0xe6, 0xd7, 0x40, 0x71, 0x22, 0x13, + 0x7e, 0x4f, 0x1c, 0x2d, 0xba, 0x8b, 0xd8, 0xe9, + 0xc7, 0xf6, 0xa5, 0x94, 0x03, 0x32, 0x61, 0x50, + 0xbb, 0x8a, 0xd9, 0xe8, 0x7f, 0x4e, 0x1d, 0x2c, + 0x02, 0x33, 0x60, 0x51, 0xc6, 0xf7, 0xa4, 0x95, + 0xf8, 0xc9, 0x9a, 0xab, 0x3c, 0x0d, 0x5e, 0x6f, + 0x41, 0x70, 0x23, 0x12, 0x85, 0xb4, 0xe7, 0xd6, + 0x7a, 0x4b, 0x18, 0x29, 0xbe, 0x8f, 0xdc, 0xed, + 0xc3, 0xf2, 0xa1, 0x90, 0x07, 0x36, 0x65, 0x54, + 0x39, 0x08, 0x5b, 0x6a, 0xfd, 0xcc, 0x9f, 0xae, + 0x80, 0xb1, 0xe2, 0xd3, 0x44, 0x75, 0x26, 0x17, + 0xfc, 0xcd, 0x9e, 0xaf, 0x38, 0x09, 0x5a, 0x6b, + 0x45, 0x74, 0x27, 0x16, 0x81, 0xb0, 0xe3, 0xd2, + 0xbf, 0x8e, 0xdd, 0xec, 0x7b, 0x4a, 0x19, 0x28, + 0x06, 0x37, 0x64, 0x55, 0xc2, 0xf3, 0xa0, 0x91, + 0x47, 0x76, 0x25, 0x14, 0x83, 0xb2, 0xe1, 0xd0, + 0xfe, 0xcf, 0x9c, 0xad, 0x3a, 0x0b, 0x58, 0x69, + 0x04, 0x35, 0x66, 0x57, 0xc0, 0xf1, 0xa2, 0x93, + 0xbd, 0x8c, 0xdf, 0xee, 0x79, 0x48, 0x1b, 0x2a, + 0xc1, 0xf0, 0xa3, 0x92, 0x05, 0x34, 0x67, 0x56, + 0x78, 0x49, 0x1a, 0x2b, 0xbc, 0x8d, 0xde, 0xef, + 0x82, 0xb3, 0xe0, 0xd1, 0x46, 0x77, 0x24, 0x15, + 0x3b, 0x0a, 0x59, 0x68, 0xff, 0xce, 0x9d, 0xac, +}; + +/** @brief Lookuptable for d1 parameter depending on supply voltage */ +static const int16_t sht1x_d1[] = { -4010, -3980, -3970, -3960, -3940 }; + +/*---------------------------------------------------------------------------*/ +static inline void clk_signal(const sht1x_dev_t *dev) +{ + gpio_set(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); + gpio_clear(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); +} + +/*---------------------------------------------------------------------------*/ +static int write_byte(const sht1x_dev_t *dev, uint8_t value) +{ + int ack; + + if (gpio_init(dev->data, GPIO_OUT) == -1) { + return -EIO; + } + + /* send value bit by bit to sht1x */ + for (int i = 0; i < 8; i++) { + if (value & BIT7) { + gpio_set(dev->data); + } + else { + gpio_clear(dev->data); + } + xtimer_usleep(SHT1X_HALF_CLOCK); + + /* trigger clock signal */ + clk_signal(dev); + + /* shift value to write next bit */ + value <<= 1; + } + + /* wait for ack */ + if (gpio_init(dev->data, GPIO_IN) == -1) { + return -EIO; + } + xtimer_usleep(SHT1X_HALF_CLOCK); + ack = gpio_read(dev->data); + + clk_signal(dev); + + return ack; +} + +/*---------------------------------------------------------------------------*/ +static int read_byte(const sht1x_dev_t *dev, uint8_t *dest, int ack) +{ + uint8_t value = 0; + + xtimer_usleep(SHT1X_HALF_CLOCK); + + /* read value bit by bit */ + for (int i = 0; i < 8; i++) { + value <<= 1; + gpio_set(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); + + if (gpio_read(dev->data)) { + /* set bit when DATA is high */ + value |= 0x01; + } + + gpio_clear(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); + } + + /* send ack if necessary */ + if (gpio_init(dev->data, GPIO_OUT) == -1) { + return -EIO; + } + + gpio_write(dev->data, ack); + xtimer_usleep(SHT1X_HALF_CLOCK); + + clk_signal(dev); + + /* release data line */ + if (gpio_init(dev->data, GPIO_IN) == -1) { + return -EIO; + } + + *dest = value; + + return 0; +} + +/*---------------------------------------------------------------------------*/ +static int transmission_start(const sht1x_dev_t *dev) +{ + /* _____ ________ + DATA: |_______| + ___ ___ + SCK : ___| |___| |______ + */ + if (gpio_init(dev->data, GPIO_OUT) == -1) { + return -EIO; + } + + /* set initial state */ + gpio_set(dev->data); + xtimer_usleep(SHT1X_HALF_CLOCK); + gpio_clear(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); + + gpio_set(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); + + gpio_clear(dev->data); + xtimer_usleep(SHT1X_HALF_CLOCK); + + gpio_clear(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); + + gpio_set(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); + + gpio_set(dev->data); + xtimer_usleep(SHT1X_HALF_CLOCK); + + gpio_clear(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); + + if (gpio_init(dev->data, GPIO_IN) == -1) { + return -EIO; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ +static int connection_reset(const sht1x_dev_t *dev) +{ + /* _____________________________________________________ ____ + DATA: |_______| + _ _ _ _ _ _ _ _ _ ___ ___ + SCK : __| |__| |__| |__| |__| |__| |__| |__| |__| |______| |___| |__ + */ + if (gpio_init(dev->data, GPIO_OUT) == -1) { + return -EIO; + } + + gpio_set(dev->data); + xtimer_usleep(SHT1X_HALF_CLOCK); + gpio_clear(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); + + for (int i = 0; i < 9; i++) { + clk_signal(dev); + } + + return transmission_start(dev); +} + +/*---------------------------------------------------------------------------*/ +static inline uint8_t crc_initial_value(uint8_t status) +{ + status &= 0x07; + + return ( + ((0x01 & status) << 7) | + ((0x02 & status) << 5) | + ((0x04 & status) << 3) + ); +} + +/*---------------------------------------------------------------------------*/ +static inline uint8_t reverse_byte(uint8_t value) +{ + uint8_t result = (value & 0x01) << 7; + + result |= (value & 0x02) << 5; + result |= (value & 0x04) << 3; + result |= (value & 0x08) << 1; + result |= (value & 0x10) >> 1; + result |= (value & 0x20) >> 3; + result |= (value & 0x40) >> 5; + result |= (value & 0x80) >> 7; + + return result; +} + +/*---------------------------------------------------------------------------*/ +static int measure(const sht1x_dev_t *dev, uint16_t *value, uint8_t mode) +{ + uint8_t data[2] = { 0, 0 }; + int retval; + + retval = transmission_start(dev); + if (retval != 0) { + return retval; + } + + switch (write_byte(dev, mode)) { + case -EIO: + return -EIO; + case 0: + break; + default: + case 1: + return -EPROTO; + } + + /* wait until sensor has finished measurement or timeout */ + { + int ack = 1; + for (int i = 0; ack != 0; i++) { + if (i > SHT1X_MEASURE_TIMEOUT) { + return -ECANCELED; + } + + xtimer_usleep(1000); + ack = gpio_read(dev->data); + } + } + + /* read MSB */ + retval = read_byte(dev, &data[0], SHT1X_ACK); + if (retval != 0) { + return retval; + } + + /* read LSB, send ACK only if CRC checking is enabled */ + retval = (dev->conf & SHT1X_CONF_SKIP_CRC) ? SHT1X_NO_ACK : SHT1X_ACK; + retval = read_byte(dev, &data[1], retval); + if (retval != 0) { + return retval; + } + + if (!(dev->conf & SHT1X_CONF_SKIP_CRC)) { + uint8_t crc; + uint8_t expected; + + retval = read_byte(dev, &crc, SHT1X_NO_ACK); + if (retval != 0) { + return retval; + } + + expected = crc_initial_value(dev->conf); + expected = crc_lookup_table[expected ^ mode]; + expected = crc_lookup_table[expected ^ data[0]]; + expected = crc_lookup_table[expected ^ data[1]]; + expected = reverse_byte(expected); + if (expected != crc) { + DEBUG("[sht1x] CRC expected: 0x%02x, got: 0x%02x\n" + " CRC0: 0x%02x, CMD: 0x%02x, data: {0x%02x, 0x%02x}\n", + (int)expected, (int)crc, + (int)crc_initial_value(dev->conf), mode, + (int)data[0], (int)data[1]); + return -EBADMSG; + } + } + + *value = (((uint16_t)data[0]) << 8) | (uint16_t)data[1]; + return 0; +} + +/*---------------------------------------------------------------------------*/ +int sht1x_init(sht1x_dev_t *dev, const sht1x_params_t *params) +{ + if ( + !dev || + !params || + (((uint8_t)params->vdd) >= sizeof(sht1x_d1) / sizeof(sht1x_d1[0])) + ) { + return -EINVAL; + } + + dev->clk = params->clk; + dev->data = params->data; + if (gpio_init(dev->clk, GPIO_OUT) || gpio_init(dev->data, GPIO_IN)) { + return -EIO; + } + + dev->temp_off = 0; + dev->hum_off = 0; + dev->conf = 0; + dev->vdd = (uint8_t)params->vdd; + return sht1x_reset(dev); +} + +/*---------------------------------------------------------------------------*/ +int16_t sht1x_temperature(const sht1x_dev_t *dev, uint16_t raw) +{ + if (!dev || (dev->vdd >= sizeof(sht1x_d1) / sizeof(sht1x_d1[0]))) { + return INT16_MIN; + } + + int16_t d1 = sht1x_d1[dev->vdd]; + int16_t d2 = (dev->conf & SHT1X_CONF_LOW_RESOLUTION) ? 4 : 1; + return d1 + d2 * ((int16_t)raw); +} + +/*---------------------------------------------------------------------------*/ +int16_t sht1x_humidity(const sht1x_dev_t *dev, uint16_t raw, int16_t temp) +{ + if (!dev) { + return -1; + } + + static const int32_t c1 = -20468; + static const int32_t t1 = 1; + int32_t c2, c3, c4, t2; + if (dev->conf & SHT1X_CONF_LOW_RESOLUTION) { + c2 = 5872; + c3 = 494801; + c4 = 1000000; + t2 = 781; + } + else { + c2 = 367; + c3 = 791684; + c4 = 100000; + t2 = 12500; + } + + /* + * Calculate linear humidity, but slightly different. Original formula: + * + * hum_lin = c1 + c2 * raw + c3 * (raw * raw) + * + * But we use: + * + * hum_lin = c1 + c2 * raw - (c4 * raw / c3') * (c4 * raw / c3') + * + * where: c3' = 1 / (sqrt(-c3) / c4) + * + * (This better fits for integer calculation) + */ + + int32_t res = ((int32_t)raw * c4) / c3; + res = c1 + c2 * (int32_t)raw - (res * res); + + /* + * Perform temperature compensation, again slightly different. + * Original formula: + * + * hum_true = (temp - 25) * (t1 + t2 * raw) + hum_lin + * + * But we use: + * + * hum_true = (temp - 25) * t1 + (temp - 25) * raw / t2') + hum_lin + * + * where t2' = 1/t2 + */ + int32_t temp_diff = temp - 2500; + res = temp_diff * t1 + (temp_diff * (int32_t)raw * 100) / t2 + res; + return (int16_t)(res / 100); +} + + +/*---------------------------------------------------------------------------*/ +int sht1x_read(const sht1x_dev_t *dev, int16_t *temp, int16_t *rel_hum) +{ + uint16_t temp_raw; + int16_t t; + uint16_t hum_raw; + int retval; + + if ( + !dev || + (dev->vdd >= sizeof(sht1x_d1) / sizeof(sht1x_d1[0])) || + (!temp && !rel_hum) + ) { + return -EINVAL; + } + + retval = measure(dev, &temp_raw, SHT1X_MEASURE_TEMP); + if (retval != 0) { + connection_reset(dev); + return retval; + } + + t = sht1x_temperature(dev, temp_raw) + dev->temp_off; + + if (temp != NULL) { + *temp = t; + } + + if (rel_hum != NULL) { + retval = measure(dev, &hum_raw, SHT1X_MEASURE_HUM); + if (retval != 0) { + connection_reset(dev); + return retval; + } + + *rel_hum = sht1x_humidity(dev, hum_raw, t) + dev->hum_off; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ +int sht1x_configure(sht1x_dev_t *dev, sht1x_conf_t conf) +{ + if (!dev) { + return -EINVAL; + } + + /* Apply config that is not stored on the sensor */ + dev->conf &= SHT1X_CONF_MASK; + dev->conf |= conf & (~(SHT1X_CONF_MASK)); + + /* Send new status byte to sensor, if on-device config was changed */ + if ((conf & SHT1X_CONF_MASK) != (dev->conf & SHT1X_CONF_MASK)) { + int retval = transmission_start(dev); + if (retval != 0) { + return retval; + } + + switch (write_byte(dev, SHT1X_STATUS_REG_W)) { + case -EIO: + return -EIO; + case 0: + break; + default: + case 1: + return -EPROTO; + } + + switch (write_byte(dev, conf & SHT1X_CONF_MASK)) { + case -EIO: + return -EIO; + case 0: + break; + default: + case 1: + return -EPROTO; + } + + /* Read back uploaded configuration to verify that sensor applied it */ + uint8_t status; + retval = sht1x_read_status(dev, &status); + if (retval != 0) { + return retval; + } + if (dev->conf != conf) { + /* Configuration was not applied by sensor */ + return -ECANCELED; + } + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ +int sht1x_read_status(sht1x_dev_t *dev, uint8_t *status) +{ + int retval; + + if (!dev || !status) { + return -EINVAL; + } + + retval = transmission_start(dev); + if (retval != 0) { + return retval; + } + + switch (write_byte(dev, SHT1X_STATUS_REG_R)) { + case -EIO: + return -EIO; + case 0: + break; + default: + case 1: + return -EPROTO; + } + + retval = read_byte(dev, status, SHT1X_ACK); + if (retval != 0) { + return retval; + } + + { + uint8_t crc; + uint8_t expected; + /* read checksum */ + retval = read_byte(dev, &crc, SHT1X_NO_ACK); + if (retval != 0) { + return retval; + } + + expected = crc_initial_value(*status); + expected = crc_lookup_table[expected ^ SHT1X_STATUS_REG_R]; + expected = crc_lookup_table[expected ^ *status]; + expected = reverse_byte(expected); + if (expected != crc) { + DEBUG("[sht1x] CRC expected: 0x%02x, got: 0x%02x\n" + " CRC0: 0x%02x, CMD: 0x%02x, data: {0x%02x}\n", + (int)expected, (int)crc, + (int)crc_initial_value(*status), SHT1X_STATUS_REG_R, + (int)*status); + return -EBADMSG; + } + } + + /* Extract config from status and store it after CRC check passed */ + dev->conf &= ~(SHT1X_CONF_MASK); + dev->conf |= *status & SHT1X_CONF_MASK; + + return 0; +} + +/*---------------------------------------------------------------------------*/ +int sht1x_reset(sht1x_dev_t *dev) +{ + int retval; + + if (!dev) { + return -EINVAL; + } + + retval = transmission_start(dev); + if (retval != 0) { + return retval; + } + + switch (write_byte(dev, SHT1X_RESET)) { + case -EIO: + return -EIO; + case 0: + break; + default: + case 1: + return -EPROTO; + } + + dev->conf = 0; + xtimer_usleep(SHT1X_RESET_WAIT); + + return 0; +} diff --git a/drivers/sht1x/sht1x_saul.c b/drivers/sht1x/sht1x_saul.c new file mode 100644 index 0000000000000000000000000000000000000000..7c0abd92551c7178b4da06e173c908aafe42b785 --- /dev/null +++ b/drivers/sht1x/sht1x_saul.c @@ -0,0 +1,87 @@ +/* + * Copyright 2018 Otto-von-Guericke-Universität Magdeburg + * + * 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_sht1x + * @{ + * + * @file + * @brief SAUL adaption for SHT10/SHT11/SHT15 devices + * + * @author Marian Buschsieweke <marian.buschsieweke@ovgu.de> + * + * @} + */ + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include "phydat.h" +#include "saul.h" +#include "sht1x_defines.h" +#include "sht1x.h" + +static int read(const sht1x_dev_t *dev, int16_t *temp, int16_t *hum) +{ + for (int retries = 0; retries < SHT1X_SAUL_RETRIES; retries++) { + switch (sht1x_read(dev, temp, hum)) { + case 0: + return 0; + case -EBADMSG: + puts("[sht1x] CRC"); + continue; + case -EPROTO: + puts("[sht1x] Sensor did not acknowledge measurement command"); + continue; + case -ECANCELED: + puts("[sht1x] Measurement times out"); + default: + /* Other failure, cannot recover so giving up */ + return -1; + } + } + + printf("[sht1x] Giving up after %i tries\n", SHT1X_SAUL_RETRIES); + + return -1; +} + +static int read_temp(const void *dev, phydat_t *res) +{ + if (read(dev, &res->val[0], NULL) == 0) { + res->unit = UNIT_TEMP_C; + res->scale = -2; + return 1; + } + + return -ECANCELED; +} + +static int read_hum(const void *dev, phydat_t *res) +{ + if (read(dev, NULL, &res->val[0]) == 0) { + res->unit = UNIT_PERCENT; + res->scale = -2; + return 1; + } + + return -ECANCELED; +} + +const saul_driver_t sht1x_saul_temp_driver = { + .read = read_temp, + .write = saul_notsup, + .type = SAUL_SENSE_TEMP +}; + +const saul_driver_t sht1x_saul_hum_driver = { + .read = read_hum, + .write = saul_notsup, + .type = SAUL_SENSE_HUM +}; diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 1e3d3198985a4150e2374ae818a85333c828de4d..0d04d3cec7c6e1f84dd6ae2aa2a37df5051c9e69 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -95,6 +95,11 @@ PSEUDOMODULES += adc121c PSEUDOMODULES += sx1272 PSEUDOMODULES += sx1276 +# include variants of SHT1X drivers as pseudo modules +PSEUDOMODULES += sht10 +PSEUDOMODULES += sht11 +PSEUDOMODULES += sht15 + # include variants of Si114x drivers as pseudo modules PSEUDOMODULES += si1145 PSEUDOMODULES += si1146 diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index 8287a2568ca327c00b8ab2dd5c1bcb7d978d3d2b..64d06a3a9f4819acf57ff361a6be389bc92ddda5 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -20,10 +20,6 @@ #include "auto_init.h" -#ifdef MODULE_SHT11 -#include "sht11.h" -#endif - #ifdef MODULE_MCI #include "diskio.h" #endif @@ -101,10 +97,6 @@ void auto_init(void) DEBUG("Auto init xtimer module.\n"); xtimer_init(); #endif -#ifdef MODULE_SHT11 - DEBUG("Auto init SHT11 module.\n"); - sht11_init(); -#endif #ifdef MODULE_MCI DEBUG("Auto init mci module.\n"); mci_initialize(); @@ -275,6 +267,12 @@ void auto_init(void) #endif /* initialize sensors and actuators */ +#ifdef MODULE_SHT1X + DEBUG("Auto init SHT1X module (SHT10/SHT11/SHT15 sensor driver).\n"); + extern void auto_init_sht1x(void); + auto_init_sht1x(); +#endif + #ifdef MODULE_AUTO_INIT_SAUL DEBUG("auto_init SAUL\n"); diff --git a/sys/auto_init/auto_init_sht1x.c b/sys/auto_init/auto_init_sht1x.c new file mode 100644 index 0000000000000000000000000000000000000000..3264ab9b45ed0c8446d4cc5ce98e3952f9d5130b --- /dev/null +++ b/sys/auto_init/auto_init_sht1x.c @@ -0,0 +1,102 @@ +/* + * Copyright 2018 Otto-von-Guericke-Universität Magdeburg + * + * 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 + * @{ + * + * @file + * @brief Auto initialization for SHT1X temperature/humidity sensors + * + * @author Marian Buschsieweke <marian.buschsieweke@ovgu.de> + * + * @} + */ + +#ifdef MODULE_SHT1X + +#include "log.h" +#include "sht1x_params.h" +#include "sht1x.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief Define the number of configured sensors + */ +#define SHT1X_NUM (sizeof(sht1x_params) / sizeof(sht1x_params[0])) + +/** + * @brief Allocate memory for the device descriptors + */ +sht1x_dev_t sht1x_devs[SHT1X_NUM]; + +#ifdef MODULE_AUTO_INIT_SAUL + +/** + * @brief Memory for the SAUL registry entries + */ +static saul_reg_t saul_entries[SHT1X_NUM * 2]; + +/** + * @name Import SAUL endpoints + * @{ + */ +extern const saul_driver_t sht1x_saul_temp_driver; +extern const saul_driver_t sht1x_saul_hum_driver; +/** @} */ + +#endif /* MODULE_AUTO_INIT_SAUL */ + +static void sht1x_error(unsigned int num, const char *reason) +{ + LOG_ERROR("[auto_init] error initializing SHT10/SHT11/SHT15 sensor " + "#%u: %s\n", num, reason); +} + +void auto_init_sht1x(void) +{ + for (unsigned int i = 0; i < SHT1X_NUM; i++) { + DEBUG("[auto_init_sht1x] Initializing SHT1X sensor #%u\n", i); + + switch (sht1x_init(&sht1x_devs[i], &sht1x_params[i])) { + case 0: + break; + case -EIO: + sht1x_error(i, "Failed to initialize GPIOs"); + continue; + case -EINVAL: + sht1x_error(i, "Invalid configuration for VDD"); + continue; + case -EPROTO: + sht1x_error(i, "Reset command not acknowledged"); + continue; + default: + /* Should not happen, but better safe than sorry */ + sht1x_error(i, "?"); + continue; + } + +#ifdef MODULE_AUTO_INIT_SAUL + saul_entries[(i * 2) ].dev = &(sht1x_devs[i]); + saul_entries[(i * 2) + 1].dev = &(sht1x_devs[i]); + saul_entries[(i * 2) ].name = sht1x_saul_info[(i * 2) ].name; + saul_entries[(i * 2) + 1].name = sht1x_saul_info[(i * 2) + 1].name; + saul_entries[(i * 2) ].driver = &sht1x_saul_temp_driver; + saul_entries[(i * 2) + 1].driver = &sht1x_saul_hum_driver; + saul_reg_add(&(saul_entries[(i * 2) ])); + saul_reg_add(&(saul_entries[(i * 2) + 1])); +#endif /* MODULE_AUTO_INIT_SAUL */ + } +} + +#else +typedef int dont_be_pedantic; +#endif /* MODULE_SHT1X */ diff --git a/sys/shell/commands/Makefile b/sys/shell/commands/Makefile index 8e8e56e3d9fc46427e64ca3c23d35c419bebb618..5d1908f2d13e9c4d903f37aab68495b514688e7d 100644 --- a/sys/shell/commands/Makefile +++ b/sys/shell/commands/Makefile @@ -8,8 +8,8 @@ endif ifneq (,$(filter ps,$(USEMODULE))) SRC += sc_ps.c endif -ifneq (,$(filter sht11,$(USEMODULE))) - SRC += sc_sht11.c +ifneq (,$(filter sht1x,$(USEMODULE))) + SRC += sc_sht1x.c endif ifneq (,$(filter lpc2387,$(USEMODULE))) SRC += sc_heap.c diff --git a/sys/shell/commands/sc_sht11.c b/sys/shell/commands/sc_sht11.c deleted file mode 100644 index 6722626125bfb714a320248cca28e11d53fae7fd..0000000000000000000000000000000000000000 --- a/sys/shell/commands/sc_sht11.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2013 INRIA. - * - * 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_shell_commands - * @{ - * - * @file - * @brief Provides shell commands to poll sht11 sensor - * - * @author Oliver Hahm <oliver.hahm@inria.fr> - * - * @} - */ - -#include <stdio.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include "sht11.h" - -#ifdef MODULE_SHT11 - -extern float sht11_temperature_offset; - -int _get_humidity_handler(int argc, char **argv) -{ - (void) argc; - (void) argv; - - uint8_t success; - sht11_val_t sht11_val; - success = sht11_read_sensor(&sht11_val, HUMIDITY | TEMPERATURE); - - if (!success) { - printf("Error reading SHT11\n"); - - return 1; - } - else { - printf("Relative humidity: %5.2f%% / Temperature compensated humidity; %5.2f%%\n", - (double) sht11_val.relhum, (double) sht11_val.relhum_temp); - - return 0; - } -} - -int _get_temperature_handler(int argc, char **argv) -{ - (void) argc; - (void) argv; - - uint8_t success; - sht11_val_t sht11_val; - success = sht11_read_sensor(&sht11_val, TEMPERATURE); - - if (!success) { - printf("Error reading SHT11\n"); - - return 1; - } - else { - printf("Temperature: %-6.2f°C\n", (double) sht11_val.temperature); - - return 0; - } -} - -int _get_weather_handler(int argc, char **argv) -{ - (void) argc; - (void) argv; - - uint8_t success; - sht11_val_t sht11_val; - success = sht11_read_sensor(&sht11_val, HUMIDITY | TEMPERATURE); - - if (!success) { - printf("Error reading SHT11\n"); - - return 1; - } - else { - printf("Relative humidity: %5.2f%% / Temperature compensated humidity; %5.2f%% ", - (double) sht11_val.relhum, (double) sht11_val.relhum_temp); - printf("Temperature: %-6.2f°C\n", (double) sht11_val.temperature); - - return 0; - } -} - -int _set_offset_handler(int argc, char **argv) -{ - if (argc != 2) { - printf("Usage: %s <OFFSET>\n", argv[0]); - - return 1; - } - else { - sht11_temperature_offset = atoi(argv[1]); - printf("Temperature offset set to %f\n", (double) sht11_temperature_offset); - - return 0; - } -} - -#endif diff --git a/sys/shell/commands/sc_sht1x.c b/sys/shell/commands/sc_sht1x.c new file mode 100644 index 0000000000000000000000000000000000000000..c814ccff484d7afb4a89ece2bfaa535377657b53 --- /dev/null +++ b/sys/shell/commands/sc_sht1x.c @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2013 INRIA + * 2018 Otto-von-Guericke-Universität Magdeburg + * + * 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_shell_commands + * @{ + * + * @file + * @brief Provides shell commands to access SHT10/SHT11/SHT15 sensors + * + * @author Oliver Hahm <oliver.hahm@inria.fr> + * @author Marian Buschsieweke <marian.buschsieweke@ovgu.de> + * + * @} + */ + +#ifdef MODULE_SHT1X + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include "sht1x.h" +#include "sht1x_params.h" + +#define SHT1X_NUM (sizeof(sht1x_params) / sizeof(sht1x_params[0])) + +extern sht1x_dev_t sht1x_devs[SHT1X_NUM]; + +static sht1x_dev_t *get_dev(int argc, char **argv) +{ + switch (argc) { + case 1: + return &sht1x_devs[0]; + case 2: + { + int pos = atoi(argv[1]); + if ((pos < 0) || (pos >= (int)SHT1X_NUM)) { + printf("No SHT10/SHT11/SHT15 device with number %i\n", pos); + return NULL; + } + return &sht1x_devs[pos]; + } + default: + break; + } + + printf("Usage: %s [DEVICE_NUMBER]\n", argv[0]); + return NULL; +} + +static void error_msg(const char *msg) +{ + printf("[sht1x] Operation failed: %s\n", msg); +} + +static int read_sensor(int16_t *temp, int16_t *hum, int argc, char **argv) +{ + const sht1x_dev_t *dev = get_dev(argc, argv); + + if (!dev) { + return -1; + } + + switch (sht1x_read(dev, temp, hum)) { + case 0: + break; + case -EIO: + error_msg("gpio_init() failed"); + return -1; + case -EBADMSG: + error_msg("CRC checksum error"); + return -1; + case -ECANCELED: + error_msg("Measurement timed out"); + return -1; + case -EPROTO: + error_msg("Sensor did not acknowledge command"); + return -1; + default: + /* Should never happen, but better safe the sorry */ + error_msg("Unknown error"); + return -1; + } + + return 0; +} + +int _get_humidity_handler(int argc, char **argv) +{ + int16_t hum; + + if (read_sensor(NULL, &hum, argc, argv)) { + return -1; + } + + printf("Relative humidity: %i.%02i%%\n", (int)hum / 100, (int)hum % 100); + return 0; +} + +int _get_temperature_handler(int argc, char **argv) +{ + int16_t temp; + + if (read_sensor(&temp, NULL, argc, argv)) { + return -1; + } + + printf("Temperature: %i.%02i°C\n", (int)temp / 100, (int)temp % 100); + return 0; +} + +int _get_weather_handler(int argc, char **argv) +{ + int16_t hum; + int16_t temp; + + if (read_sensor(&temp, &hum, argc, argv)) { + return -1; + } + + printf("Relative humidity: %i.%02i%%\n", (int)hum / 100, (int)hum % 100); + printf("Temperature: %i.%02i°C\n", (int)temp / 100, (int)temp % 100); + return 0; +} + +static void print_config(const sht1x_dev_t *dev) +{ + const char *vdds[] = { "5.0", "4.0", "3.5", "3.0", "2.5" }; + + printf("Sensor VDD = %s\n", vdds[dev->vdd]); + printf("Temperature offset [-t]: %i\n", (int)dev->temp_off); + printf("Humidity offset [-h]: %i\n", (int)dev->hum_off); + printf("Resolution [-r]: %s\n", + (dev->conf & SHT1X_CONF_LOW_RESOLUTION) ? "low" : "high"); + printf("Skip calibration (faster reading) [-c]: %s\n", + (dev->conf & SHT1X_CONF_SKIP_CALIBRATION) ? "yes" : "no"); + printf("Heater [-H]: %s\n", + (dev->conf & SHT1X_CONF_ENABLE_HEATER) ? "on" : "off"); + printf("CRC checking [-C]: %s\n", + (dev->conf & SHT1X_CONF_SKIP_CRC) ? "off" : "on"); +} + +static void unknown_parameter(int index, char **argv) +{ + printf("Unknown parameter \"%s\"\n" + "Usage: \"%s [PARAMS]\", run \"%s --help\" for help\n", + argv[index], argv[0], argv[0]); +} + +static void missing_argument(int index, char **argv) +{ + printf("Missing argument for parameter \"%s\"\n" + "Usage: \"%s [%s <ARG>][PARAMS]\", run \"%s --help\" for help\n", + argv[index], argv[0], argv[index], argv[0]); +} + +static void invalid_argument(int index, char **argv, const char *valid) +{ + printf("Invalid argument \"%s\" for parameter \"%s\"\n" + "Valid arguments are: \"%s\", run \"%s --help\" for help\n", + argv[index + 1], argv[index], valid, argv[0]); +} + +int _sht_config_handler(int argc, char **argv) +{ + uint8_t set_conf = 0; + uint8_t unset_conf = 0; + int16_t temp_off = INT16_MAX; + int16_t hum_off = INT16_MAX; + int dev_num = 0; + + if ((argc == 2) && (strcmp("--help", argv[1]) == 0)) { + printf("Usage: \"%s [PARMS]\n" + "\n" + "Supported parameters:\n" + " -d <NUM>\n" + " Use SHT10/11/15 sensor number <NUM>. Default: 0\n" + "\n" + " -t <OFFSET>\n" + " Add <OFFSET> (in e-2°C) to all temperature measurements\n" + "\n" + " -h <OFFSET>\n" + " Add <OFFSET> (in e-2%%) to all humidity measurements\n" + "\n" + " -r l/h\n" + " Set resolution to low/high. Low resolution trades " + "presicion for speed\n" + "\n" + " -H y/n\n" + " Turns heater on/off. Can increase temperature by up to " + "10°C\n" + "\n" + " -C y/n\n" + " Turns on/off CRC checking. No checking trades robustness " + "for speed\n", + argv[0]); + return 0; + } + + for (int i = 1; i < argc; i++) { + if ((argv[i][0] != '-') || (!argv[i][1]) || (argv[i][2])) { + unknown_parameter(i, argv); + return -1; + } + + switch (argv[i][1]) { + case 'd': + if (++i >= argc) { + missing_argument(i - 1, argv); + return -1; + } + dev_num = atoi(argv[i]); + if ((dev_num < 0) || (dev_num >= (int)SHT1X_NUM)) { + printf("No SHT10/11/15 sensor with number %i\n", dev_num); + return -1; + } + break; + case 't': + if (++i >= argc) { + missing_argument(i - 1, argv); + return -1; + } + temp_off = (int16_t)atoi(argv[i]); + break; + case 'h': + if (++i >= argc) { + missing_argument(i - 1, argv); + return -1; + } + hum_off = (int16_t)atoi(argv[i]); + break; + case 'r': + if (++i >= argc) { + missing_argument(i - 1, argv); + return -1; + } + if ((!argv[i][0]) || (argv[i][1])) { + invalid_argument(i - 1, argv, "l, h"); + return -1; + } + switch (argv[i][0]) { + case 'l': + set_conf |= SHT1X_CONF_LOW_RESOLUTION; + break; + case 'h': + unset_conf |= SHT1X_CONF_LOW_RESOLUTION; + break; + default: + invalid_argument(i - 1, argv, "l, h"); + return -1; + } + break; + case 'c': + if (++i >= argc) { + missing_argument(i - 1, argv); + return -1; + } + if ((!argv[i][0]) || (argv[i][1])) { + invalid_argument(i - 1, argv, "y, n"); + return -1; + } + switch (argv[i][0]) { + case 'y': + set_conf |= SHT1X_CONF_SKIP_CALIBRATION; + break; + case 'n': + unset_conf |= SHT1X_CONF_SKIP_CALIBRATION; + break; + default: + invalid_argument(i - 1, argv, "y, n"); + return -1; + } + break; + case 'H': + if (++i >= argc) { + missing_argument(i - 1, argv); + return -1; + } + if ((!argv[i][0]) || (argv[i][1])) { + invalid_argument(i - 1, argv, "y, n"); + return -1; + } + switch (argv[i][0]) { + case 'y': + set_conf |= SHT1X_CONF_ENABLE_HEATER; + break; + case 'n': + unset_conf |= SHT1X_CONF_ENABLE_HEATER; + break; + default: + invalid_argument(i - 1, argv, "y, n"); + return -1; + } + break; + case 'C': + if (++i >= argc) { + missing_argument(i - 1, argv); + return -1; + } + if ((!argv[i][0]) || (argv[i][1])) { + invalid_argument(i - 1, argv, "y, n"); + return -1; + } + switch (argv[i][0]) { + case 'y': + unset_conf |= SHT1X_CONF_SKIP_CRC; + break; + case 'n': + set_conf |= SHT1X_CONF_SKIP_CRC; + break; + default: + invalid_argument(i - 1, argv, "y, n"); + return -1; + } + break; + default: + unknown_parameter(i, argv); + return -1; + } + } + + if ((set_conf) || (unset_conf)) { + /* Apply new configuration */ + uint8_t new_conf = sht1x_devs[dev_num].conf; + new_conf &= ~(unset_conf); + new_conf |= set_conf; + switch (sht1x_configure(&sht1x_devs[dev_num], new_conf)) { + case 0: + break; + case -EIO: + error_msg("gpio_init() failed"); + return -1; + case -EBADMSG: + error_msg("CRC checksum error"); + return -1; + case -ECANCELED: + error_msg("Sensor did not apply configuration"); + return -1; + case -EPROTO: + error_msg("Sensor did not acknowledge command"); + return -1; + default: + /* Should never happen, but better safe the sorry */ + error_msg("Unknown error"); + return -1; + } + } + + if (temp_off != INT16_MAX) { + if ((temp_off > 2000) || (temp_off < -2000)) { + printf("A temperature offset of %i.%02i°C is unreasonable\n", + (int)temp_off / 100, (int)temp_off % 100); + return -1; + } + sht1x_devs[dev_num].temp_off = temp_off; + } + + if (hum_off != INT16_MAX) { + if ((hum_off > 1000) || (hum_off < -1000)) { + printf("A humidity offset of %i.%02i%% is unreasonable\n", + (int)hum_off / 100, (int)hum_off % 100); + return -1; + } + sht1x_devs[dev_num].hum_off = hum_off; + } + + print_config(&sht1x_devs[dev_num]); + return 0; +} + +#endif diff --git a/sys/shell/commands/shell_commands.c b/sys/shell/commands/shell_commands.c index 6ab2937dc293f0a4520237fce81cbe1501ee96ea..a9871517d3e64da7b5cdbd5642db37827d1f3e27 100644 --- a/sys/shell/commands/shell_commands.c +++ b/sys/shell/commands/shell_commands.c @@ -37,11 +37,11 @@ extern int _heap_handler(int argc, char **argv); extern int _ps_handler(int argc, char **argv); #endif -#ifdef MODULE_SHT11 +#ifdef MODULE_SHT1X extern int _get_temperature_handler(int argc, char **argv); extern int _get_humidity_handler(int argc, char **argv); extern int _get_weather_handler(int argc, char **argv); -extern int _set_offset_handler(int argc, char **argv); +extern int _sht_config_handler(int argc, char **argv); #endif #ifdef MODULE_LTC4150 @@ -153,11 +153,11 @@ const shell_command_t _shell_command_list[] = { #ifdef MODULE_PS {"ps", "Prints information about running threads.", _ps_handler}, #endif -#ifdef MODULE_SHT11 +#ifdef MODULE_SHT1X {"temp", "Prints measured temperature.", _get_temperature_handler}, {"hum", "Prints measured humidity.", _get_humidity_handler}, {"weather", "Prints measured humidity and temperature.", _get_weather_handler}, - {"offset", "Set temperature offset.", _set_offset_handler}, + {"sht-config", "Get/set SHT10/11/15 sensor configuration.", _sht_config_handler}, #endif #ifdef MODULE_LTC4150 {"cur", "Prints current and average power consumption.", _get_current_handler}, diff --git a/tests/driver_sht1x/Makefile b/tests/driver_sht1x/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..4c51663c52712d0b4b7e8688d150334739cd430a --- /dev/null +++ b/tests/driver_sht1x/Makefile @@ -0,0 +1,12 @@ +include ../Makefile.tests_common + +DRIVER ?= sht11 +BOARD ?= msba2 + +USEMODULE += $(DRIVER) +USEMODULE += shell +USEMODULE += saul_default +USEMODULE += shell_commands +USEMODULE += ps + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_sht1x/main.c b/tests/driver_sht1x/main.c new file mode 100644 index 0000000000000000000000000000000000000000..864ce7f6a2e9173a8d336fad82b784db1fb2c012 --- /dev/null +++ b/tests/driver_sht1x/main.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2018 Otto-von-Guericke-Universität Magdeburg + * + * 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 SHT10/11/15 family of temperature and + * humidity sensors + * + * @author Marian Buschsieweke <marian.buschsieweke@ovgu.de> + * + * @} + */ + +#include <stdio.h> + +#include "shell.h" +#include "shell_commands.h" +#include "sht1x_params.h" + +#define SHT1X_NUM (sizeof(sht1x_params) / sizeof(sht1x_params[0])) +extern sht1x_dev_t sht1x_devs[SHT1X_NUM]; +static sht1x_dev_t *dev = &sht1x_devs[0]; + +static int run_tests(void) +{ + const char *resolution[] = { "high", "low" }; + const char *calibration[] = { "on", "off" }; + const char *heater[] = { "off", "on" }; + const char *crc[] = { "on", "off" }; + uint8_t conf = 0; + + puts("Reading the sensor three times for all possible configurations:"); + + for (int i_res = 0; i_res < 2; i_res++) { + if (i_res) { + conf |= SHT1X_CONF_LOW_RESOLUTION; + } + else { + conf &= ~(SHT1X_CONF_LOW_RESOLUTION); + } + + for (int i_cal = 0; i_cal < 2; i_cal++) { + if (i_cal) { + conf |= SHT1X_CONF_SKIP_CALIBRATION; + } + else { + conf &= ~(SHT1X_CONF_SKIP_CALIBRATION); + } + + for (int i_heater = 0; i_heater < 2; i_heater++) { + if (i_heater) { + conf |= SHT1X_CONF_ENABLE_HEATER; + } + else { + conf &= ~(SHT1X_CONF_ENABLE_HEATER); + } + + for (int i_crc = 0; i_crc < 2; i_crc++) { + if (i_crc) { + conf |= SHT1X_CONF_SKIP_CRC; + } + else { + conf &= ~(SHT1X_CONF_SKIP_CRC); + } + + printf("Resolution: %s, calibration: %s, heater: %s, " + "CRC-checking: %s\n", + resolution[i_res], + calibration[i_cal], + heater[i_heater], + crc[i_crc]); + switch (sht1x_configure(dev, conf)) { + case 0: + break; + case -EIO: + puts("Error: gpio_init() failed"); + return -1; + case -EPROTO: + puts("Error: Sensor did not acknowledge command"); + return -1; + case -ECANCELED: + puts("Error: Sensor did not apply configuration"); + return -1; + case -EBADMSG: + puts("Error: CRC error while validating " + "configuration"); + return -1; + default: + /* Will never happen, but better safe than sorry */ + puts("Unknown error"); + return -1; + } + + for (int i = 0; i < 3; i++) { + int16_t temp, hum; + switch (sht1x_read(dev, &temp, &hum)) { + case 0: + break; + case -EIO: + puts("Error: gpio_init() failed"); + return -1; + case -EPROTO: + puts("Error: Sensor did not acknowledge " + "command"); + return -1; + case -ECANCELED: + puts("Error: Measurement timed out"); + return -1; + default: + /* Won't happen, but better safe than sorry */ + puts("Unknown error"); + return -1; + } + + printf("Temperature: %i.%02i°C, Humidity: %i.%02i%%\n", + (int)temp / 100, (int)temp % 100, + (int)hum / 100, (int)hum % 100); + } + } + } + } + } + + puts("Restoring default configuration"); + switch (sht1x_configure(dev, 0)) { + case 0: + break; + case -EIO: + puts("Error: gpio_init() failed"); + return -1; + case -EPROTO: + puts("Error: Sensor did not acknowledge command"); + return -1; + case -ECANCELED: + puts("Error: Sensor did not apply configuration"); + return -1; + case -EBADMSG: + puts("Error: CRC error while validating " + "configuration"); + return -1; + default: + /* Will never happen, but better safe than sorry */ + puts("Unknown error"); + return -1; + } + + return 0; +} + +int main(void) +{ + if (run_tests()) { + puts("TESTS FAILED!"); + } + else { + puts("All automatic tests finished."); + } + + puts("Dropping to shell for manual testing"); + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +} diff --git a/tests/unittests/tests-sht1x/Makefile b/tests/unittests/tests-sht1x/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..48422e909a47d7cd428d10fa73825060ccc8d8c2 --- /dev/null +++ b/tests/unittests/tests-sht1x/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-sht1x/Makefile.include b/tests/unittests/tests-sht1x/Makefile.include new file mode 100644 index 0000000000000000000000000000000000000000..4ccfa29f725f5639114c15e1fc0022c3c74308d5 --- /dev/null +++ b/tests/unittests/tests-sht1x/Makefile.include @@ -0,0 +1 @@ +USEMODULE += sht1x diff --git a/tests/unittests/tests-sht1x/tests-sht1x.c b/tests/unittests/tests-sht1x/tests-sht1x.c new file mode 100644 index 0000000000000000000000000000000000000000..3991db589c8f5b9878d2a214baa0474fd9ed324f --- /dev/null +++ b/tests/unittests/tests-sht1x/tests-sht1x.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2018 Otto-von-Guericke-Universität Magdeburg + * + * 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. + */ + +#include <sht1x.h> +#include <stdlib.h> +#include "embUnit/embUnit.h" +#include "tests-sht1x.h" + +/** @brief Maximum difference from correct temperature value [in e-02 °C] */ +static const int16_t max_diff_temp = 1; +/** @brief Maximum difference from correct humidity value [in e-02 %] */ +static const int16_t max_diff_hum = 10; + +static int16_t expected_temp(const sht1x_dev_t *dev, uint16_t _raw) +{ + static const double d1_table[] = { -40.1, -39.8, -39.7, -39.6, -39.4 }; + double d1 = d1_table[dev->vdd]; + double d2 = (dev->conf & SHT1X_CONF_LOW_RESOLUTION) ? 0.04 : 0.01; + double raw = (double)_raw; + double temp = d1 + d2 * raw; + + return (int16_t)(temp * 100.0); +} + +static int16_t expected_hum(const sht1x_dev_t *dev, uint16_t _raw, int16_t _temp) +{ + static const double c1 = -2.0468; + static const double t1 = 0.01; + double temp = ((double)_temp) / 100.0; + double raw = (double)_raw; + double c2, c3, t2, hum_linear, hum_real; + + if (dev->conf & SHT1X_CONF_LOW_RESOLUTION) { + c2 = 0.5872; + c3 = -4.0845e-04; + t2 = 0.00128; + } + else { + c2 = 0.0367; + c3 = -1.5955e-06; + t2 = 0.00008; + } + + hum_linear = c1 + c2 * raw + c3 * (raw * raw); + hum_real = (temp - 25.0) * (t1 + t2 * raw) + hum_linear; + return (int16_t)(hum_real * 100.0); +} + +static void test_sht1x_conversion(void) +{ + const uint8_t vdds[] = { + SHT1X_VDD_5_0V, SHT1X_VDD_4_0V, SHT1X_VDD_3_5V, SHT1X_VDD_3_0V, + SHT1X_VDD_2_5V, + }; + const uint8_t confs[] = { SHT1X_CONF_LOW_RESOLUTION, 0 }; + const uint16_t max_raw_temps[] = { 0xfff, 0x3fff }; + const uint16_t max_raw_hums[] = { 0xff, 0xfff }; + sht1x_dev_t dev = { .conf = 0 }; + + for (size_t i_res = 0; i_res < sizeof(confs) / sizeof(confs[0]); i_res++) { + dev.conf = confs[i_res]; + uint16_t max_raw_temp = max_raw_temps[i_res]; + uint16_t max_raw_hum = max_raw_hums[i_res]; + for (size_t i_vdd = 0; i_vdd < sizeof(vdds) / sizeof(vdds[0]); i_vdd++) { + dev.vdd = vdds[i_vdd]; + for (uint16_t raw_temp = 0; raw_temp <= max_raw_temp; raw_temp++) { + int16_t got_temp = sht1x_temperature(&dev, raw_temp); + int16_t exp_temp = expected_temp(&dev, raw_temp); + + TEST_ASSERT(((got_temp - max_diff_temp) <= exp_temp) && + ((got_temp + max_diff_temp) >= exp_temp)); + } + } + + /* Testing for temperatures in -10.00°C and 65.00°C in steps of 0.13°C */ + for (int16_t temp = -1000; temp < 6500; temp += 13) { + for (uint16_t raw_hum = 0; raw_hum <= max_raw_hum; raw_hum++) { + int16_t exp_hum = expected_hum(&dev, raw_hum, temp); + if ((exp_hum < 0) || (exp_hum > 10000)) { + /* Result out of range, ignore it */ + continue; + } + int16_t got_hum = sht1x_humidity(&dev, raw_hum, temp); + TEST_ASSERT(((got_hum - max_diff_hum) <= exp_hum) && + ((got_hum + max_diff_hum) >= exp_hum)); + } + } + } +} + +Test *tests_sht1x_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_sht1x_conversion), + }; + + EMB_UNIT_TESTCALLER(sht1x_tests, NULL, NULL, fixtures); + + return (Test *)&sht1x_tests; +} + +void tests_sht1x(void) +{ + TESTS_RUN(tests_sht1x_tests()); +} diff --git a/tests/unittests/tests-sht1x/tests-sht1x.h b/tests/unittests/tests-sht1x/tests-sht1x.h new file mode 100644 index 0000000000000000000000000000000000000000..0619efc65440e9f5d46eebaf356de8f5fba631c6 --- /dev/null +++ b/tests/unittests/tests-sht1x/tests-sht1x.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 Otto-von-Guericke-Universität Magdeburg + * + * 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. + */ + +/** + * @addtogroup unittests + * @{ + * + * @file + * @brief Unittests for the conversions used in the sht1x driver + * + * @author Marian Buschsieweke <marian.buschsieweke@ovgu.de> + */ +#ifndef TESTS_SHT1X_H +#define TESTS_SHT1X_H + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_sht1x(void); + +#ifdef __cplusplus +} +#endif +#endif /* TESTS_SHT1X_H */ +/** @} */