#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
const uint8_t ROWS = 4;
const uint8_t COLS = 4;
char keys[ROWS][COLS] = {
{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '*', '0', '#', 'D' }
};
uint8_t colPins[COLS] = { 8, 7, 6, 5 };
uint8_t rowPins[ROWS] = { 12, 11, 10, 9 };
#define lebarTextLCD 20 + 1//lebar LCD + 1 null terminated
#define menuLevel 2
LiquidCrystal_I2C lcd (0x27,20,4);
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS );
enum mode
{
UInt8,
UInt16,
textDropDown,
subMenu
};
struct Menu
{
char text[lebarTextLCD];
byte tipe;
void *variabel;
uint16_t nilaiMin;
uint16_t nilaiMax;
void *subMenu;
byte jumlahBaris;
};
struct MenuIndex
{
byte index;
Menu *menu;
byte menuLength;
char *dropDown;
byte dropDownLength;
};
//========================================================
//variabel
bool aktif;
byte suhu;
byte kelembapan;
uint16_t intensitasCahaya;
uint16_t aliranUdara;
uint16_t levelSuara;
uint16_t warnaLampu;
byte alarmJam;
byte alarmMenit;
byte alarmDetik;
//Dropdown menu
const char aktifText[][lebarTextLCD] =
{
"0. Tidak ",
"1. Ya ",
};
const char WarnaLampu[][lebarTextLCD] =
{
"1. Mati ",
"2. Dingin ",
"3. Putih ",
"4. Hangat ",
};
//Sub menu
//text tipe variabel nilaiMin nilaiMax submenu jumlahBaris
const Menu menuWaktu[] =
{
{"1. Jam " , UInt8 , &alarmJam , 1 , 24 , 0 , 0 },
{"2. Menit " , UInt8 , &alarmMenit , 0 , 59 , 0 , 0 },
{"3. Detik " , UInt8 , &alarmDetik , 0 , 59 , 0 , 0 },
};
//Menu utama
const Menu menuUtama[] =
{
{"1.Aktif " , textDropDown , &aktif , false , true , &aktifText , 2 }, //"1.Aktif ",
{"2.Suhu /\xDF\x43 " , UInt8 , &suhu , 28 , 36 , 0 , 0 }, //"2.Suhu ",celcius
{"3.Kelembapan /% " , UInt8 , &kelembapan , 50 , 100 , 0 , 0 }, //"3.Kelembapan ",%
{"4.Int Cahaya /lm" , UInt16 , &intensitasCahaya , 5 , 300 , 0 , 0 }, //"4.Inten. Cahaya ",lm
{"5.Aliran udara " , UInt8 , &aliranUdara , 0 , 5 , 0 , 0 }, //"5.Aliran udara ",m/s
{"6.Level suara/db" , UInt16 , &levelSuara , 30 , 120 , 0 , 0 }, //"6.Level suara ",db tangisan
{"7.Warna lampu " , textDropDown , &warnaLampu , 1 , 3 , &WarnaLampu , 4 }, //"7.Warna lampu ",
{"8.Alarm >" , subMenu , 0 , 1 , 3 , &menuWaktu , 3 }, //"7.Setting waktu ",
};
//========================================================
MenuIndex menuIndex[menuLevel];
long millismenuText;
String menuEntriNilai;
int8_t levelMenu = -1;
bool entriNilai;
byte menuTextIndex;
char *judulMenu;
byte judulMenuTampil;
byte lcdEntriPos;
void setup()
{
Serial.begin(9600);
lcd.begin(20, 4);
menuIdle();
millismenuText = millis();
}
void menuIdle()
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Tekan * untuk");
lcd.setCursor(0, 1);
lcd.print("masuk ke menu");
}
void loop()
{
cekMenu();
if(Serial.available())
{
if(toupper(Serial.read()) == 'H')
{
Serial.println("--------------------------");
Serial.print("aktif = ");
Serial.println(aktif);
Serial.print("suhu = ");
Serial.println(suhu);
Serial.print("kelembapan = ");
Serial.println(kelembapan);
Serial.print("intensitasCahaya = ");
Serial.println(intensitasCahaya);
Serial.print("aliranUdara = ");
Serial.println(aliranUdara);
Serial.print("levelSuara = ");
Serial.println(levelSuara);
Serial.print("warnaLampu = ");
Serial.println(warnaLampu);
Serial.print("alarmJam = ");
Serial.println(alarmJam);
Serial.print("alarmMenit = ");
Serial.println(alarmMenit);
Serial.print("alarmDetik = ");
Serial.println(alarmDetik);
Serial.println("--------------------------");
Serial.println();
}
}
}
void cekMenu()
{
char key = keypad.getKey();
if (key)
{
if(key == '*')
{
if(entriNilai)
{
uint16_t nilaiBaru = menuEntriNilai.toInt();
if((nilaiBaru >= menuIndex[levelMenu].menu[menuIndex[levelMenu].index].nilaiMin) && (nilaiBaru <= menuIndex[levelMenu].menu[menuIndex[levelMenu].index].nilaiMax))
{
switch(menuIndex[levelMenu].menu[menuIndex[levelMenu].index].tipe)
{
case UInt8:
*(uint8_t*)menuIndex[levelMenu].menu[menuIndex[levelMenu].index].variabel = nilaiBaru;
break;
case UInt16:
*(uint16_t*)menuIndex[levelMenu].menu[menuIndex[levelMenu].index].variabel = nilaiBaru;
break;
case textDropDown:
menuIndex[levelMenu].dropDownLength = 0;
*(byte*)menuIndex[levelMenu].menu[menuIndex[levelMenu].index].variabel = nilaiBaru;
menuTextIndex = 0;
break;
}
lcd.clear();
lcd.print(judulMenu);
lcd.setCursor(0, 1);
lcd.print(nilaiBaru);
lcd.print(" disimpan");
delay(1000);
}
entriNilai = false;
}
else if(levelMenu == -1)
{
levelMenu = 0;
}
else
{
levelMenu--;
}
displayMenu();
}
else if(key == '#')
{
if(entriNilai)
{
entriNilai = false;
displayMenu();
}
else if(levelMenu >= 0)
{
levelMenu--;
displayMenu();
}
}
else if((key >= '0') || (key <= '9') )
{
if(entriNilai)
{
menuEntriNilai += key;
lcd.setCursor(lcdEntriPos++, 1);
lcd.print(key);
}
else if((key != '0') && (key - '0' <= menuIndex[levelMenu].menuLength))//pilihan menu
{
menuIndex[levelMenu].index = key - '1';
judulMenu = menuIndex[levelMenu].menu[menuIndex[levelMenu].index].text;
lcd.clear();
lcd.print(judulMenu);
lcd.setCursor(0, 1);
lcdEntriPos = 8;
judulMenuTampil = 0;
uint16_t nilaiUInt8;
uint16_t nilaiUInt16;
uint16_t nilaiUInt32;
int16_t nilaiFloat;
menuEntriNilai = "";
entriNilai = true;
switch(menuIndex[levelMenu].menu[menuIndex[levelMenu].index].tipe)
{
case UInt8:
lcd.print(*(byte*)menuIndex[levelMenu].menu[menuIndex[levelMenu].index].variabel);
lcd.print(" ==> ");
break;
case UInt16:
lcd.print(*(uint16_t*)menuIndex[levelMenu].menu[menuIndex[levelMenu].index].variabel);
lcd.print(" ==> ");
break;
case textDropDown:
menuIndex[levelMenu].dropDownLength = menuIndex[levelMenu].menu[menuIndex[levelMenu].index].jumlahBaris;
menuIndex[levelMenu].dropDown = menuIndex[levelMenu].menu[menuIndex[levelMenu].index].subMenu;
menuTextIndex = 0;
lcd.print(*(uint8_t*)menuIndex[levelMenu].menu[menuIndex[levelMenu].index].variabel);
lcd.print(" ==> ");
delay(1000);
break;
case subMenu:
levelMenu++;
menuIndex[levelMenu].index = 0;
menuIndex[levelMenu].menu = menuIndex[levelMenu-1].menu[menuIndex[levelMenu-1].index].subMenu;
menuIndex[levelMenu].menuLength = menuIndex[levelMenu-1].menu[menuIndex[levelMenu-1].index].jumlahBaris;
menuIndex[levelMenu].dropDownLength = 0;
menuTextIndex = 0;
entriNilai = false;
break;
}
}
}
}
if(millis() - millismenuText > 1000)
{
millismenuText = millis();
if(menuIndex[levelMenu].dropDownLength != 0)
{
if(menuTextIndex >= menuIndex[levelMenu].dropDownLength)
{
menuTextIndex = 0;
}
lcd.setCursor(0, 1);
lcd.print(menuIndex[levelMenu].dropDown + (menuTextIndex++ * (lebarTextLCD)));
}
if(entriNilai)
{
lcd.setCursor(0, 0);
if((judulMenuTampil % 3) == 0)
{
lcd.print(judulMenu);
}
else if((judulMenuTampil % 3) == 1)
{
lcd.print("* untuk simpan ");
}
else
{
lcd.print("# untuk batal ");
}
judulMenuTampil++;
}
else if(levelMenu != -1)
{
if(menuTextIndex >= menuIndex[levelMenu].menuLength)
{
menuTextIndex = 0;
}
lcd.setCursor(0, 1);
lcd.print(menuIndex[levelMenu].menu[menuTextIndex++].text);
}
}
}
void displayMenu()
{
if(levelMenu == -1)
{
menuIdle();
menuIndex[levelMenu].dropDownLength = 0;
}
else if(levelMenu == 0)
{
menuIndex[levelMenu].index = 0;
menuIndex[levelMenu].menu = menuUtama;
menuIndex[levelMenu].menuLength = sizeof(menuUtama)/sizeof(menuUtama[0]);
menuIndex[levelMenu].dropDownLength = 0;
menuTextIndex = 0;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Pilih [1..");
lcd.print(sizeof(menuUtama)/sizeof(menuUtama[0]));
lcd.print("]");
}
else
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(menuIndex[levelMenu-1].menu[menuIndex[levelMenu-1].index].text);
menuIndex[levelMenu].dropDownLength = 0;
}
}