/* KFS Smart Farm — field node (ESP32) — MQTT version (sessions 7-8, 20) Publishes JSON to kfs/farm//telemetry (QoS 0 with PubSubClient) every 60 s Last Will: kfs/farm//status = "offline" (retained) -> the dashboard knows when a node dies Subscribes to: kfs/farm//cmd payload {"actuator":"pump","action":"on","duration_min":5} Libraries: PubSubClient (Nick O'Leary), DHT sensor library, ArduinoJson v7 */ #include #include #include #include const char* WIFI_SSID = "KFS-Farm-IoT"; const char* WIFI_PASS = "change-me"; const char* BROKER = "192.168.1.10"; // Mosquitto on the farm laptop / Raspberry Pi const char* MQTT_USER = "farmnode"; const char* MQTT_PASS = "change-me"; const char* NODE_ID = "greenhouse_01"; #define DHT_PIN 4 #define SOIL_PIN 34 #define PUMP_PIN 26 DHT dht(DHT_PIN, DHT22); WiFiClient net; PubSubClient mqtt(net); String tTelemetry, tStatus, tCmd; unsigned long last = 0, pumpOffAt = 0; void onMessage(char* topic, byte* payload, unsigned int len) { JsonDocument doc; if (deserializeJson(doc, payload, len)) return; bool on = doc["action"] == "on"; if (doc["actuator"] == "pump") { digitalWrite(PUMP_PIN, on ? LOW : HIGH); pumpOffAt = on ? millis() + min(doc["duration_min"] | 5, 30) * 60000UL : 0; } Serial.printf("cmd on %s: %.*s\n", topic, len, (char*)payload); } void reconnect() { while (!mqtt.connected()) { // clientId, user, pass, willTopic, willQoS, willRetain, willMessage if (mqtt.connect(NODE_ID, MQTT_USER, MQTT_PASS, tStatus.c_str(), 1, true, "offline")) { mqtt.publish(tStatus.c_str(), "online", true); mqtt.subscribe(tCmd.c_str(), 1); } else { Serial.printf("MQTT failed rc=%d, retry in 5 s\n", mqtt.state()); delay(5000); } } } void setup() { Serial.begin(115200); pinMode(PUMP_PIN, OUTPUT); digitalWrite(PUMP_PIN, HIGH); dht.begin(); tTelemetry = String("kfs/farm/") + NODE_ID + "/telemetry"; tStatus = String("kfs/farm/") + NODE_ID + "/status"; tCmd = String("kfs/farm/") + NODE_ID + "/cmd"; WiFi.begin(WIFI_SSID, WIFI_PASS); while (WiFi.status() != WL_CONNECTED) delay(250); mqtt.setServer(BROKER, 1883); mqtt.setCallback(onMessage); mqtt.setBufferSize(512); } void loop() { if (!mqtt.connected()) reconnect(); mqtt.loop(); if (pumpOffAt && millis() > pumpOffAt) { digitalWrite(PUMP_PIN, HIGH); pumpOffAt = 0; } if (millis() - last > 60000 || last == 0) { last = millis(); JsonDocument doc; float t = dht.readTemperature(), h = dht.readHumidity(); if (!isnan(t)) doc["air_temp_c"] = t; if (!isnan(h)) doc["air_humidity_pct"] = h; doc["soil_moisture_pct"] = constrain(map(analogRead(SOIL_PIN), 3200, 1300, 0, 100), 0, 100); doc["rssi"] = WiFi.RSSI(); char buf[256]; size_t n = serializeJson(doc, buf); mqtt.publish(tTelemetry.c_str(), (const uint8_t*)buf, n, false); } }