From a80b0a1707b2a567f3485af932229c281b82eb69 Mon Sep 17 00:00:00 2001 From: lucadentella Date: Sun, 8 Oct 2017 10:57:58 +0200 Subject: [PATCH] First version --- component.mk | 7 ++ htu21d.c | 232 +++++++++++++++++++++++++++++++++++++++++++++++++++ htu21d.h | 64 ++++++++++++++ 3 files changed, 303 insertions(+) create mode 100644 component.mk create mode 100644 htu21d.c create mode 100644 htu21d.h diff --git a/component.mk b/component.mk new file mode 100644 index 0000000..3d6fbce --- /dev/null +++ b/component.mk @@ -0,0 +1,7 @@ +# +# Component Makefile +# + +COMPONENT_SRCDIRS := . +COMPONENT_ADD_INCLUDEDIRS := . +COMPONENT_PRIV_INCLUDEDIRS := \ No newline at end of file diff --git a/htu21d.c b/htu21d.c new file mode 100644 index 0000000..0c17564 --- /dev/null +++ b/htu21d.c @@ -0,0 +1,232 @@ +/* + * HTU21D Component + * + * esp-idf component to interface with HTU21D humidity and temperature sensor + * by TE Connectivity (http://www.te.com/usa-en/product-CAT-HSC0004.html) + * + * Luca Dentella, www.lucadentella.it + */ + + +// Component header file +#include "htu21d.h" + +int htu21d_init(i2c_port_t port, int sda_pin, int scl_pin, gpio_pullup_t sda_internal_pullup, gpio_pullup_t scl_internal_pullup) { + + esp_err_t ret; + _port = port; + + // setup i2c controller + i2c_config_t conf; + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = sda_pin; + conf.scl_io_num = scl_pin; + conf.sda_pullup_en = sda_internal_pullup; + conf.scl_pullup_en = scl_internal_pullup; + conf.master.clk_speed = 100000; + ret = i2c_param_config(port, &conf); + if(ret != ESP_OK) return HTU21D_ERR_CONFIG; + + // install the driver + ret = i2c_driver_install(port, I2C_MODE_MASTER, 0, 0, 0); + if(ret != ESP_OK) return HTU21D_ERR_INSTALL; + + // verify if a sensor is present + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_WRITE, true); + i2c_master_stop(cmd); + if(i2c_master_cmd_begin(port, cmd, 1000 / portTICK_RATE_MS) != ESP_OK) + return HTU21D_ERR_NOTFOUND; + + return HTU21D_ERR_OK; +} + +float ht21d_read_temperature() { + + // get the raw value from the sensor + uint16_t raw_temperature = read_value(TRIGGER_TEMP_MEASURE_NOHOLD); + if(raw_temperature == 0) return -999; + + // return the real value, formula in datasheet + return (raw_temperature * 175.72 / 65536.0) - 46.85; +} + +float ht21d_read_humidity() { + + // get the raw value from the sensor + uint16_t raw_humidity = read_value(TRIGGER_HUMD_MEASURE_NOHOLD); + if(raw_humidity == 0) return -999; + + // return the real value, formula in datasheet + return (raw_humidity * 125.0 / 65536.0) - 6.0; +} + +uint8_t ht21d_get_resolution() { + + uint8_t reg_value = ht21d_read_user_register(); + return reg_value & 0b10000001; +} + +int ht21d_set_resolution(uint8_t resolution) { + + // get the actual resolution + uint8_t reg_value = ht21d_read_user_register(); + reg_value &= 0b10000001; + + // update the register value with the new resolution + resolution &= 0b10000001; + reg_value |= resolution; + + return ht21d_write_user_register(reg_value); +} + +int htu21d_soft_reset() { + + esp_err_t ret; + + // send the command + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, SOFT_RESET, true); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + + switch(ret) { + + case ESP_ERR_INVALID_ARG: + return HTU21D_ERR_INVALID_ARG; + + case ESP_FAIL: + return HTU21D_ERR_FAIL; + + case ESP_ERR_INVALID_STATE: + return HTU21D_ERR_INVALID_STATE; + + case ESP_ERR_TIMEOUT: + return HTU21D_ERR_TIMEOUT; + } + return HTU21D_ERR_OK; +} + +uint8_t ht21d_read_user_register() { + + esp_err_t ret; + + // send the command + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, READ_USER_REG, true); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if(ret != ESP_OK) return 0; + + // receive the answer + uint8_t reg_value; + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_READ, true); + i2c_master_read_byte(cmd, ®_value, 0x01); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if(ret != ESP_OK) return 0; + + return reg_value; +} + +int ht21d_write_user_register(uint8_t value) { + + esp_err_t ret; + + // send the command + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, WRITE_USER_REG, true); + i2c_master_write_byte(cmd, value, true); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + + switch(ret) { + + case ESP_ERR_INVALID_ARG: + return HTU21D_ERR_INVALID_ARG; + + case ESP_FAIL: + return HTU21D_ERR_FAIL; + + case ESP_ERR_INVALID_STATE: + return HTU21D_ERR_INVALID_STATE; + + case ESP_ERR_TIMEOUT: + return HTU21D_ERR_TIMEOUT; + } + return HTU21D_ERR_OK; +} + +uint16_t read_value(uint8_t command) { + + esp_err_t ret; + + // send the command + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, command, true); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if(ret != ESP_OK) return 0; + + // wait for the sensor (50ms) + vTaskDelay(50 / portTICK_RATE_MS); + + // receive the answer + uint8_t msb, lsb, crc; + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_READ, true); + i2c_master_read_byte(cmd, &msb, 0x00); + i2c_master_read_byte(cmd, &lsb, 0x00); + i2c_master_read_byte(cmd, &crc, 0x01); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if(ret != ESP_OK) return 0; + + uint16_t raw_value = ((uint16_t) msb << 8) | (uint16_t) lsb; + if(!is_crc_valid(raw_value, crc)) printf("CRC invalid\r\n"); + return raw_value & 0xFFFC; +} + +// verify the CRC, algorithm in the datasheet (see comments below) +bool is_crc_valid(uint16_t value, uint8_t crc) { + + // line the bits representing the input in a row (first data, then crc) + uint32_t row = (uint32_t)value << 8; + row |= crc; + + // polynomial = x^8 + x^5 + x^4 + 1 + // padded with zeroes corresponding to the bit length of the CRC + uint32_t divisor = (uint32_t)0x988000; + + for (int i = 0 ; i < 16 ; i++) { + + // if the input bit above the leftmost divisor bit is 1, + // the divisor is XORed into the input + if (row & (uint32_t)1 << (23 - i)) row ^= divisor; + + // the divisor is then shifted one bit to the right + divisor >>= 1; + } + + // the remainder should equal zero if there are no detectable errors + return (row == 0); +} + diff --git a/htu21d.h b/htu21d.h new file mode 100644 index 0000000..b4ffb16 --- /dev/null +++ b/htu21d.h @@ -0,0 +1,64 @@ +/* + * HTU21D Component + * + * esp-idf component to interface with HTU21D humidity and temperature sensor + * by TE Connectivity (http://www.te.com/usa-en/product-CAT-HSC0004.html) + * + * Luca Dentella, www.lucadentella.it + */ + + + // Error library +#include "esp_err.h" + +// I2C driver +#include "driver/i2c.h" + +// FreeRTOS (for delay) +#include "freertos/task.h" + + +#ifndef __ESP_HTU21D_H__ +#define __ESP_HTU21D_H__ + +// sensor address +#define HTU21D_ADDR 0x40 + +// HTU21D commands +#define TRIGGER_TEMP_MEASURE_HOLD 0xE3 +#define TRIGGER_HUMD_MEASURE_HOLD 0xE5 +#define TRIGGER_TEMP_MEASURE_NOHOLD 0xF3 +#define TRIGGER_HUMD_MEASURE_NOHOLD 0xF5 +#define WRITE_USER_REG 0xE6 +#define READ_USER_REG 0xE7 +#define SOFT_RESET 0xFE + +// return values +#define HTU21D_ERR_OK 0x00 +#define HTU21D_ERR_CONFIG 0x01 +#define HTU21D_ERR_INSTALL 0x02 +#define HTU21D_ERR_NOTFOUND 0x03 +#define HTU21D_ERR_INVALID_ARG 0x04 +#define HTU21D_ERR_FAIL 0x05 +#define HTU21D_ERR_INVALID_STATE 0x06 +#define HTU21D_ERR_TIMEOUT 0x07 + +// variables +i2c_port_t _port; + +// functions +int htu21d_init(i2c_port_t port, int sda_pin, int scl_pin, gpio_pullup_t sda_internal_pullup, gpio_pullup_t scl_internal_pullup); +float ht21d_read_temperature(); +float ht21d_read_humidity(); +uint8_t ht21d_get_resolution(); +int ht21d_set_resolution(uint8_t resolution); +int htu21d_soft_reset(); + +// helper functions +uint8_t ht21d_read_user_register(); +int ht21d_write_user_register(uint8_t value); +uint16_t read_value(uint8_t command); +bool is_crc_valid(uint16_t value, uint8_t crc); + + +#endif // __ESP_HTU21D_H__