diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 0aca183..37902db 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -16,6 +16,8 @@ idf_component_register( "i2c_helper.c" "display.c" "ui.c" + "net_weather.c" + "buzzer.c" INCLUDE_DIRS "include" @@ -28,5 +30,6 @@ idf_component_register( json driver esp_http_server + esp_http_client ) diff --git a/main/buzzer.c b/main/buzzer.c new file mode 100644 index 0000000..bbd8c7c --- /dev/null +++ b/main/buzzer.c @@ -0,0 +1,26 @@ +#include "driver/gpio.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_rom_sys.h" + +#define BUZ_GPIO GPIO_NUM_19 + +void buzzer_init(void) +{ + gpio_reset_pin(BUZ_GPIO); + gpio_set_direction(BUZ_GPIO, GPIO_MODE_OUTPUT); +} + +void buzzer_beep(uint32_t ms) +{ + const int period_us = 500; // ~2 kHz + + int cycles = (ms * 1000) / period_us; + + for (int i = 0; i < cycles; i++) { + gpio_set_level(BUZ_GPIO, 1); + esp_rom_delay_us(period_us / 2); + gpio_set_level(BUZ_GPIO, 0); + esp_rom_delay_us(period_us / 2); + } +} diff --git a/main/display.c b/main/display.c index 30078a9..0254203 100644 --- a/main/display.c +++ b/main/display.c @@ -25,6 +25,7 @@ static const char *TAG = "DISPLAY"; // FLAG GLOBAL // ======================================================= static bool s_display_enabled = false; +void display_temperature_bottom(float temp); void display_set_enabled(bool en) { s_display_enabled = en; } bool display_is_enabled(void) { return s_display_enabled; } @@ -376,3 +377,48 @@ void display_debug_segment(uint16_t bitmask) // if (!display_is_enabled()) return; // display_raw_top(0, bitmask); } + +// ======================================================= +// TEMPERATURA (BOTTOM) ex: 23.5 +// ======================================================= +void display_temperature_bottom(float temp) +{ + if (!s_display_enabled) return; + + // limites práticos + if (temp > 99.9f) temp = 99.9f; + if (temp < -9.9f) temp = -9.9f; + + bool neg = false; + if (temp < 0) { + neg = true; + temp = -temp; + } + + int inteiro = (int)temp; + int decimal = (int)((temp - inteiro) * 10 + 0.5f); + + display_clear_bottom(); + + if (neg) { + // formato: C-5.2 (raro, mas pronto) + display_char_bottom(0, 'C'); + display_raw_bottom(1, SEG_G); // '-' + display_raw_bottom(2, digit_mask[inteiro] | SEG_DP); + display_digit_bottom(3, decimal); + } else { + if (inteiro >= 10) { + // formato: C13.8 + display_char_bottom(0, 'C'); + display_digit_bottom(1, inteiro / 10); + display_raw_bottom(2, digit_mask[inteiro % 10] | SEG_DP); + display_digit_bottom(3, decimal); + } else { + // formato: C 7.2 + display_char_bottom(0, 'C'); + display_raw_bottom(1, 0); + display_raw_bottom(2, digit_mask[inteiro] | SEG_DP); + display_digit_bottom(3, decimal); + } + } +} diff --git a/main/include/buzzer.h b/main/include/buzzer.h new file mode 100644 index 0000000..cf0a866 --- /dev/null +++ b/main/include/buzzer.h @@ -0,0 +1,8 @@ +#pragma once +#include + +// inicializa o buzzer (PWM / GPIO19) +void buzzer_init(void); + +// bip simples em milissegundos +void buzzer_beep(uint32_t ms); diff --git a/main/include/display.h b/main/include/display.h index 2660a84..1cd1c0a 100644 --- a/main/include/display.h +++ b/main/include/display.h @@ -45,6 +45,7 @@ void display_number_bottom(int num); // RELÓGIO (HH:MM com DP entre horas) // ======================================================= void display_set_time_top(int horas, int minutos); +void display_temperature_bottom(float temp); // ======================================================= // DEBUG diff --git a/main/include/net_weather.h b/main/include/net_weather.h new file mode 100644 index 0000000..83473ba --- /dev/null +++ b/main/include/net_weather.h @@ -0,0 +1,4 @@ +#pragma once +#include + +bool net_weather_update(float *out_temp); diff --git a/main/main.c b/main/main.c index 5a50fd8..7ac37c0 100644 --- a/main/main.c +++ b/main/main.c @@ -32,6 +32,11 @@ #include "display.h" #include +#include "net_weather.h" +#include "buzzer.h" + + + esp_err_t i2c_init(void); esp_err_t display_init(void); @@ -53,6 +58,37 @@ typedef struct { } contadores_t; +static void weather_task(void *arg) +{ + float temp; + + ESP_LOGI("WEATHER", "🌡️ weather_task arrancou"); + + // 🔥 leitura IMEDIATA (antes do delay) + ESP_LOGI("WEATHER", "🌐 a pedir temperatura (primeira vez)"); + + if (net_weather_update(&temp)) { + ESP_LOGI("WEATHER", "🌡️ temperatura recebida: %.1f", temp); + display_temperature_bottom(temp); + } else { + ESP_LOGE("WEATHER", "❌ net_weather_update falhou"); + } + + while (1) { + vTaskDelay(pdMS_TO_TICKS(15 * 60 * 1000)); + + ESP_LOGI("WEATHER", "🌐 a pedir temperatura (loop)"); + + if (net_weather_update(&temp)) { + ESP_LOGI("WEATHER", "🌡️ temperatura recebida: %.1f", temp); + display_temperature_bottom(temp); + } else { + ESP_LOGE("WEATHER", "❌ net_weather_update falhou"); + } + } +} + + // ============================ // Task contador simples // ============================ @@ -88,6 +124,10 @@ static void on_wifi_connected(void) { ESP_LOGI(TAG, "🎬 Iniciando tasks LED e Créditos..."); xTaskCreate(led_task, "led_task", 8192, NULL, 5, NULL); xTaskCreate(creditos_task, "creditos_task", 8192, NULL, 5, NULL); + + // 🌡️ TASK DO TEMPO (AQUI!) + ESP_LOGI(TAG, "🌡️ Iniciando task de temperatura (Open-Meteo)"); + xTaskCreate(weather_task, "weather_task", 4096, NULL, 4, NULL); } @@ -218,7 +258,9 @@ void app_main(void) { // 4) Teste simples no display (se existir) display_text_top("INIT"); - + // 🔔 6) buzzer (AQUI!) + buzzer_init(); + buzzer_beep(500); // -------- Wi-Fi -------- diff --git a/main/net_weather.c b/main/net_weather.c new file mode 100644 index 0000000..21f37b8 --- /dev/null +++ b/main/net_weather.c @@ -0,0 +1,80 @@ +#include "esp_http_client.h" +#include "esp_log.h" +#include +#include +#include "buzzer.h" + +static const char *TAG = "WEATHER"; + +static bool parse_temperature_from_json(const char *json, float *out_temp) +{ + if (!json || !out_temp) return false; + + // 1) ir para o bloco "current" + const char *p = strstr(json, "\"current\""); + if (!p) return false; + + // 2) procurar temperature_2m DENTRO de current + p = strstr(p, "\"temperature_2m\""); + if (!p) return false; + + // 3) ir para o valor + p = strchr(p, ':'); + if (!p) return false; + + *out_temp = strtof(p + 1, NULL); + return true; +} + + +bool net_weather_update(float *out_temp) +{ + char buf[512]; + + esp_http_client_config_t cfg = { + .url = "http://api.open-meteo.com/v1/forecast?latitude=38.75&longitude=-9.125¤t=temperature_2m", + .method = HTTP_METHOD_GET, + .transport_type = HTTP_TRANSPORT_OVER_TCP, + .timeout_ms = 10000, + .user_agent = "esp32-weather", + }; + + ESP_LOGI(TAG, "➡️ HTTP init"); + + esp_http_client_handle_t client = esp_http_client_init(&cfg); + if (!client) { + ESP_LOGE(TAG, "❌ http_client_init falhou"); + return false; + } + + esp_err_t err = esp_http_client_open(client, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "❌ http_client_open falhou: %s", esp_err_to_name(err)); + esp_http_client_cleanup(client); + return false; + } + + int content_length = esp_http_client_fetch_headers(client); + ESP_LOGI(TAG, "📦 content-length = %d", content_length); + + int len = esp_http_client_read(client, buf, sizeof(buf) - 1); + if (len <= 0) { + ESP_LOGE(TAG, "❌ http_client_read falhou"); + esp_http_client_close(client); + esp_http_client_cleanup(client); + return false; + } + + buf[len] = 0; + ESP_LOGI(TAG, "📩 JSON: %s", buf); + + esp_http_client_close(client); + esp_http_client_cleanup(client); + + if (!parse_temperature_from_json(buf, out_temp)) { + ESP_LOGE(TAG, "❌ parse_temperature_from_json falhou"); + return false; + } + + return true; +} diff --git a/sdkconfig b/sdkconfig index 3010128..ae9cb51 100644 --- a/sdkconfig +++ b/sdkconfig @@ -602,7 +602,7 @@ CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y # ESP HTTP client # CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y -# CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set +CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH=y # CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH is not set # end of ESP HTTP client diff --git a/sdkconfig.old b/sdkconfig.old index e69859f..2799a70 100644 --- a/sdkconfig.old +++ b/sdkconfig.old @@ -317,14 +317,14 @@ CONFIG_ESPTOOLPY_FLASHFREQ_40M=y # CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set CONFIG_ESPTOOLPY_FLASHFREQ="40m" # CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y -# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y # CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE="2MB" +CONFIG_ESPTOOLPY_FLASHSIZE="4MB" # CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set CONFIG_ESPTOOLPY_BEFORE_RESET=y # CONFIG_ESPTOOLPY_BEFORE_NORESET is not set