/*******************************************************
* Laboratorio de Diseño No. 4
* Monitoreo de Radiobase (Cell Tower)
* ESP32 + DHT22 + Potenciómetro + LED de alarma
* Telemetría vía MQTT hacia ThingsBoard Cloud
*******************************************************/
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
/* ================= CONFIGURACIÓN ================= */
// --- Red Wi-Fi (valores fijos del simulador Wokwi) ---
const char* WIFI_SSID = "Wokwi-GUEST";
const char* WIFI_PASS = "";
// --- ThingsBoard Cloud ---
const char* TB_SERVER = "thingsboard.cloud"; // Plan Free
const int TB_PORT = 1883;
const char* TB_TOKEN = "6nuqnze6pj7tmlm1apro";
const char* TB_TOPIC = "v1/devices/me/telemetry";
// --- Pines de hardware ---
#define PIN_DHT 15 // Dato del sensor DHT22
#define PIN_POT 34 // ADC1_CH6 (solo entrada) - banco de baterías
#define PIN_LED 2 // LED rojo de alarma local
#define TIPO_DHT DHT22
// --- Parámetros de operación ---
const int ADC_MAX = 4095; // Resolución 12 bits
const int UMBRAL_BATERIA = 20; // % mínimo antes de alarma
const unsigned long INTERVALO_PUB_MS = 5000; // Publicar cada 5 s
/* ================= OBJETOS GLOBALES ================= */
DHT dht(PIN_DHT, TIPO_DHT);
WiFiClient espClient;
PubSubClient mqttClient(espClient);
// Estructura que agrupa la lectura del sitio
struct LecturaSitio {
float temperatura;
float humedad;
int bateria;
bool valida;
};
unsigned long ultimaPublicacion = 0;
/* ================= MÓDULO: CONECTIVIDAD ================= */
void conectarWiFi() {
Serial.print("[WiFi] Conectando a ");
Serial.print(WIFI_SSID);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS, 6); // canal 6: acelera la conexión en Wokwi
while (WiFi.status() != WL_CONNECTED) {
delay(300);
Serial.print(".");
}
Serial.print("\n[WiFi] Conectado. IP: ");
Serial.println(WiFi.localIP());
}
void conectarMQTT() {
while (!mqttClient.connected()) {
Serial.print("[MQTT] Conectando a ThingsBoard... ");
// En ThingsBoard el usuario MQTT es el Access Token y la contraseña queda vacía
if (mqttClient.connect("ESP32_Radiobase", TB_TOKEN, NULL)) {
Serial.println("OK");
} else {
Serial.print("fallo, rc=");
Serial.print(mqttClient.state());
Serial.println(" -> reintento en 3 s");
delay(3000);
}
}
}
void mantenerConexiones() {
if (WiFi.status() != WL_CONNECTED) conectarWiFi();
if (!mqttClient.connected()) conectarMQTT();
mqttClient.loop();
}
/* ================= MÓDULO: ADQUISICIÓN ================= */
// Convierte la lectura cruda del ADC (0-4095) a porcentaje de batería (0-100)
int mapearBateria(int valorAdc) {
int porcentaje = map(valorAdc, 0, ADC_MAX, 0, 100);
return constrain(porcentaje, 0, 100);
}
LecturaSitio leerSensores() {
LecturaSitio datos;
datos.temperatura = dht.readTemperature(); // °C
datos.humedad = dht.readHumidity(); // %HR
datos.bateria = mapearBateria(analogRead(PIN_POT));
datos.valida = !(isnan(datos.temperatura) || isnan(datos.humedad));
return datos;
}
/* ================= MÓDULO: EDGE COMPUTING ================= */
// La decisión se toma localmente en el ESP32, sin depender de la nube
bool evaluarAlarmaLocal(int bateria) {
bool enAlarma = (bateria < UMBRAL_BATERIA);
digitalWrite(PIN_LED, enAlarma ? HIGH : LOW);
return enAlarma;
}
/* ================= MÓDULO: TELEMETRÍA ================= */
String construirJSON(const LecturaSitio &d, bool alarma) {
String json = "{";
json += "\"temperatura\":" + String(d.temperatura, 1) + ",";
json += "\"humedad\":" + String(d.humedad, 1) + ",";
json += "\"bateria\":" + String(d.bateria) + ",";
json += "\"alarma\":" + String(alarma ? "true" : "false");
json += "}";
return json;
}
void publicarTelemetria(const String &payload) {
if (mqttClient.publish(TB_TOPIC, payload.c_str())) {
Serial.println("[MQTT] Enviado: " + payload);
} else {
Serial.println("[MQTT] Error al publicar");
}
}
/* ================= SETUP / LOOP ================= */
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("\n=== Monitoreo de Radiobase - IoT ===");
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, LOW);
analogReadResolution(12); // 0 - 4095
dht.begin();
conectarWiFi();
mqttClient.setServer(TB_SERVER, TB_PORT);
conectarMQTT();
}
void loop() {
mantenerConexiones();
LecturaSitio datos = leerSensores();
// La alarma se evalúa en cada ciclo: respuesta inmediata, no cada 5 s
bool alarma = evaluarAlarmaLocal(datos.bateria);
if (millis() - ultimaPublicacion >= INTERVALO_PUB_MS) {
ultimaPublicacion = millis();
if (datos.valida) {
publicarTelemetria(construirJSON(datos, alarma));
} else {
Serial.println("[DHT22] Lectura inválida, se omite el envío");
}
}
delay(100);
}