#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <DHT.h>
#include <ESP32Servo.h>
#include <FastLED.h>
// ----- PIN CONFIG -----
#define DHTPIN 14
#define DHTTYPE DHT22
#define SIMPLE_LED_PIN 26
#define SERVO_PIN 2
#define NEOPIXEL_PIN 4
#define NUM_LEDS 16
// ----- WIFI -----
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* BACKEND_HOST = "sustainability-boot-cosmetic-frankfurt.trycloudflare.com";
const char* LIGHT_DEVICE_ID = "dev-light-01";
const char* FAN_DEVICE_ID = "dev-fan-01";
const char* SENSOR_DEVICE_ID = "dev-dht20-01";
DHT dht(DHTPIN, DHTTYPE);
Servo servo;
CRGB leds[NUM_LEDS];
// ----- STATE -----
float tempValue = NAN;
float humValue = NAN;
bool lightState = false;
bool fanState = false;
unsigned long lastSyncMillis = 0;
unsigned long lastPollMillis = 0;
const unsigned long SYNC_INTERVAL_MS = 5000;
const unsigned long POLL_INTERVAL_MS = 2000;
// ----- HELPERS -----
String makeBaseUrl() {
return String("http") + "://" + BACKEND_HOST;
}
void setRingColor(const CRGB& color) {
fill_solid(leds, NUM_LEDS, color);
FastLED.show();
}
void setupWifi() {
Serial.print("Connecting to WiFi");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 30) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println();
Serial.println("WiFi connected");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
} else {
Serial.println();
Serial.println("WiFi connect failed");
}
}
bool readDHT22() {
float t = dht.readTemperature();
float h = dht.readHumidity();
if (isnan(t) || isnan(h)) {
Serial.println("DHT22 read failed");
return false;
}
tempValue = t;
humValue = h;
Serial.print("Temp: ");
Serial.print(tempValue);
Serial.print(" C | Humidity: ");
Serial.print(humValue);
Serial.println(" %");
return true;
}
void applyLightCommand(const String& commandName, const String& commandValue) {
if (commandName == "LIGHT_ON" || commandValue == "L_ON") {
digitalWrite(SIMPLE_LED_PIN, HIGH);
lightState = true;
setRingColor(CRGB::Yellow);
Serial.println("Light ON");
} else if (commandName == "LIGHT_OFF" || commandValue == "L_OFF") {
digitalWrite(SIMPLE_LED_PIN, LOW);
lightState = false;
setRingColor(CRGB::Blue);
Serial.println("Light OFF");
}
}
void applyFanCommand(const String& commandName, const String& commandValue) {
if (commandName == "FAN_ON" || commandValue == "F_ON") {
servo.write(90);
fanState = true;
setRingColor(CRGB::Green);
Serial.println("Fan ON -> servo 90");
} else if (commandName == "FAN_OFF" || commandValue == "F_OFF") {
servo.write(0);
fanState = false;
setRingColor(CRGB::Red);
Serial.println("Fan OFF -> servo 0");
} else if (commandName == "DOOR_OPEN" || commandValue == "DOOR_OPEN") {
servo.write(90);
Serial.println("Door open -> servo 90");
delay(1500);
servo.write(0);
Serial.println("Door close -> servo 0");
}
}
void pollCommandsForDevice(const char* deviceId) {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi not connected, skip command poll");
return;
}
String url = makeBaseUrl() + "/api/device/commands?deviceId=" + deviceId;
HTTPClient http;
if (!http.begin(url)) {
Serial.println("HTTP begin failed for command poll");
return;
}
int code = http.GET();
if (code != HTTP_CODE_OK) {
Serial.print("Poll failed, code=");
Serial.println(code);
http.end();
return;
}
String response = http.getString();
http.end();
StaticJsonDocument<2048> doc;
DeserializationError err = deserializeJson(doc, response);
if (err) {
Serial.print("JSON parse error: ");
Serial.println(err.c_str());
return;
}
JsonArray data = doc["data"].as<JsonArray>();
if (data.isNull() || data.size() == 0) {
Serial.print("No commands for ");
Serial.println(deviceId);
return;
}
Serial.print("Commands found for ");
Serial.println(deviceId);
for (JsonObject item : data) {
String commandName = item["commandName"] | "";
String commandValue = item["commandValue"] | "";
String source = item["source"] | "";
Serial.print(" - ");
Serial.print(commandName);
Serial.print(" / ");
Serial.print(commandValue);
Serial.print(" from ");
Serial.println(source);
if (String(deviceId) == LIGHT_DEVICE_ID) {
applyLightCommand(commandName, commandValue);
} else if (String(deviceId) == FAN_DEVICE_ID) {
applyFanCommand(commandName, commandValue);
}
}
}
void postSyncToBackend() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi not connected, skip sync");
return;
}
String url = makeBaseUrl() + "/api/device/sync";
StaticJsonDocument<1024> doc;
doc["source"] = "gateway";
JsonArray deviceStates = doc.createNestedArray("deviceStates");
JsonObject lightStateObj = deviceStates.createNestedObject();
lightStateObj["deviceId"] = LIGHT_DEVICE_ID;
lightStateObj["stateKey"] = "power";
lightStateObj["stateValue"] = lightState;
JsonObject fanStateObj = deviceStates.createNestedObject();
fanStateObj["deviceId"] = FAN_DEVICE_ID;
fanStateObj["stateKey"] = "power";
fanStateObj["stateValue"] = fanState;
JsonArray readings = doc.createNestedArray("readings");
if (!isnan(tempValue)) {
JsonObject tempObj = readings.createNestedObject();
tempObj["deviceId"] = SENSOR_DEVICE_ID;
tempObj["metricName"] = "temperature";
tempObj["value"] = tempValue;
tempObj["unit"] = "C";
tempObj["source"] = "wokwi";
}
if (!isnan(humValue)) {
JsonObject humObj = readings.createNestedObject();
humObj["deviceId"] = SENSOR_DEVICE_ID;
humObj["metricName"] = "humidity";
humObj["value"] = humValue;
humObj["unit"] = "%";
humObj["source"] = "wokwi";
}
JsonArray logs = doc.createNestedArray("logs");
JsonObject logObj = logs.createNestedObject();
logObj["component"] = "wokwi-esp32";
logObj["level"] = "info";
logObj["message"] = "Telemetry sync from Wokwi";
String body;
serializeJson(doc, body);
HTTPClient http;
if (!http.begin(url)) {
Serial.println("HTTP begin failed for sync");
return;
}
http.addHeader("Content-Type", "application/json");
int code = http.POST(body);
String response = http.getString();
http.end();
Serial.print("Sync code: ");
Serial.println(code);
Serial.println(response);
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("ESP32 Wokwi HTTP demo starting...");
dht.begin();
pinMode(SIMPLE_LED_PIN, OUTPUT);
digitalWrite(SIMPLE_LED_PIN, LOW);
ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
ESP32PWM::allocateTimer(2);
ESP32PWM::allocateTimer(3);
servo.attach(SERVO_PIN);
servo.write(0);
FastLED.addLeds<WS2812, NEOPIXEL_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(50);
setRingColor(CRGB::Black);
setupWifi();
Serial.println("Setup done");
}
void loop() {
if (WiFi.status() != WL_CONNECTED) {
setupWifi();
}
unsigned long now = millis();
if (now - lastPollMillis >= POLL_INTERVAL_MS) {
lastPollMillis = now;
pollCommandsForDevice(LIGHT_DEVICE_ID);
pollCommandsForDevice(FAN_DEVICE_ID);
}
if (now - lastSyncMillis >= SYNC_INTERVAL_MS) {
lastSyncMillis = now;
readDHT22();
postSyncToBackend();
}
delay(10);
}