diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index b57548d..ebce0cf 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -14,10 +14,10 @@ Este projeto foi refatorado para uma **arquitetura modular flexível** que permi - Controle via ZHA/Zigbee2MQTT ### 🚧 Esqueleto Pronto (Basta Implementar Hardware) -- **DEVICE_TYPE_TEMP_SENSOR** - Sensor de temperatura - - Clusters: Basic, Identify, Temperature Measurement - - GPIOs configuráveis para I2C (SDA/SCL) - - TODO: Implementar leitura do sensor (AHT20, SHT30, BME280, etc) +- **DEVICE_TYPE_TEMP_SENSOR** - Sensor de temperatura e umidade DHT11 + - Clusters: Basic, Identify, Temperature Measurement, Humidity Measurement + - GPIO configurável para DHT11 (one-wire) + - ✅ Implementado com DHT11 - Reporta temperatura e umidade via Zigbee - **DEVICE_TYPE_DOOR_SENSOR** - Sensor de porta/janela - Clusters: Basic, Identify, IAS Zone diff --git a/DHT11_GUIDE.md b/DHT11_GUIDE.md new file mode 100644 index 0000000..5ff0008 --- /dev/null +++ b/DHT11_GUIDE.md @@ -0,0 +1,256 @@ +# Sensor de Temperatura DHT11 - Guia de Uso + +## 🌡️ Sobre o DHT11 + +O DHT11 é um sensor digital de temperatura e umidade de baixo custo: +- **Temperatura**: 0°C a 50°C (±2°C precisão) +- **Umidade**: 20% a 90% RH (±5% precisão) +- **Interface**: One-wire digital +- **Tempo de resposta**: 1 segundo mínimo entre leituras +- **Tensão**: 3.3V a 5V + +## 🔌 Conexão de Hardware + +### Pinagem do DHT11 +``` +DHT11 Sensor: +┌─────────┐ +│ 1 │ VCC → 3.3V (ESP32-C6) +│ 2 │ DATA → GPIO 5 (configurável) +│ 3 │ NC → Não conectar +│ 4 │ GND → GND +└─────────┘ +``` + +### Esquema de Ligação +``` +ESP32-C6 DHT11 + ┌────────┐ +3.3V ──────────────►│ VCC │ + │ │ +GPIO 5 ────────────►│ DATA │ + │ │ │ + └─── 10kΩ ───┐ │ │ + │ │ │ + 3.3V │ │ │ + │ NC │ + │ │ +GND ───────────────►│ GND │ + └────────┘ +``` + +**⚠️ IMPORTANTE:** +- Use resistor pull-up de **10kΩ** entre DATA e VCC +- Alguns módulos DHT11 já incluem o resistor pull-up + +## ⚙️ Configuração + +### 1. Ativar Sensor de Temperatura + +Edite [`main/device_config.h`](main/device_config.h): + +```c +/* Descomente APENAS sensor de temperatura */ +// #define DEVICE_TYPE_RELAY +#define DEVICE_TYPE_TEMP_SENSOR /* ← Ative isso */ +// #define DEVICE_TYPE_DOOR_SENSOR +// #define DEVICE_TYPE_MOTION_SENSOR +``` + +### 2. Configurar GPIO + +No mesmo arquivo: + +```c +#ifdef DEVICE_TYPE_TEMP_SENSOR + #define GPIO_SENSOR_DHT11 5 /* GPIO do DHT11 */ + #define SENSOR_UPDATE_INTERVAL 10000 /* 10 segundos */ +#endif +``` + +**GPIOs Recomendadas:** 5, 6, 7, 10, 18, 19, 20, 21 + +### 3. Ajustar Intervalo de Atualização + +```c +#define SENSOR_UPDATE_INTERVAL 10000 /* 10 segundos (recomendado) */ +// #define SENSOR_UPDATE_INTERVAL 5000 /* 5 segundos (mínimo seguro) */ +// #define SENSOR_UPDATE_INTERVAL 60000 /* 1 minuto (economia energia) */ +``` + +**⚠️ Mínimo:** 2000ms (2 segundos) - DHT11 requer 1s entre leituras + +## 🏗️ Compilar e Gravar + +```bash +# Compilar +idf.py build + +# Gravar +idf.py -p /dev/ttyACM0 flash monitor +``` + +## 📊 Formato de Dados Zigbee + +O DHT11 reporta **temperatura e umidade** no formato Zigbee padrão: + +### Cluster de Temperatura (0x0402) +- **Atributo**: `MeasuredValue` (0x0000) +- **Tipo**: `int16_t` +- **Unidade**: 0.01°C +- **Exemplo**: 2350 = 23.50°C + +### Cluster de Umidade (0x0405) +- **Atributo**: `MeasuredValue` (0x0000) +- **Tipo**: `uint16_t` +- **Unidade**: 0.01% RH +- **Exemplo**: 6500 = 65.00% RH + +### Leitura no ZHA/Home Assistant + +O dispositivo aparece automaticamente com **dois sensores**: + +```yaml +# Sensor de Temperatura +sensor: + - platform: zha + name: "Temperatura Sala" + device: "0x98a316fffebf43a4" + cluster_id: 0x0402 # Temperature + attribute: 0 + +# Sensor de Umidade +sensor: + - platform: zha + name: "Umidade Sala" + device: "0x98a316fffebf43a4" + cluster_id: 0x0405 # Humidity + attribute: 0 +``` + +Aparecerá automaticamente como duas entidades: +- **sensor.temperatura_sala** - Temperatura em °C +- **sensor.umidade_sala** - Umidade relativa em % +- **Atualização**: A cada 10 segundos (configurável) + +## 🔍 Logs de Funcionamento + +``` +I (1250) DHT11: DHT11 initialized on GPIO 5 +I (1260) DEVICE_REGISTRY: DHT11 sensor task started (update interval: 10000 ms) +I (11260) DHT11: Temperature: 23.0°C, Humidity: 65.0% +I (11265) DEVICE_REGISTRY: Sensor updated - Temperature: 23.0°C (Zigbee: 2300), Humidity: 65.0% (Zigbee: 6500) +I (21265) DHT11: Temperature: 23.5°C, Humidity: 64.0% +I (21270) DEVICE_REGISTRY: Sensor updated - Temperature: 23.5°C (Zigbee: 2350), Humidity: 64.0% (Zigbee: 6400) +``` + +## ⚠️ Troubleshooting + +### Erro: "Timeout waiting for response" +``` +E (12000) DHT11: Timeout waiting for response LOW +``` +**Causas:** +- DHT11 não conectado corretamente +- Falta resistor pull-up de 10kΩ +- GPIO incorreta +- Sensor danificado + +**Solução:** +1. Verifique conexões (VCC, DATA, GND) +2. Adicione resistor pull-up 10kΩ +3. Teste com outra GPIO +4. Troque o sensor + +### Erro: "Checksum error" +``` +E (12000) DHT11: Checksum error: calculated=0x5A, received=0x5B +``` +**Causas:** +- Interferência eletromagnética +- Cabo muito longo +- Alimentação instável + +**Solução:** +1. Use cabo curto (<30cm) +2. Adicione capacitor 100nF próximo ao sensor +3. Estabilize alimentação 3.3V +4. Afaste de fontes de ruído + +### Aviso: "Read too soon" +``` +W (5500) DHT11: Read too soon, wait at least 1 second between reads +``` +**Causa:** Tentativa de leitura antes de 1 segundo + +**Solução:** Aumente `SENSOR_UPDATE_INTERVAL` para no mínimo 2000ms + +### Temperatura Sempre 0°C +**Causas:** +- Sensor não inicializado +- Problema no protocolo one-wire +- GPIO em conflito + +**Solução:** +1. Verifique logs de inicialização +2. Teste GPIO com `gpio_get_level()` +3. Use outra GPIO +4. Verifique se pull-up está presente + +## 🎯 Otimizações + +### Economia de Energia + +Para dispositivos battery-powered: + +```c +#define SENSOR_UPDATE_INTERVAL 300000 /* 5 minutos */ +``` + +Configure também como End Device: + +```c +/* Em esp_zb_switch.h */ +esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZED_CONFIG(); /* End Device */ +``` + +### Alta Frequência de Atualização + +Para monitoramento rápido (com alimentação): + +```c +#define SENSOR_UPDATE_INTERVAL 2000 /* 2 segundos (mínimo) */ +``` + +## 📈 Próximas Melhorias + +### ✅ Cluster de Umidade - IMPLEMENTADO! + +O DHT11 agora expõe **temperatura e umidade** via Zigbee automaticamente! + +### Alertas de Temperatura/Umidade + +Implementar notificação quando valores excederem limites: + +Implementar notificação quando temperatura exceder limites: + +```c +if (reading.temperature > 30.0f || reading.temperature < 10.0f) { + ESP_LOGW(TAG, "Temperature out of range: %.1f°C", reading.temperature); + // Enviar alerta via Zigbee +} +``` + +## 📚 Referências + +- [DHT11 Datasheet](https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf) +- [Zigbee Temperature Measurement Cluster](https://zigbeealliance.org/wp-content/uploads/2019/12/07-5123-06-zigbee-cluster-library-specification.pdf) +- [ESP-IDF GPIO API](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c6/api-reference/peripherals/gpio.html) + +## 🔗 Compatibilidade + +**Home Assistant (ZHA)**: ✅ Funciona automaticamente +**Zigbee2MQTT**: ✅ Compatível (Temperature Sensor) +**Amazon Alexa**: ✅ Via Home Assistant +**Google Home**: ✅ Via Home Assistant +**Apple HomeKit**: ✅ Via HomeAssistant Bridge diff --git a/DHT11_QUICKSTART.md b/DHT11_QUICKSTART.md new file mode 100644 index 0000000..f1fb847 --- /dev/null +++ b/DHT11_QUICKSTART.md @@ -0,0 +1,189 @@ +# Teste Rápido - DHT11 + +## 🚀 Teste de Bancada (5 minutos) + +### 1️⃣ Hardware Necessário +``` +✅ ESP32-C6 +✅ DHT11 sensor +✅ Resistor 10kΩ (pull-up) +✅ 3 fios jumper +✅ Breadboard (opcional) +``` + +### 2️⃣ Montagem +``` +ESP32-C6 DHT11 + 3.3V ────────► VCC (pino 1) + │ + GPIO5 ────┬───► DATA (pino 2) + │ + 10kΩ + │ + 3.3V + + GND ─────────► GND (pino 4) +``` + +### 3️⃣ Ativar Modo Sensor + +Edite `main/device_config.h`: +```c +// #define DEVICE_TYPE_RELAY /* ← Comente isso */ +#define DEVICE_TYPE_TEMP_SENSOR /* ← Descomente isso */ +``` + +### 4️⃣ Compilar e Gravar +```bash +cd /workspaces/bomba-esp +idf.py build flash monitor +``` + +### 5️⃣ Logs Esperados +``` +I (1250) DHT11: DHT11 initialized on GPIO 5 +I (1260) DEVICE_REGISTRY: DHT11 sensor task started (update interval: 10000 ms) +I (2260) ESP_ZB_ON_OFF_SWITCH: Start Zigbee stack +... +I (11260) DHT11: Temperature: 23.0°C, Humidity: 65.0% +I (11265) DEVICE_REGISTRY: Sensor updated - Temperature: 23.0°C (Zigbee: 2300), Humidity: 65.0% (Zigbee: 6500) +I (21265) DHT11: Temperature: 23.5°C, Humidity: 64.0% +I (21270) DEVICE_REGISTRY: Sensor updated - Temperature: 23.5°C (Zigbee: 2350), Humidity: 64.0% (Zigbee: 6400) +``` + +## 🧪 Testes de Funcionamento + +### Teste 1: Sensor Responde? +**Objetivo:** Verificar comunicação básica + +1. Conecte DHT11 +2. Grave firmware +3. Observe logs a cada 10 segundos + +**✅ Sucesso:** +``` +I (11260) DHT11: Temperature: XX.X°C, Humidity: XX.X% +``` + +**❌ Falha - Timeout:** +``` +E (11260) DHT11: Timeout waiting for response LOW +``` +→ Verifique conexões e resistor pull-up + +### Teste 2: Valores Coerentes? +**Objetivo:** Validar leituras + +**Temperatura esperada:** 15°C a 35°C (ambiente normal) +**Umidade esperada:** 30% a 80% RH + +**Teste:** +1. Sopre no sensor → Umidade aumenta +2. Aperte o sensor → Temperatura aumenta +3. Compare com termômetro ambiente + +### Teste 3: Zigbee Funcionando? +**Objetivo:** Verificar integração Zigbee + +1. Parear com coordenador ZHA +2. Verificar entidade no Home Assistant +3. Observar atualização a cada 10s + +**✅ No Home Assistant:** +``` +Dispositivo: 0x98a316fffebf43a4 +├─ Manufacturer: ESPRESSIF +├─ Model: Zigbee Temperature & Humidity Sensor DHT11 +├─ Sensor: Temperature +│ └─ Estado: 23.5°C +└─ Sensor: Humidity + └─ Estado: 64.0% +``` + +## 📊 Exemplo de Automação (Home Assistant) + +### Alerta de Temperatura Alta +```yaml +automation: + - alias: "Alerta Temperatura Alta" + trigger: + - platform: numeric_state + entity_id: sensor.temperatura_sala + above: 30 + action: + - service: notify.mobile_app + data: + title: "🌡️ Temperatura Alta!" + message: "Sala está em {{ states('sensor.temperatura_sala') }}°C" +``` + +### Alerta de Umidade Baixa +```yaml +automation: + - alias: "Alerta Umidade Baixa" + trigger: + - platform: numeric_state + entity_id: sensor.umidade_sala + below: 30 + action: + - service: notify.mobile_app + data: + title: "💧 Umidade Baixa!" + message: "Umidade está em {{ states('sensor.umidade_sala') }}%" +``` + +### Gráfico Combinado +```yaml +# configuration.yaml +sensor: + - platform: statistics + name: "Temperatura Média Diária" + entity_id: sensor.temperatura_sala + state_characteristic: mean + max_age: + hours: 24 + + - platform: statistics + name: "Umidade Média Diária" + entity_id: sensor.umidade_sala + state_characteristic: mean + max_age: + hours: 24 +``` + +## 🔧 Troubleshooting Rápido + +| Sintoma | Causa Provável | Solução | +|---------|---------------|---------| +| Sem leitura | DHT11 não conectado | Verifique VCC, GND, DATA | +| Timeout | Falta pull-up | Adicione resistor 10kΩ | +| Valores errados | Cabo longo | Use cabo <30cm | +| Checksum error | Ruído elétrico | Adicione capacitor 100nF | +| Não pareia | Modo errado | Verifique `DEVICE_TYPE_TEMP_SENSOR` | + +## 📈 Ajuste Fino + +### Temperatura Muito Estável? +**Problema:** DHT11 lê apenas inteiros (sem decimal) + +**Solução:** Normal! DHT11 tem precisão limitada + +### Quer Mais Precisão? +Use sensor melhor: +- **DHT22** (±0.5°C, ±2% RH) +- **SHT30** (±0.3°C, ±2% RH) +- **BME280** (±1°C, pressão também) + +Veja [ARCHITECTURE.md](ARCHITECTURE.md) seção "Como Adicionar Novo Dispositivo" + +## 🎯 Próximo Passo + +Agora que o DHT11 funciona, você pode: + +1. **Adicionar umidade ao Zigbee** (cluster 0x0405) +2. **Criar dispositivo combo** (relé + sensor) +3. **Implementar sensor de porta** (GPIO 5 com reed switch) + +Veja guias completos: +- [DHT11_GUIDE.md](DHT11_GUIDE.md) - Guia completo +- [ARCHITECTURE.md](ARCHITECTURE.md) - Arquitetura modular diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 13aba50..9fe9e4e 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -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 ) diff --git a/main/device_config.h b/main/device_config.h index 824482c..66ff67f 100644 --- a/main/device_config.h +++ b/main/device_config.h @@ -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 diff --git a/main/device_registry.c b/main/device_registry.c index df24c94..a2d62aa 100644 --- a/main/device_registry.c +++ b/main/device_registry.c @@ -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 = { diff --git a/main/device_registry.h b/main/device_registry.h index 7148abc..4d07250 100644 --- a/main/device_registry.h +++ b/main/device_registry.h @@ -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); diff --git a/main/dht11_driver.c b/main/dht11_driver.c new file mode 100644 index 0000000..f77822d --- /dev/null +++ b/main/dht11_driver.c @@ -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; +} diff --git a/main/dht11_driver.h b/main/dht11_driver.h new file mode 100644 index 0000000..311ee66 --- /dev/null +++ b/main/dht11_driver.h @@ -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