#include "SimpleLCD.h"
const int backlightPin = 11;
const int RS = 3;
const int RW = 4;
const int E = 5;
const int D4 = 6;
const int D5 = 7;
const int D6 = 8;
const int D7 = 9;
// buttons
const int UP = A0;
const int DOWN = A1;
const int LEFT = A2;
const int RIGHT = A3;
const int ENTER = A4;
SimpleLCD lcd(RS, RW, E, D4, D5, D6, D7);
int lcdBrightness = 255;
int incomingByte = 0;
// Pattern matching
int passcode[] = {UP, DOWN, LEFT, RIGHT, ENTER};
int patternIndex = 0;
bool inMainMenu = true;
int menuCursor = 0; // Cursor position within current menu options, 1-based
int currentView = 0; // Tracks current view
int previousView = 0; // Tracks previous view to return on cancel/back
bool editingValue = false;
int valueCursorPos = 1; // For xxx value editing, digit position 1 to 3
// Value arrays for Current Rating example
char existingVal[4] = "123";
char modifiedVal[4] = "123";
// Timing for hold detection (to go back to previous view)
unsigned long upPressedTime = 0;
unsigned long downPressedTime = 0;
const unsigned long holdThreshold = 1500; // ms
void setup() {
Serial.begin(9600);
pinMode(backlightPin, OUTPUT);
analogWrite(backlightPin, lcdBrightness);
pinMode(UP, INPUT_PULLUP);
pinMode(DOWN, INPUT_PULLUP);
pinMode(LEFT, INPUT_PULLUP);
pinMode(RIGHT, INPUT_PULLUP);
pinMode(ENTER, INPUT_PULLUP);
lcd.lcdInit();
showAutoModeView();
}
void loop() {
if (inMainMenu) {
checkPasscodePattern();
} else {
handleMenuNavigation();
}
}
// ========== VIEWS ==========
void showAutoModeView() {
currentView = 0;
previousView = 0;
lcd.lcdClear();
lcd.lcdGoToXY(0, 1);
lcd.lcdWriteText("Auto Mode Selected");
lcd.lcdGoToXY(1, 3);
lcd.lcdWriteText("Press A/M for");
lcd.lcdGoToXY(2, 4);
lcd.lcdWriteText("Manual Mode");
menuCursor = 0;
editingValue = false;
lcdBusyCursorOff();
}
void showMainMenuView() {
previousView = currentView;
currentView = 1;
lcd.lcdClear();
lcd.lcdGoToXY(0, 4);
lcd.lcdWriteText("Mode Change");
lcd.lcdGoToXY(1, 0);
lcd.lcdWriteText("1.Auto Mode");
lcd.lcdGoToXY(2, 0);
lcd.lcdWriteText("2.Manual Mode");
lcd.lcdGoToXY(3, 0);
lcd.lcdWriteText("3.Cancel");
menuCursor = constrain(menuCursor, 1, 3); // 0-based
Serial.print("Inside showMainMenuView(), menuCursor val:");
Serial.println(menuCursor);
editingValue = false;
updateCursorPosition();
}
void showManualModeView() {
previousView = currentView;
currentView = 2;
lcd.lcdClear();
lcd.lcdGoToXY(0, 0);
lcd.lcdWriteText("1.Rectifier Setting");
lcd.lcdGoToXY(1, 0);
lcd.lcdWriteText("2.Rectifier Calib");
lcd.lcdGoToXY(2, 0);
lcd.lcdWriteText("3.Overview");
lcd.lcdGoToXY(3, 0);
lcd.lcdWriteText("4.Help");
menuCursor = constrain(menuCursor, 0, 3); // 0-based
editingValue = false;
updateCursorPosition();
}
void showRectifierSettingView() {
previousView = currentView;
currentView = 3;
lcd.lcdClear();
lcd.lcdGoToXY(0, 0);
lcd.lcdWriteText("1.Rectifier Rating");
lcd.lcdGoToXY(1, 0);
lcd.lcdWriteText("2.Factor Update");
lcd.lcdGoToXY(2, 0);
lcd.lcdWriteText("3.System Threshold");
lcd.lcdGoToXY(3, 0);
lcd.lcdWriteText("4.PI Setting");
menuCursor = constrain(menuCursor, 0, 3);
editingValue = false;
updateCursorPosition();
}
void showRectifierRatingView() {
previousView = currentView;
currentView = 4;
lcd.lcdClear();
lcd.lcdGoToXY(0, 0);
lcd.lcdWriteText("1.Current Rating");
lcd.lcdGoToXY(1, 0);
lcd.lcdWriteText("2.Voltage Rating");
lcd.lcdGoToXY(2, 0);
lcd.lcdWriteText("3.System Alm Config");
lcd.lcdGoToXY(3, 0);
lcd.lcdWriteText("4.Temp Control");
menuCursor = constrain(menuCursor, 0, 3);
editingValue = false;
updateCursorPosition();
}
void showCurrentRatingView() {
previousView = currentView;
currentView = 5;
editingValue = false;
valueCursorPos = 1;
lcd.lcdClear();
lcd.lcdGoToXY(0, 4);
lcd.lcdWriteText("Current Rating");
lcd.lcdGoToXY(1, 0);
lcd.lcdWriteText("Existing Val:");
lcd.lcdGoToXY(1, 14);
lcd.lcdWriteText(existingVal);
lcd.lcdGoToXY(2, 0);
lcd.lcdWriteText("Modified Val:");
lcd.lcdGoToXY(2, 14);
lcd.lcdWriteText(modifiedVal);
lcd.lcdGoToXY(3, 0);
lcd.lcdWriteText("Cancel");
menuCursor = 1; // Start cursor at Existing Val line
updateCurrentRatingEditDisplay();
}
// ========== BUTTON HANDLING ==========
bool isPressed(int pin) {
return digitalRead(pin) == LOW;
}
void checkPasscodePattern() {
int buttonPins[] = {UP, DOWN, LEFT, RIGHT, ENTER};
for (int i = 0; i < 5; i++) {
if (isPressed(buttonPins[i])) {
if (buttonPins[i] == passcode[patternIndex]) {
patternIndex++;
delay(250); // debounce
if (patternIndex == 5) {
inMainMenu = false;
patternIndex = 0;
showMainMenuView();
}
} else {
patternIndex = 0;
}
break;
}
}
}
// Helper: go back to previous view
void goBackToPreviousView() {
// If editing value, cancel edit and restore modifiedVal to existingVal
if(editingValue) {
strncpy(modifiedVal, existingVal, 4);
editingValue = false;
updateCurrentRatingEditDisplay();
return;
}
switch(previousView) {
case 0: showAutoModeView(); break;
case 1: showMainMenuView(); break;
case 2: showManualModeView(); break;
case 3: showRectifierSettingView(); break;
case 4: showRectifierRatingView(); break;
default: showMainMenuView();
}
menuCursor = 1;
editingValue = false;
updateCursorPosition();
}
void handleMenuNavigation() {
// Handle hold up/down for going back to previous view
unsigned long now = millis();
if (isPressed(LEFT))
{
goBackToPreviousView();
}
// if (isPressed(RIGHT))
// {
// }
if(currentView == 5) {
handleCurrentRatingEdit();
return;
}
bool moved = false;
if (isPressed(UP)) {
if (menuCursor <= 0) menuCursor = getMenuLengthForCurrentView() - 1;
else menuCursor--;
moved = true;
}
if (isPressed(DOWN)) {
if (currentView == 1) {
if (menuCursor >= getMenuLengthForCurrentView()) menuCursor = 0;
else menuCursor++;
}
else {
if (menuCursor >= getMenuLengthForCurrentView() - 1) menuCursor = 0;
else menuCursor++;
}
moved = true;
}
Serial.print("Inside handleMenuNavigation(), menuCursor val:");
Serial.println(menuCursor);
if(moved) {
updateCursorPosition();
delay(250);
}
if (isPressed(ENTER)) {
delay(250);
handleEnterAction();
}
}
int getMenuLengthForCurrentView() {
switch(currentView) {
case 1: return 3; // 0,1,2
case 2: return 4; // 0,1,2,3
case 3: return 4;
case 4: return 4;
case 5: return 3; // Existing Val, Modified Val, Cancel (0,1,2)
default: return 3;
}
}
void handleEnterAction() {
if(currentView == 5) { // Current Rating view
if(editingValue) {
strncpy(existingVal, modifiedVal, 4);
editingValue = false;
goBackToPreviousView();
return;
}
// Not editing: if Cancel selected, go back, if Modified Val selected start editing
if(menuCursor == 2) { // Cancel (row 2)
strncpy(modifiedVal, existingVal, 4);
goBackToPreviousView();
return;
}
if(menuCursor == 1) { // Modified Val (row 1)
editingValue = true;
valueCursorPos = 1;
lcd.lcdGoToXY(2, 14 + valueCursorPos - 1);
lcdBusyCursorOn();
return;
}
}
// Other views
switch(currentView) {
case 1:
if(menuCursor == 1) {
inMainMenu = true;
showAutoModeView();
} else if(menuCursor == 2) {
showManualModeView();
} else if(menuCursor == 3) {
inMainMenu = true;
showAutoModeView();
}
menuCursor = 1;
break;
case 2:
if(menuCursor == 1) showRectifierSettingView();
else {
lcd.lcdClear();
lcd.lcdGoToXY(0,0);
lcd.lcdWriteText("Feature not ready");
delay(1000);
showManualModeView();
}
menuCursor = 1;
break;
case 3:
if(menuCursor == 1) showRectifierRatingView();
else {
lcd.lcdClear();
lcd.lcdGoToXY(0,0);
lcd.lcdWriteText("Feature not ready");
delay(1000);
showRectifierSettingView();
}
menuCursor = 1;
break;
case 4:
if(menuCursor == 1) showCurrentRatingView();
else {
lcd.lcdClear();
lcd.lcdGoToXY(0,0);
lcd.lcdWriteText("Feature not ready");
delay(1000);
showRectifierRatingView();
}
menuCursor = 1;
break;
}
editingValue = false;
updateCursorPosition();
}
void handleCurrentRatingEdit() {
if(!editingValue) {
// Cursor can move between Existing Val(0), Modified Val(1), Cancel(2)
bool moved = false;
if (isPressed(UP)) {
if(menuCursor <= 0) menuCursor = 2;
else menuCursor--;
moved = true;
}
if (isPressed(DOWN)) {
if(menuCursor >= 2) menuCursor = 0;
else menuCursor++;
moved = true;
}
if(moved) {
lcdBusyCursorOff();
lcd.lcdGoToXY(menuCursor, 0); // Always use menuCursor as row
lcdBusyCursorOn();
delay(250);
}
if (isPressed(ENTER)) {
delay(250);
handleEnterAction();
}
} else {
// Editing digit of modifiedVal
static unsigned long lastChange = 0;
unsigned long now = millis();
if (isPressed(UP)) {
if (now - lastChange > 200) {
incrementDigitAt(valueCursorPos - 1);
lastChange = now;
}
}
if (isPressed(DOWN)) {
if (now - lastChange > 200) {
decrementDigitAt(valueCursorPos - 1);
lastChange = now;
}
}
// Move cursor on hold (long press)
static unsigned long lastMove = 0;
if (isPressed(UP)) {
if (now - lastMove > 1000) {
if(valueCursorPos <= 1) valueCursorPos = 3;
else valueCursorPos--;
lcd.lcdGoToXY(2, 14 + valueCursorPos - 1);
lastMove = now;
}
} else if (isPressed(DOWN)) {
if (now - lastMove > 1000) {
if(valueCursorPos >= 3) valueCursorPos = 1;
else valueCursorPos++;
lcd.lcdGoToXY(2, 14 + valueCursorPos - 1);
lastMove = now;
}
}
if (isPressed(ENTER)) {
delay(250);
// Exit editing and keep modifiedVal, cursor back to Modified Val line on menu
editingValue = false;
menuCursor = 1; // Modified Val line (row 1)
lcdBusyCursorOff();
lcd.lcdGoToXY(menuCursor, 0);
lcdBusyCursorOn();
}
}
}
void incrementDigitAt(int pos) {
if(pos < 0 || pos > 2) return;
if(modifiedVal[pos] >= '9') modifiedVal[pos] = '0';
else modifiedVal[pos]++;
lcd.lcdGoToXY(2, 14 + pos);
lcd.lcdWriteText(&modifiedVal[pos]);
lcd.lcdGoToXY(2, 14 + valueCursorPos - 1);
lcdBusyCursorOn();
}
void decrementDigitAt(int pos) {
if(pos < 0 || pos > 2) return;
if(modifiedVal[pos] <= '0') modifiedVal[pos] = '9';
else modifiedVal[pos]--;
lcd.lcdGoToXY(2, 14 + pos);
lcd.lcdWriteText(&modifiedVal[pos]);
lcd.lcdGoToXY(2, 14 + valueCursorPos - 1);
lcdBusyCursorOn();
}
void updateCursorPosition() {
Serial.print("Inside updateCursorPosition(), menuCursor val:");
Serial.println(menuCursor);
lcdBusyCursorOff();
// For all menu views, cursor on option number column (0)
if (currentView == 5 && editingValue) {
// Should not call this in editing mode
return;
}
lcd.lcdGoToXY(menuCursor, 0);
lcd.lcdBusy();
lcdBusyCursorOn();
}
// ========== LCD CONTROL ==========
void lcdBusyCursorOn() {
lcd.sendCommand4Bit(0x0D); // Display ON, cursor ON, blink ON
}
void lcdBusyCursorOff() {
lcd.sendCommand4Bit(0x0C); // Display ON, cursor OFF, blink OFF
}
void updateCurrentRatingEditDisplay() {
lcd.lcdClear();
lcd.lcdGoToXY(0, 4);
lcd.lcdWriteText("Current Rating");
lcd.lcdGoToXY(1, 0);
lcd.lcdWriteText("Existing Val:");
lcd.lcdGoToXY(1, 14);
lcd.lcdWriteText(existingVal);
lcd.lcdGoToXY(2, 0);
lcd.lcdWriteText("Modified Val:");
lcd.lcdGoToXY(2, 14);
lcd.lcdWriteText(modifiedVal);
lcd.lcdGoToXY(3, 0);
lcd.lcdWriteText("Cancel");
// If in editing mode, cursor on digit; else cursor on menuCursor option (1-3)
if (editingValue) {
lcd.lcdGoToXY(2, 14 + valueCursorPos - 1); // blinking on digit
// lcd.lcdBusy();
} else {
// cursor on option number column (0)
lcd.lcdGoToXY(menuCursor, 0);
// lcd.lcdBusy();
}
lcdBusyCursorOn();
}
/************************
if 2. manual mode
1. Rectifier Setting
2. Rectifier Calib
3. Overview
4. Help
if 3. Cancel -> go back to first view
if press right button
1. Network Config
2. Program Editor
3. AC Mains Settings
4. Mode Change
if press left button go back previous view
if 1. Rectifier Setting
1. Rectifier Rating
2. Factor Update
3. System Threshold
4. PI Setting
if 1. Rectifier Rating
1. Current Rating
2. Voltage Rating
3. System Alm Config
4. Temp Control
if 1. Current Rating
Curr Rating: xxxA
if press enter as the cursor in only and in the first x:
1. Existing Val:
xxx
2. Modified Val:
xxx Cancel
* cursor in C in Cancel and press enter will go back to the previous view
same with 2. Voltage Rating
*
**************************/UP
DOWN
ENTER