El programa lee los valores de temperatura, humedad y presión atmosférica mediante un sensor BME280 conectado por I2C. Sincroniza la hora exacta mediante un servidor NTP vía Wi-Fi, da formato al reporte y realiza una publicación (Toot) en la red social Mastodon a través de su API REST HTTPS. Al finalizar el ciclo, el ESP8266 entra en modo Deep Sleep durante 15 minutos para optimizar el consumo energético.
#include <ESP8266WiFi.h> #include <ESP8266HTTPClient.h> #include <WiFiClientSecure.h> #include <Wire.h> #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> #include <time.h> // --- CONFIGURACIÓN --- const char* WIFI_SSID = "[El nombre de tu red WiFi]"; const char* WIFI_PASSWORD = "[La clave de tu red Wifi]"; const char* MASTODON_HOST = "[El nombre de la instancia, sin https://, por ejemplo mastodon.social]"; const char* ACCESS_TOKEN = "[El token de acceso al usuario en el que se publicará]"; // --- CONFIGURACIÓN PINES --- const int PIN_SDA = D2; const int PIN_SCL = D1; // --- CONFIGURACIÓN HORARIA (NTP) --- const char* MY_TZ = "CET-1CEST,M3.5.0,M10.5.0/3"; const char* NTP_SERVER = "pool.ntp.org"; // --- TIEMPO DE HIBERNACIÓN (DEEP SLEEP) --- const uint64_t TIEMPO_HIBERNACION_US = 15ULL * 60ULL * 1000000ULL; // 15 minutos Adafruit_BME280 bme; // --- FUNCIONES AUXILIARES --- bool conectarWiFi() { WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); Serial.print("Conectando a WiFi"); int intentos = 0; while (WiFi.status() != WL_CONNECTED && intentos < 30) { delay(500); Serial.print("."); intentos++; } if (WiFi.status() == WL_CONNECTED) { Serial.println("\n✅ WiFi conectado."); return true; } Serial.println("\n❌ Error: No se pudo conectar a WiFi."); return false; } void sincronizarNTP() { configTime(MY_TZ, NTP_SERVER); Serial.print("Sincronizando hora NTP"); time_t now = time(nullptr); int intentos = 0; while (now < 8 * 3600 * 2 && intentos < 30) { delay(500); Serial.print("."); now = time(nullptr); intentos++; } Serial.println("\n✅ Hora sincronizada."); } bool leerBME280(float &temp, float &hum, float &pres) { delay(100); bme.readTemperature(); // Descarte de lectura inicial delay(100); temp = bme.readTemperature(); hum = bme.readHumidity(); pres = bme.readPressure() / 100.0F; if (isnan(temp) || isnan(hum) || isnan(pres)) { return false; } Serial.printf("Lectura Obtenida -> Temp: %.1f °C | Hum: %.1f %% | Pres: %.1f hPa\n", temp, hum, pres); return true; } String obtenerFechaHora() { time_t now = time(nullptr); struct tm timeinfo; localtime_r(&now, &timeinfo); char buffer[40]; strftime(buffer, sizeof(buffer), "%d/%m/%Y - %H:%M:%S", &timeinfo); return String(buffer); } String urlEncode(String str) { String encodedString = ""; char c, code0, code1; for (size_t i = 0; i < str.length(); i++) { c = str.charAt(i); if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { encodedString += c; } else if (c == ' ') { encodedString += "+"; } else { code0 = (c >> 4) & 0xF; code1 = c & 0xF; encodedString += '%'; encodedString += (char)(code0 < 10 ? code0 + '0' : code0 - 10 + 'A'); encodedString += (char)(code1 < 10 ? code1 + '0' : code1 - 10 + 'A'); } } return encodedString; } void publicarToot(String mensaje) { WiFiClientSecure client; client.setInsecure(); HTTPClient http; String url = "https://" + String(MASTODON_HOST) + "/api/v1/statuses"; if (http.begin(client, url)) { http.addHeader("User-Agent", "ESP8266_WeatherBot/1.0"); http.addHeader("Content-Type", "application/x-www-form-urlencoded"); http.addHeader("Authorization", "Bearer " + String(ACCESS_TOKEN)); String postData = "status=" + urlEncode(mensaje) + "&visibility=public"; int httpCode = http.POST(postData); if (httpCode > 0) { Serial.printf("Respuesta HTTP: %d\n", httpCode); Serial.println("Servidor: " + http.getString()); } else { Serial.printf("Error al enviar POST: %s\n", http.errorToString(httpCode).c_str()); } http.end(); } } // --- FLUJO PRINCIPAL DE ARDUINO --- void setup() { Serial.begin(9600); delay(100); if (conectarWiFi()) { sincronizarNTP(); Wire.begin(PIN_SDA, PIN_SCL); delay(200); float temp, hum, pres; if (bme.begin(0x76, &Wire) && leerBME280(temp, hum, pres)) { String mensaje = "📊 *Estación Meteorológica*\n" "📅 " + obtenerFechaHora() + "\n\n" "🌡️ Temperatura: " + String(temp, 1) + " °C\n" "💧 Humedad: " + String(hum, 1) + " %\n" "⏲️ Presión: " + String(pres, 1) + " hPa\n\n" "#ESP8266 #WeatherBot #BME280"; Serial.println("Enviando publicación a Mastodon..."); publicarToot(mensaje); } else { Serial.println("❌ Error: No se pudo comunicar con el BME280."); } } Serial.println("Entrando en Deep Sleep..."); ESP.deepSleep(TIEMPO_HIBERNACION_US); } void loop() { // Vacío por el uso de Deep Sleep }
Debido al uso de Deep Sleep, el flujo completo del programa se ejecuta exclusivamente dentro de la función setup(). La función loop() permanece vacía puesto que el procesador se reinicia completamente tras cada despertar.