/** Test of button control menu
*/
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
//vlastni gr symboly
byte Stupen[8] = {0b00010,0b00101,0b00010,0b00000,0b00000,0b00000,0b00000,0b00000};
byte Celsius[8] = {0b10000,0b00110,0b01001,0b01000,0b01000,0b01001,0b00110,0b00000};
byte Hourglass[8] = {0b11111, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001, 0b11111, 0b00000 };
byte symPump[8] = {0b00000, 0b11111, 0b10001, 0b10101, 0b10001, 0b11111, 0b00000, 0b00000 };
byte symHeat[8] = {0b00000, 0b10101, 0b10101, 0b10101, 0b10101, 0b10101, 0b00000, 0b00000 };
byte symFilt[8] = {0b00000, 0b10101, 0b01010, 0b10101, 0b01010, 0b10101, 0b00000, 0b00000 };
byte nextMnu[8] = {0b10000, 0b00000, 0b10000, 0b00000, 0b10000, 0b00000, 0b10000, 0b00000 };
//DEKLARACE
void lcdPrintOnPos(int val, int row, int pos, bool zeroOff = false);
//-------------------------------------------------------------
// Konstanty a nastavení
//-------------------------------------------------------------
#define BTN_SET 2
#define BTN_UP 3
#define BTN_DOWN 4
#define BUZZ 5
#define LCD_LINES 2
enum DelayID {
DELAY_BUTTON,
DELAY_SENSOR,
DELAY_DISPLAY,
DELAY_WIFI,
DELAY_LED,
DELAY_COUNT
};
//pro funkci zpozodovani
unsigned long startMillis[DELAY_COUNT];
unsigned long lastActivity = 0; // čas posledního stisku klavesy nabo jine akce
const unsigned long SLEEP_DELAY = 2UL * 60UL * 1000UL; // 2 minuty delay Display
bool lcdOn = true; //ukladame stav LCD
//-------------------------------------------------------------
// Globální proměnné ctene z pameti
//-------------------------------------------------------------
int VarssunregMode = 0; // 0 = AUTO, 1 = MANUAL
int SetPoolTemp = 27;
int SetLight = 80;
bool stavPump = 0;
bool stavHeat = 0;
bool stavFilt = 0;
// ukladane jen v RAM
int8_t activeMenu = 1; // 1 - hlavní menu, 2 - subMenu, 3 - subsubMenu
int8_t subMenu = 0; // prvni polozka submenu
int8_t activeMenuItem = 0; // index aktualni položky v menu
bool firstDispSet = false; //hlida jestli se diplej nacetl aby se daly menit jen hodnoty
bool stpwAction = 0; // auto/man po skonceni stopek readParamFromEEProm(9); //okruh ohrevu max 250sec 4 minuty 10sec step 10sec
int manTime = 0; //factoryVal[4]; //vypinani manual rezimu stopky min 0-720min
//for catch and delay if but pressed in other function
bool butPressed=false;
//-------------------------------------------------------------
// Menu definice
//-------------------------------------------------------------
const char* const autMnu[] = {
"Pool temp",
"Filtration",
"Exit MENU"
};
const char* const manMnu[] = {
"Pump",
"Heating",
"Filtration",
"Stopwatch",
"Stpw action",
"STOP all",
"Exit MENU"
};
const char* const setMnu[] = {
"WIFI",
"TimeDat",
"Daylight",
"Heat circ",
"Pool circ",
"Disp sleep",
"Filt duration",
"RCHK time",
"Firmware upgrade",
"Buzzer frq",
"About SUNREG",
"Exit MENU"
};
//ulozi delky menu
const uint8_t autMnuSize = sizeof(autMnu) / sizeof(autMnu[0]);
const uint8_t manMnuSize = sizeof(manMnu) / sizeof(manMnu[0]);
const uint8_t setMnuSize = sizeof(setMnu) / sizeof(setMnu[0]);
//-------------------------------------------------------------
// Setup
//-------------------------------------------------------------
void setup() {
Serial.begin(115200);
Serial.println("Begin...");
pinMode(BTN_SET, INPUT_PULLUP);
pinMode(BTN_UP, INPUT_PULLUP);
pinMode(BTN_DOWN, INPUT_PULLUP);
pinMode(BUZZ, OUTPUT);
beep();
//for (int i = 0; i < DELAY_COUNT; i++) startMillis[i] = 0;
lcd.begin(16, 2);
lcd.backlight();
lcd.print("BUTTON TEST");
delay(500);
lastActivity = millis();
//menu size
Serial.print("Menu length autMnuSize: ");
Serial.print(autMnuSize);
Serial.print(" manMnuSize: ");
Serial.print(manMnuSize);
Serial.print(" setMnuSize: ");
Serial.println(setMnuSize);
Serial.print("Aktualni mod: ");
Serial.println(VarssunregMode);
//natahneme prvni obrazovku
if (VarssunregMode==1){
mainManScreen(); //zobrazime
}else{
mainAutoScreen(); //zobrazime
}
//def vlastni chars
lcd.createChar(1, Stupen);
lcd.createChar(2, Celsius);
lcd.createChar(3, Hourglass);
lcd.createChar(6, symPump);
lcd.createChar(7, symHeat);
lcd.createChar(8, symFilt);
lcd.createChar(9, nextMnu);
}
//-------------------------------------------------------------
// Loop
//-------------------------------------------------------------
void loop() {
//zachytavani akci tlacitek
button();
//uspime LCD po dobu necinosti, wakeUp s si resi tlacitka nebo akce systemu
if (millis() - lastActivity > SLEEP_DELAY && lcdOn) lcdSleep();
}
//-------------------------------------------------------------
// Tlačítka
// Zachytavani tlacitek, a pokud neco sta volame funnci na
// rozhodnuti co s tim
//-------------------------------------------------------------
void button() {
//millis pauza na buttony
int buttonDelay = 250;//ochrana
//if (delayElapsed(DELAY_BUTTON, 150)) {
// současný stisk UP + DOWN = nastavení
if (!digitalRead(BTN_UP) && !digitalRead(BTN_DOWN)) {
lcd.print("SERVIS MENU");
beep();
delay(800);
lcd.clear();
//vstup do servisniho menu, zobrazime prvni polozku
activeMenu = 5;
menuAction(10); //aktivace menu bez buttonu
delay(buttonDelay);
}
if (!digitalRead(BTN_SET)) {
menuAction(1);
delay(buttonDelay);
}
if (!digitalRead(BTN_UP)) {
menuAction(2);
delay(buttonDelay);
}
if (!digitalRead(BTN_DOWN)) {
menuAction(3);
delay(buttonDelay);
}
}
//-------------------------------------------------------------
// Akce menu
// hlavbni rozhodovani co se bude dit a kde
//-------------------------------------------------------------
void menuAction(int but) {
//dodelat menuMillis = millis(); //hlidani menu a navrat na uvod bez ulozeni
//take se da vzuyit pro display
//probudime display pokud spi
lastActivity = millis();
if (!lcdOn) lcdWakeUp();
// PREPINANI AUTO / MAN - HLAVNI STRANKA - UP/DOWN n
if (activeMenu == 1 && (but == 2 || but == 3)){
// 0 = přepínání AUTO / MAN, musime byt na level 1
//prepenem na AUTO
if (but == 2 && VarssunregMode == 1) {
VarssunregMode = 0; // AUTO
//title screen automode
mainAutoScreen(); //zobrazime
//!!!ulozime nastaveni
}
//prepenem na MAN
if (but == 3 && VarssunregMode == 0) {
VarssunregMode = 1; // MANUAL
//title screen MAN mode
mainManScreen(); //zobrazime
//!!!ulozime nastaveni
}
}
/*POSLEDNI POLOZKA V MENU JE VZDY EXIT
MAN, AUT, SET - Pokud je v menu vstoupeno
na posledni polozku menu a stiskunto 1(SET) - exit menu z menu, exit
*/
if ( but == 1 && (
(activeMenu == 2 && subMenu == autMnuSize-1 ) ||
(activeMenu == 3 && subMenu == manMnuSize-1 ) ||
(activeMenu == 5 && subMenu == setMnuSize-1 ) )){
subMenu = 0;
firstDispSet = false; //tohle by stacile tady a v auto navratu
activeMenu = 1; //hlavni menu
but = 0;
//navrat na hlavni stranu aut nebo man rezimu
titleScreen();
}
//vstup do AUTO menu 2
if (activeMenu == 1 && but == 1 && VarssunregMode == 0){ activeMenu = 2; but = 0; }
//vstup do MAN menu 3
if (activeMenu == 1 && but == 1 && VarssunregMode == 1){ activeMenu = 3; but = 0; }
//EXIT ze SUBMENU na predchozi level menu z menu 25 nebo 35 nebo 55
//pokud je pozadavek (savePos - ulozit do pameti), tak ukladame nastavenou hodnotu po odchodu ze submenu
if ( (activeMenu == 25 || activeMenu == 35 || activeMenu == 55 ) && but == 1){
Serial.print("EXIT - activeMenu: ");
Serial.println(activeMenu );
Serial.println("Navrat, ulozeni hodnoty do RAM a pameti");
//if (savePos != "" ){
//saveParamToEEProm(savePos, saveVal);
// savePos = ""; //reset
// saveVal = "";
// firstDispSet = false; //reset first disp pri navratu
//}
firstDispSet = false; //vzcistime pro dalsi pouzit
//navrat na predchozi stranku menu
if (activeMenu == 25 ) activeMenu = 2;
if (activeMenu == 35 ) activeMenu = 3;
if (activeMenu == 55 ) activeMenu = 5;
subMenu = 0; //reset submenu
but = 0;
}
/** hlavni vyber z AUTO menu
zpracovani pozadavku
*/
if (activeMenu == 2){
lcd.clear();
//listovani po menu nahoru,dolu
if (but == 3){ subMenu++; if (subMenu>autMnuSize-1){subMenu=0;} }
if (but == 2){ subMenu--; if (subMenu<0){subMenu = autMnuSize-1;} }
//zobrazeni menu
firstDispSet = false;
lcd.clear();
lcd.setCursor(0,0);
lcd.print( autMnu[subMenu] );
//Serial.print("MENU - ");
//Serial.println( autMnu[subMenu] );
//lcdPrintText( autMnu[subMenu].c_str(), " ");
//druhy radek
lcd.setCursor(0,1);
//pokud jesme na konci tak neni dalsi tak konec
if (subMenu == autMnuSize-1){
//lcd.print(autMnu[0]);
//konec, nerolujeme
}else{
lcd.write(9);
lcd.print(autMnu[subMenu+1]);
}
//po navratu musime zajistit zobrazeni havniho menu
//byte poz; //def pro poz
//zobrazime hodnoty v MENU podle aktualniho vybraneho submenu v AUTO rezimu
switch (subMenu){
case 0: //teplota bazenu nastavena
lcdPrintOnPos(SetPoolTemp,0,15);
lcd.print("C");
//lcd.write(2);
break;
case 1: //Daylight zobrazeni v menu
lcdPrintOnPos(SetLight,0,16);
break;
case 2: //filtrace
////S erial.println(FiltrationTime);
lcdPrintOnPos(0,0,16);
//lcd.setCursor(11,0);
//dispTime(FiltrationTime);
break;
}
Serial.print( "Aktivni polozka menu: ");
Serial.print( subMenu);
Serial.print(" Celkem: ");
Serial.println( autMnuSize);
//nastaveni nebo prechod ze submenu dalsi urovne stisknuto SET v hlavnim menu
//jenom pokud jsme na exitu
if (but == 1 && (subMenu == 0 || subMenu == 1 || subMenu == 2)) activeMenu = 25;
} // menu==2
/** hlavni menu MAN - manualniho rizeni
* nekonecne scroll menu
* kdyz je dole bude scrolovat menu
*/
if (activeMenu == 3){
////Serial.println("Vstup do MAN menu");
lcd.clear();
firstDispSet = false;
//listovani po menu nahoru,dolu
if (but == 3){ subMenu++; if (subMenu>manMnuSize-1){subMenu=0;} }
if (but == 2){ subMenu--; if (subMenu<0){subMenu = manMnuSize-1; } }
//nastaveniv submenu
if ( but == 1 ){
//podle submenu nastavujeme hodnotu
switch (subMenu){
case 0: //cerpadlo on/off
if (stavPump == 0){ stavPump = 1; }else{ stavPump = 0;}
// a take zobrazime
//ukladame
//saveParamToEEProm(5, stavPump);
break;
case 1: //ohrev on/off
if (stavHeat == 0){ stavHeat = 1; }else{ stavHeat = 0;}
//saveParamToEEProm(6, stavHeat);
break;
case 2: //filtrace
if (stavFilt == 0){ stavFilt = 1; }else{ stavFilt = 0;}
//saveParamToEEProm(7, stavFilt);
break;
case 3: //vstup na stopwatch sub menu
activeMenu = 35;
break;
case 4: //after stopwatch mode
//otoci stav
if (stpwAction == 0)
stpwAction = 1; //save
else
stpwAction = 0; //save
break;
case 5: //ALL OFF, reset
//resetMan();
//lcd.setCursor(14,0);
//lcd.print("OK");
break;
}
}
//subMenu=startMenu; subMenu <= startMenu+1; subMenu++){
//vypis menu
lcd.setCursor(0,0);
//pokud p,h,f tak symbol
if (subMenu == 0) lcd.write(6);
if (subMenu == 1) lcd.write(7);
if (subMenu == 2) lcd.write(8);
lcd.print(manMnu[subMenu]);
lcd.setCursor(13,0);
dispManStatus(subMenu);
//druhy radek
//pokud jesme na konci tak neni dalsi a zobrazime prvni - 0
lcd.setCursor(0,1);
if (subMenu == manMnuSize-1){
//lcd.print(autMnu[0]);
}else{
lcd.write(9);
lcd.print(manMnu[subMenu+1]);
}
} //menu 3 MAN
//vstup MAN na nastaveni hodnot podle subMenu
if (activeMenu == 35){
lcd.clear();
switch (subMenu){
//manual mode
case 3: //manual off time setting
manTime= setValue(manTime, 15, 0, 720);
//if (but == 2){ manTime+=15; if (manTime > 720){ manTime = 720; beep(); } }
//if (but == 3){ manTime-=15; if (manTime <= 0){ manTime=0; beep(); } }
//SetPoolTemp
lcd.setCursor(0,0);
lcd.print(manMnu[subMenu]);
lcd.setCursor(15,0);
lcd.write(3);
lcd.setCursor(0,1);
lcd.print("(0-12h)");
lcd.setCursor(11,1);
dispTime(manTime);
//ulozeni casu pro vypnuti
//savePos = 4;
//saveVal = manTime/10;
break;
}
}
} //menu Action
/*
//pokud na hlavni strance (1) but SET (1) tak vtup do submenu oblasti
//vstup do AUTO menu 2
if (activeMenu==1 && but==1 && VarssunregMode == 0){ activeMenu=2; but = 0; }
//vstup do MAN menu 3
if (activeMenu==1 && but==1 && VarssunregMode == 1){ activeMenu=3; but = 0; }
if (activeMenu == 1 && but == 1){
activeMenu==1; //preeneme na submenu
but=0; //rest but
Serial.println("Vstup do Submenu");
}
*/
/** hlavni vyber z AUTO menu
zpracovani pozadavku
*/
/*
if (activeMenu == 2){
//listovani po menu nahoru,dolu
if (but == 3){ activeSubMenu++; if (activeSubMenu>autMnuSize-1){activeSubMenu=0;} }
if (but == 2){ activeSubMenu--; if (activeSubMenu<0){activeSubMenu = autMnuSize-1;} }
//zobrazeni menu
firstDispSet = false;
lcd.clear();
lcd.setCursor(0,0);
lcd.print( autMnu[activeSubMenu] );
//Serial.print("MENU - ");
//Serial.println( autMnu[subMenu] );
//lcdPrintText( autMnu[subMenu].c_str(), " ");
//druhy radek
lcd.setCursor(0,1);
//pokud jesme na konci tak neni dalsi tak konec
if (activeSubMenu == autMnuSize-1){
//lcd.print(autMnu[0]);
//konec, nerolujeme
}else{
//lcd.write(9);
lcd.print("/");
lcd.print(autMnu[activeSubMenu+1]);
}
//po navratu musime zajistit zobrazeni havniho menu
byte poz; //def pro poz
//zobrazime hodnoty v MENU podle aktualniho vybraneho submenu v AUTO rezimu
/*
switch (subMenu){
case 0: //teplota bazenu nastavena
lcdPrintOnPos(SetPoolTemp,0,15);
lcd.write(2);
break;
case 1: //Daylight zobrazeni v menu
lcdPrintOnPos(SetLight,0,16);
break;
case 2: //filtrace
////S erial.println(FiltrationTime);
lcd.setCursor(11,0);
dispTime(FiltrationTime);
break;
}
*/
//nastaveni nebo prechod ze submenu dalsi urovne stisknuto SET v hlavnim menu
//if (but == 1 && (activeSubMenu == 0 || activeSubMenu == 1 || activeSubMenu == 2)) menu = 25;
//} // menu==2
//}
//vstup do AUTO menu 2
//if (activeMenu==1 && but == 1 && SetMode == 0){ menu = 2; but = 0; }
//vstup do MAN menu 3
//if (activeMenu = 1 && but == 1 && SetMode == 1){ menu = 3; but = 0; }
//vstup do SET menu 3
//if (activeMenu = 1 && but == 1 && SetMode == 1){ menu = 3; but = 0; }
/*
// Navigace v submenu podle oblasti
if ((but == 2 || but == 3) && activeMenu == 1) {
//vzbereme oblast ktrou bududeme zpracovavat
//najdrive MAN
if (VarssunregMode == 1){
if (but == 2 && activeMenuItem > 0) activeMenuItem--;
if (but == 3 && activeMenuItem < autMnuSize - 1)activeMenuItem++;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(autMnu[activeMenuItem]);
if (activeMenuItem + 1 < autMnuSize) {
lcd.setCursor(0, 1);
lcd.print("|");
lcd.print(autMnu[activeMenuItem + 1]);
}
Serial.print("Active item: ");
Serial.println(activeMenuItem);
}
}
// SET v submenu = návrat do hlavního menu
//pokud je SET jsme na posledni polozce, navrat na hlavni menu
Serial.print("activeMenuItem ");
Serial.print(activeMenuItem);
if (but == 1 && activeMenu == 1) {
if (activeMenuItem == autMnuSize) {
activeMenu = 0;
lcd.clear();
//musime prekreslit LCD podle aktualni homeScreen
//lcd.print("Exit menu");
Serial.println("Returning to main screen");
}
}
/*
uint8_t currentSize;
switch (VarssunregMode){
case 0: currentSize = autMnuSize; break;
case 1: currentSize = manMnuSize; break;
}
if (activeMenuItem == currentSize - 1) {
activeMenu = 1;
lcd.clear();
lcd.print("Exit menu");
}
*/
//}
//-------------------------------------------------------------
// Zobrazí submenu
//-------------------------------------------------------------
/*
void displaySubMenu(int pos) {
if (pos >= autMnuSize) pos = 0;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(autMnu[pos]);
if (pos + 1 < autMnuSize) {
lcd.setCursor(0, 1);
lcd.print(autMnu[pos + 1]);
}
Serial.print("Submenu displayed at pos ");
Serial.println(pos);
}*/
//-------------------------------------------------------------
// Delay handler
// s předdefinovynými id aby se nepopletlo
//-------------------------------------------------------------
bool delayElapsed(DelayID id, unsigned long ms) {
unsigned long currentMillis = millis();
if (currentMillis - startMillis[id] >= ms) {
startMillis[id] = currentMillis;
return true;
}
return false;
}
//-------------------------------------------------------------
// LCD Sleep / Wake
//-------------------------------------------------------------
void lcdSleep() {
lcd.noBacklight();
lcd.noDisplay();
lcdOn = false;
}
void lcdWakeUp() {
lcd.display();
lcd.backlight();
lcdOn = true;
}
//-------------------------------------------------------------
// Test beep
//-------------------------------------------------------------
void beep() {
digitalWrite(BUZZ, HIGH);
delay(50);
digitalWrite(BUZZ, LOW);
}
/* Hlavni obrazovka MAN a ATO modu do ktere se vpisují hodnoty
z čidel a jiné stavové prvky
*/
void mainManScreen(){
lcd.clear();
lcd.setCursor(0,0);
lcd.print("POOL 99.9C MAN");
lcd.setCursor(0,1);
lcd.print("#off #on #off ");
}
void mainAutoScreen(){
lcd.clear();
lcd.setCursor(0,0);
lcd.print("POOL 99.9");
lcd.write(2);
lcd.print(" AUTO");
lcd.setCursor(0,1);
lcd.print("HEAT 99.9");
lcd.write(2);
lcd.print(" 27");
lcd.write(2);
lcd.write(3);
}
/** zobrazovani celych hodnot cisel zarovnanych na pravou definovanou pozici
mame take zajistit odmazavani dlouhych cisel?
*/
void lcdPrintOnPos(int val, int row, int pos, bool zeroOff = false) {
char buf[5]; // max 4 číslice + '\0'
// speciální případ: 0 → "off"
if (val == 0 && zeroOff) {
lcd.setCursor(pos - 2, row); // „off“ má 3 znaky
lcd.print("off");
return;
}
// převedení čísla do textu
snprintf(buf, sizeof(buf), "%d", val);
int len = strlen(buf);
// vymazání prostoru (4 znaky)
lcd.setCursor(pos - 3, row);
lcd.print(" ");
// výpočet pozice zarovnání doprava
int start = pos - (len - 1);
lcd.setCursor(start, row);
lcd.print(buf);
}
void titleScreen(){
//zobrazit teplotu bazenu, nastavenou teplotu
lcd.clear();
//pokud MAN tak symboly a stav
if (VarssunregMode == 1){
mainManScreen();
//tady vzpsat on/off podlle nastaveni
/*
if (stavPump == 1) lcd.print("on "); else lcd.print("off");
lcd.setCursor(5,1);
lcd.write(7);
if (stavHeat == 1) lcd.print("on "); else lcd.print("off");
lcd.setCursor(10,1);
lcd.write(8);
if (stavFilt == 1) lcd.print("on "); else lcd.print("off");
*/
}else{
mainAutoScreen();
}
//reset menu
subMenu = 0;
activeMenu= 1;
}
//zobrazeni stavu vybrane polozky manualniho rezimu
void dispManStatus(byte subMenu){
switch (subMenu){
case 0: //cerpadlo on/off
if (stavPump == 0){
lcd.print("off");
}else{
lcd.print(" on");
}
break;
case 1: //ohrev on/off
if (stavHeat == 0){
lcd.print("off");
}else{
lcd.print(" on");
}
break;
case 2: //filtrace on/off
if (stavFilt == 0){
lcd.print("off");
}else{
lcd.print(" on");
}
break;
case 3: //man cas vypnuti casovac
/*
if (manTime == 0){
lcd.print("off");
}else{
lcd.setCursor(11,0);
//dispTime(manTime);
}
*/
lcd.setCursor(11,0);
lcd.print("TIME");
break;
case 4: //akce po ukonceni stopwatch
lcd.setCursor(12,0);
if (stpwAction == 0)
lcd.print("AUTO");
else
lcd.print(" MAN");
break;
}
}
/** Setting values int-/+ 32767
int setValue(int val, int step, int minVal, int maxVal){
val - incoming value
step - value step
minVal - minimal value
maxVal - maximal value
return val, set butPressed=true for but delay after disp value
*/
int setValue(int val, int step, int minVal, int maxVal){
//nastovovani hodnoty
if (digitalRead(BTN_UP) == LOW){ val+=step; if (val > maxVal){ val = maxVal; beep(); } delay(80); butPressed=true; }
if (digitalRead(BTN_DOWN) == LOW){ val-=step; if (val < minVal){ val=minVal; beep(); } delay(80); butPressed=true; }
return val;
}
/** Zobrazeni casu z celkovych minut
*/
void dispTime(int minutes) {
if (minutes == 0) {
lcd.print(" off");
} else {
byte min = minutes % 60;
byte hour = minutes / 60;
char timestring[9];
snprintf_P(timestring,
sizeof(timestring),
PSTR("%02u:%02u"),
hour,
min);
lcd.print(timestring);
}
}