/*
SMART ENERGY MANAGEMENT SYSTEM
ESP32 + Solar/Grid + IoT + Fault Diagnosis
Wokwi + ThingSpeak
*/
#include <WiFi.h>
#include <ThingSpeak.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <DHT.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <SD.h>
#include <string.h>
// ==================== PIN DEFINITIONS ====================
#define BATTERY_PIN 34
#define INTERNET_PIN 35
#define SOLAR_PIN 33
#define USERS_PIN 32
#define RELAY_PIN 23
#define GREEN_LED 18
#define RED_LED 19
#define YELLOW_LED 17
#define BUZZER_PIN 5
#define BUTTON1_PIN 26
#define BUTTON2_PIN 25
#define DHT_PIN 16
#define DHT_TYPE DHT22
#define DS1_PIN 27
#define DS2_PIN 12
#define DS3_PIN 0
#define SD_CS 13
#define SD_SCK 14
#define SD_MISO 4
#define SD_MOSI 15
// ==================== OLED DISPLAY ====================
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define OLED_ADDRESS 0x3C
Adafruit_SSD1306 display(
SCREEN_WIDTH,
SCREEN_HEIGHT,
&Wire,
OLED_RESET
);
// ==================== WIFI ====================
const char* WIFI_SSID = "Wokwi-GUEST";
const char* WIFI_PASSWORD = "";
WiFiClient client;
// ==================== THINGSPEAK ====================
const unsigned long CHANNEL_ID = 3538735UL;
// ضع Write API Key الحالي الخاص بقناتك هنا
const char* WRITE_API_KEY =
"VTHSO4RL6MB28QVZ";
// ==================== SENSORS ====================
DHT dht(DHT_PIN, DHT_TYPE);
OneWire oneWire1(DS1_PIN);
OneWire oneWire2(DS2_PIN);
OneWire oneWire3(DS3_PIN);
DallasTemperature ds1(&oneWire1);
DallasTemperature ds2(&oneWire2);
DallasTemperature ds3(&oneWire3);
// ==================== THRESHOLDS ====================
const float BATTERY_LOW = 25.0f;
const float BATTERY_RECOVERY = 30.0f;
const float SOLAR_THRESHOLD = 15.0f;
const float SOLAR_RECOVERY = 20.0f;
const float INTERNET_LOW = 20.0f;
const float TEMP_HIGH = 45.0f;
const float DS_TEMP_HIGH = 60.0f;
// ==================== TIMING ====================
const unsigned long DISPLAY_INTERVAL = 1000;
const unsigned long LOG_INTERVAL = 15000;
const unsigned long WIFI_INTERVAL = 5000;
const unsigned long PAGE_INTERVAL = 4000;
const unsigned long THINGSPEAK_INTERVAL = 20000;
const unsigned long ALARM_ON_TIME = 250;
const unsigned long ALARM_OFF_TIME = 250;
// ==================== SYSTEM VARIABLES ====================
float batteryPercent = 0.0f;
float solarPercent = 0.0f;
float internetPercent = 0.0f;
float temperature = NAN;
float humidity = NAN;
float dsTemp1 = NAN;
float dsTemp2 = NAN;
float dsTemp3 = NAN;
int simulatedUsers = 0;
// Fault states
bool dhtSensorFault = false;
bool dsSensorFault = false;
bool batteryFault = false;
bool solarFault = false;
bool internetFault = false;
bool temperatureFault = false;
bool dsTemperatureFault = false;
// System states
bool relayOnGrid = false;
bool oledAvailable = false;
bool sdAvailable = false;
bool buzzerTestMode = false;
bool alarmTestMode = false;
bool button1Previous = HIGH;
bool button2Previous = HIGH;
bool alarmSoundOn = false;
int displayPage = 0;
// Timers
unsigned long lastDisplayTime = 0;
unsigned long lastLogTime = 0;
unsigned long lastWiFiTime = 0;
unsigned long lastPageTime = 0;
unsigned long lastAlarmTime = 0;
unsigned long lastThingSpeakTime = 0;
unsigned long lastSensorReadTime = 0;
// ==================== FAULT COUNT ====================
int getFaultCount() {
int count = 0;
if (batteryFault) count++;
if (solarFault) count++;
if (internetFault) count++;
if (temperatureFault) count++;
if (dhtSensorFault) count++;
if (dsSensorFault) count++;
if (dsTemperatureFault) count++;
return count;
}
// ==================== ANALOG SENSORS ====================
float readPercent(int pin) {
int raw = analogRead(pin);
float percent = (raw / 4095.0f) * 100.0f;
if (percent < 0.0f) percent = 0.0f;
if (percent > 100.0f) percent = 100.0f;
return percent;
}
void readAnalogSensors() {
batteryPercent = readPercent(BATTERY_PIN);
solarPercent = readPercent(SOLAR_PIN);
internetPercent = readPercent(INTERNET_PIN);
simulatedUsers = (int)map(
analogRead(USERS_PIN),
0,
4095,
0,
10
);
}
// ==================== DHT22 SENSOR ====================
void readDHTSensor() {
float newTemperature = dht.readTemperature();
float newHumidity = dht.readHumidity();
if (isnan(newTemperature) || isnan(newHumidity)) {
dhtSensorFault = true;
Serial.println("FAULT: DHT22 reading failed");
return;
}
temperature = newTemperature;
humidity = newHumidity;
dhtSensorFault = false;
}
// ==================== DS18B20 SENSORS ====================
bool validDSTemperature(float value) {
return !isnan(value) &&
value != DEVICE_DISCONNECTED_C &&
value >= -55.0f &&
value <= 125.0f;
}
void readDS18B20Sensors() {
ds1.requestTemperatures();
ds2.requestTemperatures();
ds3.requestTemperatures();
float t1 = ds1.getTempCByIndex(0);
float t2 = ds2.getTempCByIndex(0);
float t3 = ds3.getTempCByIndex(0);
bool valid1 = validDSTemperature(t1);
bool valid2 = validDSTemperature(t2);
bool valid3 = validDSTemperature(t3);
dsSensorFault = !(valid1 && valid2 && valid3);
dsTemp1 = valid1 ? t1 : NAN;
dsTemp2 = valid2 ? t2 : NAN;
dsTemp3 = valid3 ? t3 : NAN;
dsTemperatureFault =
(valid1 && t1 >= DS_TEMP_HIGH) ||
(valid2 && t2 >= DS_TEMP_HIGH) ||
(valid3 && t3 >= DS_TEMP_HIGH);
}
// ==================== FAULT DETECTION ====================
void updateFaults() {
if (batteryPercent <= BATTERY_LOW) {
batteryFault = true;
}
else if (batteryPercent >= BATTERY_RECOVERY) {
batteryFault = false;
}
if (solarPercent <= SOLAR_THRESHOLD) {
solarFault = true;
}
else if (solarPercent >= SOLAR_RECOVERY) {
solarFault = false;
}
internetFault = (internetPercent <= INTERNET_LOW);
if (!isnan(temperature)) {
temperatureFault = (temperature >= TEMP_HIGH);
}
else {
temperatureFault = false;
}
}
// ==================== POWER MANAGEMENT ====================
void managePower() {
// HIGH selects grid in this simulation.
// Verify relay polarity before real installation.
relayOnGrid = batteryFault || solarFault;
digitalWrite(
RELAY_PIN,
relayOnGrid ? HIGH : LOW
);
}
// ==================== BUZZER ====================
void buzzerOn() {
tone(BUZZER_PIN, 2000);
}
void buzzerOff() {
noTone(BUZZER_PIN);
}
// ==================== INDICATORS AND ALARM ====================
void updateIndicators() {
int faults = getFaultCount();
digitalWrite(
GREEN_LED,
faults == 0 ? HIGH : LOW
);
digitalWrite(
RED_LED,
faults > 0 ? HIGH : LOW
);
digitalWrite(
YELLOW_LED,
relayOnGrid ? HIGH : LOW
);
if (buzzerTestMode) {
buzzerOn();
alarmSoundOn = true;
return;
}
if (alarmTestMode || faults > 0) {
unsigned long now = millis();
unsigned long interval =
alarmSoundOn ? ALARM_ON_TIME : ALARM_OFF_TIME;
if (now - lastAlarmTime >= interval) {
lastAlarmTime = now;
alarmSoundOn = !alarmSoundOn;
if (alarmSoundOn) {
buzzerOn();
}
else {
buzzerOff();
}
}
return;
}
buzzerOff();
alarmSoundOn = false;
}
// ==================== WIFI CONNECTION ====================
bool connectWiFi() {
if (WiFi.status() == WL_CONNECTED) {
return true;
}
Serial.println("Connecting to WiFi...");
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
unsigned long started = millis();
while (
WiFi.status() != WL_CONNECTED &&
millis() - started < 15000
) {
delay(250);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println();
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
return true;
}
Serial.println();
Serial.print("WiFi connection failed. Status = ");
Serial.println((int)WiFi.status());
return false;
}
// ==================== THINGSPEAK UPLOAD ====================
void uploadThingSpeak() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("ThingSpeak skipped: WiFi disconnected.");
return;
}
if (
strcmp(
WRITE_API_KEY,
"PASTE_CURRENT_WRITE_API_KEY_HERE"
) == 0 ||
strlen(WRITE_API_KEY) < 8
) {
Serial.println(
"ThingSpeak skipped: enter your Write API Key."
);
return;
}
// Field 1: Battery percentage
ThingSpeak.setField(
1,
(float)batteryPercent
);
// Field 2: Solar percentage
ThingSpeak.setField(
2,
(float)solarPercent
);
// Field 3: Internet simulation percentage
ThingSpeak.setField(
3,
(float)internetPercent
);
// Field 4: Temperature
float temperatureValue =
isnan(temperature) ? -1.0f : temperature;
ThingSpeak.setField(
4,
(float)temperatureValue
);
// Field 5: Humidity
float humidityValue =
isnan(humidity) ? -1.0f : humidity;
ThingSpeak.setField(
5,
(float)humidityValue
);
// Field 6: Power source
// 1 = Grid, 0 = Solar/Battery
int powerSourceValue = relayOnGrid ? 1 : 0;
ThingSpeak.setField(
6,
(int)powerSourceValue
);
// Field 7: Active faults
int activeFaults = getFaultCount();
ThingSpeak.setField(
7,
(int)activeFaults
);
// Field 8: Simulated users
ThingSpeak.setField(
8,
(int)simulatedUsers
);
Serial.println("Sending data to ThingSpeak...");
int response = ThingSpeak.writeFields(
CHANNEL_ID,
WRITE_API_KEY
);
if (response == 200) {
Serial.println(
"ThingSpeak update successful!"
);
}
else {
Serial.print("ThingSpeak response code: ");
Serial.println(response);
if (response == -301) {
Serial.println(
"Connection to ThingSpeak failed. "
"Check WiFi, Wokwi connectivity, and library."
);
}
else if (response == 0) {
Serial.println(
"No valid HTTP response received."
);
}
else {
Serial.println(
"Check API key, channel ID, permissions, "
"and ThingSpeak update interval."
);
}
}
}
// ==================== SD CARD INITIALIZATION ====================
void initializeSD() {
SPI.begin(
SD_SCK,
SD_MISO,
SD_MOSI,
SD_CS
);
if (SD.begin(SD_CS, SPI)) {
sdAvailable = true;
Serial.println("SD card initialized.");
if (!SD.exists("/energy_log.csv")) {
File file = SD.open(
"/energy_log.csv",
FILE_WRITE
);
if (file) {
file.println(
"Time_ms,Battery,Solar,Internet,"
"Temperature,Humidity,Grid,Faults,Users"
);
file.close();
}
}
}
else {
sdAvailable = false;
Serial.println(
"SD card initialization failed."
);
}
}
// ==================== SD LOGGING ====================
void logToSD() {
if (!sdAvailable) {
return;
}
File file = SD.open(
"/energy_log.csv",
FILE_APPEND
);
if (!file) {
Serial.println("Could not open CSV log.");
return;
}
file.print(millis());
file.print(",");
file.print(batteryPercent, 2);
file.print(",");
file.print(solarPercent, 2);
file.print(",");
file.print(internetPercent, 2);
file.print(",");
if (isnan(temperature)) {
file.print("N/A");
}
else {
file.print(temperature, 2);
}
file.print(",");
if (isnan(humidity)) {
file.print("N/A");
}
else {
file.print(humidity, 2);
}
file.print(",");
file.print(relayOnGrid ? 1 : 0);
file.print(",");
file.print(getFaultCount());
file.print(",");
file.println(simulatedUsers);
file.close();
}
// ==================== SYSTEM STATUS ====================
void printStatus() {
Serial.println();
Serial.println("========== SYSTEM STATUS ==========");
Serial.print("Battery: ");
Serial.print(batteryPercent, 1);
Serial.println("%");
Serial.print("Solar: ");
Serial.print(solarPercent, 1);
Serial.println("%");
Serial.print("Internet simulation: ");
Serial.print(internetPercent, 1);
Serial.println("%");
Serial.print("Temperature: ");
if (isnan(temperature)) {
Serial.println("ERROR");
}
else {
Serial.print(temperature, 1);
Serial.println(" C");
}
Serial.print("Humidity: ");
if (isnan(humidity)) {
Serial.println("ERROR");
}
else {
Serial.print(humidity, 1);
Serial.println("%");
}
Serial.print("DS18B20 #1: ");
if (isnan(dsTemp1)) {
Serial.println("ERROR");
}
else {
Serial.print(dsTemp1, 1);
Serial.println(" C");
}
Serial.print("DS18B20 #2: ");
if (isnan(dsTemp2)) {
Serial.println("ERROR");
}
else {
Serial.print(dsTemp2, 1);
Serial.println(" C");
}
Serial.print("DS18B20 #3: ");
if (isnan(dsTemp3)) {
Serial.println("ERROR");
}
else {
Serial.print(dsTemp3, 1);
Serial.println(" C");
}
Serial.print("DHT sensor fault: ");
Serial.println(dhtSensorFault ? "YES" : "NO");
Serial.print("DS sensor fault: ");
Serial.println(dsSensorFault ? "YES" : "NO");
Serial.print("Battery fault: ");
Serial.println(batteryFault ? "YES" : "NO");
Serial.print("Solar fault: ");
Serial.println(solarFault ? "YES" : "NO");
Serial.print("Internet fault: ");
Serial.println(internetFault ? "YES" : "NO");
Serial.print("Temperature fault: ");
Serial.println(temperatureFault ? "YES" : "NO");
Serial.print("DS temperature fault: ");
Serial.println(dsTemperatureFault ? "YES" : "NO");
Serial.print("Active faults: ");
Serial.println(getFaultCount());
Serial.print("Grid selected: ");
Serial.println(relayOnGrid ? "YES" : "NO");
Serial.print("WiFi connected: ");
Serial.println(
WiFi.status() == WL_CONNECTED ? "YES" : "NO"
);
Serial.print("SD available: ");
Serial.println(sdAvailable ? "YES" : "NO");
Serial.print("Simulated users: ");
Serial.println(simulatedUsers);
Serial.println("==================================");
}
// ==================== SERIAL COMMANDS ====================
void handleSerialCommands() {
while (Serial.available() > 0) {
char command = Serial.read();
if (command == 'S' || command == 's') {
printStatus();
}
else if (command == 'B' || command == 'b') {
buzzerTestMode = !buzzerTestMode;
if (!buzzerTestMode) {
buzzerOff();
}
Serial.print("Buzzer test: ");
Serial.println(buzzerTestMode ? "ON" : "OFF");
}
else if (command == 'A' || command == 'a') {
alarmTestMode = !alarmTestMode;
if (!alarmTestMode) {
buzzerOff();
alarmSoundOn = false;
}
Serial.print("Alarm test: ");
Serial.println(alarmTestMode ? "ON" : "OFF");
}
else if (command == 'P' || command == 'p') {
displayPage = (displayPage + 1) % 2;
lastDisplayTime = 0;
Serial.print("Display page: ");
Serial.println(displayPage + 1);
}
}
}
// ==================== OLED PAGE 1 ====================
void showEnergyPage() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("ENERGY MANAGEMENT");
display.drawLine(
0, 10, 127, 10,
SSD1306_WHITE
);
display.setCursor(0, 14);
display.print("Battery: ");
display.print(batteryPercent, 0);
display.println("%");
display.setCursor(0, 25);
display.print("Solar: ");
display.print(solarPercent, 0);
display.println("%");
display.setCursor(0, 36);
display.print("Internet: ");
display.print(internetPercent, 0);
display.println("%");
display.setCursor(0, 47);
display.print("Source: ");
display.println(relayOnGrid ? "GRID" : "SOLAR/BAT");
display.display();
}
// ==================== OLED PAGE 2 ====================
void showDiagnosticsPage() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("SYSTEM DIAGNOSTICS");
display.drawLine(
0, 10, 127, 10,
SSD1306_WHITE
);
display.setCursor(0, 13);
display.print("Temp: ");
if (isnan(temperature)) {
display.println("ERROR");
}
else {
display.print(temperature, 1);
display.println(" C");
}
display.setCursor(0, 24);
display.print("Humidity: ");
if (isnan(humidity)) {
display.println("ERROR");
}
else {
display.print(humidity, 0);
display.println("%");
}
display.setCursor(0, 35);
display.print("Faults: ");
display.println(getFaultCount());
display.setCursor(0, 46);
display.println(
getFaultCount() == 0
? "STATUS: NORMAL"
: "STATUS: FAULT"
);
display.display();
}
// ==================== UPDATE DISPLAY ====================
void updateDisplay() {
if (!oledAvailable) {
return;
}
if (displayPage == 0) {
showEnergyPage();
}
else {
showDiagnosticsPage();
}
}
// ==================== BUTTONS ====================
void handleButtons() {
bool button1Now = digitalRead(BUTTON1_PIN);
bool button2Now = digitalRead(BUTTON2_PIN);
if (
button1Previous == HIGH &&
button1Now == LOW
) {
displayPage = (displayPage + 1) % 2;
lastDisplayTime = 0;
Serial.print("OLED page: ");
Serial.println(displayPage + 1);
delay(30);
}
if (
button2Previous == HIGH &&
button2Now == LOW
) {
alarmTestMode = !alarmTestMode;
if (!alarmTestMode) {
buzzerOff();
alarmSoundOn = false;
}
Serial.print("Alarm test: ");
Serial.println(alarmTestMode ? "ON" : "OFF");
delay(30);
}
button1Previous = button1Now;
button2Previous = button2Now;
}
// ==================== SETUP ====================
void setup() {
Serial.begin(115200);
delay(500);
Serial.println();
Serial.println("SMART ENERGY SYSTEM STARTING");
pinMode(GREEN_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
pinMode(YELLOW_LED, OUTPUT);
pinMode(RELAY_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BUTTON1_PIN, INPUT_PULLUP);
pinMode(BUTTON2_PIN, INPUT_PULLUP);
digitalWrite(GREEN_LED, LOW);
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(RELAY_PIN, LOW);
buzzerOff();
analogReadResolution(12);
Wire.begin(21, 22);
// Initialize OLED
if (
display.begin(
SSD1306_SWITCHCAPVCC,
OLED_ADDRESS
)
) {
oledAvailable = true;
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 10);
display.println("SMART ENERGY SYSTEM");
display.setCursor(0, 30);
display.println("Initializing...");
display.display();
Serial.println("OLED initialized.");
}
else {
oledAvailable = false;
Serial.println("OLED initialization failed.");
}
// Initialize sensors
dht.begin();
ds1.begin();
ds2.begin();
ds3.begin();
Serial.print("DS18B20 #1 devices: ");
Serial.println(ds1.getDeviceCount());
Serial.print("DS18B20 #2 devices: ");
Serial.println(ds2.getDeviceCount());
Serial.print("DS18B20 #3 devices: ");
Serial.println(ds3.getDeviceCount());
// Initialize SD card
initializeSD();
// Connect WiFi
connectWiFi();
// Initialize ThingSpeak
ThingSpeak.begin(client);
// Initial readings
readAnalogSensors();
readDHTSensor();
readDS18B20Sensors();
updateFaults();
managePower();
updateIndicators();
updateDisplay();
printStatus();
Serial.println("System ready.");
Serial.println(
"Commands: S=status, B=buzzer test, "
"A=alarm test, P=change OLED page."
);
}
// ==================== MAIN LOOP ====================
void loop() {
unsigned long now = millis();
// Read analog sensors
readAnalogSensors();
// Read temperature sensors every two seconds
if (
now - lastSensorReadTime >= 2000 ||
lastSensorReadTime == 0
) {
lastSensorReadTime = now;
readDHTSensor();
readDS18B20Sensors();
}
// Fault detection and power management
updateFaults();
managePower();
updateIndicators();
// Refresh OLED
if (
now - lastDisplayTime >= DISPLAY_INTERVAL
) {
lastDisplayTime = now;
updateDisplay();
}
// Automatically change OLED page
if (
now - lastPageTime >= PAGE_INTERVAL
) {
lastPageTime = now;
displayPage = (displayPage + 1) % 2;
}
// Save data to SD
if (
now - lastLogTime >= LOG_INTERVAL
) {
lastLogTime = now;
logToSD();
}
// Maintain WiFi connection
if (
now - lastWiFiTime >= WIFI_INTERVAL
) {
lastWiFiTime = now;
if (WiFi.status() != WL_CONNECTED) {
connectWiFi();
}
}
// Upload data to ThingSpeak
if (
now - lastThingSpeakTime >=
THINGSPEAK_INTERVAL
) {
lastThingSpeakTime = now;
uploadThingSpeak();
}
// Handle buttons and serial commands
handleButtons();
handleSerialCommands();
delay(5);
}