Dinamik menu dan submenu dengan keypad dan lcd 16×2 menggunakan Arduino

Dalam perancangan perangkat berbasis arduino yang mudah beradaptasi dengan kondisi baru haruslah memiliki sistem setingan variabel yang dinamis. Untuk keperluan setting variabel dinamis ini perangkat bisa dilengkapi panel potensio atau sistem menu yang bisa melakukan setting variabel interaktif. Membuat menu arduino bisa diterapkan secara dinamik dengan menggunakan database / tabel menu. Database menu dibuat fleksibel sehingga pengaturan variabel bisa dikelompokkan dalam sub menu.

Kelebihan database menu dinamis dengan arduino:

  1. Template menu arduino yang fleksibel, bisa ditambah/kurang/edit dengan mudah
  2. Sub-menu yang tak terbatas
  3. Akses alamat variabel yang dinamis sehingga mampu menjangkau ratusan variabel
  4. Tampilan skroll texk bagi item-menu
  5. Respon cepat
  6. Nilai variabel langsung bisa di potong dalam batas min dan max
  7. Menu interaktif arduino

Video submenu Arduino dinamis:

Membuat Menu arduino serta submenu-nya membutuhkan komponen :

  1. Arduino Mega
  2. Membrane keypad 3×4
  3. LCD 16×2/1602

skema arduino menu:

Sebelum digunakan sistem menu fleksibel ini dengan mudah dapat dikonfigurasi sesuai kebutuhan, terutama menu utama dan submenu-submenunya. Kolom text, tipe, alamat variabel, nilai minimal variabel, minai maksimum variabel, alamat submenu dan ukuran submenu harus dikonfigurasi dengan benar.

Struktur menu dibuat fleksibel sehingga bisa menukar urutan/pindah posisi dengan mudah, selain itu juga mudah untuk diedit, dihapus, disisip.

Sistem menu di buat dinamik sehingga cocok digunakan untuk semua perangkat. untuk membangun sebuah menu bisa mengisi item-item berikut:

1
2
3
4
5
6
7
8
9
10
11
12
//text                          tipe            variabel          nilaiMin nilaiMax submenu      jumlahBaris
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  ",
};

penjelasan item menu:

  1. Text, yaitu teks yang akan ditampilkan oleh lcd
  2. Tipe, adalah jenis data/submenu antara lain : UInt8, UInt16, textDropDown, subMenu
  3. variabel, menyimpan alamat variabel yang akan disetting
  4. nilaiMin, merupakan nilai minimum variabel
  5. nilaiMax, merupakan nilai maksimum variabel
  6. Submenu, diisi jika item ini bertipe sub-menu yang diisi dengan alamat menu yang akan dijadikan submenu
  7. jumlahBaris merupakan jumlah baris dari submenu yang berkaitan

berikut koding/sketch lengkapnya:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
#include <Keypad.h>
#include <LiquidCrystal.h>
 
const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
 
//========================================================
//pin
byte rowPins[ROWS] = {23, 25, 27, 29};
byte colPins[COLS] = {31, 33, 35};
 
LiquidCrystal lcd(53, 51, 49, 47, 45, 43);
 
//Variabel lcd dan menu
#define lebarTextLCD 16 + 1//lebar LCD + 1 null terminated
#define menuLevel 2
//========================================================
 
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);
  Serial.println("Dinamik menu tak terbatas sub-menu dengan keypad 3x4 dan arduino");
  Serial.println("(Aplikasi Inkubator)");
  Serial.println("entri/kirim 'h' untuk melihat nilai variabel");
  Serial.println("https://www.project.semesin.com/");
 
  lcd.begin(16, 2);
  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;
  }
}

Library yang digunakan dalam sketch Arduino menu:

  1. Keypad.zip

Pengujian menu dinamik Arduino:

  1. tekan ‘*’ untuk masuk ke menu
  2. tekan ‘*’ pada saat menu aktif berfungsi untuk simpan dan kembali
  3. tekan ‘#’ untuk kembali ke level menu diatasnya
  4. Jika entri nilai valid akan muncul pesan ‘disimpan’

9 thoughts on “Dinamik menu dan submenu dengan keypad dan lcd 16×2 menggunakan Arduino

  1. kalo saya pake lcd 20×4 itu dirubah dimananya aja yah? saya rubah disini malah ga muncul apa apa yah?

    #include
    #include
    #include

    const byte ROWS = 4; //four rows
    const byte COLS = 4; //three columns
    char keys[ROWS][COLS] = {
    {‘1′,’4′,’7′,’*’},
    {‘2′,’5′,’8′,’0’},
    {‘3′,’6′,’9′,’#’},
    {‘A’,’B’,’C’,’D’}
    };

    //========================================================
    //pin
    byte rowPins[COLS] = {7,6,5,4}; //connect to the row pinouts of the keypad
    byte colPins[ROWS] = {11,10,9,8}; //connect to the column pinouts of the keypad

    LiquidCrystal_I2C lcd(0x27,20,4); //0x27 is the i2c address, while 16 = columns, and 4 = rows.

    //Variabel lcd dan menu
    #define lebarTextLCD 20 + 1//lebar LCD + 1 null terminated
    #define menuLevel 4
    //========================================================

    Yang Variabel lcd dan menu buat apa yah?

Leave a Reply

Your email address will not be published. Required fields are marked *