/**
// E-Jeep Charging
//
// Copyright (C) 2023, Sephalias.
*/
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <ctype.h>
#include <EEPROM.h>
// #include "ACS712.h"
// ACS712 sensor(ACS712_30A, A0);
/* Display setup */
LiquidCrystal_I2C lcd(0x27, 20, 4);
/* Keypad setup */
const byte KEYPAD_ROWS = 4;
const byte KEYPAD_COLS = 4;
byte rowPins[KEYPAD_ROWS] = {2, 3, 4, 5};
byte colPins[KEYPAD_COLS] = {6, 7, 8, 9};
char keys[KEYPAD_ROWS][KEYPAD_COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'.', '0', '#', 'D'}};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, KEYPAD_ROWS, KEYPAD_COLS);
unsigned int mode = 0;
unsigned int settingsMode = 0;
// System States
bool confirm = false;
bool charging = false;
bool cancelled = false;
bool completed = false;
String input = "";
// VARIABLES: Amount Mode
float amount = 0.00f;
// VARIABLES: Time Mode
int seconds = 0;
// Sensor values
int adc_voltage = 0;
float voltage = 0.00;
float current = 0.00;
double energy;
float total_energy;
// VARIABLES: Rate
float rate = 0.0000f;
float price = 0.00;
unsigned long charging_time = 0;
unsigned long lastExecutedMillis = 0;
unsigned long lastMillisAbout = 0;
int aboutInterval = 3000;
bool aboutFirstPage = true;
bool cancelLED = false;
int interval = 2000;
unsigned int address = 0;
unsigned int length = EEPROM.length();
void setup()
{
Serial.begin(115200);
lcd.begin(20, 4);
lcd.init();
lcd.backlight();
lcd.noBlink();
pinMode(11, OUTPUT); // Relay
pinMode(12, OUTPUT); // LED G
pinMode(13, OUTPUT); // LED R
pinMode(A0, INPUT); // Current Sensor
pinMode(A1, INPUT); // Voltage Sensor
digitalWrite(11, HIGH); // Set default state of relay
Serial.println("Calibrating... Ensure that no current flows through the sensor at this moment");
// int zero = sensor.calibrate();
Serial.println("Done!");
Serial.print("Zero point for this sensor = ");
// Serial.println(zero);
// ======== EEPROM ===========
Serial.println("Initialize EEPROM...");
Serial.print("EEPROM Length: ");
Serial.println(length);
// Read from EEPROM
EEPROM.get(address, rate);
if (isnan(rate))
{
Serial.println("EEPROM: EMPTY. Adding default values.");
EEPROM.write(address, 0.00f); // Set value
EEPROM.get(address, rate); // Read again
Serial.print("EEPROM data added: ");
Serial.println(rate);
}
else
{
Serial.println("EEPROM: Has value.");
Serial.print("EEPROM data: ");
Serial.println(rate);
}
showSplashScreen();
showMenuScreen();
}
unsigned long lastMillisEnergy = 0;
void loop()
{
unsigned long currentMillis = millis();
voltage = map(analogRead(A0), 0, 1023, 0, 70.0);
current = map(analogRead(A1), 0, 1023, 0, 10.0);
// adc_voltage = analogRead(A1);
// voltage = (adc_voltage * 5.0) / 1023.0;
// voltage = (voltage / 0.09090909091);
// current = sensor.getCurrentDC();
// if (current < 0.01)
// {
// current = 0;
// }
float power = voltage * current;
if (charging)
{
if (currentMillis - lastMillisEnergy >= 1000)
{
lastMillisEnergy = currentMillis;
energy += (power / 3600); // in Wh
Serial.print(energy);
Serial.println(" Wh");
}
if (mode == 1)
showChargingScreenAmount();
if (mode == 2)
showChargingScreenTime();
}
if (completed)
{
if (mode == 1)
showChargeCompleteAmount();
if (mode == 2)
showChargeCompleteTime();
charging = false;
}
char key = keypad.getKey();
if (key)
{
processInput(key);
}
if (charging)
{
digitalWrite(11, LOW);
digitalWrite(12, HIGH);
digitalWrite(13, LOW);
}
else
{
digitalWrite(11, HIGH);
if (cancelled)
{
if (currentMillis - lastExecutedMillis >= interval)
{
lastExecutedMillis = currentMillis;
digitalWrite(12, cancelLED);
cancelLED = !cancelLED;
}
showCancelledScreen();
}
else
{
digitalWrite(12, LOW);
digitalWrite(13, HIGH);
}
}
if (mode == 3 && settingsMode == 2)
{
if (currentMillis - lastMillisAbout >= aboutInterval)
{
lastMillisAbout = currentMillis;
if (aboutFirstPage)
{
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 2);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("* Alemania, Ronn");
lcd.setCursor(0, 2);
lcd.print("* Carmona, Andrea");
}
else
{
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 2);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("* Ngalatan, Francis");
lcd.setCursor(0, 2);
lcd.print("* Quizon, John Lloyd");
}
aboutFirstPage = !aboutFirstPage;
}
}
}
String getTimeFromSeconds(int seconds) {
unsigned int sec = seconds;
unsigned int mins = seconds / 60;
unsigned int hour = seconds / 3600;
String timeString = String(hour) + "h " + String(mins % 60) + "m " + String(sec % 60) + "s";
return timeString;
}
void writeRate(float newRate)
{
Serial.print("EEPROM: Writing new rate ");
Serial.println(newRate, 4);
Serial.print("EEPROM: Previous data: ");
Serial.println(rate, 4);
EEPROM.put(address, newRate);
EEPROM.get(address, rate);
Serial.print("EEPROM: Data updated: ");
Serial.println(rate);
}
void showSplashScreen()
{
lcd.clear();
lcd.setCursor(2, 1);
lcd.print("E-Jeep Prepaid");
lcd.setCursor(2, 2);
String message = "Charging Station";
for (byte i = 0; i < message.length(); i++)
{
lcd.print(message[i]);
delay(50);
}
delay(2000);
}
void showMenuScreen()
{
energy = 0.0;
lcd.clear();
lcd.noBlink();
lcd.setCursor(0, 0);
lcd.print("E-Jeep Charging");
lcd.setCursor(0, 1);
lcd.print("[A] Amount Mode");
lcd.setCursor(0, 2);
lcd.print("[B] Time Mode");
lcd.setCursor(0, 3);
lcd.print("[C] Set Mode");
lcd.setCursor(17, 0);
lcd.write(255);
lcd.print(" M");
lcd.setCursor(17, 1);
lcd.write(255);
lcd.print(" E");
lcd.setCursor(17, 2);
lcd.write(255);
lcd.print(" N");
lcd.setCursor(17, 3);
lcd.write(255);
lcd.print(" U");
}
unsigned long lastMillisUpdateAmount = 0;
bool updateAmount = true;
// Charging Screens
void showChargingScreenAmount()
{
float target_energy = ((float)((float)amount * (float)((float)1 / (float)rate))*1000)/1000;
if (energy >= target_energy)
{
charging = 0;
completed = 1;
updateAmount = true;
}
unsigned long currentMillis = millis();
if (currentMillis - lastMillisUpdateAmount >= 1000)
{
lastMillisUpdateAmount = currentMillis;
updateAmount = true;
}
lcd.noBlink();
lcd.setCursor(0, 0);
lcd.print("CHARGING: AMOUNT D>");
if (updateAmount)
{
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("PHP ");
lcd.print(amount);
lcd.setCursor(0, 2);
lcd.print(" ");
lcd.setCursor(0, 2);
lcd.print(energy);
lcd.print("/");
lcd.print(target_energy);
lcd.print(" Wh");
lcd.setCursor(0, 3);
lcd.print(" ");
lcd.setCursor(0, 3);
lcd.print("V: ");
lcd.print(voltage);
lcd.setCursor(9, 3);
lcd.print(" A: ");
lcd.print(current);
updateAmount = false;
}
}
unsigned long lastMillisUpdateTime = 0;
bool updateTime = true;
void showChargingScreenTime()
{
if (seconds == 0)
{
charging = 0;
completed = 1;
updateTime = true;
}
unsigned long currentMillis = millis();
if (currentMillis - lastMillisUpdateTime >= 1000)
{
lastMillisUpdateTime = currentMillis;
seconds -= 1;
updateTime = true;
}
lcd.noBlink();
lcd.setCursor(0, 0);
lcd.print("CHARGING: TIME D>");
if (updateTime)
{
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("Time ");
lcd.print(getTimeFromSeconds(seconds));
lcd.setCursor(0, 2);
lcd.print(" ");
lcd.setCursor(0, 2);
lcd.print(energy);
lcd.print(" Wh");
lcd.setCursor(0, 3);
lcd.print(" ");
lcd.setCursor(0, 3);
lcd.print("V: ");
lcd.print(voltage);
lcd.setCursor(9, 3);
lcd.print(" A: ");
lcd.print(current);
updateTime = false;
}
}
// Charging Complete Screens
bool chargeCompleteAmountClear = true;
void showChargeCompleteAmount()
{
lcd.noBlink();
if (chargeCompleteAmountClear)
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" CHARGING COMPLETE");
lcd.setCursor(0, 1);
lcd.print("Amount : PHP ");
lcd.print(amount);
lcd.setCursor(0, 2);
lcd.print("Wh : ");
lcd.print(energy);
lcd.setCursor(0, 3);
lcd.setCursor(14, 3);
lcd.print("[D] OK");
chargeCompleteAmountClear = false;
}
}
bool chargeCompleteTimeClear = true;
void showChargeCompleteTime()
{
lcd.noBlink();
if (chargeCompleteTimeClear)
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("CHARGING COMPLETE");
lcd.setCursor(0, 1);
lcd.print("Time: ");
lcd.print(getTimeFromSeconds(input.toInt() * 60));
lcd.setCursor(0, 2);
lcd.print("Pay : PHP ");
lcd.print((rate * energy)/1000);
lcd.setCursor(0, 3);
lcd.print("Wh : ");
lcd.print(energy);
lcd.setCursor(14, 3);
lcd.print("[D] OK");
chargeCompleteTimeClear = false;
}
}
// Charging Cancelled Screens
bool cancelledScreenClear = true;
void showCancelledScreen()
{
if (cancelledScreenClear)
{
lcd.clear();
lcd.noBlink();
lcd.setCursor(0, 0);
lcd.print("CHARGING CANCELLED");
if (mode == 1)
{
lcd.setCursor(0, 1);
lcd.print("Amount: ");
lcd.print (amount);
lcd.setCursor(0, 2);
lcd.setCursor(0, 3);
lcd.print("Wh : ");
lcd.print (energy);
lcd.setCursor(14, 3);
lcd.print("[D] OK");
}
if (mode == 2)
{
lcd.setCursor(0, 1);
lcd.print("Time: ");
lcd.print(getTimeFromSeconds(input.toInt() * 60 - seconds));
lcd.setCursor(0, 2);
lcd.print("Pay : PHP ");
lcd.print("amount");
lcd.setCursor(0, 3);
lcd.print("Wh : ");
lcd.print("energy");
lcd.setCursor(14, 3);
lcd.print("[D] OK");
}
cancelledScreenClear = false;
}
}
// Charging Configuration Screens
void enterAmountMode()
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("--- Enter Amount ---");
lcd.setCursor(0, 2);
lcd.print(" [#] ENTER");
lcd.setCursor(0, 3);
lcd.print("[C] DEL [D] CANCEL");
lcd.setCursor(0, 1);
lcd.print("PHP ");
lcd.setCursor(4, 1);
lcd.print(input);
lcd.blink();
}
void showAmountComfirmScreen()
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Confirm Amount");
lcd.setCursor(0, 1);
lcd.print("PHP ");
lcd.print(input);
lcd.setCursor(0, 2);
lcd.print("Rate: ");
lcd.print(rate);
lcd.print("/kWh");
lcd.setCursor(0, 3);
lcd.print(" [D]CANCEL [#]OK");
lcd.noBlink();
}
void enterTimeMode()
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("--- Enter Time ---");
lcd.setCursor(0, 2);
lcd.print(" [#] ENTER");
lcd.setCursor(0, 3);
lcd.print("[C] DEL [D] CANCEL");
lcd.setCursor(0, 1);
lcd.print("Min ");
lcd.setCursor(4, 1);
lcd.print(input);
lcd.blink();
}
void showTimeComfirmScreen()
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Confirm Time");
lcd.setCursor(0, 1);
lcd.print("Time ");
lcd.print(getTimeFromSeconds(input.toInt() * 60));
lcd.setCursor(0, 2);
lcd.print("Rate: PHP ");
lcd.print(rate);
lcd.print("/Ah");
lcd.setCursor(0, 3);
lcd.print(" [D]CANCEL [#]OK");
lcd.noBlink();
}
void showSettingsSetRate()
{
input = rate;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("----- Set Rate -----");
lcd.setCursor(0, 2);
lcd.print(" /kWh [#] ENTER");
lcd.setCursor(0, 3);
lcd.print("[C] DEL [D] CANCEL");
lcd.setCursor(0, 1);
lcd.print("PHP ");
lcd.setCursor(4, 1);
lcd.print(input.toFloat());
lcd.blink();
}
void showSettingsAbout()
{
lcd.clear();
lcd.noBlink();
lcd.setCursor(0, 0);
lcd.print(" MEMBERS ");
lcd.setCursor(0, 3);
lcd.print(" [D] Back");
}
void showSettingsSaved()
{
lcd.clear();
lcd.noBlink();
lcd.setCursor(0, 0);
lcd.print("------ SAVED! ------");
lcd.setCursor(0, 1);
lcd.write(255);
lcd.print(" Rate: ");
lcd.print(rate, 4);
lcd.setCursor(0, 2);
lcd.write(255);
lcd.setCursor(8, 2);
lcd.print("PHP/Ah");
lcd.setCursor(0, 3);
lcd.write(255);
lcd.print(" [#] OK ");
lcd.setCursor(19, 1);
lcd.write(255);
lcd.setCursor(19, 2);
lcd.write(255);
lcd.setCursor(19, 3);
lcd.write(255);
}
void enterSetMode()
{
lcd.clear();
lcd.noBlink();
lcd.setCursor(0, 0);
lcd.print(" SETTINGS ");
lcd.setCursor(0, 1);
lcd.print("[A] Set Rate");
lcd.setCursor(0, 2);
lcd.print("[B] About");
lcd.setCursor(0, 3);
lcd.print(" [D] Back");
}
void keyInput(char key)
{
// Limit input to 4 charcaters for time, 12 characters for amount.
if (mode == 2)
{
if (input.length() > 3)
{
return;
}
}
if (mode == 1) {
if (input.length() > 12)
{
return;
}
}
// Check if input is digit.
if (key >= '0' && key <= '9')
{
// Check if input has decimal and limit decimal to 2 places.
if (mode != 3)
{
if (input.indexOf(".") != -1 && input.substring(input.indexOf(".") + 1, input.length()).length() == 2)
{
return;
}
}
else
{
if (input.indexOf(".") != -1 && input.substring(input.indexOf(".") + 1, input.length()).length() == 4)
{
return;
}
}
input += key;
lcd.print(key);
}
else if (key == '.')
{
if (mode == 2 || mode == 3)
{
// Restrict input to only one decimal.
if (input.indexOf('.') < 0)
{
input += key;
lcd.print(key);
}
}
}
}
void processInput(char key)
{
switch (mode)
{
case 0:
switch (key)
{
case 'A':
mode = 1;
enterAmountMode();
break;
case 'B':
mode = 2;
enterTimeMode();
break;
case 'C':
mode = 3;
enterSetMode();
break;
default:
break;
}
break;
case 1:
if (charging)
{
if (key == 'D')
{
charging = false;
cancelled = true;
break;
}
else
{
return;
}
}
if (cancelled)
{
if (key == 'D')
{
showMenuScreen();
input = "";
mode = 0;
cancelled = false;
break;
}
}
if (completed)
{
if (key == 'D')
{
showMenuScreen();
input = "";
mode = 0;
completed = false;
break;
}
}
if (confirm)
{
if (key == '#')
{
if (input.length() <= 0)
return;
charging = true;
confirm = false;
amount = input.toFloat();
lcd.clear();
break;
}
if (key == 'D')
{
confirm = false;
enterAmountMode();
break;
}
}
if (!confirm && key == 'C')
{
input.remove(input.length() - 1);
lcd.setCursor(4 + input.length(), 1);
lcd.print(" ");
lcd.setCursor(4, 1);
lcd.print(input);
break;
}
if (key == '#')
{
confirm = true;
showAmountComfirmScreen();
break;
}
if (key == 'D')
{
showMenuScreen();
input = "";
mode = 0;
break;
}
keyInput(key);
break;
case 2:
if (charging)
{
if (key == 'D')
{
charging = false;
cancelled = true;
break;
}
else
{
return;
}
}
if (cancelled)
{
if (key == 'D')
{
showMenuScreen();
input = "";
mode = 0;
cancelled = false;
cancelledScreenClear = true;
break;
}
}
if (completed)
{
if (key == 'D')
{
showMenuScreen();
input = "";
completed = false;
chargeCompleteTimeClear = true;
mode = 0;
break;
}
}
if (confirm)
{
if (key == '#')
{
if (input.length() <= 0)
return;
charging = true;
confirm = false;
seconds = input.toInt() * 60;
lcd.clear();
break;
}
if (key == 'D')
{
confirm = false;
enterTimeMode();
break;
}
}
if (!confirm && key == 'C')
{
input.remove(input.length() - 1);
lcd.setCursor(4 + input.length(), 1);
lcd.print(" ");
lcd.setCursor(4, 1);
lcd.print(input);
break;
}
if (key == '#')
{
if (input.toInt() == 0)
return;
confirm = true;
showTimeComfirmScreen();
break;
}
if (key == 'D')
{
showMenuScreen();
input = "";
mode = 0;
break;
}
keyInput(key);
break;
case 3:
switch (settingsMode)
{
case 0:
if (key == 'A')
{
showSettingsSetRate();
settingsMode = 1;
break;
}
if (key == 'B')
{
showSettingsAbout();
settingsMode = 2;
break;
}
if (key == 'D')
{
showMenuScreen();
mode = 0;
break;
}
break;
case 1:
if (key == 'C')
{
input.remove(input.length() - 1);
lcd.setCursor(4 + input.length(), 1);
lcd.print(" ");
lcd.setCursor(4, 1);
lcd.print(input);
break;
}
if (key == 'D')
{
enterSetMode();
settingsMode = 0;
input = "";
break;
}
if (key == '#')
{
writeRate(input.toFloat());
showSettingsSaved();
settingsMode = 3;
input = "";
break;
}
keyInput(key);
break;
case 2:
if (key == 'D')
{
enterSetMode();
settingsMode = 0;
break;
}
break;
case 3:
if (key == '#')
{
enterSetMode();
settingsMode = 0;
break;
}
break;
}
break;
}
Serial.println("mode " + String(mode));
Serial.println("settings " + String(settingsMode));
Serial.println("input " + String(input));
Serial.println("confirm " + String(confirm));
Serial.println("cancelled " + String(cancelled));
Serial.println("charging " + String(charging));
Serial.println("complete " + String(completed));
Serial.println();
}