#include <stdio.h>
#include <string.h>
#include <time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h" // Para eventos Wi-Fi
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_wifi.h" // Para Wi-Fi
#include "esp_event.h" // Para Event Loop
#include "esp_netif.h" // Para TCP/IP
#include "driver/adc.h" // Para LDR
#include "esp_sntp.h" // Para NTP
#include "mqtt_client.h" // Para MQTT
#include "ssd1306.h" // Nosso driver do display
// --- Definições do Projeto ---
#define TAG "PROJETO_DCA3706"
// Wi-Fi (Wokwi usa "Wokwi-GUEST" com senha em branco)
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASS ""
// MQTT
#define MQTT_BROKER_URL "mqtt://test.mosquitto.org"
#define MQTT_TOPIC "/dca3706/seu-nome" // MUDE "seu-nome"
// NTP
#define NTP_SERVER "pool.ntp.org"
// LDR (Sensor)
#define LDR_ADC_CHANNEL ADC1_CHANNEL_7 // GPIO 35 é ADC1_CHANNEL_7
// Fila de Amostras
#define TAMANHO_FILA 10
int fila_leituras[TAMANHO_FILA];
int contador_leituras = 0;
// --- Variáveis Globais ---
static SSD1306_t display; // Nosso dispositivo de display
static esp_mqtt_client_handle_t mqtt_client;
// Event Group para sinalizar conexão Wi-Fi
static EventGroupHandle_t s_wifi_event_group;
#define WIFI_CONNECTED_BIT BIT0 // Bit que sinaliza Wi-Fi conectado
#define WIFI_FAIL_BIT BIT1 // Bit que sinaliza falha
// --- Funções de Wi-Fi ---
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGI(TAG, "Falha ao conectar. Tentando novamente...");
esp_wifi_connect();
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "Conectado! IP: " IPSTR, IP2STR(&event->ip_info.ip));
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&wifi_event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASS,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finalizado.");
// Espera até que a conexão seja estabelecida
xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
ESP_LOGI(TAG, "Conectado ao Wi-Fi %s", WIFI_SSID);
}
// --- Funções de NTP ---
void time_sync_notification_cb(struct timeval *tv)
{
ESP_LOGI(TAG, "NTP: Sincronização de hora concluída!");
}
static void start_sntp(void)
{
ESP_LOGI(TAG, "Inicializando SNTP...");
esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
esp_sntp_setservername(0, NTP_SERVER);
sntp_set_time_sync_notification_cb(time_sync_notification_cb);
esp_sntp_init();
// Define o fuso horário (Opcional, mas bom para o display)
// Para Natal (GMT-3)
setenv("TZ", "GMT+3", 1);
tzset();
}
// --- Funções de MQTT ---
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
esp_mqtt_event_handle_t event = event_data;
switch ((esp_mqtt_event_id_t)event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
default:
ESP_LOGI(TAG, "Outro evento MQTT: %d", event->event_id);
break;
}
}
// Correto (depois)
static void start_mqtt(void)
{
esp_mqtt_client_config_t mqtt_cfg = {
.broker.address.uri = MQTT_BROKER_URL,
};
mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(mqtt_client);
}
// --- Funções de Hardware ---
void configure_adc(void)
{
// Configura o ADC1
adc1_config_width(ADC_WIDTH_BIT_12); // 12 bits (0-4095)
// Correto (depois)
adc1_config_channel_atten(LDR_ADC_CHANNEL, ADC_ATTEN_DB_12); // Atenuação para range 0-3.3V
ESP_LOGI(TAG, "ADC configurado.");
}
// --- Função Principal (app_main) ---
void app_main(void)
{
ESP_LOGI(TAG, "Iniciando o sistema...");
// 1. Inicializar NVS (Necessário para Wi-Fi)
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// 2. Inicializar Hardware
i2c_master_init(); // I2C (definido em ssd1306.c)
ssd1306_init(&display, SSD1306_WIDTH, SSD1306_HEIGHT);
configure_adc();
ssd1306_clear_screen(&display, false);
ssd1306_display_text(&display, 0, "Iniciando...", 13, false);
// 3. Conectar à Rede
ESP_LOGI(TAG, "Conectando ao Wi-Fi...");
wifi_init_sta();
ssd1306_display_text(&display, 2, "WiFi Conectado!", 16, false);
// 4. Sincronizar NTP
start_sntp();
ssd1306_display_text(&display, 4, "NTP Sincronizado!", 17, false);
// 5. Conectar ao MQTT
start_mqtt();
ssd1306_display_text(&display, 6, "MQTT Conectado!", 15, false);
vTaskDelay(2000 / portTICK_PERIOD_MS); // Pequena pausa
ESP_LOGI(TAG, "Entrando no loop principal...");
// Buffers para formatar texto
char buffer_leitura[32];
char buffer_tempo[32];
char buffer_payload_mqtt[256]; // Buffer para os 10 dados
while (1)
{
// 1. Ler o sensor (LDR)
int valor_ldr = adc1_get_raw(LDR_ADC_CHANNEL);
// 2. Obter a hora atual (do NTP)
time_t now;
struct tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);
strftime(buffer_tempo, sizeof(buffer_tempo), "%H:%M:%S", &timeinfo);
// 3. Atualizar o display
sprintf(buffer_leitura, "Luz: %d", valor_ldr);
ssd1306_clear_screen(&display, false);
ssd1306_display_text(&display, 0, "DCA3706 - Projeto", 17, false); // Linha 0
ssd1306_display_text(&display, 2, buffer_leitura, strlen(buffer_leitura), false); // Linha 2
ssd1306_display_text(&display, 4, buffer_tempo, strlen(buffer_tempo), false); // Linha 4
// 4. Armazenar na fila
fila_leituras[contador_leituras] = valor_ldr;
contador_leituras++;
sprintf(buffer_leitura, "Fila: %d/10", contador_leituras);
ssd1306_display_text(&display, 6, buffer_leitura, strlen(buffer_leitura), false); // Linha 6
// 5. Verificar se a fila está cheia
if (contador_leituras == TAMANHO_FILA)
{
ESP_LOGI(TAG, "Fila cheia. Publicando no MQTT...");
// Montar o payload (string separada por vírgula)
int len = 0;
len += sprintf(buffer_payload_mqtt + len, "%d", fila_leituras[0]);
for (int i = 1; i < TAMANHO_FILA; i++) {
len += sprintf(buffer_payload_mqtt + len, ",%d", fila_leituras[i]);
}
ESP_LOGI(TAG, "Payload: %s", buffer_payload_mqtt);
// Publicar
esp_mqtt_client_publish(mqtt_client,
MQTT_TOPIC,
buffer_payload_mqtt,
0, // Tamanho (0 = auto)
1, // QoS
0 // Reter
);
// Reiniciar a fila
contador_leituras = 0;
// Feedback no display
ssd1306_display_text(&display, 6, "Publicado!", 10, false);
vTaskDelay(500 / portTICK_PERIOD_MS); // Mostra "Publicado!" por 0.5s
}
// 6. Esperar 1 segundo
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}