feat: Adiciona suporte para sensor de temperatura e umidade DHT11 com integração Zigbee

This commit is contained in:
2026-01-15 23:56:47 +00:00
parent 95ebb660a2
commit fcecd9d273
9 changed files with 833 additions and 22 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
idf_component_register(
SRC_DIRS "."
INCLUDE_DIRS "."
PRIV_REQUIRES nvs_flash esp_driver_gpio esp_driver_uart ieee802154 heap esp_driver_rmt
PRIV_REQUIRES nvs_flash esp_driver_gpio esp_driver_uart ieee802154 heap esp_driver_rmt esp_timer
)
+4 -5
View File
@@ -9,8 +9,8 @@ extern "C" {
/* ========================= SELEÇÃO DO TIPO DE DISPOSITIVO ========================= */
/* Descomente APENAS UMA das opções abaixo para definir o tipo de dispositivo */
#define DEVICE_TYPE_RELAY /* Relé controlável (on/off light) */
// #define DEVICE_TYPE_TEMP_SENSOR /* Sensor de temperatura */
// #define DEVICE_TYPE_RELAY /* Relé controlável (on/off light) */
#define DEVICE_TYPE_TEMP_SENSOR /* Sensor de temperatura */
// #define DEVICE_TYPE_DOOR_SENSOR /* Sensor de porta/janela */
// #define DEVICE_TYPE_MOTION_SENSOR /* Sensor de movimento */
@@ -27,9 +27,8 @@ extern "C" {
#endif
#ifdef DEVICE_TYPE_TEMP_SENSOR
#define GPIO_SENSOR_I2C_SDA 6 /* I2C SDA para sensor de temperatura */
#define GPIO_SENSOR_I2C_SCL 7 /* I2C SCL para sensor de temperatura */
#define SENSOR_UPDATE_INTERVAL 30000 /* Intervalo de atualização em ms */
#define GPIO_SENSOR_DHT11 5 /* GPIO para DHT11 (DATA pin) */
#define SENSOR_UPDATE_INTERVAL 10000 /* Intervalo de atualização em ms (mín. 1s para DHT11) */
#endif
#ifdef DEVICE_TYPE_DOOR_SENSOR
+99 -12
View File
@@ -4,11 +4,17 @@
#include "zcl/esp_zigbee_zcl_common.h"
#include "esp_log.h"
#include "esp_zb_switch.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#ifdef DEVICE_TYPE_RELAY
#include "switch_driver.h"
#endif
#ifdef DEVICE_TYPE_TEMP_SENSOR
#include "dht11_driver.h"
#endif
#if FEATURE_LED_INDICATOR
#include "led_indicator.h"
#endif
@@ -77,6 +83,66 @@ static const device_type_t relay_device = {
#ifdef DEVICE_TYPE_TEMP_SENSOR
static TaskHandle_t temp_sensor_task_handle = NULL;
/* Task para ler sensor DHT11 periodicamente */
static void temp_sensor_task(void *pvParameters)
{
dht11_reading_t reading;
TickType_t last_wake_time = xTaskGetTickCount();
while (1) {
/* Aguarda intervalo configurado */
vTaskDelayUntil(&last_wake_time, pdMS_TO_TICKS(SENSOR_UPDATE_INTERVAL));
/* Lê temperatura e umidade do DHT11 */
esp_err_t ret = dht11_read(&reading);
if (ret == ESP_OK && reading.valid) {
/* Converte temperatura para formato Zigbee (0.01°C) */
int16_t temp_zigbee = (int16_t)(reading.temperature * 100);
/* Converte umidade para formato Zigbee (0.01% RH) */
uint16_t humidity_zigbee = (uint16_t)(reading.humidity * 100);
/* Atualiza atributos Zigbee */
esp_zb_lock_acquire(portMAX_DELAY);
/* Atualiza temperatura */
esp_zb_zcl_set_attribute_val(
HA_ONOFF_SWITCH_ENDPOINT,
ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT,
ESP_ZB_ZCL_CLUSTER_SERVER_ROLE,
ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID,
&temp_zigbee,
false
);
/* Atualiza umidade */
esp_zb_zcl_set_attribute_val(
HA_ONOFF_SWITCH_ENDPOINT,
ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT,
ESP_ZB_ZCL_CLUSTER_SERVER_ROLE,
ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID,
&humidity_zigbee,
false
);
esp_zb_lock_release();
ESP_LOGI(TAG, "Sensor updated - Temperature: %.1f°C (Zigbee: %d), Humidity: %.1f%% (Zigbee: %u)",
reading.temperature, temp_zigbee, reading.humidity, humidity_zigbee);
#if FEATURE_LED_INDICATOR
/* Pisca LED para indicar leitura */
led_indicator_set_state(LED_CONNECTED);
#endif
} else {
ESP_LOGW(TAG, "Failed to read DHT11 sensor: %s", esp_err_to_name(ret));
}
}
}
static esp_zb_cluster_list_t* temp_sensor_create_clusters(void)
{
esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create();
@@ -84,37 +150,48 @@ static esp_zb_cluster_list_t* temp_sensor_create_clusters(void)
esp_zb_cluster_list_add_basic_cluster(cluster_list, device_create_basic_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_identify_cluster(cluster_list, device_create_identify_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_temperature_meas_cluster(cluster_list, device_create_temperature_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_humidity_meas_cluster(cluster_list, device_create_humidity_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
return cluster_list;
}
static esp_err_t temp_sensor_attribute_handler(const esp_zb_zcl_set_attr_value_message_t *message)
{
/* Sensor de temperatura geralmente não recebe comandos, apenas reporta */
/* Sensor de temperatura não recebe comandos, apenas reporta */
return ESP_ERR_NOT_FOUND;
}
static void temp_sensor_hardware_init(void)
{
ESP_LOGI(TAG, "Initializing temperature sensor (I2C SDA:%d SCL:%d)",
GPIO_SENSOR_I2C_SDA, GPIO_SENSOR_I2C_SCL);
ESP_LOGI(TAG, "Initializing DHT11 temperature sensor on GPIO %d", GPIO_SENSOR_DHT11);
/* TODO: Implementar inicialização do sensor I2C */
/* Exemplo: inicializar AHT20, SHT30, BME280, etc. */
/* Inicializa driver DHT11 */
esp_err_t ret = dht11_init(GPIO_SENSOR_DHT11);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize DHT11: %s", esp_err_to_name(ret));
return;
}
/* Cria task para leitura periódica */
xTaskCreate(
temp_sensor_task,
"temp_sensor",
4096,
NULL,
5,
&temp_sensor_task_handle
);
ESP_LOGI(TAG, "DHT11 sensor task started (update interval: %d ms)", SENSOR_UPDATE_INTERVAL);
}
static void temp_sensor_update_state(void)
{
/* TODO: Ler temperatura do sensor e atualizar atributo Zigbee */
/* Exemplo:
* float temp = read_temperature_sensor();
* int16_t temp_zigbee = (int16_t)(temp * 100); // Zigbee usa 0.01°C
* esp_zb_zcl_set_attribute_val(..., ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID, &temp_zigbee, false);
*/
/* A atualização é feita automaticamente pela task periódica */
}
static const device_type_t temp_sensor_device = {
.name = "Zigbee Temperature Sensor",
.name = "Zigbee Temperature & Humidity Sensor DHT11",
.device_id = ESP_ZB_HA_TEMPERATURE_SENSOR_DEVICE_ID,
.endpoint = HA_ONOFF_SWITCH_ENDPOINT,
.create_clusters = temp_sensor_create_clusters,
@@ -267,6 +344,16 @@ esp_zb_attribute_list_t* device_create_temperature_cluster(void)
return esp_zb_temperature_meas_cluster_create(&temp_cfg);
}
esp_zb_attribute_list_t* device_create_humidity_cluster(void)
{
esp_zb_humidity_meas_cluster_cfg_t humidity_cfg = {
.measured_value = ESP_ZB_ZCL_REL_HUMIDITY_MEASUREMENT_MEASURED_VALUE_DEFAULT,
.min_value = 0, /* 0% RH em unidades de 0.01% */
.max_value = 10000, /* 100% RH em unidades de 0.01% */
};
return esp_zb_humidity_meas_cluster_create(&humidity_cfg);
}
esp_zb_attribute_list_t* device_create_occupancy_cluster(void)
{
esp_zb_occupancy_sensing_cluster_cfg_t occupancy_cfg = {
+3
View File
@@ -49,6 +49,9 @@ esp_zb_attribute_list_t* device_create_onoff_cluster(void);
/* Cria cluster de temperatura (para sensores) */
esp_zb_attribute_list_t* device_create_temperature_cluster(void);
/* Cria cluster de umidade relativa (para sensores) */
esp_zb_attribute_list_t* device_create_humidity_cluster(void);
/* Cria cluster de ocupação (para sensores de movimento/porta) */
esp_zb_attribute_list_t* device_create_occupancy_cluster(void);
+220
View File
@@ -0,0 +1,220 @@
/* DHT11 Temperature and Humidity Sensor Driver Implementation */
#include "dht11_driver.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "rom/ets_sys.h"
static const char *TAG = "DHT11";
static gpio_num_t dht_gpio;
static int64_t last_read_time = -2000000; // Inicializa para permitir primeira leitura
/* Timeout para espera de resposta (em microsegundos) */
#define DHT11_TIMEOUT_US 85
/* Tempo mínimo entre leituras (1 segundo) */
#define DHT11_MIN_INTERVAL_US 1000000
/**
* @brief Espera o pino mudar para o nível especificado
*
* @param level Nível esperado (0 ou 1)
* @param timeout_us Timeout em microsegundos
* @return Tempo de espera em us, ou -1 se timeout
*/
static int dht11_wait_for_level(int level, int timeout_us)
{
int elapsed = 0;
while (gpio_get_level(dht_gpio) != level) {
if (elapsed++ > timeout_us) {
return -1;
}
ets_delay_us(1);
}
return elapsed;
}
/**
* @brief Envia sinal de start para o DHT11
*/
static esp_err_t dht11_send_start_signal(void)
{
/* Configura GPIO como saída */
gpio_set_direction(dht_gpio, GPIO_MODE_OUTPUT_OD);
/* Envia sinal de start: LOW por 18ms */
gpio_set_level(dht_gpio, 0);
ets_delay_us(20000); // 20ms para garantir
/* Libera o barramento (pull-up externo fará HIGH) */
gpio_set_level(dht_gpio, 1);
ets_delay_us(40);
/* Configura GPIO como entrada */
gpio_set_direction(dht_gpio, GPIO_MODE_INPUT);
return ESP_OK;
}
/**
* @brief Lê 1 byte de dados do DHT11
*/
static uint8_t dht11_read_byte(void)
{
uint8_t byte = 0;
for (int i = 0; i < 8; i++) {
/* Espera sinal LOW (início do bit) */
if (dht11_wait_for_level(0, DHT11_TIMEOUT_US) < 0) {
return 0;
}
/* Espera sinal HIGH (duração determina bit 0 ou 1) */
if (dht11_wait_for_level(1, DHT11_TIMEOUT_US) < 0) {
return 0;
}
/* Mede duração do HIGH */
ets_delay_us(30);
/* Se ainda HIGH após 30us, é bit 1, senão é bit 0 */
if (gpio_get_level(dht_gpio) == 1) {
byte |= (1 << (7 - i));
}
}
return byte;
}
esp_err_t dht11_init(gpio_num_t gpio_num)
{
dht_gpio = gpio_num;
/* Configura GPIO com pull-up */
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_INPUT,
.pin_bit_mask = (1ULL << dht_gpio),
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_ENABLE,
};
esp_err_t ret = gpio_config(&io_conf);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to configure GPIO %d", dht_gpio);
return ret;
}
ESP_LOGI(TAG, "DHT11 initialized on GPIO %d", dht_gpio);
/* Aguarda sensor estabilizar (1 segundo) */
vTaskDelay(pdMS_TO_TICKS(1000));
return ESP_OK;
}
esp_err_t dht11_read(dht11_reading_t *reading)
{
if (reading == NULL) {
return ESP_ERR_INVALID_ARG;
}
/* Inicializa leitura como inválida */
reading->valid = false;
reading->temperature = 0.0f;
reading->humidity = 0.0f;
/* Verifica intervalo mínimo entre leituras */
int64_t current_time = esp_timer_get_time();
if ((current_time - last_read_time) < DHT11_MIN_INTERVAL_US) {
ESP_LOGW(TAG, "Read too soon, wait at least 1 second between reads");
return ESP_ERR_INVALID_STATE;
}
/* Desabilita interrupções temporariamente para timing preciso */
portDISABLE_INTERRUPTS();
/* Envia sinal de start */
dht11_send_start_signal();
/* Espera resposta do sensor (LOW por 80us) */
if (dht11_wait_for_level(0, DHT11_TIMEOUT_US) < 0) {
portENABLE_INTERRUPTS();
ESP_LOGE(TAG, "Timeout waiting for response LOW");
return ESP_ERR_TIMEOUT;
}
/* Espera HIGH de resposta (80us) */
if (dht11_wait_for_level(1, DHT11_TIMEOUT_US) < 0) {
portENABLE_INTERRUPTS();
ESP_LOGE(TAG, "Timeout waiting for response HIGH");
return ESP_ERR_TIMEOUT;
}
/* Espera início dos dados (LOW) */
if (dht11_wait_for_level(0, DHT11_TIMEOUT_US) < 0) {
portENABLE_INTERRUPTS();
ESP_LOGE(TAG, "Timeout waiting for data start");
return ESP_ERR_TIMEOUT;
}
/* Lê 5 bytes de dados (umidade_int, umidade_dec, temp_int, temp_dec, checksum) */
uint8_t data[5] = {0};
for (int i = 0; i < 5; i++) {
data[i] = dht11_read_byte();
}
portENABLE_INTERRUPTS();
/* Atualiza timestamp da última leitura */
last_read_time = esp_timer_get_time();
/* Verifica checksum */
uint8_t checksum = data[0] + data[1] + data[2] + data[3];
if (checksum != data[4]) {
ESP_LOGE(TAG, "Checksum error: calculated=0x%02X, received=0x%02X",
checksum, data[4]);
return ESP_ERR_INVALID_CRC;
}
/* DHT11 retorna valores inteiros (sem parte decimal) */
reading->humidity = (float)data[0];
reading->temperature = (float)data[2];
reading->valid = true;
ESP_LOGI(TAG, "Temperature: %.1f°C, Humidity: %.1f%%",
reading->temperature, reading->humidity);
return ESP_OK;
}
esp_err_t dht11_read_temperature(float *temperature)
{
dht11_reading_t reading;
esp_err_t ret = dht11_read(&reading);
if (ret == ESP_OK && reading.valid) {
*temperature = reading.temperature;
return ESP_OK;
}
return ret;
}
esp_err_t dht11_read_humidity(float *humidity)
{
dht11_reading_t reading;
esp_err_t ret = dht11_read(&reading);
if (ret == ESP_OK && reading.valid) {
*humidity = reading.humidity;
return ESP_OK;
}
return ret;
}
+57
View File
@@ -0,0 +1,57 @@
/* DHT11 Temperature and Humidity Sensor Driver */
#pragma once
#include "esp_err.h"
#include "driver/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Estrutura para armazenar leitura do DHT11
*/
typedef struct {
float temperature; /* Temperatura em °C */
float humidity; /* Umidade relativa em % */
bool valid; /* Indica se a leitura é válida */
} dht11_reading_t;
/**
* @brief Inicializa o sensor DHT11
*
* @param gpio_num Número do GPIO conectado ao pino DATA do DHT11
* @return ESP_OK em caso de sucesso
*/
esp_err_t dht11_init(gpio_num_t gpio_num);
/**
* @brief Lê temperatura e umidade do DHT11
*
* @param reading Ponteiro para estrutura que receberá os dados
* @return ESP_OK em caso de sucesso, ESP_ERR_TIMEOUT se falhar
*
* @note O DHT11 requer intervalo mínimo de 1 segundo entre leituras
*/
esp_err_t dht11_read(dht11_reading_t *reading);
/**
* @brief Obtém apenas a temperatura do DHT11
*
* @param temperature Ponteiro para receber temperatura em °C
* @return ESP_OK em caso de sucesso
*/
esp_err_t dht11_read_temperature(float *temperature);
/**
* @brief Obtém apenas a umidade do DHT11
*
* @param humidity Ponteiro para receber umidade em %
* @return ESP_OK em caso de sucesso
*/
esp_err_t dht11_read_humidity(float *humidity);
#ifdef __cplusplus
}
#endif