Download backend/iot_telemetry.py from bapi143/CropAI: direct link, hf CLI and curl.
- Browser
- Download file 8.96 kB
-
https://huggingface.co/spaces/bapi143/CropAI/resolve/main/backend/iot_telemetry.py
- Command line
-
hf download hf://spaces/bapi143/CropAI/backend/iot_telemetry.py
-
curl -L -o iot_telemetry.py https://huggingface.co/spaces/bapi143/CropAI/resolve/main/backend/iot_telemetry.py
8.96 kB
| # -*- coding: utf-8 -*- | |
| """ | |
| CropAI Solar-Powered Smart Farm IoT Sensor Hub & LoRaWAN Telemetry Engine | |
| Ingests continuous edge microclimate and soil moisture packets from low-power | |
| solar ESP32 / LoRaWAN Class A field nodes (865-867 MHz India band). | |
| Monitors: | |
| 1. Dual-depth Volumetric Water Content (VWC at 15cm & 30cm) | |
| 2. Permanent Wilting Point (PWP) Deficit & Irrigation Automation Alerts | |
| 3. Leaf Wetness Duration for Bio-Twin Spore Germination coupling | |
| 4. Solar LiPo Battery Voltage (V_bat) & Brownout Safeguards | |
| """ | |
| from typing import Dict, Any, Optional, List, Tuple | |
| import logging | |
| import time | |
| from datetime import datetime, timezone | |
| logger = logging.getLogger("cropai.iot_telemetry") | |
| Tuple_Status = Tuple[str, str, str] | |
| # ========================================================================= | |
| # In-Memory Telemetry & Device Registry Store | |
| # ========================================================================= | |
| ACTIVE_IOT_DEVICES: Dict[str, Dict[str, Any]] = { | |
| "NODE-SURGUJA-01": { | |
| "device_id": "NODE-SURGUJA-01", | |
| "name": "Paddy Field North Parcel (धान खेत उत्तरी खंड)", | |
| "location": "Lundra Block, Surguja (C.G.)", | |
| "crop": "Rice / धान (Swarna Sub-1)", | |
| "hardware_type": "ESP32-S3 + Semtech SX1262 LoRaWAN Node", | |
| "power_source": "5W Monocrystalline Solar Panel + 2500mAh 18650 Li-ion", | |
| "registered_at": "2026-08-15T06:00:00Z", | |
| "last_seen_utc": datetime.now(timezone.utc).isoformat(), | |
| "status": "ONLINE", | |
| "latest_packet": { | |
| "battery_voltage_v": 3.94, | |
| "solar_charging": True, | |
| "rssi_dbm": -82, | |
| "snr_db": 9.2, | |
| "soil_moisture_vwc_15cm": 28.5, | |
| "soil_moisture_vwc_30cm": 31.0, | |
| "canopy_temp_c": 26.8, | |
| "canopy_rh_pct": 84.0, | |
| "leaf_wetness_pct": 40.0 | |
| } | |
| }, | |
| "NODE-SURGUJA-02": { | |
| "device_id": "NODE-SURGUJA-02", | |
| "name": "Vegetable & Tomato Patch (टमाटर बाड़ी)", | |
| "location": "Ambikapur Rural, Surguja (C.G.)", | |
| "crop": "Tomato / टमाटर (Abhilash)", | |
| "hardware_type": "ESP32-C3 + SX1262 LoRaWAN Node", | |
| "power_source": "5W Solar Panel + 2500mAh 18650 Li-ion", | |
| "registered_at": "2026-09-01T08:30:00Z", | |
| "last_seen_utc": datetime.now(timezone.utc).isoformat(), | |
| "status": "ONLINE", | |
| "latest_packet": { | |
| "battery_voltage_v": 3.86, | |
| "solar_charging": True, | |
| "rssi_dbm": -89, | |
| "snr_db": 7.8, | |
| "soil_moisture_vwc_15cm": 22.0, | |
| "soil_moisture_vwc_30cm": 25.4, | |
| "canopy_temp_c": 27.5, | |
| "canopy_rh_pct": 76.0, | |
| "leaf_wetness_pct": 25.0 | |
| } | |
| } | |
| } | |
| def evaluate_moisture_status(vwc_pct: float) -> Tuple_Status: | |
| """Evaluates soil moisture status against agronomic thresholds.""" | |
| if vwc_pct <= 14.0: | |
| return "PERMANENT_WILTING_POINT", "अत्यधिक सूखा - पौधे मुरझा रहे हैं!", "Critical drought stress below permanent wilting point" | |
| elif vwc_pct < 20.0: | |
| return "MOISTURE_DEFICIT", "हल्की सिंचाई की आवश्यकता है", "Moisture deficit; schedule irrigation within 24h" | |
| elif vwc_pct <= 35.0: | |
| return "OPTIMAL_FIELD_CAPACITY", "मिट्टी में आदर्श नमी है", "Optimal field capacity; robust root uptake" | |
| else: | |
| return "SOIL_SATURATED", "खेत में पानी भरा है", "Soil saturation / flood conditions" | |
| def ingest_telemetry_packet(packet: Dict[str, Any]) -> Dict[str, Any]: | |
| """ | |
| Ingests and validates raw telemetry transmission from field node: | |
| 1. Validates physical sensor ranges. | |
| 2. Evaluates Permanent Wilting Point (PWP) deficit. | |
| 3. Checks battery voltage against brownout thresholds. | |
| 4. Triggers emergency agricultural alerts. | |
| """ | |
| device_id = packet.get("device_id", "NODE-SURGUJA-UNKNOWN") | |
| v_bat = float(packet.get("battery_voltage_v", 3.7)) | |
| solar_chg = bool(packet.get("solar_charging", False)) | |
| rssi = int(packet.get("rssi_dbm", -90)) | |
| snr = float(packet.get("snr_db", 6.0)) | |
| vwc_15 = float(packet.get("soil_moisture_vwc_15cm", 25.0)) | |
| vwc_30 = float(packet.get("soil_moisture_vwc_30cm", 28.0)) | |
| temp = float(packet.get("canopy_temp_c", 26.0)) | |
| rh = float(packet.get("canopy_rh_pct", 75.0)) | |
| wetness = float(packet.get("leaf_wetness_pct", 30.0)) | |
| # Evaluate moisture status | |
| moist_code, moist_hi, moist_en = evaluate_moisture_status(vwc_15) | |
| # Battery health | |
| if v_bat < 3.4: | |
| batt_status = "BROWNOUT_WARNING" | |
| batt_desc = "बैटरी कम (Low Battery < 3.4V) - रेडियो पावर सेव मोड सक्रिय" | |
| elif v_bat >= 4.0: | |
| batt_status = "FULLY_CHARGED" | |
| batt_desc = "सोलर से पूर्ण चार्ज (Fully Charged)" | |
| else: | |
| batt_status = "HEALTHY_NORMAL" | |
| batt_desc = "बैटरी स्वस्थ (Healthy Nominal)" | |
| # RF link health | |
| rf_health = "EXCELLENT" if rssi >= -80 else ("GOOD" if rssi >= -95 else "WEAK_MARGINAL") | |
| # Generate automated alerts | |
| active_alarms = [] | |
| if moist_code == "PERMANENT_WILTING_POINT": | |
| active_alarms.append({ | |
| "alarm_type": "CRITICAL_SOIL_DROUGHT", | |
| "message_hi": "🚨 तत्काल सिंचाई करें! 15 सेमी गहराई पर मिट्टी की नमी 14% से नीचे आ गई है।", | |
| "message_en": "Critical Soil Drought: VWC at 15cm is <= 14%. Activate irrigation immediately." | |
| }) | |
| if temp <= 2.0: | |
| active_alarms.append({ | |
| "alarm_type": "FROST_CANOPY_DROP", | |
| "message_hi": "❄️ पाला अलर्ट: कैनोपी का तापमान 2°C से नीचे गिर गया है। धुआं व रात्रि पानी चालू करें!", | |
| "message_en": "Nocturnal freezing drop: Canopy temp <= 2.0°C. Deploy thermal mitigation." | |
| }) | |
| if wetness > 80.0 and (24.0 <= temp <= 28.0): | |
| active_alarms.append({ | |
| "alarm_type": "BIOTWIN_FUNGAL_SPORULATION_WINDOW", | |
| "message_hi": "🍄 फंगस बीजाणु पनपने का अनुकूल मौसम: पत्तियां 80% से अधिक गीली हैं और तापमान 26°C है।", | |
| "message_en": "High disease risk: Leaf wetness > 80% at 26°C indicates active fungal infection window." | |
| }) | |
| timestamp = datetime.now(timezone.utc).isoformat() | |
| # Update device registry | |
| if device_id in ACTIVE_IOT_DEVICES: | |
| ACTIVE_IOT_DEVICES[device_id]["last_seen_utc"] = timestamp | |
| ACTIVE_IOT_DEVICES[device_id]["status"] = "ONLINE" | |
| ACTIVE_IOT_DEVICES[device_id]["latest_packet"] = { | |
| "battery_voltage_v": v_bat, | |
| "solar_charging": solar_chg, | |
| "rssi_dbm": rssi, | |
| "snr_db": snr, | |
| "soil_moisture_vwc_15cm": vwc_15, | |
| "soil_moisture_vwc_30cm": vwc_30, | |
| "canopy_temp_c": temp, | |
| "canopy_rh_pct": rh, | |
| "leaf_wetness_pct": wetness | |
| } | |
| return { | |
| "status": "success", | |
| "device_id": device_id, | |
| "packet_timestamp_utc": timestamp, | |
| "soil_moisture_assessment": { | |
| "vwc_15cm_percentage": vwc_15, | |
| "vwc_30cm_percentage": vwc_30, | |
| "classification": moist_code, | |
| "status_hi": moist_hi, | |
| "status_en": moist_en | |
| }, | |
| "microclimate": { | |
| "canopy_temp_c": temp, | |
| "canopy_rh_pct": rh, | |
| "leaf_wetness_pct": wetness | |
| }, | |
| "hardware_telemetry": { | |
| "battery_voltage_v": v_bat, | |
| "battery_health": batt_status, | |
| "battery_description_hi": batt_desc, | |
| "solar_charging": solar_chg, | |
| "rf_link_rssi_dbm": rssi, | |
| "rf_link_quality": rf_health | |
| }, | |
| "active_alarms": active_alarms | |
| } | |
| def get_registered_devices() -> List[Dict[str, Any]]: | |
| """Returns list of registered field IoT nodes and their operational status.""" | |
| return list(ACTIVE_IOT_DEVICES.values()) | |
| def get_live_device_telemetry(device_id: str) -> Dict[str, Any]: | |
| """Returns live telemetry and recent status for a specific IoT node.""" | |
| device = ACTIVE_IOT_DEVICES.get(device_id) | |
| if not device: | |
| # Fallback simulated dynamic node | |
| return { | |
| "status": "device_not_registered", | |
| "device_id": device_id, | |
| "message": "Device not found in active registry. Ingest first packet to auto-register." | |
| } | |
| return { | |
| "status": "success", | |
| "device": device | |
| } | |