/* NovaGuard Firmware V1 LoRa RX · Departamento · Cliente 103 ESP32 + LoRa RX + Buzzer + WiFi HTTP Worker Opcional OLED. Función: - Recibe paquetes desde el TX del sótano. - Muestra por Serial. - Activa buzzer si detecta "alarma":true. - Reenvía el JSON recibido a Cloudflare Worker / Firestore. IMPORTANTE: - Ajustar pines LoRa según placa real. - Para Heltec ESP32 S3 LoRa SX1262 puede requerir RadioLib/Heltec, no LoRa clásica. */ #include #include #include #include // Pines LoRa genéricos. Ajustar según placa. #define LORA_SS 18 #define LORA_RST 14 #define LORA_DIO0 26 #define LORA_FREQ 433E6 #define PIN_BUZZER 25 const char* WIFI_SSID = "TU_WIFI"; const char* WIFI_PASS = "TU_PASSWORD"; const char* ENDPOINT = "https://tu-worker-novaguard.workers.dev/ingest-lora"; const char* CLIENTE_ID = "103"; const char* EQUIPO_ID = "depto_rx_103"; const char* UBICACION = "Departamento Garcia"; unsigned long lastPacketAt = 0; const unsigned long LINK_TIMEOUT = 60000; void conectarWiFi(){ if(WiFi.status() == WL_CONNECTED) return; Serial.print("Conectando WiFi"); WiFi.begin(WIFI_SSID, WIFI_PASS); unsigned long start = millis(); while(WiFi.status() != WL_CONNECTED && millis() - start < 15000){ delay(500); Serial.print("."); } Serial.println(); if(WiFi.status() == WL_CONNECTED){ Serial.print("WiFi OK · IP: "); Serial.println(WiFi.localIP()); }else{ Serial.println("WiFi NO conectado"); } } void iniciarLoRa(){ LoRa.setPins(LORA_SS, LORA_RST, LORA_DIO0); if(!LoRa.begin(LORA_FREQ)){ Serial.println("ERROR: no inicia LoRa RX"); while(true){ digitalWrite(PIN_BUZZER, HIGH); delay(150); digitalWrite(PIN_BUZZER, LOW); delay(850); } } LoRa.setSpreadingFactor(10); LoRa.setSignalBandwidth(125E3); LoRa.setCodingRate4(5); LoRa.enableCrc(); Serial.println("LoRa RX listo"); } void enviarWorker(String payload){ if(WiFi.status() != WL_CONNECTED){ conectarWiFi(); if(WiFi.status() != WL_CONNECTED) return; } String wrapped = "{"; wrapped += "\"cliente_id\":\"" + String(CLIENTE_ID) + "\","; wrapped += "\"equipo_id\":\"" + String(EQUIPO_ID) + "\","; wrapped += "\"ubicacion\":\"" + String(UBICACION) + "\","; wrapped += "\"tipo\":\"LORA_RX_DEPTO\","; wrapped += "\"rssi\":" + String(LoRa.packetRssi()) + ","; wrapped += "\"snr\":" + String(LoRa.packetSnr()) + ","; wrapped += "\"payload\":" + payload; wrapped += "}"; HTTPClient http; http.begin(ENDPOINT); http.addHeader("Content-Type", "application/json"); int code = http.POST(wrapped); Serial.print("HTTP Worker: "); Serial.println(code); if(code > 0){ Serial.println(http.getString()); } http.end(); } void setup(){ Serial.begin(115200); delay(800); pinMode(PIN_BUZZER, OUTPUT); digitalWrite(PIN_BUZZER, LOW); conectarWiFi(); iniciarLoRa(); Serial.println("NovaGuard LoRa RX Departamento listo"); } void loop(){ int packetSize = LoRa.parsePacket(); if(packetSize){ String payload = ""; while(LoRa.available()){ payload += (char)LoRa.read(); } lastPacketAt = millis(); int rssi = LoRa.packetRssi(); float snr = LoRa.packetSnr(); Serial.println("RX LoRa:"); Serial.println(payload); Serial.print("RSSI: "); Serial.print(rssi); Serial.print(" · SNR: "); Serial.println(snr); bool alarma = payload.indexOf("\"alarma\":true") >= 0; digitalWrite(PIN_BUZZER, alarma ? HIGH : LOW); enviarWorker(payload); } if(millis() - lastPacketAt > LINK_TIMEOUT && lastPacketAt > 0){ // Aviso de posible pérdida de enlace: dos pitidos cortos cada ciclo de timeout. digitalWrite(PIN_BUZZER, HIGH); delay(120); digitalWrite(PIN_BUZZER, LOW); delay(120); digitalWrite(PIN_BUZZER, HIGH); delay(120); digitalWrite(PIN_BUZZER, LOW); lastPacketAt = millis(); Serial.println("Advertencia: sin paquetes LoRa recientes"); } delay(50); }