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

#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:


//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:

#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:

#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:

#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: