-
-
Notifications
You must be signed in to change notification settings - Fork 7
Add AHT10 temperature and humidity sensor application #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| .idea/ | ||
| .DS_Store | ||
| .cache | ||
|
|
||
| build/ | ||
| cmake-build-*/ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| cmake_minimum_required(VERSION 3.20) | ||
|
|
||
| include($ENV{IDF_PATH}/tools/cmake/project.cmake) | ||
|
|
||
| if (DEFINED ENV{TACTILITY_SDK_PATH}) | ||
| set(TACTILITY_SDK_PATH $ENV{TACTILITY_SDK_PATH}) | ||
| else() | ||
| set(TACTILITY_SDK_PATH "../../release/TactilitySDK") | ||
| message(WARNING "⚠️ TACTILITY_SDK_PATH environment variable is not set, defaulting to ${TACTILITY_SDK_PATH}") | ||
| endif() | ||
|
|
||
| include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") | ||
| set(EXTRA_COMPONENT_DIRS ${TACTILITY_SDK_PATH}) | ||
|
|
||
| project(AHT10Sensor) | ||
| tactility_project(AHT10Sensor) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| file(GLOB_RECURSE SOURCE_FILES Source/*.c*) | ||
|
|
||
| idf_component_register( | ||
| SRCS ${SOURCE_FILES} | ||
| REQUIRES TactilitySDK driver | ||
| ) |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,102 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| #include "aht10.h" | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| #include <tactility/device.h> | ||||||||||||||||||||||||||||||||||||||||||||||
| #include <tactility/drivers/i2c_controller.h> | ||||||||||||||||||||||||||||||||||||||||||||||
| #include <tactility/freertos/freertos.h> | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| #define AHT10_ADDR 0x38 | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| static struct Device* s_i2c = NULL; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| /* ------------------------------ */ | ||||||||||||||||||||||||||||||||||||||||||||||
| /* Find I2C controller */ | ||||||||||||||||||||||||||||||||||||||||||||||
| /* ------------------------------ */ | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| static bool find_i2c(struct Device* dev, void* ctx) | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| s_i2c = dev; | ||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| static bool setup_i2c(void) | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| s_i2c = NULL; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| device_for_each_of_type( | ||||||||||||||||||||||||||||||||||||||||||||||
| &I2C_CONTROLLER_TYPE, | ||||||||||||||||||||||||||||||||||||||||||||||
| NULL, | ||||||||||||||||||||||||||||||||||||||||||||||
| find_i2c); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| if (!s_i2c) | ||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| return device_is_ready(s_i2c); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| /* ------------------------------ */ | ||||||||||||||||||||||||||||||||||||||||||||||
| /* Init sensor */ | ||||||||||||||||||||||||||||||||||||||||||||||
| /* ------------------------------ */ | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| bool aht10_init(void) | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!setup_i2c()) | ||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| uint8_t init_cmd[3] = {0xE1, 0x08, 0x00}; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| return i2c_controller_write( | ||||||||||||||||||||||||||||||||||||||||||||||
| s_i2c, | ||||||||||||||||||||||||||||||||||||||||||||||
| AHT10_ADDR, | ||||||||||||||||||||||||||||||||||||||||||||||
| init_cmd, | ||||||||||||||||||||||||||||||||||||||||||||||
| sizeof(init_cmd), | ||||||||||||||||||||||||||||||||||||||||||||||
| pdMS_TO_TICKS(100)) | ||||||||||||||||||||||||||||||||||||||||||||||
| == ERROR_NONE; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| /* ------------------------------ */ | ||||||||||||||||||||||||||||||||||||||||||||||
| /* Read measurement */ | ||||||||||||||||||||||||||||||||||||||||||||||
| /* ------------------------------ */ | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| bool aht10_read(float* temperature, float* humidity) | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!s_i2c) | ||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| uint8_t cmd[3] = {0xAC, 0x33, 0x00}; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| if (i2c_controller_write( | ||||||||||||||||||||||||||||||||||||||||||||||
| s_i2c, | ||||||||||||||||||||||||||||||||||||||||||||||
| AHT10_ADDR, | ||||||||||||||||||||||||||||||||||||||||||||||
| cmd, | ||||||||||||||||||||||||||||||||||||||||||||||
| sizeof(cmd), | ||||||||||||||||||||||||||||||||||||||||||||||
| pdMS_TO_TICKS(100)) != ERROR_NONE) | ||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| vTaskDelay(pdMS_TO_TICKS(80)); | ||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The call chain is The recommended fix is to decouple the I2C read from the LVGL thread — either dedicate a FreeRTOS task to sensor reading and share results with the LVGL thread via a mutex-protected struct, or split the read into a two-step state machine (trigger in one timer tick, read in the next after 80 ms has elapsed). |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| uint8_t data[6]; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| if (i2c_controller_read( | ||||||||||||||||||||||||||||||||||||||||||||||
| s_i2c, | ||||||||||||||||||||||||||||||||||||||||||||||
| AHT10_ADDR, | ||||||||||||||||||||||||||||||||||||||||||||||
| data, | ||||||||||||||||||||||||||||||||||||||||||||||
| sizeof(data), | ||||||||||||||||||||||||||||||||||||||||||||||
| pdMS_TO_TICKS(100)) != ERROR_NONE) | ||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+77
to
+85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Status byte ( Per the AHT10 datasheet, byte 0 of the response contains the status register:
Skipping these checks means that if the sensor is still busy (however unlikely after 80 ms) or uncalibrated, corrupt values are silently written to 🛡️ Proposed fix if (i2c_controller_read(
s_i2c,
AHT10_ADDR,
data,
sizeof(data),
pdMS_TO_TICKS(100)) != ERROR_NONE)
return false;
+ // Verify sensor is not busy and is calibrated
+ if ((data[0] & 0x80) || !(data[0] & 0x08))
+ return false;
+
uint32_t raw_h =📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| uint32_t raw_h = | ||||||||||||||||||||||||||||||||||||||||||||||
| ((uint32_t)data[1] << 12) | | ||||||||||||||||||||||||||||||||||||||||||||||
| ((uint32_t)data[2] << 4) | | ||||||||||||||||||||||||||||||||||||||||||||||
| (data[3] >> 4); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| uint32_t raw_t = | ||||||||||||||||||||||||||||||||||||||||||||||
| ((uint32_t)(data[3] & 0x0F) << 16) | | ||||||||||||||||||||||||||||||||||||||||||||||
| ((uint32_t)data[4] << 8) | | ||||||||||||||||||||||||||||||||||||||||||||||
| data[5]; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| *humidity = raw_h * 100.0f / 1048576.0f; | ||||||||||||||||||||||||||||||||||||||||||||||
| *temperature = raw_t * 200.0f / 1048576.0f - 50.0f; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| #pragma once | ||
| #include <stdbool.h> | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| bool aht10_init(void); | ||
| bool aht10_read(float* temperature, float* humidity); | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| #include <tt_app.h> | ||
| #include <tt_lvgl_toolbar.h> | ||
|
|
||
| #include "aht10.h" | ||
|
|
||
| #include <lvgl.h> | ||
| #include <stdio.h> | ||
|
|
||
| static lv_obj_t* arc_temp; | ||
| static lv_obj_t* label_temp; | ||
| static lv_obj_t* label_hum; | ||
|
|
||
| static lv_timer_t* sensor_timer = NULL; | ||
|
|
||
| /* ------------------------------ */ | ||
| /* Sensor update */ | ||
| /* ------------------------------ */ | ||
|
|
||
| static void sensor_update(lv_timer_t* timer) | ||
| { | ||
| if (!label_temp || !label_hum) | ||
| return; | ||
|
Comment on lines
+21
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
🛡️ Proposed fix- if (!label_temp || !label_hum)
+ if (!arc_temp || !label_temp || !label_hum)
return; |
||
|
|
||
| float t, h; | ||
|
|
||
| if (!aht10_read(&t, &h)) | ||
| return; | ||
|
|
||
| char buf[32]; | ||
|
|
||
| int t_int = (int)t; | ||
| lv_arc_set_value(arc_temp, t_int); | ||
|
|
||
| snprintf(buf, sizeof(buf), "%.1f °C", t); | ||
| lv_label_set_text(label_temp, buf); | ||
|
|
||
| snprintf(buf, sizeof(buf), "Humidity %.1f %%", h); | ||
| lv_label_set_text(label_hum, buf); | ||
| } | ||
|
|
||
| /* ------------------------------ */ | ||
| /* Show app */ | ||
| /* ------------------------------ */ | ||
|
|
||
| static void onShowApp(AppHandle app, void* data, lv_obj_t* parent) | ||
| { | ||
| lv_obj_t* toolbar = | ||
| tt_lvgl_toolbar_create_for_app(parent, app); | ||
| lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); | ||
|
|
||
| arc_temp = lv_arc_create(parent); | ||
| lv_obj_set_size(arc_temp, 200, 200); | ||
| lv_obj_align(arc_temp, LV_ALIGN_CENTER, 0, 30); | ||
|
|
||
| lv_arc_set_range(arc_temp, 0, 50); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Arc range The AHT10 measures from −40°C to +85°C. Temperatures outside 0–50°C will be silently clamped by |
||
| lv_arc_set_value(arc_temp, 25); | ||
|
|
||
| lv_arc_set_rotation(arc_temp, 135); | ||
| lv_arc_set_bg_angles(arc_temp, 0, 270); | ||
|
|
||
| label_temp = lv_label_create(parent); | ||
| lv_label_set_text(label_temp, "--.- °C"); | ||
| lv_obj_align(label_temp, LV_ALIGN_CENTER, 0, 10); | ||
|
|
||
| label_hum = lv_label_create(parent); | ||
| lv_label_set_text(label_hum, "Humidity --%"); | ||
| lv_obj_align(label_hum, LV_ALIGN_CENTER, 0, 60); | ||
|
|
||
| aht10_init(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If the sensor is absent or I2C setup fails, the app proceeds normally — the timer fires, |
||
|
|
||
| sensor_timer = lv_timer_create(sensor_update, 2000, NULL); | ||
| } | ||
|
|
||
| /* ------------------------------ */ | ||
| /* Hide app (IMPORTANT FIX) */ | ||
| /* ------------------------------ */ | ||
|
|
||
| static void onHideApp(AppHandle app, void* data) | ||
| { | ||
| if (sensor_timer) { | ||
| lv_timer_del(sensor_timer); | ||
| sensor_timer = NULL; | ||
| } | ||
|
|
||
| label_temp = NULL; | ||
| label_hum = NULL; | ||
| arc_temp = NULL; | ||
| } | ||
|
|
||
| /* ------------------------------ */ | ||
| /* Entry */ | ||
| /* ------------------------------ */ | ||
|
|
||
| int main(int argc, char* argv[]) | ||
| { | ||
| tt_app_register((AppRegistration) { | ||
| .onShow = onShowApp, | ||
| .onHide = onHideApp | ||
| }); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| [manifest] | ||
| version=0.1 | ||
| [target] | ||
| sdk=0.7.0-dev | ||
| platforms=esp32 | ||
| [app] | ||
| id=one.tactility.aht10 | ||
| versionName=0.1.0 | ||
| versionCode=1 | ||
| name=AHT10 Sensor |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../../tactility.py |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
find_i2calways selects the first I2C controller found.On hardware with multiple I2C buses (e.g., a bus dedicated to a display and another to sensors), the AHT10 may not be on the first controller discovered. Consider accepting an I2C bus index or identifier via
ctx(already available as the callback parameter) to allow callers to specify the intended bus.