#include <WiFi.h>
#include <WebSocketsClient.h>
#include <SocketIOclient.h>
#include <ArduinoJson.h>
#include "DHT.h"
#include "config.h"
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
SocketIOclient socketIO;
unsigned long lastSensorMillis = 0;
unsigned long lastWifiCheckMillis = 0;
const unsigned long SENSOR_INTERVAL = 5000;
const unsigned long WIFI_CHECK_INTERVAL = 5000;
volatile bool socketConnected = false;
void socketIOEvent(socketIOmessageType_t type, uint8_t *payload, size_t length) {
switch (type) {
case sIOtype_DISCONNECT:
socketConnected = false;
Serial.println("\n[IOc] DISCONNECTED");
break;
case sIOtype_CONNECT:
Serial.println("\n[IOc] ENGINE CONNECTED: ");
Serial.println(payload ? (char *)payload : "(unknown)");
socketIO.send(sIOtype_CONNECT, "/");
socketConnected = true;
Serial.println("[IOc] Namespace join (40) sent");
break;
case sIOtype_EVENT: {
Serial.println("\n[IOc] EVENT: ");
if (payload != nullptr) {
Serial.println((char *)payload);
} else {
Serial.println("(empty)");
break;
}
DynamicJsonDocument doc(2048);
DeserializationError err = deserializeJson(doc, payload, length);
if (err) {
Serial.print("[IOc] JSON parse failed: ");
Serial.println(err.c_str());
break;
}
const char *eventName = doc[0];
if (eventName != nullptr && strcmp(eventName, "control_command") == 0) {
const char *action = doc[1]["command"]["action"] | "(none)";
Serial.print("[CMD] Action: ");
Serial.println(action);
}
break;
}
case sIOtype_ACK:
Serial.println("[IOc] ACK received");
break;
case sIOtype_ERROR:
Serial.println("\n[IOc] ERROR, length: ");
Serial.println(length);
if (payload != nullptr) {
Serial.println((char *)payload);
}
break;
case sIOtype_BINARY_EVENT:
case sIOtype_BINARY_ACK:
Serial.println("[IOc] Binary message (ignored)");
break;
}
}
void startSocketIO() {
String serverIP = WiFi.gatewayIP().toString();
String socketPath = "/socket.io/?EIO=4&role=" + String(DEVICE_ROLE) + "&token=" + String(AUTH_TOKEN);
Serial.println("\n=================================");
Serial.println("Socket.IO Configuration");
Serial.println("=================================");
Serial.print("Server IP : "); Serial.println(serverIP);
Serial.print("Port : "); Serial.println(SERVER_PORT);
Serial.print("Role : "); Serial.println(DEVICE_ROLE);
Serial.println("Token : ********");
Serial.println("\nConnecting to Socket.IO server...");
socketIO.begin(serverIP.c_str(), SERVER_PORT, socketPath.c_str());
socketIO.onEvent(socketIOEvent);
socketIO.setReconnectInterval(5000);
Serial.println("Socket.IO client started.");
}
void sendSensorData() {
float humidity = dht.readHumidity();
float temperature = dht.readTemperature();
// Read & calculate Rain Sensor percentage (0 = Dry, 4095 = Wet or vice versa)
int rawRainValue = analogRead(RAIN_ANALOG_PIN);
float rainPercentage = map(rawRainValue, 4095, 0, 0, 100);
rainPercentage = constrain(rainPercentage, 0.0, 100.0);
// Read & calculate Soil Moisture percentage
// Note: Standard capacitive/resistive sensors read ~4095 in dry air and ~1500-2000 fully submerged in water.
int rawSoilValue = analogRead(SOIL_ANALOG_PIN);
float soilMoisturePercentage = map(rawSoilValue, 4095, 1500, 0, 100);
soilMoisturePercentage = constrain(soilMoisturePercentage, 0.0, 100.0);
// Read digital status on GPIO 22: LOW = Rain Detected, HIGH = Dry
int digitalRainState = digitalRead(RAIN_DIGITAL_PIN);
if (isnan(humidity) || isnan(temperature)) {
Serial.println("\n[SENSOR] Failed to read DHT22");
return;
}
DynamicJsonDocument doc(1024);
JsonArray event = doc.to<JsonArray>();
event.add("message.upsert");
JsonObject data = event.createNestedObject();
data["Type"] = "sensors";
JsonObject message = data.createNestedObject("Message");
message["temperature"] = temperature;
message["humidity"] = humidity;
message["soilMoisture"] = soilMoisturePercentage;
message["rainDrop"] = rainPercentage;
message["isRaining"] = (digitalRainState == LOW);
String output;
serializeJson(doc, output);
Serial.println("\n------------ SENSOR DATA ------------");
Serial.print("Temperature : "); Serial.print(temperature, 2); Serial.println(" C");
Serial.print("Humidity : "); Serial.print(humidity, 2); Serial.println(" %");
Serial.print("Soil Moisture : "); Serial.print(soilMoisturePercentage, 2); Serial.println(" %");
Serial.print("Rain Level : "); Serial.print(rainPercentage, 2); Serial.println(" %");
Serial.print("Rain State : "); Serial.println(digitalRainState == LOW ? "RAINING" : "DRY");
Serial.print("Socket : "); Serial.println(socketConnected ? "CONNECTED" : "DISCONNECTED");
if (!socketConnected) {
Serial.println("[Socket.IO] Not connected. Data not sent.");
Serial.println("-------------------------------------");
return;
}
bool sent = socketIO.sendEVENT(output);
if (sent) {
Serial.println("[Socket.IO] message.upsert sent");
} else {
Serial.println("[Socket.IO] Failed to send message.upsert");
}
Serial.println("-------------------------------------");
}
void connectWiFi() {
Serial.println("\n=================================");
Serial.println("WiFi Connection");
Serial.println("=================================");
Serial.print("SSID: "); Serial.println(WIFI_SSID);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
attempts++;
if (attempts >= 60) {
Serial.println("\n[WiFi] Connection timeout, retrying");
WiFi.disconnect(true);
delay(1000);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
attempts = 0;
}
}
Serial.println("\n=================================");
Serial.println("WiFi Connected!");
Serial.println("=================================");
Serial.print("ESP32 IP Address : "); Serial.println(WiFi.localIP());
Serial.print("Gateway IP : "); Serial.println(WiFi.gatewayIP());
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n---------------------------------");
Serial.println("ESP32 BOOT");
Serial.println("---------------------------------");
dht.begin();
Serial.println("[DHT] DHT22 initialized on GPIO 19");
pinMode(RAIN_DIGITAL_PIN, INPUT);
connectWiFi();
startSocketIO();
Serial.println("\nESP32 initialization complete.\n");
}
void loop() {
socketIO.loop();
if (millis() - lastSensorMillis >= SENSOR_INTERVAL) {
lastSensorMillis = millis();
sendSensorData();
}
if (millis() - lastWifiCheckMillis >= WIFI_CHECK_INTERVAL) {
lastWifiCheckMillis = millis();
if (WiFi.status() != WL_CONNECTED) {
socketConnected = false;
Serial.println("[WiFi] Disconnected, reconnecting");
WiFi.reconnect();
}
}
delay(10);
}