#define BLYNK_TEMPLATE_ID ""
#define BLYNK_TEMPLATE_NAME ""
#define BLYNK_AUTH_TOKEN ""
#define BLYNK_PRINT Serial
#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include "EmonLib.h"
// ======================================================
// WIFI
// ======================================================
char ssid[] = "Wokwi-GUEST";
char pass[] = "";
// ======================================================
// PIN CONFIG
// ======================================================
// ADC1 ONLY (ESP32 WIFI SAFE)
// GPIO32 -> Solar Voltage
// GPIO33 -> Battery Voltage
// GPIO34 -> Load Current
// GPIO35 -> Solar Current
#define SOL_ADC 32
#define BAT_ADC 33
#define LOAD_CURRENT_ADC 34
#define SOL_CURRENT_ADC 35
// BUTTONS
#define BTN_UP 15
#define BTN_DOWN 18
// LED
#define LED_PIN 13
// OPTIONAL LOCAL BUTTON
#define BTN_LED 12
#define AVG_NUM 10
// ======================================================
// LCD
// ======================================================
LiquidCrystal_I2C lcd(0x27, 20, 4);
// ======================================================
// ENERGY MONITOR
// ======================================================
EnergyMonitor emon1;
const float vCalibration = 41.5;
const float currCalibration = 0.15;
// ======================================================
// VARIABLES
// ======================================================
float solar_volt = 0;
float bat_volt = 0;
float load_current = 0;
float solar_current = 0;
float load_watts = 0;
float solar_watts = 0;
float load_wattHours = 0;
float solar_wattHours = 0;
float kWh = 0.0;
float offsetVoltage = 2.5;
float Sensitivity = 0.066;
// LCD PAGE
int page_counter = 1;
// BUTTON STATES
bool lastUpState = HIGH;
bool lastDownState = HIGH;
// LED STATE
int ledState = LOW;
// TIMER
BlynkTimer timer;
// EEPROM
const int addrKWh = 0;
// ENERGY TIMER
unsigned long lastMillis = 0;
// ======================================================
// FUNCTION DECLARATION
// ======================================================
void read_data();
void updateLCD();
void sendEnergyDataToBlynk();
void checkButtons();
void saveEnergyDataToEEPROM();
void readEnergyDataFromEEPROM();
// ======================================================
// BLYNK CONNECTED
// ======================================================
BLYNK_CONNECTED()
{
Serial.println("Blynk Connected!");
Blynk.syncVirtual(V4);
Blynk.virtualWrite(V0, emon1.Vrms);
Blynk.virtualWrite(V1, emon1.Irms);
Blynk.virtualWrite(V2, emon1.apparentPower);
Blynk.virtualWrite(V3, kWh);
}
// ======================================================
// BLYNK BUTTON
// ======================================================
BLYNK_WRITE(V4)
{
ledState = param.asInt();
digitalWrite(LED_PIN, ledState);
Serial.print("LED State From Blynk: ");
Serial.println(ledState);
}
// ======================================================
// SETUP
// ======================================================
void setup()
{
Serial.begin(115200);
// LCD
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print(" IoT ENERGY METER ");
lcd.setCursor(0, 1);
lcd.print(" INITIALIZING...");
delay(2000);
lcd.clear();
// PINS
pinMode(LED_PIN, OUTPUT);
pinMode(BTN_UP, INPUT_PULLUP);
pinMode(BTN_DOWN, INPUT_PULLUP);
pinMode(BTN_LED, INPUT_PULLUP);
digitalWrite(LED_PIN, LOW);
// EEPROM
EEPROM.begin(32);
readEnergyDataFromEEPROM();
// ADC CONFIG
analogReadResolution(12);
// EMONLIB
emon1.voltage(35, vCalibration, 1.7);
emon1.current(34, currCalibration);
// WIFI + BLYNK
Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass, "blynk.cloud", 80);
// TIMERS
timer.setInterval(1000L, sendEnergyDataToBlynk);
lastMillis = millis();
}
// ======================================================
// LOOP
// ======================================================
void loop()
{
Blynk.run();
timer.run();
read_data();
checkButtons();
updateLCD();
// LOCAL BUTTON TOGGLE LED
if (digitalRead(BTN_LED) == LOW)
{
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
Blynk.virtualWrite(V4, ledState);
delay(250);
}
delay(50);
}
// ======================================================
// BUTTON HANDLER
// ======================================================
void checkButtons()
{
bool upState = digitalRead(BTN_UP);
bool downState = digitalRead(BTN_DOWN);
// BUTTON UP
if (lastUpState == HIGH && upState == LOW)
{
page_counter++;
if (page_counter > 4)
page_counter = 1;
lcd.clear();
Serial.print("PAGE: ");
Serial.println(page_counter);
delay(200);
}
// BUTTON DOWN
if (lastDownState == HIGH && downState == LOW)
{
page_counter--;
if (page_counter < 1)
page_counter = 4;
lcd.clear();
Serial.print("PAGE: ");
Serial.println(page_counter);
delay(200);
}
lastUpState = upState;
lastDownState = downState;
}
// ======================================================
// READ ADC DATA
// ======================================================
int read_adc(int pin)
{
long sum = 0;
for (int i = 0; i < AVG_NUM; i++)
{
sum += analogRead(pin);
}
return sum / AVG_NUM;
}
void read_data()
{
// POT VALUE -> VOLTAGE
solar_volt =
(read_adc(SOL_ADC) / 4095.0) * 100.0;
bat_volt =
(read_adc(BAT_ADC) / 4095.0) * 100.0;
// POT VALUE -> CURRENT
load_current =
(read_adc(LOAD_CURRENT_ADC) / 4095.0) * 10.0;
solar_current =
(read_adc(SOL_CURRENT_ADC) / 4095.0) * 10.0;
// POWER
load_watts = bat_volt * load_current;
solar_watts = solar_volt * solar_current;
// ENERGY
unsigned long currentMillis = millis();
float hours =
(currentMillis - lastMillis) / 3600000.0;
kWh += (load_watts * hours) / 1000.0;
load_wattHours += load_watts * hours;
solar_wattHours += solar_watts * hours;
lastMillis = currentMillis;
// DEBUG SERIAL
Serial.print("SolarV: ");
Serial.print(solar_volt);
Serial.print(" | BatV: ");
Serial.print(bat_volt);
Serial.print(" | LoadA: ");
Serial.print(load_current);
Serial.print(" | SolarA: ");
Serial.print(solar_current);
Serial.print(" | Power: ");
Serial.print(load_watts);
Serial.print(" | kWh: ");
Serial.println(kWh);
}
// ======================================================
// LCD
// ======================================================
void updateLCD()
{
switch (page_counter)
{
case 1:
{
lcd.setCursor(0, 0);
lcd.print("Voltage:");
lcd.print(bat_volt, 1);
lcd.print("V ");
lcd.setCursor(0, 1);
lcd.print("Current:");
lcd.print(load_current, 2);
lcd.print("A ");
lcd.setCursor(0, 2);
lcd.print("Power:");
lcd.print(load_watts, 1);
lcd.print("W ");
lcd.setCursor(0, 3);
lcd.print("Energy:");
lcd.print(kWh, 4);
lcd.print("kWh ");
}
break;
case 2:
{
lcd.setCursor(0, 0);
lcd.print("SOLAR DATA");
lcd.setCursor(0, 1);
lcd.print("Solar Volt:");
lcd.print(solar_volt, 1);
lcd.setCursor(0, 2);
lcd.print("Solar Amp:");
lcd.print(solar_current, 2);
lcd.setCursor(0, 3);
lcd.print("Solar Watt:");
lcd.print(solar_watts, 1);
}
break;
case 3:
{
lcd.setCursor(0, 0);
lcd.print("LOAD DATA");
lcd.setCursor(0, 1);
lcd.print("Load Amp:");
lcd.print(load_current, 2);
lcd.setCursor(0, 2);
lcd.print("Load Watt:");
lcd.print(load_watts, 1);
lcd.setCursor(0, 3);
lcd.print("LED:");
lcd.print(ledState ? "ON " : "OFF");
}
break;
case 4:
{
lcd.setCursor(0, 0);
lcd.print("ENERGY DATA");
lcd.setCursor(0, 1);
lcd.print("Load Wh:");
lcd.print(load_wattHours, 1);
lcd.setCursor(0, 2);
lcd.print("Solar Wh:");
lcd.print(solar_wattHours, 1);
lcd.setCursor(0, 3);
lcd.print("Pages:");
lcd.print(page_counter);
}
break;
}
}
// ======================================================
// SEND TO BLYNK
// ======================================================
void sendEnergyDataToBlynk()
{
// MAIN ENERGY DATA
Blynk.virtualWrite(V0, bat_volt);
Blynk.virtualWrite(V1, load_current);
Blynk.virtualWrite(V2, load_watts);
Blynk.virtualWrite(V3, kWh);
// SOLAR DATA
Blynk.virtualWrite(V5, solar_volt);
Blynk.virtualWrite(V6, solar_current);
Blynk.virtualWrite(V7, solar_watts);
// ENERGY DATA
Blynk.virtualWrite(V8, load_wattHours);
Blynk.virtualWrite(V9, solar_wattHours);
// PAGE STATUS
Blynk.virtualWrite(V10, page_counter);
saveEnergyDataToEEPROM();
Serial.println("All data sent to Blynk");
}
// ======================================================
// EEPROM
// ======================================================
void readEnergyDataFromEEPROM()
{
EEPROM.get(addrKWh, kWh);
if (isnan(kWh))
{
kWh = 0;
}
}
void saveEnergyDataToEEPROM()
{
EEPROM.put(addrKWh, kWh);
EEPROM.commit();
}Solar Voltage
Solar Current
Load Current
Battery Voltage