|
| 1 | +/* |
| 2 | + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +#include <stdio.h> |
| 8 | +#include "esp_check.h" |
| 9 | +#include "iot_sensor_hub.h" |
| 10 | +#include "aht30.h" |
| 11 | + |
| 12 | +#define I2C_CLK_SPEED 400000 |
| 13 | + |
| 14 | +/******************************************************************************* |
| 15 | +* Types definitions |
| 16 | +*******************************************************************************/ |
| 17 | + |
| 18 | +typedef struct { |
| 19 | + i2c_master_dev_handle_t i2c_handle; |
| 20 | +} aht30_dev_t; |
| 21 | + |
| 22 | +/******************************************************************************* |
| 23 | +* Function definitions |
| 24 | +*******************************************************************************/ |
| 25 | +static esp_err_t aht30_write(aht30_handle_t sensor, const uint8_t *data_buf, const uint8_t data_len); |
| 26 | +static esp_err_t aht30_receive(const aht30_handle_t sensor, uint8_t *data_buf, const uint8_t data_len); |
| 27 | +static uint8_t aht30_calc_crc(uint8_t *data, uint8_t length); |
| 28 | + |
| 29 | +/******************************************************************************* |
| 30 | +* Local variables |
| 31 | +*******************************************************************************/ |
| 32 | +static const char *TAG = "AHT30"; |
| 33 | + |
| 34 | +/******************************************************************************* |
| 35 | +* Public API functions |
| 36 | +*******************************************************************************/ |
| 37 | + |
| 38 | +esp_err_t aht30_create(i2c_master_bus_handle_t i2c_bus, const uint8_t dev_addr, aht30_handle_t *handle_ret) |
| 39 | +{ |
| 40 | + ESP_RETURN_ON_FALSE(i2c_bus != NULL, ESP_ERR_INVALID_ARG, TAG, "I2C handle can't be NULL"); |
| 41 | + ESP_RETURN_ON_FALSE(handle_ret != NULL, ESP_ERR_INVALID_ARG, TAG, "AHT30 handle can't be NULL"); |
| 42 | + |
| 43 | + esp_err_t ret = ESP_OK; |
| 44 | + |
| 45 | + // Allocate memory and initialize the driver object |
| 46 | + aht30_dev_t *sensor = (aht30_dev_t *) calloc(1, sizeof(aht30_dev_t)); |
| 47 | + ESP_RETURN_ON_FALSE(sensor != NULL, ESP_ERR_NO_MEM, TAG, "Not enough memory"); |
| 48 | + |
| 49 | + const i2c_device_config_t i2c_dev_cfg = { |
| 50 | + .device_address = dev_addr, |
| 51 | + .scl_speed_hz = I2C_CLK_SPEED, |
| 52 | + }; |
| 53 | + |
| 54 | + ret = i2c_master_bus_add_device(i2c_bus, &i2c_dev_cfg, &sensor->i2c_handle); |
| 55 | + if (ret != ESP_OK) { |
| 56 | + ESP_LOGE(TAG, "Failed to add a new I2C device"); |
| 57 | + aht30_delete(sensor); |
| 58 | + return ret; |
| 59 | + } |
| 60 | + assert(sensor->i2c_handle); |
| 61 | + |
| 62 | + ESP_LOGD(TAG, "Successfully created a AHT30 device"); |
| 63 | + *handle_ret = sensor; |
| 64 | + return ret; |
| 65 | +} |
| 66 | + |
| 67 | +void aht30_delete(aht30_handle_t sensor) |
| 68 | +{ |
| 69 | + aht30_dev_t *sens = (aht30_dev_t *) sensor; |
| 70 | + |
| 71 | + if (sens->i2c_handle) { |
| 72 | + i2c_master_bus_rm_device(sens->i2c_handle); |
| 73 | + } |
| 74 | + |
| 75 | + free(sens); |
| 76 | +} |
| 77 | + |
| 78 | +esp_err_t aht30_get_temperature_humidity_value(aht30_handle_t sensor, float *temperature, float *humidity) |
| 79 | +{ |
| 80 | + ESP_RETURN_ON_FALSE(sensor != NULL, ESP_ERR_INVALID_ARG, TAG, "AHT30 handle can't be NULL"); |
| 81 | + ESP_RETURN_ON_FALSE(temperature != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to the busy variable can't be NULL"); |
| 82 | + ESP_RETURN_ON_FALSE(humidity != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to the busy variable can't be NULL"); |
| 83 | + |
| 84 | + esp_err_t ret = ESP_OK; |
| 85 | + |
| 86 | + // Start measurement command specified by the manufacturer |
| 87 | + uint8_t start_measurement[] = {0xAC, 0x33, 0x00}; |
| 88 | + |
| 89 | + ret = aht30_write(sensor, start_measurement, sizeof(start_measurement)); |
| 90 | + if (ret != ESP_OK) { |
| 91 | + ESP_LOGE(TAG, "Failed to start AHT30 measurement"); |
| 92 | + return ret; |
| 93 | + } |
| 94 | + |
| 95 | + bool busy; |
| 96 | + uint8_t max_tries = 18; |
| 97 | + // Wait for the measurement to finish |
| 98 | + do { |
| 99 | + ret = aht30_get_busy(sensor, &busy); |
| 100 | + if (ret != ESP_OK) { |
| 101 | + ESP_LOGE(TAG, "Failed to read AHT30 busy status"); |
| 102 | + return ret; |
| 103 | + } |
| 104 | + vTaskDelay(pdMS_TO_TICKS(10)); |
| 105 | + } while (--max_tries && busy); |
| 106 | + |
| 107 | + if (busy) { |
| 108 | + ESP_LOGE(TAG, "AHT30 timeout of measurement"); |
| 109 | + return ESP_FAIL; |
| 110 | + } |
| 111 | + |
| 112 | + uint8_t crc_result; |
| 113 | + uint8_t receive_buf[7]; |
| 114 | + ESP_RETURN_ON_ERROR(aht30_receive(sensor, receive_buf, sizeof(receive_buf)), TAG, "Failed to receive measurement data"); |
| 115 | + |
| 116 | + crc_result = aht30_calc_crc(receive_buf, sizeof(receive_buf) - 1); |
| 117 | + ESP_RETURN_ON_FALSE(crc_result == receive_buf[sizeof(receive_buf) - 1], ESP_FAIL, TAG, |
| 118 | + "Result of CRC calculation does not match"); |
| 119 | + |
| 120 | + uint32_t raw_value; |
| 121 | + raw_value = (receive_buf[3] & 0x0F) << 16 | receive_buf[4] << 8 | receive_buf[5]; |
| 122 | + *temperature = (float)raw_value * 200 / (1 << 20) - 50; |
| 123 | + |
| 124 | + raw_value = receive_buf[1] << 12 | receive_buf[2] << 4 | receive_buf[3] >> 4; |
| 125 | + *humidity = (float)raw_value * 100 / (1 << 20) ; |
| 126 | + |
| 127 | + return ret; |
| 128 | +} |
| 129 | + |
| 130 | +esp_err_t aht30_get_busy(aht30_handle_t sensor, bool *busy) |
| 131 | +{ |
| 132 | + ESP_RETURN_ON_FALSE(sensor != NULL, ESP_ERR_INVALID_ARG, TAG, "AHT30 handle can't be NULL"); |
| 133 | + ESP_RETURN_ON_FALSE(busy != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to the busy variable can't be NULL"); |
| 134 | + uint8_t data = 0; |
| 135 | + |
| 136 | + ESP_RETURN_ON_ERROR(aht30_receive(sensor, &data, 1), TAG, "Read sensor busy failed"); |
| 137 | + |
| 138 | + *busy = (data & BIT7); |
| 139 | + return ESP_OK; |
| 140 | +} |
| 141 | + |
| 142 | +/******************************************************************************* |
| 143 | +* Private functions |
| 144 | +*******************************************************************************/ |
| 145 | +static esp_err_t aht30_write(const aht30_handle_t sensor, const uint8_t *data_buf, const uint8_t data_len) |
| 146 | +{ |
| 147 | + aht30_dev_t *sens = (aht30_dev_t *) sensor; |
| 148 | + assert(sens); |
| 149 | + |
| 150 | + return i2c_master_transmit(sens->i2c_handle, data_buf, data_len, -1); |
| 151 | +} |
| 152 | + |
| 153 | +static esp_err_t aht30_receive(const aht30_handle_t sensor, uint8_t *data_buf, const uint8_t data_len) |
| 154 | +{ |
| 155 | + aht30_dev_t *sens = (aht30_dev_t *) sensor; |
| 156 | + assert(sens); |
| 157 | + |
| 158 | + return i2c_master_receive(sens->i2c_handle, data_buf, data_len, -1); |
| 159 | +} |
| 160 | + |
| 161 | +static uint8_t aht30_calc_crc(uint8_t *data, uint8_t length) |
| 162 | +{ |
| 163 | + uint8_t i; |
| 164 | + uint8_t byte; |
| 165 | + uint8_t crc = 0xFF; |
| 166 | + for (byte = 0; byte < length; byte++) { |
| 167 | + crc ^= (data[byte]); |
| 168 | + for (i = 8; i > 0; --i) { |
| 169 | + if (crc & 0x80) { |
| 170 | + crc = (crc << 1) ^ 0x31; |
| 171 | + } else { |
| 172 | + crc = (crc << 1); |
| 173 | + } |
| 174 | + } |
| 175 | + } |
| 176 | + return crc; |
| 177 | +} |
| 178 | + |
| 179 | +/******************************************************************************* |
| 180 | +* Private functions for sensor hub implementation |
| 181 | +*******************************************************************************/ |
| 182 | + |
| 183 | +static aht30_handle_t sensor_hub_aht30_handle; |
| 184 | + |
| 185 | +esp_err_t aht30_impl_init(bus_handle_t bus_handle, uint8_t addr) |
| 186 | +{ |
| 187 | + esp_err_t ret; |
| 188 | + |
| 189 | + ret = aht30_create(bus_handle, addr, &sensor_hub_aht30_handle); |
| 190 | + if (ret != ESP_OK) { |
| 191 | + ESP_LOGE(TAG, "Failed to create AHT30 with error %s", esp_err_to_name(ret)); |
| 192 | + } |
| 193 | + |
| 194 | + return ret; |
| 195 | +} |
| 196 | + |
| 197 | +esp_err_t aht30_impl_deinit(void) |
| 198 | +{ |
| 199 | + aht30_delete(sensor_hub_aht30_handle); |
| 200 | + sensor_hub_aht30_handle = NULL; |
| 201 | + return ESP_OK; |
| 202 | +} |
| 203 | + |
| 204 | +esp_err_t aht30_impl_test(void) |
| 205 | +{ |
| 206 | + bool busy; |
| 207 | + return aht30_get_busy(sensor_hub_aht30_handle, &busy); |
| 208 | +} |
| 209 | + |
| 210 | +esp_err_t aht30_impl_acquire_humidity(float *humidity) |
| 211 | +{ |
| 212 | + float temperature; |
| 213 | + return aht30_get_temperature_humidity_value(sensor_hub_aht30_handle, &temperature, humidity); |
| 214 | +} |
| 215 | + |
| 216 | +esp_err_t aht30_impl_acquire_temperature(float *temperature) |
| 217 | +{ |
| 218 | + float humidity; |
| 219 | + return aht30_get_temperature_humidity_value(sensor_hub_aht30_handle, temperature, &humidity); |
| 220 | +} |
| 221 | + |
| 222 | +static humiture_impl_t aht30_impl = { |
| 223 | + .init = aht30_impl_init, |
| 224 | + .deinit = aht30_impl_deinit, |
| 225 | + .test = aht30_impl_test, |
| 226 | + .acquire_humidity = aht30_impl_acquire_humidity, |
| 227 | + .acquire_temperature = aht30_impl_acquire_temperature, |
| 228 | +}; |
| 229 | + |
| 230 | +SENSOR_HUB_DETECT_FN(HUMITURE_ID, sensor_hub_aht30, &aht30_impl); |
0 commit comments