/**
// Arduino Display Keypad
//
// Copyright (C) 2023, Bins.
*/
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <ctype.h>
#include <EEPROM.h>
/* 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;
int minutes = 0;
String timeString = "";
// Sensor values
float voltage = 0.00;
float current = 0.00;
// VARIABLES: Rate
float rate = 0.00f;
float price = 0.00;
float area_last_inner_segment = 0.0;
float area_inner = 0.0;
float area = 0.5 * area_inner;
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);
Serial.println("E-Jeep Charging");
Serial.println("Initializing LCD");
lcd.init();
lcd.backlight();
lcd.noBlink();
Serial.println("LCD Initialized.");
pinMode(11, OUTPUT);
pinMode(12, OUTPUT);
pinMode(13, OUTPUT);
// ======== 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.put(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();
}
void loop() {
unsigned long currentMillis = millis();
voltage = map(analogRead(A0), 0, 1023, 0, 70.0);
current = map(analogRead(A1), 0, 1023, 0, 10.0);
if (charging) {
if (mode == 1) showChargingScreen();
if (mode == 2) showChargingScreenTime();
}
if (completed) {
if (mode == 2) showChargeCompleteTime();
charging = false;
}
char key = keypad.getKey();
if (key) {
processInput(key);
}
digitalWrite(12, cancelLED);
if (charging) {
digitalWrite(11, HIGH);
digitalWrite(12, HIGH);
digitalWrite(13, LOW);
} else {
if (cancelled) {
digitalWrite(11, LOW);
if (currentMillis - lastExecutedMillis >= interval) {
lastExecutedMillis = currentMillis;
cancelLED = !cancelLED;
}
} else {
digitalWrite(11, LOW);
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;
char buffer[40];
sprintf(buffer, "%dh %dm %ds", hour, int(mins % 60), int(sec % 60));
String timeString = buffer;
return timeString;
}
void writeRate(float newRate) {
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() {
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");
}
void showChargingScreen() {
lcd.noBlink();
lcd.setCursor(0, 0);
lcd.print("CHARGING: AMOUNT");
lcd.setCursor(0, 1);
lcd.print("PHP ");
lcd.print(amount);
lcd.setCursor(0, 2);
lcd.print("12.23");
lcd.print("/12.23 Ah");
lcd.setCursor(0, 3);
lcd.print("V: ");
lcd.print(voltage);
lcd.setCursor(9, 3);
lcd.print(" A: ");
lcd.print(current);
}
unsigned long lastMillisChargeScreen = 0;
unsigned long lastMillisUpdateTime = 0;
bool chargingScreenTimeInfo = false;
bool chargingScreenClean = false;
bool updateTime = true;
void showChargingScreenTime() {
if (seconds == 0) {
charging = 0;
completed = 1;
Serial.println("Charging Completed");
lcd.setCursor(0, 3);
lcd.clear();
}
unsigned long currentMillis = millis();
if (currentMillis - lastMillisChargeScreen >= 4000) {
lastMillisChargeScreen = currentMillis;
chargingScreenTimeInfo = !chargingScreenTimeInfo;
}
if (currentMillis - lastMillisUpdateTime >= 1000) {
lastMillisUpdateTime = currentMillis;
seconds -= 1;
updateTime = true;
}
lcd.noBlink();
lcd.setCursor(0, 0);
lcd.print("CHARGING: TIME");
if (updateTime) {
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("Time ");
lcd.print(getTimeFromSeconds(seconds));
updateTime = false;
}
lcd.setCursor(0, 2);
lcd.print(currentMillis * current);
lcd.print(" Ah");
if (chargingScreenTimeInfo) {
if (chargingScreenClean) {
lcd.setCursor(0, 3);
lcd.print(" ");
chargingScreenClean = false;
}
lcd.setCursor(0, 3);
lcd.print("V: ");
lcd.print(voltage);
lcd.setCursor(9, 3);
lcd.print(" A: ");
lcd.print(current);
} else {
lcd.setCursor(0, 3);
lcd.print(" [D] Stop");
chargingScreenClean = true;
}
}
void showChargeCompleteTime() {
lcd.noBlink();
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("10500.00");
lcd.setCursor(0, 3);
lcd.print("Ah: ");
lcd.print("100.00");
lcd.print(" [D] OK");
}
void showCancelledScreen() {
lcd.clear();
lcd.noBlink();
lcd.setCursor(0, 0);
lcd.print("CHARGING Cancelled");
}
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("/Ah");
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);
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);
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 (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 (completed) {
if (key == 'D') {
showMenuScreen();
input = "";
mode = 0;
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) {
// input = "";
// mode = 0;
}
if (completed) {
if (key == 'D') {
showMenuScreen();
input = "";
completed = false;
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() * ((float)(6 / 125)));
Serial.println(input.toFloat());
Serial.println(float(input.toFloat() * ((float)(6 / 125))));
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();
}