Input keyboard untuk LCD 16×2 menggunakan Arduino

Keyboard dan mouse merupakan perangkat PC (personal computer) yang berfungsi sebagai interface atau perantara antara pengguna dan pc. Perangkat ini umumnya terhubung menggunakan interface usb, akan tetapi umumnya keyboard dan mouse juga memiliki interface PS2 dan AT Bus.

PS2

PS2 atau Personal System/2 adalah salah satu protokol komunikasi antara komputer dan perangkat lain yang dikembangkan pada tahun 1987. Untuk keyboard dan mouse standar soket PS2-nya berbentuk seperti ini :

AT Bus

AT atau advanced technology bus dikembangkan pada tahun 1984. Standar interface AT Bus untuk keyboad berbentuk seperti ini:

USB Keyboard / mouse

Saat sekarang keyboad dan mouse dengan interface PS2 dan AT bus sudah tidak ditemukan yang digantikan dengan soket dengan interface USB. Namun kebanyakan keyboard dan mouse masih mendukung interface PS2 dengan kombinasi sebagai berikut :

Skema penggunaan keyboard dan mouse sebagai input

contoh sketch/program arduino menggunakan keyboard

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
#include <PS2_Semesin.h>
#include <LiquidCrystal_I2C.h>
#include <PS2Code.h>
 
#define keyboardDATAPIN   4
#define keyboardClockPIN  3
 
#define MAX_COL 16
#define MAX_ROW  2
int8_t cols = 0;
int8_t rows = 0;
 
LiquidCrystal_I2C lcd(0x3F, MAX_COL, MAX_ROW);
PS2 keyboard;
 
void setup()
{
  Serial.begin(9600);
  Serial.println("Keyboard arduino dengan tampilan I2C LCD");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();
 
  Wire.begin();
  Wire.beginTransmission(0x3F);
  if (Wire.endTransmission())
  {
    lcd = LiquidCrystal_I2C(0x27, 16, 2);
  }
  lcd.begin();
  lcd.backlight();
 
  keyboard.begin( keyboardDATAPIN, keyboardClockPIN );
  keyboard.setNoBreak(1);
  keyboard.setNoRepeat( 1 );
}
 
 
void loop()
{
  if ( keyboard.available() )
  {
    char keyboardData = keyboard.read();
    if(keyboardData >= ' ' && keyboardData <= '~')
    {
      lcd.print(keyboardData);
      cols++;
      if ( cols >= MAX_COL )
      {
        cols = 0;
        rows++;
        if ( rows >= MAX_ROW )
        {
          rows = 0;
        }
      }
      lcd.setCursor( cols, rows );
      Serial.print(keyboardData);
    }
    else
    {
      Serial.println();
      Serial.print("Special char = ");
      Serial.println(keyboardData,HEX);
      switch ( keyboardData )
      {
        case PS2_KEY_ENTER:
        case PS2_KEY_KP_ENTER:
          cols = 0;
          rows++;
          if ( rows >= MAX_ROW )
            rows = 0;
          break;
        case PS2_KEY_PGDN:
          rows = MAX_ROW - 1;
          break;
        case PS2_KEY_PGUP:
          rows = 0;
          break;
        case PS2_KEY_L_ARROW:
          cols--;
          if ( cols < 0 )
          {
            cols = MAX_COL - 1;
            rows--;
            if ( rows < 0 )
              rows = MAX_ROW - 1;
          }
          break;
        case PS2_KEY_R_ARROW:
          cols++;
          if ( cols >= MAX_COL )
          {
            cols = 0;
            rows++;
            if ( rows >= MAX_ROW )
              rows = 0;
          }
          break;
        case PS2_KEY_UP_ARROW:
          rows--;
          if ( rows < 0 )
            rows = 0;
          break;
        case PS2_KEY_DN_ARROW:
          rows++;
          if ( rows >= MAX_ROW )
            rows = MAX_ROW - 1;
          break;
        case PS2_KEY_BS:
          cols--;
          if ( cols < 0 )
          {
            cols = MAX_COL - 1;
            rows--;
            if ( rows < 0 )
              rows = MAX_ROW - 1;
          }
          lcd.setCursor( cols, rows );
          lcd.write( ' ' );
          break;
        case PS2_KEY_HOME:
          cols = 0;
          rows = 0;
          break;
        case PS2_KEY_END:
          cols = MAX_COL - 1;
          rows = MAX_ROW - 1;
          break;
      }
      lcd.setCursor( cols, rows );
    }
  }
}

library LiquidCrystal-I2C.zip, PS2_Semesin.zip

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’

Pengaturan alarm dengan arduino dan RTC DS1307 melalui 4 tombol

Alarm berfungsi sebagai pengingat atau pemberitahu baik melalui visual dan suara. Biasanya alarm diaktifkan pada waktu-waktu tertentu sesuai kebutuhan, adakalanya dalam satu hari ada beberapa waktu alarm diaktifkan.

Dalam desain ini saya hanya menggunakan satu entri waktu alarm yang bisa diatur dengan 4 (empat) tombol, fungsi masing-masing tombol adalah:

  1. tombol kiri (M) untuk menu, tekan pertama untuk pengaturan jam, kedua untuk menit, ketiga untuk detik dan ke-empat untuk kembali.
  2. tombol kanan (E) untuk exit/langsung kembali jika sudah dalam menu.
  3. tombol atas untuk tambah
  4. tombol bawah untuk kurang

breadboard:

komponen yang digunakan:

  1. Arduino Uno
  2. LCD matrik 16×2
  3. I2C to LCD PCF8574
  4. RTC DS1307
  5. Buzzer
  6. Tombol 4bh

sketch/program:

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
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <EEPROM.h>
 
//pin
byte tombolUp = 9;
byte tombolDn = 10;
byte tombolMinus = 11;
byte tombolPlus = 8;
byte buzzer = 13;
 
LiquidCrystal_I2C  lcd(0x3F, 16, 2);
byte detikTerakhir = 60;
 
int alarmJamAddr = 0;
int alarmMenitAddr = 1;
int alarmDetikAddr = 2;
 
byte alarmJam;
byte alarmMenit;
byte alarmDetik;
 
bool alarmStatus = false;
unsigned long millisMulai;
unsigned long millisAlarmMulai;
 
bool buzzStatus;
 
uint16_t jedaBuzzer = 1000;
uint16_t waktuAlarm = 10000;//Alarm 10 detik
 
byte menu = 0;
byte menuLCDPos = 8;
byte temp;
 
void setup()
{
  Serial.begin(9600);
  Serial.println("Setting alarm menggunakan Arduino dan RTC1307 melalui 4 push button");
  Serial.println("https://www.project.semesin.com");
 
  pinMode(buzzer, OUTPUT);
  pinMode(tombolUp, INPUT_PULLUP);
  pinMode(tombolDn, INPUT_PULLUP);
  pinMode(tombolMinus, INPUT_PULLUP);
  pinMode(tombolPlus, INPUT_PULLUP);
 
  Wire.begin();
  Wire.beginTransmission(0x3F);
  if (Wire.endTransmission())
  {
    lcd = LiquidCrystal_I2C(0x27, 16, 2);
  }
  lcd.begin();
  lcd.backlight();
  lcd.setBacklight(HIGH);
 
  lcd.setCursor (0, 0);
  lcd.print("Waktu:    :  :  ");
  lcd.setCursor(0, 1);
  lcd.print("Alarm:    :  :  ");
 
  readEEPROMDataWaktu();
 
  lcd.setCursor(8, 1);
  if (alarmJam < 10)lcd.print('0');
  lcd.print(alarmJam);
  lcd.setCursor(11, 1);
  if (alarmMenit < 10)lcd.print('0');
  lcd.print(alarmMenit);
  lcd.setCursor(14, 1);
  if (alarmDetik < 10)lcd.print('0');
  lcd.print(alarmDetik);
}
 
void loop()
{
  tmElements_t tm;
 
  do
  {
    lcd.setCursor(menuLCDPos, 1);
    switch (bacaTombol())
    {
      case '+':
        temp = EEPROM.read(menu - 1);
        if (((temp >= 23) && (menu == 1)) || ((temp >= 59) && (menu > 1)))
          temp = 0;
        else
          temp++;
        EEPROM.write(menu - 1, temp);
        if (temp < 10)lcd.print('0');
        lcd.print(temp);
        break;
      case '-':
        temp = EEPROM.read(menu - 1);
        if ((temp == 0) && (menu == 1))
          temp = 23;
        else if ((temp == 0) && (menu > 1))
          temp = 59;
        else
          temp--;
        EEPROM.write(menu - 1, temp);
        if (temp < 10)lcd.print('0');
        lcd.print(temp);
        break;
      case 'M':
        menu++;
        if (menu == 1)
        {
          menuLCDPos = 8;
          temp = alarmJam;
          lcd.blink();
        }
        else if (menu == 2)
        {
          menuLCDPos = 11;
        }
        else if (menu == 3)
        {
          menuLCDPos = 14;
        }
        if (menu == 4)
        {
          menu = 0;
          lcd.noBlink();
          readEEPROMDataWaktu();
        }
        break;
      case 'E':
        menu = 0;
        lcd.noBlink();
        readEEPROMDataWaktu();
        break;
    }
  }
  while (menu);
 
  if (alarmStatus)
  {
    if (millisMulai + jedaBuzzer < millis())
    {
      buzzStatus = !buzzStatus;
      digitalWrite(buzzer, buzzStatus);
      millisMulai = millis();
    }
    if (millisAlarmMulai + waktuAlarm < millis())
    {
      alarmStatus = false;
      digitalWrite(buzzer, LOW);
    }
  }
 
  if (RTC.read(tm)) {
    if (tm.Second != detikTerakhir)
    {
      lcd.setCursor(8, 0);
      if (tm.Hour < 10)lcd.print('0');
      lcd.print(tm.Hour);
      lcd.setCursor(11, 0);
      if (tm.Minute < 10)lcd.print('0');
      lcd.print(tm.Minute);
      lcd.setCursor(14, 0);
      if (tm.Second < 10)lcd.print('0');
      lcd.print(tm.Second);
 
      if ((alarmJam == tm.Hour) && (alarmMenit == tm.Minute) && (alarmDetik == tm.Second))
      {
        alarmStatus = true;
        millisMulai = millis();
        millisAlarmMulai = millisMulai;
      }
 
      detikTerakhir = tm.Second;
    }
  }
}
 
char bacaTombol()
{
  char tombol = ' ';
  if (!digitalRead(tombolUp))
  {
    tombol = '+';
  }
  else if (!digitalRead(tombolDn))
  {
    tombol = '-';
  }
  else if (!digitalRead(tombolMinus))
  {
    tombol = 'M';
  }
  else if (!digitalRead(tombolPlus))
  {
    tombol = 'E';//exit
  }
  while (!digitalRead(tombolUp));
  while (!digitalRead(tombolDn));
  while (!digitalRead(tombolMinus));
  while (!digitalRead(tombolPlus));
  delay(200);
 
  return tombol;
}
 
void readEEPROMDataWaktu()
{
  alarmJam = EEPROM.read(alarmJamAddr);
  alarmMenit = EEPROM.read(alarmMenitAddr);
  alarmDetik = EEPROM.read(alarmDetikAddr);
}

 

I2C scanner

apabila ditemui kesulitan dalam mencari alamat I2C dari PCF8574 (modul I2C ke LCD 16×2) gunakan I2C scanner berikut:

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
#include <Wire.h>
  
void setup()
{
  Wire.begin();
  
  Serial.begin(9600);
  Serial.println("\nI2C Scanner");
}
  
void loop()
{
  byte error, address;
  int nDevices;
  
  Serial.println("Scanning...");
  
  nDevices = 0;
  for(address = 1; address < 127; address++ )
  {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
  
    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address<16)
        Serial.print("0");
      Serial.print(address,HEX);
       
      byte addressFull = address << 1;
      Serial.print("(");
      if (addressFull<16)
        Serial.print("0");
      Serial.print(addressFull,HEX);
      Serial.println(")");
  
      nDevices++;
    }
    else if (error==4)
    {
      Serial.print("Unknown error at address 0x");
      if (address<16)
        Serial.print("0");
      Serial.println(address,HEX);
    }   
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");
  
  delay(5000);
}

alamat tulis I2C yang umum:

  • DS1307 = 0xD0
  • AT24C32 = 0xA0 – oxAE

Library: