Menu LCD Arduino dengan keypad

Aplikasi menu arduino memang menarik namun tidak mudah untuk dibuat. Arduino menggunakan menu merupakan aplikasi yang menampilkan sejumlah pilihan sehingga pengguna bisa memilih/merubah pilihannya.

Menu interaktif lebih cocok digunakan apabila sejumlah pilihan tidak bisa ditampilkan dalam satu halaman. Misalnya menampilkan menu pada LCD karakter 16×2 yang hanya bisa menampung 16 karakter setiap barisnya.

Salah satu menu yang sering digunakan adalah menu-menu makanan dan minuman pada penerapan restoran yang menggunakan sistem digital terkoneksi.

Kelebihan Menu I2C LCD Arduino ini adalah:

  1. Tampilan interaktif bergilir setiap 1 detik dan tampil 5 detik ketika hendak dipilih.
  2. Menggunakan keypad 4×4 sehingga lebih lega.

Video menu interaktif arduino:

Dalam perancangan berbasis arduino ini digunakan komponen berikut :

  1. Arduino Uno
  2. LCD 1602 + I2C
  3. Keypad 4×4

skema menu arduino LCD dan keypad:

koding/sketch menu keypad arduino:

#include <LiquidCrystal_I2C.h>
#include <Keypad.h>

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

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

LiquidCrystal_I2C lcd(0x3F, 16, 2);

struct daftarMenu {
  char strMenu[17];
  uint32_t harga;
  bool pilihan;
};

char menuUtama[][17] = {
  "0..9 - Pilih    ",
  "* - Pilih       ",
  "# - Batal       ",
  "A - Makanan     ",
  "B - Minuman     ",
  "C - Total/Pesan ",
  "D - Batal       ",
};
daftarMenu menuMakanan[] = {
  {"1 Nasi Goreng   ", 13000L, false},
  {"2 Mie Goreng    ", 8000L, false},
  {"3 Bihun Goreng  ", 8000L, false},
  {"4 Mie Rebus     ", 6000L, false},
  {"5 Gado-gado     ", 13000L, false},
  {"6 Soto Padang   ", 15000L, false},
  {"7 Sate Padang   ", 18000L, false},
};
daftarMenu menuMinuman[] = {
  {"1 Es Campur     ", 6000L, false},
  {"2 Es Tebak      ", 7500L, false},
  {"3 Es Kosong     ", 2000L, false},
  {"4 Jus Jeruk     ", 6000L, false},
  {"5 Jus Pokat     ", 6500L, false},
  {"6 Kopi          ", 4000L, false},
  {"7 Teh Panas     ", 4000L, false},
  {"8 Teh Telur     ", 8000L, false},
};

int8_t indexMenu = -1;
byte menuLevel = 0;
byte menuLevelSebelumnya = -1;

enum ModeMenu {
  modeMenuMakanan,
  modeMenuMinuman,
};
ModeMenu modeMenu;
#define standarWaktuTampil  1000L
#define LihatWaktuTampil    5000L
#define jumlahMakanan       sizeof(menuMakanan)/sizeof(menuMakanan[0])
#define jumlahMinuman       sizeof(menuMinuman)/sizeof(menuMinuman[0])

uint16_t waktuTampil;
unsigned long millisMulai;

void setup()
{
  Serial.begin(9600);
  Serial.println("Menu LCD Arduino dengan keypad");
  Serial.println("https://www.project.semesin.com/");

  Wire.begin();
  Wire.beginTransmission(0x3F);
  if (Wire.endTransmission())
  {
    lcd = LiquidCrystal_I2C(0x27, 16, 2);
  }
  lcd.begin ();
  lcd.backlight();
  //  tampilanDepan();
  millisMulai = millis();
  resetPilihan();
}

void loop()
{
  char key = keypad.getKey();

  if (key) {
    Serial.println(key);
    switch (key)
    {
      case 'A':
        menuLevel = 1;
        indexMenu = -1;
        modeMenu = modeMenuMakanan;
        break;
      case 'B':
        menuLevel = 1;
        indexMenu = -1;
        modeMenu = modeMenuMinuman;
        break;
      case 'C':
        menuLevel = 2;
        updateMenu();
        break;
      case 'D':
        resetPilihan();
        menuLevel = 0;
        indexMenu = -1;
        updateMenu();
        break;
      case '*':
        if (menuLevel == 2)
        {
          pesananMasuk();
        }
        else
        {
          if (waktuTampil == LihatWaktuTampil)
          {
            if (modeMenu == modeMenuMakanan)
            {
              menuMakanan[indexMenu].pilihan = true;
            }
            else if (modeMenu == modeMenuMinuman)
            {
              menuMinuman[indexMenu].pilihan = true;
            }
            updateMenu();
          }
          else
          {
            waktuTampil = LihatWaktuTampil;
            millisMulai = millis();
          }
        }
        break;
      case '#':
        if (menuLevel == 2)
        {
          menuLevel = 1;
        }
        else
        {
          if (waktuTampil == LihatWaktuTampil)
          {
            if (modeMenu == modeMenuMakanan)
            {
              menuMakanan[indexMenu].pilihan = false;
            }
            else if (modeMenu == modeMenuMinuman)
            {
              menuMinuman[indexMenu].pilihan = false;
            }
            updateMenu();
          }
          else
          {
            waktuTampil = LihatWaktuTampil;
            millisMulai = millis();
          }
        }
        break;
      default:
        indexMenu = key - '1';
        updateMenu();
        millisMulai = millis();
        waktuTampil = LihatWaktuTampil;
        break;
    }
  }

  if (millis() - millisMulai > waktuTampil)
  {
    millisMulai = millis();
    waktuTampil = standarWaktuTampil;
    indexMenu++;
    updateMenu();
    menuLevelSebelumnya = menuLevel;
  }
}
void pesananMasuk()
{
  //Aksi pesanan masuk
  lcd.setCursor(0, 0);
  lcd.println("  Terima Kasih  ");
  lcd.setCursor(0, 1);
  lcd.println("Silahkan tunggu ");
  delay(3000);
  Serial.println("Pesanan masuk!!!");
  menuLevel = 0;
  indexMenu = -1;
}
void updateMenu()
{
  if (menuLevel == 0)
  {
    if (indexMenu == sizeof(menuUtama) / sizeof(menuUtama[0]))
    {
      indexMenu = 0;
    }
    if (menuLevelSebelumnya != menuLevel)
    {
      lcd.clear();
      lcd.print("Selamat Datang");
    }
    tampilMenuUtama(indexMenu);
  }
  else if (menuLevel == 2)
  {
    lcd.setCursor(0, 0);
    lcd.print("Rp. ");
    formatStrHarga(totalPilihan());
    lcd.setCursor(0, 1);
    lcd.print("* Ya   # kembali");
  }
  else if (modeMenu == modeMenuMakanan)
  {
    if (indexMenu >= jumlahMakanan)
    {
      indexMenu = 0;
    }
    tampilMenuMakanan(indexMenu);
  }
  else if (modeMenu == modeMenuMinuman)
  {
    if (indexMenu >= jumlahMinuman)
    {
      indexMenu = 0;
    }
    tampilMenuMinuman(indexMenu);
  }
}
void resetPilihan()
{
  for (byte i = 0; i < jumlahMakanan; i++)
  {
    menuMakanan[i].pilihan = false;
  }
  for (byte i = 0; i < jumlahMinuman; i++)
  {
    menuMinuman[i].pilihan = false;
  }
}
uint32_t totalPilihan()
{
  uint32_t total = 0;
  for (byte i = 0; i < jumlahMakanan; i++)
  {
    if (menuMakanan[i].pilihan)
    {
      total += menuMakanan[i].harga;
    }
  }
  for (byte i = 0; i < jumlahMinuman; i++)
  {
    if (menuMinuman[i].pilihan)
    {
      total += menuMinuman[i].harga;
    }
  }
  return total;
}
void tampilMenuUtama(byte index)
{
  lcd.setCursor(0, 1);
  lcd.print(menuUtama[index]);
}
void tampilMenuMakanan(byte index)
{
  lcd.setCursor(0, 0);
  lcd.print(menuMakanan[index].strMenu);
  lcd.setCursor(0, 1);
  lcd.print("Rp. ");
  formatStrHarga(menuMakanan[index].harga);
  if (menuMakanan[index].pilihan)
  {
    lcd.setCursor(15, 1);
    lcd.print("*");
  }
}
void tampilMenuMinuman(byte index)
{
  lcd.setCursor(0, 0);
  lcd.print(menuMinuman[index].strMenu);
  lcd.setCursor(0, 1);
  lcd.print("Rp. ");
  formatStrHarga(menuMinuman[index].harga);
  if (menuMinuman[index].pilihan)
  {
    lcd.setCursor(15, 1);
    lcd.print("*");
  }
}
void formatStrHarga(uint32_t harga)
{
  String strHarga = String(harga);
  uint8_t panjangStr = strHarga.length();
  uint8_t offset = 3 - (panjangStr % 3);
  for (byte i = 0; i < strHarga.length(); i++)
  {
    lcd.print(strHarga[i]);
    if (!((strHarga.length() + i - offset + 1) % 3))
    {
      if (i != strHarga.length() - 1)
      {
        lcd.print('.');
      }
    }
  }
  for (byte i = 0; i < 16 - 5 - strHarga.length(); i++)
  {
    lcd.print(' ');
  }
}

Library:

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: