|
| 1 | +/* |
| 2 | + * HTU21D Component |
| 3 | + * |
| 4 | + * esp-idf component to interface with HTU21D humidity and temperature sensor |
| 5 | + * by TE Connectivity (http://www.te.com/usa-en/product-CAT-HSC0004.html) |
| 6 | + * |
| 7 | + * Luca Dentella, www.lucadentella.it |
| 8 | + */ |
| 9 | + |
| 10 | + |
| 11 | +// Component header file |
| 12 | +#include "htu21d.h" |
| 13 | + |
| 14 | +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) { |
| 15 | + |
| 16 | + esp_err_t ret; |
| 17 | + _port = port; |
| 18 | + |
| 19 | + // setup i2c controller |
| 20 | + i2c_config_t conf; |
| 21 | + conf.mode = I2C_MODE_MASTER; |
| 22 | + conf.sda_io_num = sda_pin; |
| 23 | + conf.scl_io_num = scl_pin; |
| 24 | + conf.sda_pullup_en = sda_internal_pullup; |
| 25 | + conf.scl_pullup_en = scl_internal_pullup; |
| 26 | + conf.master.clk_speed = 100000; |
| 27 | + ret = i2c_param_config(port, &conf); |
| 28 | + if(ret != ESP_OK) return HTU21D_ERR_CONFIG; |
| 29 | + |
| 30 | + // install the driver |
| 31 | + ret = i2c_driver_install(port, I2C_MODE_MASTER, 0, 0, 0); |
| 32 | + if(ret != ESP_OK) return HTU21D_ERR_INSTALL; |
| 33 | + |
| 34 | + // verify if a sensor is present |
| 35 | + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); |
| 36 | + i2c_master_start(cmd); |
| 37 | + i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_WRITE, true); |
| 38 | + i2c_master_stop(cmd); |
| 39 | + if(i2c_master_cmd_begin(port, cmd, 1000 / portTICK_RATE_MS) != ESP_OK) |
| 40 | + return HTU21D_ERR_NOTFOUND; |
| 41 | + |
| 42 | + return HTU21D_ERR_OK; |
| 43 | +} |
| 44 | + |
| 45 | +float ht21d_read_temperature() { |
| 46 | + |
| 47 | + // get the raw value from the sensor |
| 48 | + uint16_t raw_temperature = read_value(TRIGGER_TEMP_MEASURE_NOHOLD); |
| 49 | + if(raw_temperature == 0) return -999; |
| 50 | + |
| 51 | + // return the real value, formula in datasheet |
| 52 | + return (raw_temperature * 175.72 / 65536.0) - 46.85; |
| 53 | +} |
| 54 | + |
| 55 | +float ht21d_read_humidity() { |
| 56 | + |
| 57 | + // get the raw value from the sensor |
| 58 | + uint16_t raw_humidity = read_value(TRIGGER_HUMD_MEASURE_NOHOLD); |
| 59 | + if(raw_humidity == 0) return -999; |
| 60 | + |
| 61 | + // return the real value, formula in datasheet |
| 62 | + return (raw_humidity * 125.0 / 65536.0) - 6.0; |
| 63 | +} |
| 64 | + |
| 65 | +uint8_t ht21d_get_resolution() { |
| 66 | + |
| 67 | + uint8_t reg_value = ht21d_read_user_register(); |
| 68 | + return reg_value & 0b10000001; |
| 69 | +} |
| 70 | + |
| 71 | +int ht21d_set_resolution(uint8_t resolution) { |
| 72 | + |
| 73 | + // get the actual resolution |
| 74 | + uint8_t reg_value = ht21d_read_user_register(); |
| 75 | + reg_value &= 0b10000001; |
| 76 | + |
| 77 | + // update the register value with the new resolution |
| 78 | + resolution &= 0b10000001; |
| 79 | + reg_value |= resolution; |
| 80 | + |
| 81 | + return ht21d_write_user_register(reg_value); |
| 82 | +} |
| 83 | + |
| 84 | +int htu21d_soft_reset() { |
| 85 | + |
| 86 | + esp_err_t ret; |
| 87 | + |
| 88 | + // send the command |
| 89 | + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); |
| 90 | + i2c_master_start(cmd); |
| 91 | + i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_WRITE, true); |
| 92 | + i2c_master_write_byte(cmd, SOFT_RESET, true); |
| 93 | + i2c_master_stop(cmd); |
| 94 | + ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS); |
| 95 | + i2c_cmd_link_delete(cmd); |
| 96 | + |
| 97 | + switch(ret) { |
| 98 | + |
| 99 | + case ESP_ERR_INVALID_ARG: |
| 100 | + return HTU21D_ERR_INVALID_ARG; |
| 101 | + |
| 102 | + case ESP_FAIL: |
| 103 | + return HTU21D_ERR_FAIL; |
| 104 | + |
| 105 | + case ESP_ERR_INVALID_STATE: |
| 106 | + return HTU21D_ERR_INVALID_STATE; |
| 107 | + |
| 108 | + case ESP_ERR_TIMEOUT: |
| 109 | + return HTU21D_ERR_TIMEOUT; |
| 110 | + } |
| 111 | + return HTU21D_ERR_OK; |
| 112 | +} |
| 113 | + |
| 114 | +uint8_t ht21d_read_user_register() { |
| 115 | + |
| 116 | + esp_err_t ret; |
| 117 | + |
| 118 | + // send the command |
| 119 | + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); |
| 120 | + i2c_master_start(cmd); |
| 121 | + i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_WRITE, true); |
| 122 | + i2c_master_write_byte(cmd, READ_USER_REG, true); |
| 123 | + i2c_master_stop(cmd); |
| 124 | + ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS); |
| 125 | + i2c_cmd_link_delete(cmd); |
| 126 | + if(ret != ESP_OK) return 0; |
| 127 | + |
| 128 | + // receive the answer |
| 129 | + uint8_t reg_value; |
| 130 | + cmd = i2c_cmd_link_create(); |
| 131 | + i2c_master_start(cmd); |
| 132 | + i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_READ, true); |
| 133 | + i2c_master_read_byte(cmd, ®_value, 0x01); |
| 134 | + i2c_master_stop(cmd); |
| 135 | + ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS); |
| 136 | + i2c_cmd_link_delete(cmd); |
| 137 | + if(ret != ESP_OK) return 0; |
| 138 | + |
| 139 | + return reg_value; |
| 140 | +} |
| 141 | + |
| 142 | +int ht21d_write_user_register(uint8_t value) { |
| 143 | + |
| 144 | + esp_err_t ret; |
| 145 | + |
| 146 | + // send the command |
| 147 | + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); |
| 148 | + i2c_master_start(cmd); |
| 149 | + i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_WRITE, true); |
| 150 | + i2c_master_write_byte(cmd, WRITE_USER_REG, true); |
| 151 | + i2c_master_write_byte(cmd, value, true); |
| 152 | + i2c_master_stop(cmd); |
| 153 | + ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS); |
| 154 | + i2c_cmd_link_delete(cmd); |
| 155 | + |
| 156 | + switch(ret) { |
| 157 | + |
| 158 | + case ESP_ERR_INVALID_ARG: |
| 159 | + return HTU21D_ERR_INVALID_ARG; |
| 160 | + |
| 161 | + case ESP_FAIL: |
| 162 | + return HTU21D_ERR_FAIL; |
| 163 | + |
| 164 | + case ESP_ERR_INVALID_STATE: |
| 165 | + return HTU21D_ERR_INVALID_STATE; |
| 166 | + |
| 167 | + case ESP_ERR_TIMEOUT: |
| 168 | + return HTU21D_ERR_TIMEOUT; |
| 169 | + } |
| 170 | + return HTU21D_ERR_OK; |
| 171 | +} |
| 172 | + |
| 173 | +uint16_t read_value(uint8_t command) { |
| 174 | + |
| 175 | + esp_err_t ret; |
| 176 | + |
| 177 | + // send the command |
| 178 | + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); |
| 179 | + i2c_master_start(cmd); |
| 180 | + i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_WRITE, true); |
| 181 | + i2c_master_write_byte(cmd, command, true); |
| 182 | + i2c_master_stop(cmd); |
| 183 | + ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS); |
| 184 | + i2c_cmd_link_delete(cmd); |
| 185 | + if(ret != ESP_OK) return 0; |
| 186 | + |
| 187 | + // wait for the sensor (50ms) |
| 188 | + vTaskDelay(50 / portTICK_RATE_MS); |
| 189 | + |
| 190 | + // receive the answer |
| 191 | + uint8_t msb, lsb, crc; |
| 192 | + cmd = i2c_cmd_link_create(); |
| 193 | + i2c_master_start(cmd); |
| 194 | + i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_READ, true); |
| 195 | + i2c_master_read_byte(cmd, &msb, 0x00); |
| 196 | + i2c_master_read_byte(cmd, &lsb, 0x00); |
| 197 | + i2c_master_read_byte(cmd, &crc, 0x01); |
| 198 | + i2c_master_stop(cmd); |
| 199 | + ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS); |
| 200 | + i2c_cmd_link_delete(cmd); |
| 201 | + if(ret != ESP_OK) return 0; |
| 202 | + |
| 203 | + uint16_t raw_value = ((uint16_t) msb << 8) | (uint16_t) lsb; |
| 204 | + if(!is_crc_valid(raw_value, crc)) printf("CRC invalid\r\n"); |
| 205 | + return raw_value & 0xFFFC; |
| 206 | +} |
| 207 | + |
| 208 | +// verify the CRC, algorithm in the datasheet (see comments below) |
| 209 | +bool is_crc_valid(uint16_t value, uint8_t crc) { |
| 210 | + |
| 211 | + // line the bits representing the input in a row (first data, then crc) |
| 212 | + uint32_t row = (uint32_t)value << 8; |
| 213 | + row |= crc; |
| 214 | + |
| 215 | + // polynomial = x^8 + x^5 + x^4 + 1 |
| 216 | + // padded with zeroes corresponding to the bit length of the CRC |
| 217 | + uint32_t divisor = (uint32_t)0x988000; |
| 218 | + |
| 219 | + for (int i = 0 ; i < 16 ; i++) { |
| 220 | + |
| 221 | + // if the input bit above the leftmost divisor bit is 1, |
| 222 | + // the divisor is XORed into the input |
| 223 | + if (row & (uint32_t)1 << (23 - i)) row ^= divisor; |
| 224 | + |
| 225 | + // the divisor is then shifted one bit to the right |
| 226 | + divisor >>= 1; |
| 227 | + } |
| 228 | + |
| 229 | + // the remainder should equal zero if there are no detectable errors |
| 230 | + return (row == 0); |
| 231 | +} |
| 232 | + |
0 commit comments