#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Preferences.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Button Pins
const int BTN_NEXT = 14;
const int BTN_SCROLL = 12;
const int BTN_SELECT = 13;
// UI States
enum Screen { HOME, IO_WATCH, SETTINGS, CLOUD, EVENTS};
Screen currentScreen = HOME;
int scrollPage = 0; // 0, 1, or 2 depending on the screen
int settingCursor = 0; // Tracks which setting is selected
// Data Variables (Placeholders)
int voltage = 230;
float current = 1.2;
int waterLevel = 85;
bool wifi_sts = 1;
bool motor1_sts = true, motor2_sts = false, sw1 = true, sw2 = false, sw3 = true;
bool timer1 = false, timer2 = true;
bool button_nxt_mem = 0;
bool button_scroll_mem = 0;
bool button_ok_mem = 0 ;
// Settings (0/1)
struct Config {
bool overV = 0;
bool overC = 0;
bool underV = 0;
bool dryRun = 0;
bool m1wl = 0;
bool m2wl = 0;
bool slaveCmd = 0;
int overV_sp = 270;
int underV_sp = 170;
float overC_sp_M1 = 16.0;
float overC_sp_M2 = 16.0;
float dry_sp_M1 = 9.0;
float dry_sp_M2 = 9.0;
} settings;
const char* event_info_arr[24] = {
"", "M1 water level", "M2 water level", "M1 over Voltage", "M2 over Voltage", "M1 under Voltage", "M2 under Voltage", "M1 over current", "M2 over current", "M1 dry run", "M2 dry run",
"M1 remote start", "M1 remote stop", "M2 remote start", "M2 remote stop", "M1 timer stop", "M2 timer stop", "M1 AT start-timer", "M2 AT start-timer", "M1 AT start WL", "M2 A start WL",
"M1 AT restart", "M2 AT restart", "E_stop"
};
int event_record[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
Preferences preferences;
void setup() {
Serial.begin(115200);
pinMode(BTN_NEXT, INPUT_PULLUP);
pinMode(BTN_SCROLL, INPUT_PULLUP);
pinMode(BTN_SELECT, INPUT_PULLUP);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
// --- WELCOME NOTE START ---
display.clearDisplay();
display.setTextColor(WHITE);
// Title
display.setTextSize(2);
display.setCursor(5, 3);
display.println("Smart pump");
// Divider line
display.drawLine(0, 25, 128, 25, WHITE);
// Credits
display.setTextSize(1);
display.setCursor(0, 30);
display.println("Developed by,");
display.setCursor(0, 42);
display.println("Roopal Kandippady");
display.setCursor(0, 54);
display.println("ver - 1.25.26");
display.display();
delay(4000); // Show for 4 seconds
// Load saved settings from ROM
preferences.begin("hmi-settings", false);
settings.overV = preferences.getBool("ov", 0);
// ... load others similarly
}
void loop() {
handleButtons();
drawUI();
}
void handleButtons() {
// NEXT Button: Cycles through main screens
if ((digitalRead(BTN_NEXT) == LOW) && (button_nxt_mem == HIGH)) {
currentScreen = (Screen)((currentScreen + 1) % 5);
scrollPage = 0;
settingCursor = 0;
}
// SCROLL Button: Cycles pages within a screen
if ((digitalRead(BTN_SCROLL) == LOW) && (button_scroll_mem == HIGH)) {
if (currentScreen == IO_WATCH) scrollPage = (scrollPage + 1) % 4;
if (currentScreen == SETTINGS) {
settingCursor = (settingCursor + 1) % 14; // 7 settings + Save option
if (settingCursor < 4) scrollPage = 0;
else if (settingCursor < 6) scrollPage = 1;
else if (settingCursor < 8) scrollPage = 2;
else if (settingCursor < 12) scrollPage = 3;
else if (settingCursor < 13) scrollPage = 4;
else
scrollPage = 5;
}
}
// SELECT Button: Toggle values in Settings
if ((digitalRead(BTN_SELECT) == LOW) && (currentScreen == SETTINGS) && (button_ok_mem == HIGH)) {
updateSetting(settingCursor);
}
button_nxt_mem = digitalRead(BTN_NEXT);
button_scroll_mem = digitalRead(BTN_SCROLL);
button_ok_mem = digitalRead(BTN_SELECT);
}
void drawUI() {
display.clearDisplay();
display.setTextSize(1);
switch (currentScreen) {
case HOME:
drawHeader("Home");
display.setCursor(0, 20);
display.printf("Voltage: %d%V\n", voltage);
display.setCursor(0, 30);
display.printf("Current: %.1fA\n", current);
display.setCursor(0, 40);
display.printf("Water lvl: %d%%\n", waterLevel);
display.setCursor(0, 50);
if (wifi_sts == 1 ) {
display.printf("Wifi: Connected");
}
else
{
display.printf("Wifi: Disconnected");
}
break;
case IO_WATCH:
drawHeader("IO Watch Table");
display.setCursor(0, 20);
if (scrollPage == 0) {
display.printf("I Motor 1 sts: %s\n", motor1_sts ? "ON" : "OFF");
display.setCursor(0, 30);
display.printf("I Motor 2 sts: %s\n", motor2_sts ? "ON" : "OFF");
display.setCursor(0, 40);
display.printf("I Switch 1: %s\n", sw1 ? "ON" : "OFF");
display.setCursor(0, 50);
display.printf("I Switch 2: %s\n", sw2 ? "ON" : "OFF");
}
else if (scrollPage == 1) {
display.printf("I Switch 3: %s\n", sw2 ? "ON" : "OFF");
display.setCursor(0, 30);
display.printf("O Relay 1 : %s\n", motor2_sts ? "ON" : "OFF");
display.setCursor(0, 40);
display.printf("O Relay 2 : %s\n", motor2_sts ? "ON" : "OFF");
display.setCursor(0, 50);
display.printf("O Relay 3 : %s\n", motor2_sts ? "ON" : "OFF");
}
else if (scrollPage == 2) {
display.printf("O Relay 4 : %s\n", motor2_sts ? "ON" : "OFF");
display.setCursor(0, 30);
display.printf("O Relay 5 : %s\n", motor2_sts ? "ON" : "OFF");
display.setCursor(0, 40);
display.printf("O Relay 6 : %s\n", motor2_sts ? "ON" : "OFF");
display.setCursor(0, 50);
display.printf("O Fun LED : %s\n", motor2_sts ? "ON" : "OFF");
}
else {
display.printf("SI Timer 1: %s\n", timer1 ? "ON" : "OFF");
display.setCursor(0, 30);
display.printf("SI Timer 2: %s", timer2 ? "ON" : "OFF");
}
break;
case SETTINGS:
renderSettingsMenu();
break;
case CLOUD:
drawHeader("Cloud Read Values");
display.setCursor(0, 20);
display.println("Motor 1 cmd: **");
display.setCursor(0, 30);
display.println("Motor 2 cmd: **");
display.setCursor(0, 40);
display.println("Settings value:");
display.print (settings.overV * 1000000 + settings.overC * 100000 + settings.underV * 10000 + settings.dryRun * 1000 + settings.m1wl * 100 + settings.m2wl * 10 + settings.slaveCmd);
display.setCursor(0, 60);
display.println("Auto start value:");
break;
case EVENTS:
drawHeader("Events");
display.setCursor(0, 20);
display.printf("1) %s", event_info_arr[2]);
break;
}
display.display();
}
void drawHeader(String title) {
display.setCursor(0, 0);
display.print( title);
display.drawLine(0, 12, 128, 12, WHITE);
}
void renderSettingsMenu() {
// Logic to show a cursor '>' next to the adjustable parameter
display.setCursor(0, 20);
// Page 1
if (settingCursor < 4) {
drawHeader("Motor protection");
display.setCursor(0, 20);
display.printf("%c Over Voltage: %d\n", (settingCursor == 0 ? '>' : ' '), settings.overV);
display.setCursor(0, 30);
display.printf("%c Over Current: %d\n", (settingCursor == 1 ? '>' : ' '), settings.overC);
display.setCursor(0, 40);
display.printf("%c Under Voltage: %d\n", (settingCursor == 2 ? '>' : ' '), settings.underV);
display.setCursor(0, 50);
display.printf("%c Dry run: %d\n", (settingCursor == 3 ? '>' : ' '), settings.dryRun);
}
// Page 2
else if (settingCursor < 6) {
drawHeader("Auto water level");
display.setCursor(0, 20);
display.printf("%c Motor 1 WL: %d\n", (settingCursor == 4 ? '>' : ' '), settings.m1wl);
display.setCursor(0, 30);
display.printf("%c Motor 2 WL: %d\n", (settingCursor == 5 ? '>' : ' '), settings.m2wl);
}
else if (settingCursor < 7) {
drawHeader("Other settings");
display.setCursor(0, 20);
display.printf("%c Slave CMD: %d\n", (settingCursor == 6 ? '>' : ' '), settings.slaveCmd);
}
else if (settingCursor < 11) {
drawHeader("Protection SP");
display.setCursor(0, 20);
display.printf("%c Over V SP: %dV\n", (settingCursor == 7 ? '>' : ' '), settings.overV_sp);
display.setCursor(0, 30);
display.printf("%c Under V SP: %dV\n", (settingCursor == 8 ? '>' : ' '), settings.underV_sp);
display.setCursor(0, 40);
display.printf("%c Over I SP M1: %.1fA\n", (settingCursor == 9 ? '>' : ' '), settings.overC_sp_M1);
display.setCursor(0, 50);
display.printf("%c Over I SP M2: %.1fA\n", (settingCursor == 10 ? '>' : ' '), settings.overC_sp_M2);
}
else if (settingCursor < 13) {
drawHeader("Protection SP");
display.setCursor(0, 20);
display.printf("%c DryRun SP M1: %.1fA\n", (settingCursor == 11 ? '>' : ' '), settings.dry_sp_M1);
display.setCursor(0, 30);
display.printf("%c DryRun SP M2: %.1fA\n", (settingCursor == 12 ? '>' : ' '), settings.dry_sp_M2);
}
// Page 3 (Save)
else {
drawHeader("Save changes");
display.setCursor(0, 20);
display.println("\nSave RAM to ROM?");
display.printf("%c [ YES ]", (settingCursor == 13 ? '>' : ' '));
}
}
void updateSetting(int index) {
switch (index) {
case 0: settings.overV = !settings.overV; break;
case 1: settings.overC = !settings.overC; break;
case 2: settings.underV = !settings.underV; break;
case 3: settings.dryRun = !settings.dryRun; break;
case 4: settings.m1wl = !settings.m1wl; break;
case 5: settings.m2wl = !settings.m2wl; break;
case 6: settings.slaveCmd = !settings.slaveCmd; break;
case 7: // Voltage: 230 to 280, step 5
settings.overV_sp += 5;
if (settings.overV_sp > 280) settings.overV_sp = 230;
break;
case 8: // Voltage: 150 to 220, step 5
settings.underV_sp += 5;
if (settings.underV_sp > 220) settings.underV_sp = 150;
break;
case 9: // Current: 12.0 to 16.0, step 0.5
settings.overC_sp_M1 += 0.5;
if (settings.overC_sp_M1 > 16.0) settings.overC_sp_M1 = 12.0;
break;
case 10: // Current: 12.0 to 16.0, step 0.5
settings.overC_sp_M2 += 0.5;
if (settings.overC_sp_M2 > 16.0) settings.overC_sp_M2 = 12.0;
break;
case 11: // Current: 5.0 to 12.0, step 0.5
settings.dry_sp_M1 += 0.5;
if (settings.dry_sp_M1 > 12.0) settings.dry_sp_M1 = 5.0;
break;
case 12: // Current: 5.0 to 12.0, step 0.5
settings.dry_sp_M2 += 0.5;
if (settings.dry_sp_M2 > 12.0) settings.dry_sp_M2 = 5.0;
break;
case 13: saveToROM(); break;
}
}
void saveToROM() {
preferences.putBool("ov", settings.overV);
// ... repeat for all settings
display.clearDisplay();
display.setCursor(20, 30);
display.print("SAVED TO ROM!");
display.display();
delay(1000);
}(click to edit)