Bel sekolah dengan pengaturan melalui tombol

Bel sekolah merupakan pengingat waktu terjadwal yang menandai pergantian antar waktu pelajaran di sekolah. Ber sekolah juga memiliki penjawalan mingguan dan bulanan.

Perangkat bel sekolah otomatis bisa diaplikasikan menggunakan arduino sebagai unit prosesornya, tidak seperti bel sekolah digital berbasis komputer yang menyimpan jadwal dalam harddisk, bel sekolah digital arduino menyimpan data jadwal pelajaran didalam EEPROM. Data Jadwal disimpan dalam format/struktur berikut :

  • Aktif
  • Jam
  • Menit

Guna mengatur data jadwal tersebut, perangkat bel sekolah ini dilengkapi dengan tombol-tombol dengan fungsi [menu], [tambah], [kurang] dan [ok].

serta penggunaan LCD sebagai tampilan waktu sekarang dan tampilan jadwal apabila waktunya telah tepat.

skema bel sekolah dengan pengaturan melalui tombol/button:

komponen yang digunakan :

  1. Arduino uno
  2. RTC DS3231
  3. LCD i2C
  4. push button 4x

sketch/koding bel sekolah arduino :

#define pinTombolMenu       5
#define pinTombolTambah     4
#define pinTombolKurang     3
#define pinTombolOk         2

#include <EEPROM.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SoftwareSerial.h>
#include "RTC_Semesin.h"

LiquidCrystal_I2C lcd(0x3F, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display
RTC_DS3231 rtc;
char namaHari[][7] = {"Minggu", "Senin", "Selasa", " Rabu", "Kamis", "Jum'at", "Sabtu"};
char textMenuUtama[][17] = {
  "Bel masuk +pel 1",
  "Ganti pel 2     ",
  "Bel istirahat   ",
  "Bel masuk +pel 3",
  "Ganti pel 4     ",
  "Bel pulang      ",
  "Set Jam         ",
};
char textAktif[][6] = {"Mati ", "Aktif"};

char bufWaktu[40];

struct Jadwal
{
  bool aktif;
  byte jam;
  byte menit;
};

Jadwal jadwal[6] = {{0, 7, 0}, {0, 7, 0}, {0, 7, 0}, {0, 7, 0}, {0, 7, 0}, {0, 7, 0},} ;
Jadwal jadwalSet;

void setup()
{
  pinMode(pinTombolMenu, INPUT_PULLUP);
  pinMode(pinTombolTambah, INPUT_PULLUP);
  pinMode(pinTombolKurang, INPUT_PULLUP);
  pinMode(pinTombolOk, INPUT_PULLUP);

  Serial.begin (9600);

  Serial.begin(9600);

  if (! rtc.begin()) {
    Serial.println(F("Modul RTC tidak ditemukan"));
    while (1);
  }

  if (rtc.lostPower()) 
  {
    Serial.println(F("Waktu RTC di set ulang"));
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

  lcd.begin();
  // Print a message to the LCD.
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Nama");
  lcd.setCursor(0, 1);
  lcd.print("Judul");

  //  delay(3000);

  if (EEPROM.read(0) != 0x48)
  {
    simpanJadwal();
    EEPROM.write(0, 0x48);
  }
  else
  {
    ambilJadwal();
  }
  Serial.println("Bel sekolah dimulai");
}

byte detikSebelumnya = 60;
byte menitSebelumnya = 60;
byte menuLevel = 0;
byte menuIndex[2];
DateTime setWaktu;

void loop()
{
  DateTime now = rtc.now();
  if (detikSebelumnya != now.detik)
  {
    if (!menuLevel)
    {
      detikSebelumnya != now.detik;
      sprintf(bufWaktu, "Pukul : %02d:%02d:%02d", now.jam, now.menit, now.detik);
      lcd.setCursor(0, 0);
      lcd.print(bufWaktu);
      sprintf(bufWaktu, "%s, %02d/%02d/%02d", namaHari[now.hari - 1], now.tanggal, now.bulan, now.tahun - 2000);
      lcd.setCursor(0, 1);
      lcd.print(bufWaktu);

    }
    if (menitSebelumnya != now.menit)
    {
      menitSebelumnya = now.menit;
      for (byte i = 0; i < 6; i++)
      {
        if (jadwal[i].aktif)
        {
          if ((jadwal[i].jam == now.jam) && (jadwal[i].menit == now.menit))
          {
            lcd.setCursor(0, 0);
            lcd.print("  Bel Sekolah   ");
            lcd.setCursor(0, 1);
            lcd.print(textMenuUtama[i]);
            delay(10000);
          }
        }
      }
    }
  }

  if (!digitalRead(pinTombolMenu))
  {
    delay(100);
    if (!digitalRead(pinTombolMenu))
    {
      if (menuLevel == 0)
      {
        menuLevel = 1;
        menuIndex[0] = 0;
      }
      else
      {
        menuLevel = 0;
      }
      tampilanMenu();
      while (!digitalRead(pinTombolMenu));
    }
  }
  if (!digitalRead(pinTombolTambah))
  {
    delay(100);
    if (!digitalRead(pinTombolTambah))
    {
      if (menuLevel == 1)
      {
        menuIndex[0]++;
        if (menuIndex[0] >= sizeof(textMenuUtama) / sizeof(textMenuUtama[0]))
        {
          menuIndex[0] = 0;
        }
        tampilanMenu();
      }
      if (menuLevel == 2)
      {
        if (menuIndex[0] == 6)
        {
          switch (menuIndex[1])
          {
            case 0:
              setWaktu.jam++;
              if (setWaktu.jam >= 24)
              {
                setWaktu.jam = 0;
              }
              break;
            case 1:
              setWaktu.menit++;
              if (setWaktu.menit >= 24)
              {
                setWaktu.menit = 0;
              }
              break;
            case 2:
              setWaktu.detik++;
              if (setWaktu.detik >= 60)
              {
                setWaktu.detik = 0;
              }
              break;
          }
        }
        else
        {
          switch (menuIndex[1])
          {
            case 0:
              jadwalSet.aktif = !jadwalSet.aktif;
              break;
            case 1:
              jadwalSet.jam++;
              if (jadwalSet.jam >= 24)
              {
                jadwalSet.jam = 0;
              }
              break;
            case 2:
              jadwalSet.menit++;
              if (jadwalSet.menit >= 60)
              {
                jadwalSet.menit = 0;
              }
              break;
          }
        }
        tampilanMenu();
      }
      delay(100);
    }
  }
  if (!digitalRead(pinTombolKurang))
  {
    delay(100);
    if (!digitalRead(pinTombolKurang))
    {
      if (menuLevel == 1)
      {
        if (menuIndex[0] == 0)
        {
          menuIndex[0] = sizeof(textMenuUtama) / sizeof(textMenuUtama[0]) - 1;
        }
        else
        {
          menuIndex[0]--;
        }
        tampilanMenu();
      }
      if (menuLevel == 2)
      {
        if (menuIndex[0] == 6)
        {
          switch (menuIndex[1])
          {
            case 0:
              if (setWaktu.jam == 0)
              {
                setWaktu.jam = 23;
              }
              else
              {
                setWaktu.jam--;
              }
              break;
            case 1:
              if (setWaktu.menit == 0)
              {
                setWaktu.menit = 59;
              }
              else
              {
                setWaktu.menit--;
              }
              break;
            case 2:
              if (setWaktu.detik == 0)
              {
                setWaktu.detik = 59;
              }
              else
              {
                setWaktu.detik--;
              }
              break;
          }
        }
        else
        {
          switch (menuIndex[1])
          {
            case 0:
              jadwalSet.aktif = !jadwalSet.aktif;
              break;
            case 1:
              if (jadwalSet.jam == 0)
              {
                jadwalSet.jam = 23;
              }
              else
              {
                jadwalSet.jam--;
              }
              break;
            case 2:
              if (jadwalSet.menit == 0)
              {
                jadwalSet.menit = 59;
              }
              else
              {
                jadwalSet.menit--;
              }
              break;
          }
        }
        tampilanMenu();
      }
      delay(100);
    }
  }
  if (!digitalRead(pinTombolOk))
  {
    delay(100);
    if (!digitalRead(pinTombolOk))
    {
      if (menuLevel == 1)
      {
        menuLevel = 2;
        menuIndex[1] = 0;
        if (menuIndex[0] == 6)
        {
          setWaktu = rtc.now();
          tampilanMenu();
          lcd.setCursor(4, 1);
          lcd.blink();
        }
        else
        {
          jadwalSet.aktif = jadwal[menuIndex[0]].aktif;
          jadwalSet.jam = jadwal[menuIndex[0]].jam;
          jadwalSet.menit = jadwal[menuIndex[0]].menit;
          tampilanMenu();
          lcd.setCursor(2, 1);
          lcd.blink();
        }
      }
      else if (menuLevel == 2)
      {
        menuIndex[1]++;
        if (menuIndex[0] == 6)
        {
          switch (menuIndex[1])
          {
            case 1:
              tampilanMenu();
              lcd.setCursor(7, 1);
              lcd.blink();
              break;
            case 2:
              tampilanMenu();
              lcd.setCursor(10, 1);
              lcd.blink();
              break;
            case 3:
              menuLevel = 1;
              rtc.adjust(setWaktu);
              tampilanMenu();
              lcd.noBlink();
              break;
          }
        }
        else
        {
          switch (menuIndex[1])
          {
            case 1:
              tampilanMenu();
              lcd.setCursor(8, 1);
              lcd.blink();
              break;
            case 2:
              tampilanMenu();
              lcd.setCursor(11, 1);
              lcd.blink();
              break;
            case 3:
              menuLevel = 1;
              jadwal[menuIndex[0]].aktif = jadwalSet.aktif;
              jadwal[menuIndex[0]].jam = jadwalSet.jam;
              jadwal[menuIndex[0]].menit = jadwalSet.menit;
              simpanJadwal();
              lcd.noBlink();
              tampilanMenu();
              break;
          }
        }
        while (!digitalRead(pinTombolOk));
        delay(500);
      }
    }
  }
}
void tampilanMenu()
{
  if (menuLevel == 1)
  {
    lcd.setCursor(0, 0);
    lcd.print("   Menu Utama   ");
    lcd.setCursor(0, 1);
    lcd.print(textMenuUtama[menuIndex[0]]);
  }
  else if (menuLevel == 2)
  {
    if (menuIndex[0] == 6)
    {
      lcd.setCursor(0, 0);
      lcd.print(textMenuUtama[menuIndex[0]]);
      sprintf(bufWaktu, "    %02d:%02d:%02d    ", setWaktu.jam, setWaktu.menit, setWaktu.detik);
      lcd.setCursor(0, 1);
      lcd.print(bufWaktu);
    }
    else
    {
      lcd.setCursor(0, 0);
      lcd.print(textMenuUtama[menuIndex[0]]);
      sprintf(bufWaktu, "  %s %02d:%02d   ", textAktif[jadwalSet.aktif], jadwalSet.jam, jadwalSet.menit);
      lcd.setCursor(0, 1);
      lcd.print(bufWaktu);
    }
  }
}

void ambilJadwal()
{
  byte *alamat = (byte*)jadwal;
  for (byte i = 1; i < sizeof(jadwal) + 1; i++)
  {
    *alamat++ = EEPROM.read(i);
  }
}
void simpanJadwal()
{
  byte *alamat = (byte*)jadwal;
  for (byte i = 1; i < sizeof(jadwal) + 1; i++)
  {
    EEPROM.update(i, *alamat++);
  }
}

library yang digunakan:

Menampilkan data audio dari sound card dengan GUI Matlab

Matlab menyedian fasilitas pengambilan data audio langsung dari hardware soundcard (internal/external) menggunakan system audio toolbox atau data acquisition toolbox. Namun toolbox tersebut hanya mendukung beberapa jenis soundcard saja.

Jika hardware tidak didukung dan pengambilan data audio realtime bisa dikesampingkan, maka pengambilan data audio melalui mic/line in masih bisa dilakukan dengan fungsi standar yang disediakan matlab.

Dalam proyek ini digunakan fungsi-fungsi utama berikut:

  1. audiodevinfo, bertugas mengambil informasi perangkat/hardware audio input seperti mic dan line in. Daftar perangkat masukan suara ini ditampilkan dalam pop-up menu sehingga pengguna bisa memilih perangkat yang akan digunakan sebagai masukan audio.
  2. audiorecorder, merupakan fungsi perekam audio standar matlab yang akan mulai merekam saat diberi perintah start() dan akan berhenti saat diberi perintah stop(). Data suara yang terekam bisa diambil dengan perintah getaudiodata();
  3. Timer, berfungsi mengatur jeda pengambilan data suara.

Metode ini akan memiliki jeda tergantung pengaturan waktu di timer, agar terlihat lebih realtime, perioda timer dibuat lebih kecil dan dalam mode tetap/fixedSpacing. Selain itu waktu proses lanjutan seperti analisa ampltudo/phase, FFT, Filter dan lain-lain dibuat seefektif mungkin sehingga jeda (kehilangan data audio) bisa diperkecil.

berikut koding fungsi merekam data suara matlab yang digunakan:

function varargout = audioSoundcard(varargin)
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
    'gui_Singleton',  gui_Singleton, ...
    'gui_OpeningFcn', @audioSoundcard_OpeningFcn, ...
    'gui_OutputFcn',  @audioSoundcard_OutputFcn, ...
    'gui_LayoutFcn',  [] , ...
    'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end

% --- Executes just before audioSoundcard is made visible.
function audioSoundcard_OpeningFcn(hObject, eventdata, handles, varargin)
% Choose default command line output for AudioSpectrumAnalyzer
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);

% uiwait(handles.figure1);
global guiHandle;
global recorder;
global audioData;

global panjangDataRekaman;
global frequencySampling;
global bitsPerSample;
global audioChannel;

panjangDataRekaman = 8191;
frequencySampling = 22050;
bitsPerSample = 16;
audioChannel = 1;

ylim(handles.axes1, [-0.5, 0.5]);
xlim(handles.axes1, [0, panjangDataRekaman]);
title(handles.axes1, 'Real time');
xlabel(handles.axes1, 'sampling (bit)')
ylabel(handles.axes1, 'Amplitude')
hold(handles.axes1,'on');
guiHandle = guidata(hObject);
set(handles.checkboxAktif,'value', 0);

info = audiodevinfo;
nDevices = audiodevinfo(1);
str = {};
set(handles.popupmenuDevice,'string',str);

for i = 1:nDevices
    str = [str, char(info.input(i).Name)];
end
set(handles.popupmenuDevice,'string',str);
set(handles.checkboxAktif,'value',0);

deviceID = get(handles.popupmenuDevice,'value') - 1;
recorder = audiorecorder(frequencySampling, bitsPerSample, audioChannel, deviceID);
audioData = double.empty();

% --- Outputs from this function are returned to the command line.
function varargout = audioSoundcard_OutputFcn(hObject, eventdata, handles)
varargout{1} = handles.output;


% --- Executes on button press in checkboxAktif.
function checkboxAktif_Callback(hObject, eventdata, handles)
global  timerRekam
T = timerfind;
if isempty(T)
    disp('timer empty')
    timerRekam = timerRekaman();
end

if get(handles.checkboxAktif,'value')
    start(timerRekam)
else
    stop(timerRekam)
end

% --- Executes on selection change in popupmenuDevice.
function popupmenuDevice_Callback(hObject, eventdata, handles)
global recorder
global frequencySampling
global bitsPerSample;
global audioChannel;

deviceID = get(handles.popupmenuDevice,'value') - 1;
recorder = audiorecorder(frequencySampling, bitsPerSample, audioChannel, deviceID);


% --- Executes during object creation, after setting all properties.
function popupmenuDevice_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

% --- Executes during object deletion, before destroying properties.
function figure1_DeleteFcn(hObject, eventdata, handles)
T = timerfind;
if ~isempty(T)
    stop(T)
    delete(T)
end

function t = timerRekaman()
t = timer;
t.StartDelay = 0;
t.TimerFcn = @rekamSuara;
t.StopFcn  = @selesaiRekamSuara;
t.Period = 0.5;
t.ExecutionMode = 'fixedSpacing';

function rekamSuara(mTimer,~)
global recorder
global audioData;
global plotData;
global panjangDataRekaman;

if recorder.isrecording
    stop(recorder);
    delete(plotData);
    audioData = [audioData; getaudiodata(recorder)];
    
    if length(audioData) > panjangDataRekaman
        audioData = audioData(length(audioData)-panjangDataRekaman:length(audioData));
    end
    
    tampilGrafik;
end
disp('AmbilSuara...')
recorder.record;

function selesaiRekamSuara(mTimer,~)
disp('Selesai.')

function tampilGrafik()
global guiHandle;
global audioData;
global plotData;
global panjangDataRekaman;

if ~isempty(audioData)
    plotData = plot(audioData, 'b', 'Parent', guiHandle.axes1);
end

Contah capture audio matlab menggunakan fungsi standar pembacaan soundcard:

file pendukung pengambilan data suara dengan matlab:

audioSoundcard.fig

Papan skor arduino 3 panel P10 dengan kontrol android

Papan skor (score board) adalah papan tempat informasi dan hasil pertandingan yang dapat dilihat oleh semua orang yang berada di arena pertandingan dan penonton.

Papan skor dengan dot matrix (dmd) dengan kontrol android melalui bluetooth ini memiliki fitur :

  1. Tambah-kurang point, skor, waktu, dan ronde pertandingan serta reset point.
  2. Informasi running text yang bisa tampil di tengah pertandingan.
  3. Master reset untuk menginisialisasi semua informasi pertandingan.

Skema papan skor arduino

lepas ‘pin 0’ arduino – bluetooth saat upload sketch:

komponen papan skor bluetooth

  1. Arduino Uno
  2. 3 buah DMD Panel P10
  3. Bluetooth HC-05
  4. Android

Sketch/koding scoring board arduino dengan android melalui bluetooth

#include <SoftwareSerial.h>
#include <DMD_Semesin.h>
#include <fonts/Arial_Black_16.h>
#include <fonts/SystemFont5x7.h>
#include <fonts/SystemFont5x7Gemuk.h>
#include <Wire.h>
#include "RTC_Semesin.h"

//defenisi pin
#define pinOE         9
#define pinSCK        8
#define pinA          6
#define pinB          7

#define DISPLAYS_WIDE 3
#define DISPLAYS_HIGH 1
#define fontSkor Arial_Black_16

SPIDMD dmd(DISPLAYS_WIDE, DISPLAYS_HIGH, pinOE, pinA, pinB, pinSCK);
//SoftwareSerial bluetooth(2, 3);
HardwareSerial *bluetooth = &Serial;
RTC_DS1307 rtc;

byte pointA, pointB, skorA, skorB, ronde, menit, detik = 0;
byte Arial14TengahY;
char strInformasi[200] = "Selamat Datang";
char bufferBluetooth[200];
bool pertandinganBerjalan;
long millisDetik;
long millisEfek;
byte detikSebelumnya = 60;
bool modeInformasi;

EfekMarque efekMarque;
EfekMarqueClear efekMarqueClear;

enum perintah {
  initPerangkat,
  pointAplus,
  pointAminus,
  pointBplus,
  pointBminus,
  skorAplus,
  skorAminus,
  skorBplus,
  skorBminus,
  rondePlus,
  rondeMinus,
  menitPlus,
  menitMinus,
  text,
  resetPoint,
  resetSemua,
  mulai,
};

DMD_TextBox boxSkorA(dmd, 0,0, 28,16);
DMD_TextBox boxSkorB(dmd, 68,0, 28,16);



void setup() {
//  Serial.begin(9600);
  bluetooth->begin(9600);

  dmd.setBrightness(128);
  dmd.selectFont(fontSkor);
  dmd.clearScreen();
  dmd.begin();
  dmd.drawString(0, 0, F("Papan Skor bluetooth"));
  delay(1000);
  dmd.clearScreen();

  efekMarque.mode = nonAktif;
  efekMarque.init = false;
  efekMarque.sumber = sumberRAM;
  efekMarque.alamat = strInformasi;
  efekMarque.kiri = 0;
  efekMarque.atas = 0;
  efekMarque.tinggi = 16;
  efekMarque.lebar = 96;
  efekMarque.step = 1;
  efekMarque.skip = 0;

  efekMarqueClear.mode = nonAktif;
  efekMarqueClear.kiri = 0;
  efekMarqueClear.atasInit = 0;
  efekMarqueClear.atas = 0;
  efekMarqueClear.lebarInit = dmd.width;
  efekMarqueClear.lebar = dmd.width;
  efekMarqueClear.tinggi = dmd.height;
  efekMarqueClear.step = 1;

  tampilanUtama();

  Serial.println("Sistem dimulai");

  millisDetik = millis();
}

void loop() {
  if(millisDetik != millis() / 1000L)
  {
    millisDetik = millis() / 1000L;
    if(pertandinganBerjalan)
    {
      detik++;
      if(detik == 60)
      {
        detik = 0;
        menit++;
      }
    }
  }
  
  if(millisEfek < millis() - 100)
  {
    millisEfek = millis();
    if (efekMarque.mode == XMinus)
    {
      dmd.marqueeXMinus(&efekMarque);
    }
    else if (efekMarqueClear.mode == XMinus)
    {
      dmd.marqueeClearXMinus(&efekMarqueClear);
    }
    else if(modeInformasi)
    {
      modeInformasi = false;
      tampilanUtama();
    }
  }

  if (bluetooth->available())
  {
    byte tokenMulai = bluetooth->read();
    if(tokenMulai == 0xFE)
    {
      delay(2);
      byte perintah = bluetooth->read();
      delay(2);
      byte panjang = bluetooth->read();

      for(uint16_t i=0;i<panjang;i++)
      {
        delay(2);
        char c = bluetooth->read();
        bufferBluetooth[i] = c;
      }
      delay(2);
      byte tokenSelesai = bluetooth->read();
      if(tokenSelesai == 0xFF)
      {
        uint16_t i;
        switch(perintah)
        {
          case initPerangkat:
            bluetooth->write(237);
            break;
          case pointAplus:
            pointA++;
            break;
          case pointAminus:
            pointA--;
            break;
          case pointBplus:
            pointB++;
            break;
          case pointBminus:
            pointB--;
            break;
          case skorAplus:
            skorA++;
            break;
          case skorAminus:
            skorA--;
            break;
          case skorBplus:
            skorB++;
            break;
          case skorBminus:
            skorB--;
            break;
          case rondePlus:
            ronde++;
            break;
          case rondeMinus:
            ronde--;
            break;
          case menitPlus:
            menit++;
            break;
          case menitMinus:
            menit--;
            break;
          case resetPoint:
            pointA = 0;
            pointB = 0;
            break;
          case resetSemua:
            pointA = 0;
            pointB = 0;
            skorA = 0;
            skorB = 0;
            ronde = 0;
            menit = 0;
            detik = 0;
            break;
          case mulai:
            pertandinganBerjalan = true;
            break;
          case text:
            for(i=0;i<panjang;i++)
            {
              strInformasi[i] = bufferBluetooth[i];
            }
            strInformasi[i] = 0;
            dmd.clearScreen();
            dmd.selectFont(fontSkor);
            efekMarqueClear.mode = XMinus;
            efekMarque.init = true;
            efekMarque.alamat = strInformasi;
            efekMarque.mode = XMinus;
            modeInformasi = true;
        
            millisEfek = millis();
            break;
        }
        if(pointA == 255)
          pointA = 0;
        if(pointB == 255)
          pointB = 0;
        if(skorA == 255)
          skorA = 0;
        if(skorB == 255)
          skorB = 0;
        if(ronde == 255)
          ronde = 0;
        if(menit == 255)
          menit = 0;
        
        if (!modeInformasi)
        {
          tampilanUtama();
        }
      }
    }
  }
}
byte bin2bcd(byte val)
{
  return val + 6 * (val / 10);
}

void tampilanUtama()
{
  byte lebarText;
  dmd.selectFont(fontSkor);
  Arial14TengahY = (dmd.height - dmd.fontHeader.height) / 2;

  boxSkorA.clear();
  boxSkorB.clear();
  dmd.drawString((28-lebarText)/2 + 0, Arial14TengahY, String(pointA));
  lebarText = dmd.stringWidth(String(pointB));
  dmd.drawString((28 - lebarText)/2 + 68, Arial14TengahY, String(pointB));
  

  dmd.selectFont(SystemFont5x7);
  char waktu[] = "00:00";
  byte menitBCD = bin2bcd(menit);
  byte detikBCD = bin2bcd(detik);
  waktu[0] = (menitBCD >> 4) + 0x30;
  waktu[1] = (menitBCD & 0x0F) + 0x30;
  waktu[3] = (detikBCD >> 4) + 0x30;
  waktu[4] = (detikBCD & 0x0F) + 0x30;
  lebarText = dmd.stringWidth(waktu);
  dmd.drawString((dmd.width - lebarText)/2, 0, String(waktu));

  dmd.selectFont(SystemFont5x7Gemuk);

  dmd.drawString(28, 8, String(skorA));
  lebarText = dmd.stringWidth(String(skorB));
  dmd.drawString((68 - lebarText), 8, String(skorB));

  lebarText = dmd.stringWidth(String(ronde));
  dmd.drawString((dmd.width - lebarText)/2, 8, String(ronde));
}

screenshot apk papan skor

File papan skor android

 

Properti tabel dinamis app inventor dengan WebViewer

MIT App Inventor 2 menawarkan kemudahan dalam membuat aplikasi apk android. Namun dibalik kesederhanaannya App Inventor memiliki kelemahan pada aplikasi dengan jumlah komponen yang banyak. Penyebabnya adalah area design yang terbatas dan bersifat semi statis.

Perancangan tabel dinamis AI2 ini dapat memudahkan dalam merancang apk aplikasi android dengan fitur:

  1. Komponen/Palette bisa disesuaikan, sementara yang tersedia adalah tabel, judul, label, input checkbox, input select 1-4, input byte, input int, input slider, input float, input text, button, button horizontal, horizontal line.
  2. Palette tabel dengan tipe yang tersedia : label increment, label integer, label text, input checkbox, input select 1-4, input byte, input int, input slider, input float, input text, button.
  3. Penggunaan WebViewer memungkinkan tampilan sizeable atau dapat diperbesar/diperkecil.

Designer Dinamis App Inventor dengan WebViewer

Untuk menggunakan platform ini, buatlah di designer cukup satu komponen saja yaitu : WebViewer

upload file “semesin.html”, file ini berisi script yang berfungsi untuk berkomunikasi antara webViewer dengan apk app Inventor.

<body onload="document.getElementById('content').innerHTML = window.AppInventor.getWebViewString();">
<div id="content"></div>
</body>
<script type="text/javascript">
  function checkboxChange(id, value)
  {
    if (value == true)
    {
      window.AppInventor.setWebViewString(id + ',1');
    }
    else
    {
      window.AppInventor.setWebViewString(id + ',0');
    }
  }
  function inputChange(id, value)
  {
    window.AppInventor.setWebViewString(id + ',' + value);
  }
</script>

fungsi window.AppInventor.getWebViewString() adalah mengambil nilai dari app inventor (dalam format html)

sedangkan window.AppInventor.setWebViewString() berfungsi mengirimkan data ke app inventor.

 

Struktur properti dan tabel dinamis menggunakan App Inventor

Untuk membangun skema properti App Inventor, buatlah list dalam format berikut :

  • List teks
  • List tipe
  • List nilai

Apabila  list tipe bernilai tabel maka list nilai diisi dengan format :

  • List tabel teks
  • List tabel tipe
  • List tabel nilai

berikut ini contoh struktur properti designer dinamis webViewer:

disamping itu perlu juga dibangun mekanisme handler/penanganan khususnya tombol-tombol mirip properti button.onclick().

contoh tampilan membuat tabel dengan mit app inventor :

File membuat tabel dinamis dengan app inventor:

  1. DinamikPropertiWebViewer.aia
  2. DinamikPropertiWebViewer.apk
  3. semesin.html

Bel sekolah arduino dengan kontrol android

Bel sekolah adalah pengingat waktu yang digunakan di sekolah sebagai penanda pergantian jadwal.

Bel sekolah digital bisa di aplikasikan dalam bentuk:

    1. Aplikasi komputer
      adalah aplikasi yang ditanam dalam komputer dan bisa dijalankan secara otomatis. sistem ini mudah dalam pengaturan, memiliki data jadwal sangat besar,  peringatan admin, backup, update suara mudah, perekan suara, kunci aplikasi dll. Kelemahannya adalah komputer dan aplifier suara harus tetap hidup dan bergantung kepada adanya daya listrik/ups termasuk biaya listrik, pesan sponsor pada aplikasi gratis,  biaya pengadaan dan perawatan komputer.
    2. Aplikasi android
      Aplikasi / apk android memiliki sama dengan aplikasi komputer dan aplikasi android dapat terhubung dengan  sistem tata suara melalui bluetooth dan kabel audio. kekurangannya adalah device android harus selalu berada dekat dengan sistem (selalu standby)
    3. Aplikasi perangkat mandiri
      Sistem bel sekolah otomatis dapat dibangun dengan perangkat mandiri menggunakan microkontroller dengan biaya murah dan bisa dikembangkan untuk keperluan lainnya seperti :

      • bisa menggunakan baterai sebagai daya cadangannya
      • Pendeteksi gempa dan peringatan
      • Notifikasi sms ke pengajar
      • Mekanuisme kunci pagar
      • Panggilan melalui microphone yang tertuju langsung ke kelas tertentu
      • dan lain-lain sesuai kebutuhan

      Kekurangan sistem ini adalah ruang penyimpanan data jadwal dan suara terbatas, pengaturan jadwal yang sulit melalui tombol/keypad.

Dalam proyek ini menggabungkan perangkat mandiri dengan platform arduino yang dikombinasikan dengan aplikasi/apk android sebagai kontrolnya.

Skema bel sekolah otomatis dengan kontrol android:

komponen bel sekolah arduino control android:

  • Arduino Uno
  • Bluetooth HC-05
  • RTC DS3231
  • DF Player mini mp3
  • Speaker

tampilan Aplikasi bel sekolah android :

sketch/aplikasi bel sekolah bluettoth android :

#define SQWPin        A3

#include <avr/sleep.h>
#include <SoftwareSerial.h>
#include <DFPlayer_Mini_Mp3.h>
#include "RTC_Semesin.h"
#include <EEPROM.h>

#define tokenEEPROM 0x83
SoftwareSerial bluetooth(2, 3); // RX, TX
SoftwareSerial mp3Serial(4, 5); // RX, TX

struct Waktu
{
  byte jam;
  byte menit;
};

struct TabelMataPelajaran
{
  byte aktif;
  Waktu waktu;
  byte hariAktif;
  byte mingguAktif;
  byte kegiatan;
};

const char kegiatanText[][17] PROGMEM = {
"-",
"Jam Pelajaran 1",
"Jam Pelajaran 2",
"Jam Pelajaran 3",
"Jam Pelajaran 4",
"Jam Pelajaran 5",
"Jam Pelajaran 6",
"Jam Pelajaran 7",
"Jam Pelajaran 8",
"Jam Pelajaran 9",
"Jam Pelajaran 10",
"Jam Pelajaran 11",
"Jam Pelajaran 12",
"Jam Pelajaran 13",
"Jam Pelajaran 14",
"Jam Pelajaran 15",
"Masuk",
"Upacara",
"Istirahat",
"Selesai istirahat",
"Kepramukaan",
"Khusus",
"Jam pelajaran telah selesai, sampai jumpa esok hari",
"Jam pelajaran telah selesai, sampai jumpa minggu depan",
"Jam pelajaran telah selesai, sampai jumpa minggu depan",
};

enum _kegiatan
{
  TidakAda,
  JamPelajaran1,
  JamPelajaran2,
  JamPelajaran3,
  JamPelajaran4,
  JamPelajaran5,
  JamPelajaran6,
  JamPelajaran7,
  JamPelajaran8,
  JamPelajaran9,
  JamPelajaran10,
  JamPelajaran11,
  JamPelajaran12,
  JamPelajaran13,
  JamPelajaran14,
  JamPelajaran15,
  Masuk,
  Upacara,
  Istirahat,
  Istirahat1,
  Istirahat2,
  Istirahat3,
  SelesaiIstirahat,
  Kepramukaan,
  Khusus,
  Pulang,
  PulangJumat,
  PulangSabtu
};
enum PengaturanAndroid
{
  cekAses,
  pengaturanJadwal,
  pengaturanWaktu,

};

volatile bool interupsiDetik;
byte indexMataPelajaran;
RTC_DS3231 rtc;
DateTime now;
bool rtcValid;
byte indexPengaturanJadwal = 0;

#define hariAktifSenin 1<<6
#define hariAktifSelasa 1<<5
#define hariAktifRabu 1<<4
#define hariAktifKamis 1<<3
#define hariAktifJumat 1<<2
#define hariAktifSabtu 1<<1
#define hariAktifMinggu 1<<7

char namaHari[][7] = {"Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jum'at", "Sabtu"};
#define _hariAktif(Sen,Sel,Rab,Kam,Jum,Sab,Mgu) (Mgu<<7)|(Sen<<6)|(Sel<<5)|(Rab<<4)|(Kam<<3)|(Jum<<2)|(Sab<<1)
#define _mingguAktif(Mgu1,Mgu2,Mgu3,Mgu4,Mgu5) (Mgu1<<7)|(Mgu2<<6)|(Mgu3<<5)|(Mgu4<<4)|(Mgu5<<3)
#define _waktu(Jam, Menit) {Jam, Menit}
#define Aktif 1
#define TidakAktif 0

TabelMataPelajaran jadwalBelajar[40];
Waktu waktu;

void setup() {
  pinMode(SQWPin, INPUT_PULLUP);

  Serial.begin(9600);
  Serial.println(F("Bel Sekolah Dengan Kontrol Android"));
  Serial.println(F("https://www.semesin.com/project"));

  mp3Serial.begin(9600);
  bluetooth.begin (9600);
  bluetooth.listen();

  mp3_set_serial (mp3Serial);
  mp3_set_volume (15);

  if (! rtc.begin()) {
    Serial.println(F("Modul RTC tidak ditemukan"));
    while (1);
  }

  if (rtc.lostPower()) {
    Serial.println(F("RTC lost power, lets set the time!"));
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
  rtc.writeSqwPinMode(DS3231_SquareWave1Hz);

  if (EEPROM.read(sizeof(jadwalBelajar)) != tokenEEPROM)
  {
    nilaiAwal();
    EEPROM.write(sizeof(jadwalBelajar), tokenEEPROM);
  }
  ambilSetting();
  Serial.println(F("Sistem bel sekolah dimulai"));
}

void loop() {
  if (digitalRead(SQWPin))
  {
    if (rtcValid)
    {
      rtcValid = false;

      now = rtc.now();

      char bufWaktu[40];
      sprintf(bufWaktu, "%02d:%02d:%02d %s, %02d/%02d/%02d", now.jam, now.menit, now.detik, namaHari[now.hari-1], now.tanggal, now.bulan, now.tahun - 2000);
      Serial.println(bufWaktu);
      

      if (now.detik == 0)
      {

        byte hariKeDiTanggal1 = (((now.hari + 8)  - (now.tanggal % 7)) % 7);
        byte SeninKe = ((now.tanggal + 7 - hariKeDiTanggal1) / 7);
        byte mingguKe = ((now.tanggal + 8 - hariKeDiTanggal1) / 7) + 1;

        
        for (byte i = 0; i < sizeof(jadwalBelajar)/sizeof(TabelMataPelajaran) ; i++)
        {
          if (jadwalBelajar[i].aktif)
          {
            if ((jadwalBelajar[i].waktu.jam == now.jam) &&
                (jadwalBelajar[i].waktu.menit == now.menit) &&
                (jadwalBelajar[i].hariAktif & (1 << (8 - now.hari))) &&
                (jadwalBelajar[i].mingguAktif & (1 << (8 - SeninKe))))
            {
              mp3_play (jadwalBelajar[i].kegiatan);
              Serial.println((__FlashStringHelper *)kegiatanText[jadwalBelajar[i].kegiatan]);
            }
          }
        }
      }
    }
  }
  else
  {
    rtcValid = true;
  }
  cekBluetooth();
}

void cekBluetooth()
{
  uint8_t tokenMulai;
  uint8_t perintah;
  uint8_t parameter;
  uint8_t panjang1;
  uint8_t panjang2;
  char c;
  uint8_t i, j;
  uint8_t tokenSelesai;
  byte bufferSerial[100];
  byte *alamat;

  if (bluetooth.available())
  {
    tokenMulai = bluetoothRead();
    if (tokenMulai == 0xFD)
    {
      panjang1 = bluetoothRead();
      panjang2 = bluetoothRead();

      if (panjang2 == 254 - panjang1)
      {
        if (panjang1 >= sizeof(bufferSerial))
        {
          panjang1 = sizeof(bufferSerial);
        }

        uint16_t timeOut = 0xFFF;
        i = 0;
        do
        {
          if (bluetooth.available())
          {
            c = bluetoothRead();
            bufferSerial[i++] = c;
          }
        } while ((i < panjang1 + 3) && (timeOut--));

        perintah = bufferSerial[0];
        parameter = bufferSerial[1];

        tokenSelesai = bufferSerial[i - 1];
        if (tokenSelesai == 0x00)
        {
          delay(10);
          bluetooth.write(254);
          switch (perintah)
          {
            case cekAses:
              bluetooth.write(1);
              bluetooth.write(254);
              break;
            case pengaturanJadwal:
              memcpy((byte*)&jadwalBelajar[parameter], bufferSerial + 2, sizeof(TabelMataPelajaran));
              if(parameter == (sizeof(jadwalBelajar)/sizeof(TabelMataPelajaran)) - 1)
              {
                simpanSetting();
              }
              break;
            case pengaturanWaktu:
              memcpy((byte*)&now, bufferSerial + 2, sizeof(DateTime));
              rtc.adjust(now);
              break;
          }
        }
        else
        {
          bluetooth.write(252);//data tidak benar
        }
      }
    }
  }
}
byte bluetoothRead()
{
  uint16_t timeOut = 0xFFF;
  while (!bluetooth.available() && timeOut--);
  return bluetooth.read();
}



void nilaiAwal()
{
  byte i = 0;
  jadwalBelajar[i++] = {Aktif, _waktu(  6, 45 ), hariAktifSenin                 , _mingguAktif(1, 0, 0, 0, 0), Upacara};
  jadwalBelajar[i++] = {Aktif, _waktu(  6, 45 ), hariAktifSenin                 , _mingguAktif(0, 1, 1, 1, 1), JamPelajaran1};
  jadwalBelajar[i++] = {Aktif, _waktu(  6, 45 ), _hariAktif(0, 1, 1, 1, 1, 0, 0), _mingguAktif(1, 1, 1, 1, 1), JamPelajaran1};

  jadwalBelajar[i++] = {Aktif, _waktu(  7, 30 ), _hariAktif(1, 1, 1, 1, 0, 0, 0), _mingguAktif(1, 1, 1, 1, 1), JamPelajaran2};
  jadwalBelajar[i++] = {Aktif, _waktu(  8, 15 ), _hariAktif(1, 1, 1, 1, 0, 0, 0), _mingguAktif(1, 1, 1, 1, 1), JamPelajaran3};
  jadwalBelajar[i++] = {Aktif, _waktu(  9, 0  ), _hariAktif(1, 1, 1, 1, 0, 0, 0), _mingguAktif(1, 1, 1, 1, 1), JamPelajaran4};
  jadwalBelajar[i++] = {Aktif, _waktu(  9, 45 ), _hariAktif(1, 1, 1, 1, 0, 0, 0), _mingguAktif(1, 1, 1, 1, 1), Istirahat};
  jadwalBelajar[i++] = {Aktif, _waktu( 10, 15 ), _hariAktif(1, 1, 1, 1, 0, 0, 0), _mingguAktif(1, 1, 1, 1, 1), JamPelajaran5};
  jadwalBelajar[i++] = {Aktif, _waktu( 11, 0  ), _hariAktif(1, 1, 1, 1, 0, 0, 0), _mingguAktif(1, 1, 1, 1, 1), JamPelajaran6};
  jadwalBelajar[i++] = {Aktif, _waktu( 11, 45 ), _hariAktif(1, 1, 1, 1, 0, 0, 0), _mingguAktif(1, 1, 1, 1, 1), Istirahat};
  jadwalBelajar[i++] = {Aktif, _waktu( 12, 30 ), _hariAktif(1, 1, 1, 1, 0, 0, 0), _mingguAktif(1, 1, 1, 1, 1), JamPelajaran7};
  jadwalBelajar[i++] = {Aktif, _waktu( 13, 15 ), _hariAktif(1, 1, 1, 1, 0, 0, 0), _mingguAktif(1, 1, 1, 1, 1), JamPelajaran8};

  jadwalBelajar[i++] = {Aktif, _waktu( 14, 0  ), _hariAktif(1, 1, 1, 0, 0, 0, 0), _mingguAktif(1, 1, 1, 1, 1), JamPelajaran9};
  jadwalBelajar[i++] = {Aktif, _waktu( 14, 45 ), _hariAktif(1, 1, 1, 0, 0, 0, 0), _mingguAktif(1, 1, 1, 1, 1), JamPelajaran10};

  jadwalBelajar[i++] = {Aktif, _waktu( 14, 0  ), hariAktifKamis,            _mingguAktif(1, 1, 1, 1, 1), Kepramukaan};
  jadwalBelajar[i++] = {Aktif, _waktu( 14, 45 ), hariAktifKamis,            _mingguAktif(1, 1, 1, 1, 1), Khusus};

  jadwalBelajar[i++] = {Aktif, _waktu( 15, 30 ), _hariAktif(1, 1, 1, 1, 0, 0, 0), _mingguAktif(1, 1, 1, 1, 1), Pulang};

  jadwalBelajar[i++] = {Aktif, _waktu(  7, 25 ), hariAktifJumat,            _mingguAktif(1, 1, 1, 1, 1), JamPelajaran2};
  jadwalBelajar[i++] = {Aktif, _waktu(  8, 5  ), hariAktifJumat,            _mingguAktif(1, 1, 1, 1, 1), JamPelajaran3};
  jadwalBelajar[i++] = {Aktif, _waktu(  8, 45 ), hariAktifJumat,            _mingguAktif(1, 1, 1, 1, 1), JamPelajaran4};
  jadwalBelajar[i++] = {Aktif, _waktu(  9, 25 ), hariAktifJumat,            _mingguAktif(1, 1, 1, 1, 1), Istirahat};
  jadwalBelajar[i++] = {Aktif, _waktu(  9, 55 ), hariAktifJumat,            _mingguAktif(1, 1, 1, 1, 1), JamPelajaran5};
  jadwalBelajar[i++] = {Aktif, _waktu( 10, 35 ), hariAktifJumat,            _mingguAktif(1, 1, 1, 1, 1), JamPelajaran6};
  jadwalBelajar[i++] = {Aktif, _waktu( 11, 15 ), hariAktifJumat,            _mingguAktif(1, 1, 1, 1, 1), PulangJumat};
  
  simpanSetting();
}
void simpanSetting()
{
  byte *alamatSetting = (byte*)&jadwalBelajar;
  for (byte i = 0; i < sizeof(jadwalBelajar); i++)
  {
    EEPROM.write(i, *alamatSetting++);
  }
}
void ambilSetting()
{
  byte *alamatSetting = (byte*)&jadwalBelajar;
  for (byte i = 0; i < sizeof(jadwalBelajar); i++)
  {
    *alamatSetting++ = EEPROM.read(i);
  }
}

library arduino bel sekolah dengan bluetooth dan aplikasi android yang digunakan :

Suara mp3 bel sekolah arduino dengan bluetooth:

mp3.zip

aplikasi apk android untuk bel sekolah arduino (evaluasi):

Bel_sekolah_v1_evaluasi.apk

cara penggunaan :

  1. Buat rangkaian arduino seperti skema dan upload sketch yang diberikan.
  2. Masukkan file suara dalam kartu memori/SD card (file mp3 dan folder mp3).
  3. install aplikasi bel sekolah v1 evaluasi di android (evaluasi = 10 jadwal yang aktif).

Menu arduino dengan rotary encoder

Rotary encoder (shaft encoder) adalah komponen pengukuran arah putaran. Komponen ini memiliki dua pin keluaran yang menghasilkan sinyal pulsa yang diproses dahulu untuk mendapatkan arah putarannya.

Sinyal rotary encoder:

 

Rotary encoder tersedia dalam bermacam type, dan tipe yang digunakan dalam program ini ada type potensio rotary encoder.

Sinyal keluaran dari type ini terlihat dapat digambarkan sbb:

komponen aplikasi sketch menu rotary encoder berbasis arduino:

  1. Arduino Uno
  2. LCD 16×2 backpack I2C
  3. Momentary Rotary Encoder

Skema arduino menu menggunakan rotary encoder:

 

Sketch menu arduino ini menggunakan external interrupt. untuk arduino uno hanya bisa menggunakan pin 2 dan 3, sedangkan arduino mega bisa menggunakan pin 2, 3, 18, 19, 20 dan 21.

program/aplikasi arduino menu arduino dengan lcd I2c 16×2 menggunakan rotary encoder:

 
#define pinRotaryEncoderCLK     2
#define pinRotaryEncoderDT      3
#define pinRotaryEncoderSwitch  4

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3f, 16, 2); // coba juga alamat 0x27 (tergantung seri back pack)

uint8_t maskSensorA;
uint8_t maskSensorB;
uint8_t *pinSensorA;
uint8_t *pinSensorB;
volatile bool encoderAFlag = 0;
volatile bool encoderBFlag = 0;

int8_t nilaiEncoder = 0;
int nilaiSetting[4];
byte setMode;

void setup() {
  Serial.begin(9600);
  Serial.println("Menu rotary encoder");
  Serial.println("https://www.semesin.com/project/");
  Serial.println();

  pinMode(pinRotaryEncoderCLK, INPUT_PULLUP);
  pinMode(pinRotaryEncoderDT, INPUT_PULLUP);
  pinMode(pinRotaryEncoderSwitch, INPUT_PULLUP);

  lcd.begin();
  lcd.backlight();

  attachInterrupt(digitalPinToInterrupt(pinRotaryEncoderCLK), encoderARising, RISING);
  attachInterrupt(digitalPinToInterrupt(pinRotaryEncoderDT), encoderBRising, RISING);

  maskSensorA  = digitalPinToBitMask(pinRotaryEncoderCLK);
  pinSensorA = portInputRegister(digitalPinToPort(pinRotaryEncoderCLK));
  maskSensorB  = digitalPinToBitMask(pinRotaryEncoderDT);
  pinSensorB = portInputRegister(digitalPinToPort(pinRotaryEncoderDT));

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("A:0     C:0");
  lcd.setCursor(0, 1);
  lcd.print("B:0     D:0");
}

void loop() {
  if (nilaiEncoder != 0)
  {
    Serial.println(nilaiEncoder);
    nilaiSetting[setMode] += nilaiEncoder;
    nilaiEncoder = 0;

    lcd.setCursor(((setMode / 2) * 8) + 2, setMode % 2);
    lcd.print(nilaiSetting[setMode]);
    lcd.print(" ");
  }
  if (!digitalRead(pinRotaryEncoderSwitch))
  {
    delay(50);
    setMode = (setMode + 1) % 4;
    while (!digitalRead(pinRotaryEncoderSwitch));
    Serial.println(setMode);
  }
}

void encoderARising() {
  if ((*pinSensorA & maskSensorA) &&  (*pinSensorB & maskSensorB) && encoderAFlag)
  {
    nilaiEncoder = -1;
    encoderAFlag = false;
    encoderBFlag = false;
  }
  else if (*pinSensorA & maskSensorA)
  {
    encoderBFlag = true;
  }
  EIFR = 0xFF;
}

void encoderBRising() {
  if ((*pinSensorA & maskSensorA) &&  (*pinSensorB & maskSensorB) && encoderBFlag)
  {
    nilaiEncoder = 1;
    encoderAFlag = false;
    encoderBFlag = false;
  }
  else if (*pinSensorB & maskSensorB)
  {
    encoderAFlag = true;
  }
  EIFR = 0xFF;
}

library yang digunakan:

penggunaan aplikasi sketch menu rotary encoder:

  1. tekan knob untuk memilih menu setting
  2. putar knob ke untuk mengganti nilai setting

Dokumentasi menu rotary encoder lcd i2c:

Menu dan submenu LCD TFT dengan arduino

Sistem menu bertingkat (menu dan sub menu) adalah sistem pilihan/pengaturan yang terstruktur dalam kelompok-kelompok (sub menu).

Menu arduino juga bisa diterapkan pada lcd TFT (2.4″). Keunggulan sistem menu ini adalah pengaturan item menu bisa dengan mudah dimodifikasi, karena disusun dalam ‘struct’.

 
komponen yang digunakan:

  1. Arduino Mega 2560
  2. LCD TFT 2.4″

Pengaturan menu

menu dalam program ini menggunakan struktur :

struct Menu
{
  byte tipe;
  void *variabel;
  uint16_t nilaiMin;
  uint16_t nilaiMax;
  void *subMenu;
};

struktur menu harus dibuat mengikuti struktur diatas yaitu:

  1. tipe dapat berupa UInt8, UInt16, Float, textDropDown, subMenu, dll
  2. variabel merupakan alamat dari nilai setting sesuai tipe yang diberikan
  3. nilaiMin dan nilaiMax merupakan batas setting.
  4. subMenu alamat struktur menu apabila tipenya adalah subMenu.

coding arduino menu tft lcd:

#define LCD_CS                A3
#define LCD_CD                A2
#define LCD_WR                A1
#define LCD_RD                A0

#define LCD_RESET             A4

#define YP                    A1
#define XM                    A2
#define YM                    7
#define XP                    6

#define jumlahLevelMenu       2
#define panjangTextMenu       17
#define jumlahMenuDalamSatuLayar         4

#define lebarKolom1           210
#define tengahKolom1          (lebarKolom1/2)
#define tinggiBaris1          197
#define lebarKolom2           106
#define tengahKolom2          (214 + (lebarKolom2/2))
#define ringXPos              0
#define ringYPos              0
#define ringRadius            100

#define TS_MINX 130
#define TS_MINY 141
#define TS_MAXX 920
#define TS_MAXY 935

#define TS_MINPRESSURE 10
#define TS_MAXPRESSURE 1000

//========================================================
#include <Adafruit_GFX.h>
#include <SPFD5408_Adafruit_TFTLCD.h>
#include <SPFD5408_TouchScreen.h>

#define BLACK   0x0000
#define WHITE   0xFFFF
#define RED     0xF800
#define GREEN   0x07E0
#define BLUE    0x001F
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define GREY    0x2108

#define RED2RED 0
#define GREEN2GREEN 1
#define BLUE2BLUE 2
#define BLUE2RED 3
#define GREEN2RED 4
#define RED2GREEN 5

enum MenuMode
{
  UInt8,
  UInt16,
  Float,
  textDropDown,
  subMenu,
  commandSettingPabrik,
};

struct Menu
{
  byte tipe;
  void *variabel;
  uint16_t nilaiMin;
  uint16_t nilaiMax;
  void *subMenu;
};

struct MenuIndex
{
  byte index;
  char *menutext;
  Menu *menu;
  byte showIndex;
  byte menuLength;
  char *dropDown;
  byte dropDownLength;
};

struct Setting
{
  byte lampu;
  byte alarm;
  byte kipas;
  byte kontras;
  byte kecerahan;
  byte suhuSet;
  byte kelembabanSet;
  byte rollerMode;
  long rollerJeda;
  long rollerDurasi;
  long istirahatMode;
  long istirahatkipas;
  long istirahatJeda;
  long istirahatDurasi;
  byte lampuLatar;
  byte humidifier;
  long rollerSebelumnya;
  long istirahatSebelumnya;
  byte tombol;
};

struct TouchScreenKode
{
  uint16_t x1;
  uint16_t y1;
  uint16_t x2;
  uint16_t y2;
  byte kode;
};

//variabel
bool aktif;
float suhu;
float kelembaban;

uint16_t intensitasCahaya;
uint16_t aliranUdara;
uint16_t levelSuara;
uint16_t warnaLampu;

uint8_t jam;
uint8_t menit;
uint8_t detik;
uint8_t hari;
uint8_t tanggal;
uint8_t bulan;
uint8_t tahun;

Setting setting;

//Dropdown menu
const char aktifText[][panjangTextMenu] PROGMEM =
{
  "Tidak",
  "Ya",
};
const char pilihanBatalLanjut[][panjangTextMenu] PROGMEM  =
{
  "Batal",
  "Lanjut",
};
const char pilihanHidupMati[][panjangTextMenu] PROGMEM  =
{
  "Hidup",
  "Mati",
};
const char pilihanHidupMatiAuto[][panjangTextMenu] PROGMEM  =
{
  "Hidup",
  "Mati",
  "Auto",
};

const char pilihanKipas[][panjangTextMenu] PROGMEM =
{
  "Hidup",
  "Mati",
  "Humidity",
  "Lampu",
};

const char pilihanAlarm[][panjangTextMenu] PROGMEM =
{
  "Temp",
  "roll",
  "istrh",
  "Mati"
};

const char menuStrWaktu[][panjangTextMenu] PROGMEM =
{
  "1. Jam",
  "2. Menit",
  "3. Detik",
  "4. Tanggal",
  "5. Bulan",
  "6. Tahun",
};

//Sub menu
//tipe            variabel  nilaiMin nilaiMax submenu  jumlahBaris
const Menu menuWaktu[] =
{
  {UInt8         , &jam     , 1     , 24    , 0 },
  {UInt8         , &menit   , 0     , 59    , 0 },
  {UInt8         , &detik   , 0     , 59    , 0 },
  {UInt8         , &tanggal , 1     , 31    , 0 },
  {UInt8         , &bulan   , 1     , 12    , 0 },
  {UInt8         , &tahun   , 0     , 99    , 0 },
};

const char menuStrPeralatan[][panjangTextMenu] PROGMEM =
{
  "1. Kipas",
  "2. Alarm",
  "3. Kontras",
  "4. Kecerahan",
  "5. Lampu Latar",
  "6. Bunyi Tmbol",
};

const Menu menuPeralatan[] =
{
  {textDropDown  , &setting.kipas      , 0     , (sizeof(pilihanKipas) / sizeof(pilihanKipas[0]) - 1)                , &pilihanKipas         },
  {textDropDown  , &setting.alarm      , 0     , (sizeof(pilihanAlarm) / sizeof(pilihanAlarm[0]) - 1)                  , &pilihanAlarm         },
  {UInt8         , &setting.kontras    , 0     , 99                                                                  , 0                     },
  {UInt8         , &setting.kecerahan  , 0     , 99                                                                  , 0                     },
  {textDropDown  , &setting.lampuLatar , 0     , (sizeof(pilihanHidupMatiAuto) / sizeof(pilihanHidupMatiAuto[0]) - 1)  , &pilihanHidupMatiAuto },
  {textDropDown  , &setting.tombol     , 0     , (sizeof(pilihanHidupMati) / sizeof(pilihanHidupMati[0]) - 1)          , &pilihanHidupMati     },
};

const char menuStrRoller[][panjangTextMenu] PROGMEM =
{
  "1. Mode",
  "2. Jeda (jam)",
  "3. Lama(menit)",
};

const Menu menuRoller[] =
{
  {textDropDown  , &setting.rollerMode      , 0     , (sizeof(pilihanHidupMati) / sizeof(pilihanHidupMati[0])) - 1     , &pilihanHidupMati  },
  {UInt8         , &setting.rollerJeda      , 1     , 24                                                               , 0                  },
  {UInt8         , &setting.rollerDurasi    , 1     , 59                                                               , 0                  },
};

const char menuStrIstirahat[][panjangTextMenu] PROGMEM =
{
  "1. Istirahat",
  "2. Kipas",
  "3. Jeda (jam)",
  "4. Lama(menit)",
};

const Menu menuIstirahat[] =
{
  {textDropDown  , &setting.istirahatMode    , 0     , (sizeof(pilihanHidupMati) / sizeof(pilihanHidupMati[0])) - 1    , &pilihanHidupMati },
  {textDropDown  , &setting.istirahatkipas   , 0     , (sizeof(pilihanHidupMati) / sizeof(pilihanHidupMati[0])) - 1    , &pilihanHidupMati },
  {UInt8         , &setting.istirahatJeda    , 10    , 23                                                              , 0                 },
  {UInt8         , &setting.istirahatDurasi  , 1     , 59                                                              , 0                 },
};

//Menu utama
const char menuStrUtama[][panjangTextMenu] PROGMEM  =
{
  "1.Waktu",
  "2.Peralatan",
  "3.Temperatur",
  "4.Kelembaban",
  "5.Roller",
  "6.Istirahat",
  "7.Set. pabrik",
};

const Menu menuUtama[] =
{
  {subMenu                , &menuStrWaktu          , 0    , sizeof(menuWaktu) / sizeof(menuWaktu[0])                    , &menuWaktu          },
  {subMenu                , &menuStrPeralatan      , 0    , sizeof(menuPeralatan) / sizeof(menuPeralatan[0])            , &menuPeralatan      },
  {UInt8                  , &setting.suhuSet       , 0    , 99                                                          , 0                   },
  {UInt8                  , &setting.kelembabanSet , 0    , 99                                                          , 0                   },
  {subMenu                , &menuStrRoller         , 0    , sizeof(menuRoller) / sizeof(menuRoller[0])                  , &menuRoller         },
  {subMenu                , &menuStrIstirahat      , 0    , sizeof(menuIstirahat) / sizeof(menuIstirahat[0])            , &menuIstirahat      },
  {commandSettingPabrik   , 0                      , 0    , sizeof(pilihanBatalLanjut) / sizeof(pilihanBatalLanjut[0])  , &pilihanBatalLanjut },
};
//========================================================



TouchScreenKode tcKodeIdle[] = {
  {305, 730, 191, 943, 'M'},//menu utama
  {670, 716, 319, 928, 'T'},
  {867, 717, 724, 931, 'W'},
};
TouchScreenKode tcKodeMenu[] = {
  {745, 180, 692, 660, '1'},
  {634, 180, 554, 660, '2'},
  {511, 180, 442, 660, '3'},
  {390, 180, 316, 660, '4'},
  {263, 189, 176, 424, '<'},
  {263, 444, 190, 667, '>'},
  {305, 730, 191, 943, 'B'},
};

TouchScreenKode tcKodeEdit[] = {
  {416, 297, 327, 547, 'S'},
  {263, 189, 176, 424, '-'},
  {263, 444, 190, 667, '+'},
  {305, 730, 191, 943, 'B'},
};


MenuIndex menuIndex[jumlahLevelMenu];

long millismenuText;
int8_t levelMenu = -1;
bool menuEntriNilai;
char *judulMenu;
byte judulMenuTampil;
byte menu_Value8;
uint16_t menu_Value16;
float menu_ValueFloat;

float suhuSebelumnya;
float kelembabanSebelumnya;

Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);

byte lebarText;
byte ukuranText;

bool statusLampu;
bool statusKipas;
bool statusHumidifier;
bool rollerStatus;
bool istirahatStatus;

long millisRoller;
long millisIstirahat;

bool alarmStatus;
bool alarmPinStatus;

void setup()
{
  Serial.begin(9600);
  Serial.println("Menu dan submenu menggunakan LCD TFT 2.4\" berbasis arduino");
  Serial.println("https://www.semesin.com/project/");
  Serial.println();
  
  tft.reset();
  tft.begin(0x9341);
  tft.setRotation(1);

  idle();

  suhu = 32;
  kelembaban = 65;

  ambilDefault();
}

void loop()
{
  char tcKode = 0;
  if (levelMenu == -1)
  {
    tcKode = ambilKodeTouchScreen((TouchScreenKode*)&tcKodeIdle, sizeof(tcKodeIdle) / sizeof(TouchScreenKode));
  }
  else if (menuEntriNilai)
  {
    tcKode = ambilKodeTouchScreen((TouchScreenKode*)&tcKodeEdit, sizeof(tcKodeEdit) / sizeof(TouchScreenKode));
  }
  else
  {
    tcKode = ambilKodeTouchScreen((TouchScreenKode*)&tcKodeMenu, sizeof(tcKodeMenu) / sizeof(TouchScreenKode));
  }
  if (tcKode)
  {
    switch (tcKode)
    {
      case 'M':
        levelMenu++;
        menu_Display();
        break;
      case '>':
        menuIndex[levelMenu].showIndex++;
        if (menuIndex[levelMenu].showIndex > (menuIndex[levelMenu].menuLength - (jumlahMenuDalamSatuLayar / 2)))
        {
          menuIndex[levelMenu].showIndex = 0;
        }
        menu_ShowItem();
        break;
      case '<':
        if (menuIndex[levelMenu].showIndex == 0)
        {
          menuIndex[levelMenu].showIndex = (menuIndex[levelMenu].menuLength - (jumlahMenuDalamSatuLayar / 2));
        }
        else
        {
          menuIndex[levelMenu].showIndex--;
        }
        menu_ShowItem();
        break;
      case '1':
      case '2':
      case '3':
      case '4':
        menuIndex[levelMenu].index = menuIndex[levelMenu].showIndex + (tcKode - '1');
        levelMenu++;
        menu_Display();
        break;
      case 'B':
        menuEntriNilai = false;
        levelMenu--;
        menu_Display();
        break;
      case 'S':
        menu_EditSelesai();
        break;
      case '-':
        menu_KurangNilai();
        break;
      case '+':
        menu_TambahNilai();
        break;

    }
  }
}

void menu_Display()
{
  if (levelMenu == -1)
  {
    idle();
    menuIndex[levelMenu].dropDownLength = 0;
  }
  else if (levelMenu == 0)
  {
    menuIndex[levelMenu].index = 0;
    menuIndex[levelMenu].menutext = (char*)menuStrUtama;
    menuIndex[levelMenu].menu = menuUtama;
    menuIndex[levelMenu].showIndex = 0;
    menuIndex[levelMenu].menuLength = sizeof(menuUtama) / sizeof(menuUtama[0]);
    menuIndex[levelMenu].dropDownLength = 0;

    tft.fillRect(0, tinggiBaris1 + 4, 208, 35, BLACK);

    ukuranText = 3;
    tft.setFont();
    tft.setTextColor (WHITE, BLACK);

    tft.fillRect(0, 0, lebarKolom1, tinggiBaris1, BLACK);
    tft.setTextSize (ukuranText);
    lebarText = 10 * ukuranText * 6;
    tft.setCursor(tengahKolom1 - (lebarText / 2), 0);
    tft.print("MENU UTAMA");

    menu_TcMenu();
    menu_ShowItem();
  }
  else
  {
    char buf[panjangTextMenu];
    Menu menuLama = menuIndex[levelMenu - 1].menu[menuIndex[levelMenu - 1].index];

    ukuranText = 3;
    tft.setFont();
    tft.setTextColor (WHITE, BLACK);

    tft.fillRect(0, 0, lebarKolom1, ukuranText * 8, BLACK);
    tft.setTextSize (ukuranText);

    copyFlashString(buf, menuIndex[levelMenu - 1].menutext + (menuIndex[levelMenu - 1].index * panjangTextMenu));

    lebarText = strlen(buf) * ukuranText * 6;
    tft.setCursor(tengahKolom1 - (lebarText / 2), 0);
    tft.print(buf);

    switch (menuLama.tipe)
    {
      case UInt8:
      case textDropDown:
        menu_Value8 = *(uint8_t*)menuLama.variabel;
        menuEntriNilai = true;
        break;
      case UInt16:
        menu_Value16 = *(uint16_t*)menuLama.variabel;
        menuEntriNilai = true;
        break;
      case Float:
        menu_Value16 = *(float*)menuLama.variabel;
        menuEntriNilai = true;
        break;
      case subMenu:
        menuIndex[levelMenu].index = 0;
        menuIndex[levelMenu].menutext = menuLama.variabel;
        menuIndex[levelMenu].menu = menuLama.subMenu;
        menuIndex[levelMenu].showIndex = 0;
        menuIndex[levelMenu].menuLength = menuLama.nilaiMax;
        menuIndex[levelMenu].dropDownLength = 0;

        menu_TcMenu();
        menu_ShowItem();
        break;
      case commandSettingPabrik:
        menu_Value8 = 0;
        menuEntriNilai = true;
        break;
    }
    if (menuEntriNilai)
    {
      menu_TcEdit();
      menu_ShowNilai();
    }
  }
}
void menu_tcIdle()
{
  tft.fillRect(210, 200, 110, 40, YELLOW);
  ukuranText = 3;
  tft.setFont();
  tft.setTextColor (BLACK, YELLOW);
  tft.setTextSize (ukuranText);
  lebarText = 4 * ukuranText * 6;
  tft.setCursor(tengahKolom2 - (lebarText / 2), 208);
  tft.print("MENU");

}
void menu_TcMenu()
{
  tft.fillRect(0, tinggiBaris1 + 4, (lebarKolom1 / 2) - 2, 36, YELLOW);
  tft.fillRect((lebarKolom1 / 2) - 2, tinggiBaris1 + 4, 4, 40, BLUE);
  tft.fillRect((lebarKolom1 / 2) + 2, tinggiBaris1 + 4, (lebarKolom1 / 2) - 2, 36, YELLOW);
  tft.fillRect(lebarKolom1 + 4, tinggiBaris1 + 4, 106, 36, YELLOW);

  ukuranText = 3;
  tft.setFont();
  tft.setTextColor (BLACK, YELLOW);
  tft.setTextSize (ukuranText);
  lebarText = 1 * ukuranText * 6;
  tft.setCursor(((lebarKolom1 / 2) - lebarText) / 2, 208);
  tft.print("<");

  tft.setCursor((((lebarKolom1 / 2) - lebarText) / 2) + (lebarKolom1 / 2), 208);
  tft.print(">");


  tft.setTextSize (ukuranText);
  lebarText = 5 * ukuranText * 6;
  tft.setCursor(tengahKolom2 - (lebarText / 2), 208);
  tft.print("BALIK");
}

void menu_TcEdit()
{
  tft.fillRect(0, tinggiBaris1 - 44, lebarKolom1, 44, BLACK);
  tft.fillRect((lebarKolom1 / 4) - 4, tinggiBaris1 - 44 - 4, (lebarKolom1 / 2) + 8, 36 + 8, BLUE);
  tft.fillRect((lebarKolom1 / 4), tinggiBaris1 - 44, (lebarKolom1 / 2), 36, YELLOW);

  tft.fillRect(0, tinggiBaris1 + 4, (lebarKolom1 / 2) - 2, 36, YELLOW);
  tft.fillRect((lebarKolom1 / 2) - 2, tinggiBaris1 + 4, 4, 40, BLUE);
  tft.fillRect((lebarKolom1 / 2) + 2, tinggiBaris1 + 4, (lebarKolom1 / 2) - 2, 36, YELLOW);
  tft.fillRect(lebarKolom1 + 4, tinggiBaris1 + 4, 106, 36, YELLOW);

  ukuranText = 3;
  lebarText = 3 * ukuranText * 6;
  tft.setFont();
  tft.setTextColor (BLACK, YELLOW);
  tft.setTextSize (ukuranText);
  tft.setCursor((lebarKolom1 - lebarText) / 2, tinggiBaris1 - 40);
  tft.print("Set");

  lebarText = 1 * ukuranText * 6;
  tft.setCursor(((lebarKolom1 / 2) - lebarText) / 2, 208);
  tft.print("-");

  tft.setCursor((((lebarKolom1 / 2) - lebarText) / 2) + (lebarKolom1 / 2), 208);
  tft.print("+");


  tft.setTextSize (ukuranText);
  lebarText = 5 * ukuranText * 6;
  tft.setCursor(tengahKolom2 - (lebarText / 2), 208);
  tft.print("BALIK");
}
void menu_ShowItem()
{
  char buf[panjangTextMenu];
  byte indexMenuMulai;

  ukuranText = 3;
  tft.fillRect(0, ukuranText * 8, lebarKolom1, tinggiBaris1 - ukuranText * 8, BLACK);

  ukuranText = 2;
  tft.setTextSize (ukuranText);
  tft.setFont();
  tft.setTextColor (WHITE, BLACK);

  byte menuItemSize = min(jumlahMenuDalamSatuLayar, menuIndex[levelMenu].menuLength - menuIndex[levelMenu].showIndex);

  for ( byte i = 0; i < menuItemSize; i++)
  {
    byte showIndex = menuIndex[levelMenu].showIndex + i;
    tft.setTextSize (ukuranText);
    tft.setCursor(0, (i * ukuranText * 20) + 50);
    copyFlashString(buf, menuIndex[levelMenu].menutext + (showIndex * panjangTextMenu));
    tft.print(buf);

    char *alamat;

    switch (menuIndex[levelMenu].menu[showIndex].tipe)
    {
      case UInt8:
        itoa(*(uint8_t*)menuIndex[levelMenu].menu[showIndex].variabel, buf, 10);
        lebarText = strlen(buf) * ukuranText * 6;
        tft.setCursor(lebarKolom1 - (lebarText + 10), (i * ukuranText * 20) + 50);
        tft.print(buf);
        break;
      case UInt16:
        itoa(*(uint16_t*)menuIndex[levelMenu].menu[showIndex].variabel, buf, 10);
        lebarText = strlen(buf) * ukuranText * 6;
        tft.setCursor(lebarKolom1 - (lebarText + 10), (i * ukuranText * 20) + 50);
        tft.print(buf);
        break;
      case Float:
        dtostrf(*(float*)menuIndex[levelMenu].menu[showIndex].variabel, 6, 2, buf);
        lebarText = strlen(buf) * ukuranText * 6;
        tft.setCursor(lebarKolom1 - (lebarText + 10), (i * ukuranText * 20) + 50);
        tft.print(buf);
        break;
      case textDropDown:
        copyFlashString(buf, menuIndex[levelMenu].menu[showIndex].subMenu + (*(uint8_t*)menuIndex[levelMenu].menu[showIndex].variabel * panjangTextMenu));
        lebarText = strlen(buf) * ukuranText * 6;
        tft.setCursor(lebarKolom1 - (lebarText + 10), (i * ukuranText * 20) + 50);
        tft.print(buf);
        break;
      case subMenu:
        lebarText = 1 * ukuranText * 6;
        tft.setCursor(lebarKolom1 - (lebarText + 10), (i * ukuranText * 20) + 50);
        tft.print('>');
        break;
      case commandSettingPabrik:
        break;
    }
  }
}

void menu_ShowNilai()
{
  char buf[panjangTextMenu];

  tft.fillRect(0, 50, lebarKolom1, 100, BLACK);
  Menu menuEdit = menuIndex[levelMenu - 1].menu[menuIndex[levelMenu - 1].index];
  switch (menuEdit.tipe)
  {
    case UInt8:
      itoa(menu_Value8, buf, 10);
      break;
    case UInt16:
      itoa(menu_Value16, buf, 10);
      break;
    case Float:
      dtostrf(menu_ValueFloat, 6, 2, buf);
      break;
    case textDropDown:
    case commandSettingPabrik:
      copyFlashString(buf, menuEdit.subMenu + (menu_Value8 * panjangTextMenu));
      break;
  }
  ukuranText = 5;
  tft.setFont();
  tft.setTextColor (WHITE, BLACK);
  tft.setTextSize (ukuranText);
  lebarText = strlen(buf) * ukuranText * 6;

  tft.setCursor((lebarKolom1 - lebarText) / 2, 70);
  tft.print(buf);
}

void menu_TambahNilai()
{
  Menu menuEdit = menuIndex[levelMenu - 1].menu[menuIndex[levelMenu - 1].index];
  switch (menuEdit.tipe)
  {
    case UInt8:
    case textDropDown:
    case commandSettingPabrik:
      if (menu_Value8 < menuEdit.nilaiMax)
      {
        menu_Value8++;
      }
      else
      {
        menu_Value8 = menuEdit.nilaiMin;
      }
      break;
    case UInt16:
      if (menu_Value16 < menuEdit.nilaiMax)
      {
        menu_Value16++;
      }
      else
      {
        menu_Value16 = menuEdit.nilaiMin;
      }
      break;
    case Float:
      if (menu_Value8 < menuEdit.nilaiMax)
      {
        menu_ValueFloat += 0.1;
      }
      else
      {
        menu_ValueFloat = menuEdit.nilaiMin;
      }
      break;
  }
  menu_ShowNilai();

}
void menu_KurangNilai()
{
  Menu menuEdit = menuIndex[levelMenu - 1].menu[menuIndex[levelMenu - 1].index];
  switch (menuEdit.tipe)
  {
    case UInt8:
    case textDropDown:
    case commandSettingPabrik:
      if (menu_Value8 != menuEdit.nilaiMin)
      {
        menu_Value8--;
      }
      else
      {
        menu_Value8 = menuEdit.nilaiMax;
      }
      break;
    case UInt16:
      if (menu_Value16 != menuEdit.nilaiMax)
      {
        menu_Value16--;
      }
      else
      {
        menu_Value16 = menuEdit.nilaiMax;
      }
      break;
    case Float:
      if (menu_Value8 != menuEdit.nilaiMax)
      {
        menu_ValueFloat -= 0.1;
      }
      else
      {
        menu_ValueFloat = menuEdit.nilaiMax;
      }
      break;
  }
  menu_ShowNilai();
}
void menu_EditSelesai()
{
  Menu menuEdit = menuIndex[levelMenu - 1].menu[menuIndex[levelMenu - 1].index];

  switch (menuEdit.tipe)
  {
    case UInt8:
    case textDropDown:
      *(uint8_t*)menuEdit.variabel = menu_Value8;
      break;
    case UInt16:
      *(uint8_t*)menuEdit.variabel = menu_Value16;
      break;
    case Float:
      *(uint8_t*)menuEdit.variabel = menu_ValueFloat;
      break;
    case commandSettingPabrik:
      ambilDefault();
      break;
  }
  menuEntriNilai = false;

  tft.fillRect(0, 50, lebarKolom1, 100, BLACK);
  ukuranText = 3;
  tft.setFont();
  tft.setTextColor (WHITE, BLACK);
  tft.setTextSize (ukuranText);
  lebarText = strlen("Tersimpan") * ukuranText * 6;

  tft.setCursor((lebarKolom1 - lebarText) / 2, 70);
  tft.print("Tersimpan");

  delay(1000);

  levelMenu--;
  menu_Display();

}
void idle()
{
  tft.fillScreen(BLACK);
  tft.setFont();

  menu_tcIdle();
}

char ambilKodeTouchScreen(TouchScreenKode *tcKode, byte jumlahArea)
{
  TSPoint p = ts.getPoint();
  char returnValue = 0;
  if ((p.z > TS_MINPRESSURE ) && (p.z < TS_MAXPRESSURE))
  {
    for (byte i = 0; i < jumlahArea; i++)
    {
      if ((tcKode[i].x1 > p.x) && (tcKode[i].x2 < p.x) && (tcKode[i].y1 < p.y) && (tcKode[i].y2 > p.y))
      {
        byte tsCounter;
        do
        {
          p = ts.getPoint();
          if ((p.z < TS_MINPRESSURE ) || (p.z > TS_MAXPRESSURE))
          {
            tsCounter++;
          }
          else
          {
            tsCounter = 0;
          }
        }
        while (tsCounter < 20);
        returnValue = tcKode[i].kode;
        break;
      }
    }
  }

  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT);

  return returnValue;
}
void ambilDefault()
{
  setting.lampu = 2;
  setting.alarm = 0;
  setting.kipas = 2;
  setting.kontras = 55;
  setting.kecerahan = 100;
  setting.suhuSet = 30;
  setting.kelembabanSet = 80;
  setting.rollerMode = 0;
  setting.rollerJeda = 1;
  setting.rollerDurasi = 1;
  setting.istirahatMode = 0;
  setting.istirahatkipas = 0;
  setting.istirahatJeda = 1;
  setting.istirahatDurasi = 1;
  setting.lampuLatar = 0;
  setting.tombol = 0;
  setting.humidifier = 2;
}
byte copyFlashString(char* buf, const char* alamat)
{
  char c;
  byte l = 0;
  while (c = pgm_read_byte(alamat++))
  {
    *buf++ = c;
    l++;
  }
  *buf = 0;
  return l;

}

 
library yang digunakan :

  1. Adafruit_GFX_Library.zip
  2. SPFD5408.zip

Konversi font dmd ke gfx (c font vertikal ke horizontal)

Metode menyimpanan data font memiliki perbedaan antara vendor library.

Font Library DMD

DMD, DMD2, DMD3 menggunakan font dengan metode vertikal seperti ditunjukkan dalam diagram berikut:

Font library GFX

Font yang dikembangkan oleh adafruit dalam GFX menggunakan metode mendatar seperti diagram berikut :

Untuk keperluan konversi font dari dmd ke gfx bisa dilakukan dengan metode invert.

Coding berikut ditulis dengan bahasa php, jadi harus menggunakan web server seperti xampp:

<? php
$fontHorizontal = "";
$fontVertikal = "";
$tinggi = 0;
$lebar = 0;
$charCount = 0;
if (isset($_GET['konversiKeHorizontal']))
{
  $fontVertikal = $_GET['fontVertikal'];
  $tinggi = $_GET['tinggi'];
  $lebar = $_GET['lebar'];
  $charCount = $_GET['jumlahKarakter'];


  $verticalHex = str_getcsv ($fontVertikal, ',');
  for ($i = 0; $i < sizeof($verticalHex); $i++)
  {
    $verticalByte[$i] = hexDec($verticalHex[$i]);
  }

  for ($cc = 0; $cc < $charCount; $cc++)
  {
    $horizontalByte = array();
    for ($i = 0; $i < ($tinggi*$lebar / 8); $i++)
    {
      array_push($horizontalByte, 0);
    }

    $bit = 0;
    $byte = 0;
    $indexHorizontal = 0;
    $byteMask = 1;

    for ($x = 0; $x < $lebar; $x++)
    {
      for ($y = 0; $y < $tinggi; $y++)
      {
        $x2 = $x;
        $y2 = $y;
        if ($y >= 8)
        {
          $x2 += ((int)($y / 8) * $lebar);
          $y2 = $y % 8;
        }
        $indexVertical = ($x2 * 8) + $y2 + ($cc*$lebar*$tinggi);

        $indexVerticalByte = (int)($indexVertical / 8);
        $indexVerticalBit = $indexVertical % 8;


        $indexHorizontal = ($y * $lebar ) + $x;
        $indexHorizontalByte = (int)($indexHorizontal / 8);
        $indexHorizontalBit = $indexHorizontal % 8;

        $verticalBit = $verticalByte[$indexVerticalByte] & pow(2, $indexVerticalBit);
        if ($verticalBit)
        {
          $horizontalByte[$indexHorizontalByte] |= pow(2, 7 - $indexHorizontalBit);
        }
      }
    }
    for ($i = 0; $i < sizeof($horizontalByte); $i++)
    {
      $fontHorizontal . = "0x";
      if ($horizontalByte[$i] < 0x10)
      {
        $fontHorizontal . = '0';
      }

      $fontHorizontal . = decHex($horizontalByte[$i]);
      $fontHorizontal . = ', ';
    }
    $fontHorizontal . = '&#13;&#10;';
  }
}
?>

<html>
  <head><title>font Invert ( vertical to horizontal) - semesin.com</title>
  </head>
  <body>
    <h4>Konversi font DMD menjadi font GFX</h4><br>
    <a href="https://www.semesin.com/project/">https://www.semesin.com/project/</a>
    <form action = "" method = "get">
      Font vertikal : <br><textarea name = "fontVertikal" rows = "10" cols = "100%"> <? php echo $fontVertikal ?> < / textarea > <br>
      Tinggi : <input type = "text" name = "tinggi" value = "<?php echo $tinggi ?>"><br>
      Lebar : <input type = "text" name = "lebar" value = "<?php echo $lebar; ?>"><br>
      Jumlah : <input type = "text" name = "jumlahKarakter" value = "<?php echo $charCount; ?>"><br>
      <input type = "submit" name = "konversiKeHorizontal" value = "Konversi ke Horizontal">
    </form>
    Font horizontal : <br><textarea rows = "10" cols = "100%"> <? php echo $fontHorizontal ?> < / textarea > <br>
  </body >
</html >

 

tampilan konversi font dmd ke gfx:

Konversi masehi ke Pasaran Jawa dan Hijriah dengan metode Julian Date

Julian Date

Julian date adalah tetapan bilangan penanggalan matahari yang dihitung semenjak siang tanggal 1 Januari 4713 SM.

Penanggalan Hijriah

Tanggal 1 Muharram tahun pertama sama dengan bilangan Julian 1948439.5.

Penanggalan Jawa

Sistem Pasaran Jawa memadukan metode penanggalan Hindu, Hijriah dan Julian. Keistimewaan lainnya adalah memiliki siklus pekan, siklus pasaran, siklus wuku, siklus windu.

Sketch program konversi masehi ke pasaran jawa dan Hijriah:

#define epochHijriah          1948439.5f //math.harvard.edu
#define tambahKurangHijriah   0

struct TanggalDanWaktu
{
  uint8_t detik;
  uint8_t menit;
  uint8_t jam;
  uint8_t hari;
  uint8_t tanggal;
  uint8_t bulan;
  uint8_t tahun;
};
struct Tanggal
{
  uint8_t tanggal;
  uint8_t bulan;
  uint16_t tahun;
};
struct TanggalJawa
{
  uint8_t pasaran;
  uint8_t wuku;
  uint8_t tanggal;
  uint8_t bulan;
  uint16_t tahun;
};
struct JamDanMenit
{
  uint8_t jam;
  uint8_t menit;
};

uint8_t jumlahHariPerBulan[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
char namaBulanMasehi[][10] = {"Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober",
                            "November", "Desember"
                           };
char namaHariMasehi[][7] = {"Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jum'at", "Sabtu",};
char namaBulanHijriah[][14] = {"Muharram", "Safar", "Rabiul awal", "Rabiul akhir", "Jumadil awal", "Jumadil akhir", "Rajab",
                             "Sya'ban", "Ramadhan", "Syawal", "Dzulkaidah", "Dzulhijjah"
                            };
char namaBulanJawa[][12] = { "Sura", "Sapar", "Mulud", "Bakda Mulud", "Jumadilawal", "Jumadilakir", "Rejeb", "Ruwah", "Pasa",
                           "Sawal", "Sela", "Besar"
                         };
char namaWukuJawa[][13] = {"Shinta", "Landhep", "Wukit", "Kurantil", "Tala", "Gumbreg", "Warigalit", "Warigagung", "Julungwangi",
                         "Sungsang", "Galungan", "Kuningan", "Langkir", "Mandasia", "Julungpujut", "Pahang", "Kuruwelut", "Mrakeh", "Tambir",
                         "Madangkungan", "Maktal", "Wuye", "Manahil", "Prangbakat", "Bala", "Wungu", "Wayang", "Kulawu", "Dhukut", "Watugunung"
                        };
char namaHariPasaran[][7] = {"Legi", "Pahing", "Pon", "Wage", "Kliwon"};

TanggalDanWaktu tanggalMasehi;
Tanggal tanggalHijriah;
TanggalJawa tanggalJawa;
JamDanMenit waktuMagrib;

void setup() {
  Serial.begin(9600);
  Serial.println("Konversi Penanggalan Masehi ke Hijriah (metode Julian)");
  Serial.println("https://www.semesin.com/project/");
  Serial.println();
}

void loop() {
  waktuMagrib = {18, 12};

  uint32_t jumlahHari;
  double jumlahHariMatahari;

  tanggalMasehi.jam = 12;
  tanggalMasehi.menit = 0;
  tanggalMasehi.detik = 0;

  tanggalMasehi.tanggal = 8;
  tanggalMasehi.bulan = 9;
  tanggalMasehi.tahun = 18;

  tanggalMasehi.hari = hariDariTanggal(tanggalMasehi);
  masehiKeHijriah(tanggalMasehi, waktuMagrib, tambahKurangHijriah, tanggalHijriah, tanggalJawa);
  printKonversi(tanggalMasehi, tanggalHijriah, tanggalJawa);

  tanggalMasehi.tanggal = 9;
  tanggalMasehi.bulan = 9;
  tanggalMasehi.tahun = 18;

  tanggalMasehi.hari = hariDariTanggal(tanggalMasehi);
  masehiKeHijriah(tanggalMasehi, waktuMagrib, tambahKurangHijriah, tanggalHijriah, tanggalJawa);
  printKonversi(tanggalMasehi, tanggalHijriah, tanggalJawa);

  tanggalMasehi.tanggal = 10;
  tanggalMasehi.bulan = 9;
  tanggalMasehi.tahun = 18;

  tanggalMasehi.hari = hariDariTanggal(tanggalMasehi);
  masehiKeHijriah(tanggalMasehi, waktuMagrib, tambahKurangHijriah, tanggalHijriah, tanggalJawa);
  printKonversi(tanggalMasehi, tanggalHijriah, tanggalJawa);

  tanggalMasehi.tanggal = 11;
  tanggalMasehi.bulan = 9;
  tanggalMasehi.tahun = 18;

  tanggalMasehi.hari = hariDariTanggal(tanggalMasehi);
  masehiKeHijriah(tanggalMasehi, waktuMagrib, tambahKurangHijriah, tanggalHijriah, tanggalJawa);
  printKonversi(tanggalMasehi, tanggalHijriah, tanggalJawa);

  tanggalMasehi.tanggal = 12;
  tanggalMasehi.bulan = 9;
  tanggalMasehi.tahun = 18;

  tanggalMasehi.hari = hariDariTanggal(tanggalMasehi);
  masehiKeHijriah(tanggalMasehi, waktuMagrib, tambahKurangHijriah, tanggalHijriah, tanggalJawa);
  printKonversi(tanggalMasehi, tanggalHijriah, tanggalJawa);

  while (1);
}

double get_julian_date(Tanggal tanggal)
{
  if (tanggal.bulan <= 2)
  {
    tanggal.tahun -= 1;
    tanggal.bulan += 12;
  }

  double a = floor(tanggal.tahun / 100.0);
  double b = 2 - a + floor(a / 4.0);

  if (tanggal.tahun < 1583)
    b = 0;
  if (tanggal.tahun == 1582) {
    if (tanggal.bulan > 10)
      b = -10;
    if (tanggal.bulan == 10) {
      b = 0;
      if (tanggal.tanggal > 4)
        b = -10;
    }
  }

  return floor(365.25 * (tanggal.tahun + 4716)) + floor(30.6001 * (tanggal.bulan + 1)) + tanggal.tanggal + b - 1524.5;
}

double konversiTanggalHijriahKeJulianDate(uint16_t tahun, uint8_t bulan, uint8_t tanggal)
{
  return (epochHijriah + tanggal + ceil(29.5 * (bulan - 1)) + (354L * (tahun - 1)) + floor((3 + (11 * tahun)) / 30)) - 1;
}

void masehiKeHijriah(TanggalDanWaktu masehi, JamDanMenit waktuSholatMagrib, int8_t koreksiHijriah, Tanggal &hijriah, TanggalJawa &jawa)
{
  uint16_t sisaHari;
  double julianDate = get_julian_date({masehi.tanggal, masehi.bulan, masehi.tahun + 2000});

  uint16_t menitMagrib = waktuSholatMagrib.jam * 60  + waktuSholatMagrib.menit;
  uint16_t menitSekarang = masehi.jam * 60 + masehi.menit;

  if (menitSekarang >= menitMagrib)
  {
    julianDate++;//Pergantian hari hijrah pada magrib
  }

  julianDate = floor(julianDate) + 0.5;

  Tanggal tanggalHijriah;

  hijriah.tahun = floor(((30 * (julianDate - epochHijriah)) + 10646) / 10631);
  hijriah.bulan = min(12.0, ceil((julianDate - (29 + konversiTanggalHijriahKeJulianDate(hijriah.tahun, 1, 1))) / 29.5) + 1);
  hijriah.tanggal = (julianDate - konversiTanggalHijriahKeJulianDate(hijriah.tahun, hijriah.bulan, 1)) + 1;

  long julianLong = (long)julianDate;
  jawa.pasaran = ((julianLong + 1) % 5); //0 = legi
  jawa.wuku = (((julianLong + 65) % 210) / 7); //0 = Shinta
  jawa.tanggal = hijriah.tanggal;
  jawa.bulan = hijriah.bulan;
  jawa.tahun = hijriah.tahun + 512;

}
uint8_t hariDariTanggal(TanggalDanWaktu tanggalDanWaktu) {
  uint16_t jumlahHari = tanggalDanWaktu.tanggal;
  for (uint8_t i = 1; i < tanggalDanWaktu.bulan; ++i)
    jumlahHari += jumlahHariPerBulan[i-1];
  if (tanggalDanWaktu.bulan > 2 && tanggalDanWaktu.tahun % 4 == 0)
    ++jumlahHari;
  jumlahHari += (365 * tanggalDanWaktu.tahun) + ((tanggalDanWaktu.tahun + 3) / 4) - 1;

  return ((jumlahHari + 6) % 7) + 1; // 1 Januari 2000 hari sabtu = 7
}

void printKonversi(TanggalDanWaktu tanggalMasehi, Tanggal tanggalHijriah, TanggalJawa tanggalJawa)
{
  Serial.print("Masehi :\t");
  Serial.print(namaHariMasehi[tanggalMasehi.hari-1]);
  Serial.print(", ");
  Serial.print(tanggalMasehi.tanggal);
  Serial.print(" ");
  Serial.print(namaBulanMasehi[tanggalMasehi.bulan - 1]);
  Serial.print(" ");
  Serial.println(tanggalMasehi.tahun + 2000);

  Serial.print("Hijriah :\t");
  Serial.print(tanggalHijriah.tanggal);
  Serial.print(" ");
  Serial.print(namaBulanHijriah[tanggalHijriah.bulan - 1]);
  Serial.print(" ");
  Serial.println(tanggalHijriah.tahun);

  Serial.print("Pasaran Jawa :\t");
  Serial.print(tanggalJawa.tanggal);
  Serial.print(" ");
  Serial.print(namaHariPasaran[tanggalJawa.pasaran]);
  Serial.print(", ");
  Serial.print(namaWukuJawa[tanggalJawa.wuku]);
  Serial.print(", ");
  Serial.print(namaBulanJawa[tanggalJawa.bulan - 1]);
  Serial.print(" ");
  Serial.println(tanggalJawa.tahun);
  Serial.println();
}

 

Mengirim data detektor kebakaran dari arduino ke internet dengan antarmuka code igniter

Data sensor arduino

Sensor adalah instrumen atau komponen yang mampu mendeteksi perubahan kondisi objek dalam jangkauannya. Pengukuran besaran perubahan tersebut harus diubah dahulu menjadi bentuk digital sehingga dapat diproses oleh perangkat digital lainnya.

Data variabel ini bisa dimonitoring secara lokal melalui layar monitor maupun global melalui internet.

Hal yang mendukung keandalan sistem monitoring :

  1. realtime, yaitu data yang ditampilkan merupakan kondisi masa yang singkat, misalnya di perbarui setiap 1 detik.
  2. Data memiliki identitas seperti lokasi, waktu.
  3. Data yang diterima memiliki mekanisme penyaringan sehingga data yang ditampilkan terjamin.

Sensor detektor kebakaran

Terdapat beberapa Indikasi kebakaran yaitu :

  1. Api dideteksi dengan sensor flame
  2. Suhu dibaca dengan sensor suhu
  3. Asap dibaca dengan sensor asap (smoke detector)

Dalam hal pencegahan kebakaran, ketiga variabel ini terus menerus dimonitoring secara lokal.

Monitoring detektor kebakaran ini secara global juga diperlukan untuk memberikan informasi kepada pihak terkait.

Dalam contoh ini ketiga (modul) sensor ini digunakan bersama arduino dan akan dikirimkan ke mysql server.

Akses informasi sensor dengan codeIgniter

Informasi data saat ini dapat dengan mudah diakses dimanapun, namun dengan menggunakan codeIgniter diperoleh beberapa keuntungan yaitu :

  1. Pengembangan lebih mudah dengan baris program yang dapat disederhanakan.
  2. Aksesibilitas dapat dengan mudah dikontrol.
  3. Perlindungan terhadap server terutama server data lebih terjamin.

Skema Monitoring detektor kebakaran dengan codeIgniter

Komponen yang digunakan :

  1. Arduino Uno
  2. ESP8266
  3. Flame detector
  4. Sensor asap MQ7
  5. Sensor suhu dht11
  6. Relay
  7. Buzzer

Sketch / koding monitoring detektor kebakaran esp8266 – WebServer.

#include "WiFiEsp.h"
#include <SoftwareSerial.h>
#include "DHT.h"
 
char ssid[] = "****";        // Isi dengan nama profil Wifi
char pass[] = "********";            // password wifi
char server[] = "x.x.x.x";

long waktuMintaData = 1000; //minta data setiap 1000ms

#define pinFlame    A0
#define pinMQ       A1
#define pinDHT      A2
#define pinBuzzer   8
#define pinRelay    9

float setSuhu = 31.0;

String Respon = "";
long waktuMulai;
bool responDariServer = false;
bool prosesKirimDataKeServer = false;
 
WiFiEspClient client;
int status = WL_IDLE_STATUS;

SoftwareSerial wifi(2,3);
DHT dht(pinDHT, DHT11);
 
void setup()
{
  pinMode(pinFlame, INPUT_PULLUP);
  pinMode(pinMQ, INPUT_PULLUP);
  pinMode(pinDHT, INPUT_PULLUP);
  pinMode(pinBuzzer, OUTPUT);
  digitalWrite(pinRelay, HIGH);
  pinMode(pinRelay, OUTPUT);

  Serial.begin(115200);
 
  wifi.begin(115200);
  WiFi.init(&wifi);
 
  // check for the presence of the shield
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    // don't continue
    while (true);
  }
 
  // attempt to connect to WiFi network
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network
    status = WiFi.begin(ssid, pass);
  }
 
  // you're connected now, so print out the data
  Serial.println("You're connected to the network");
   
  printWifiStatus();
  dht.begin();
  waktuMulai = millis();
}
 
void loop()
{
  float suhu = dht.readTemperature();
//print status
  Serial.println();
  Serial.print("Api = ");
  Serial.println(digitalRead(pinFlame));
  Serial.print("Asap = ");
  Serial.println(digitalRead(pinMQ));
  Serial.print("suhu = ");
  Serial.println(suhu);
  
  if(!digitalRead(pinFlame) && !digitalRead(pinMQ) && (suhu < setSuhu))
  {
    digitalWrite(pinRelay, HIGH);
    digitalWrite(pinBuzzer, LOW);
    
  }
  if(digitalRead(pinFlame))
  {
    digitalWrite(pinRelay, LOW);
    digitalWrite(pinBuzzer, HIGH);
  }
  if(digitalRead(pinMQ))
  {
    digitalWrite(pinRelay, LOW);
    digitalWrite(pinBuzzer, HIGH);
  }
  if(suhu >= setSuhu)
  {
    digitalWrite(pinBuzzer, HIGH);
  }
  //kirim data
  if(waktuMintaData < millis() - waktuMulai)
  {
    waktuMulai = millis();
    prosesKirimDataKeServer = kirimDataKeServer();
  }

  while (client.available()) 
  {
    char c = client.read();
    Respon += c;
  }

  Serial.print("prosesKirimDataKeServer = ");
  Serial.println(prosesKirimDataKeServer);

  if (!client.connected() && prosesKirimDataKeServer) {
    Serial.println("Disconnecting from server...");
    client.stop();
    responDariServer = true;
    prosesKirimDataKeServer = false;
  }

  // penanganan data yang diterima dari server
  if(responDariServer)
  {
    responDariServer = false;
    Serial.println(Respon);
    int posisiData = Respon.indexOf("\r\n\r\n");
    String Data = Respon.substring(posisiData+4);
    Data.trim();
 
    String variabel;
    String nilai;
 
    Serial.println("Data dari server");
    posisiData = Data.indexOf('=');
    if(posisiData > 0)
    {
      variabel = Data.substring(0,posisiData);
      nilai = Data.substring(posisiData+1);
   
      //===========Penanganan respon disini
      if(variabel == "setSuhu")
      {
        setSuhu = nilai.toFloat();
      }
//      Serial.print(variabel);
//      Serial.print(" = ");
//      Serial.println(nilai);
    }
    Respon = "";
  }
}

bool kirimDataKeServer()
{
  Serial.println();
  Serial.println("Starting connection to server...");
  // if you get a connection, report back via serial
  if (client.connect(server, 80)) {
    Serial.println("Connected to server");
    // Make a HTTP request
 
    client.print("GET /index.php/databaseArduino/dariArduino");
    client.print("?Api=");
    client.print(digitalRead(pinFlame));
     
    client.print("&Asap=");
    client.print(digitalRead(pinMQ));

    client.print("&Suhu=");
    client.print(dht.readTemperature());
     
    client.println(" HTTP/1.1");
    client.print("Host: ");
    client.println(server);
    client.println("Connection: close");
    client.println();
    return true;
  }
  return false;
}
 
void printWifiStatus()
{
  // print the SSID of the network you're attached to
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());
 
  // print your WiFi shield's IP address
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
 
  // print the received signal strength
  long rssi = WiFi.RSSI();
  Serial.print("Signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
 
  IPAddress gateway = WiFi.gatewayIP();
  Serial.print("gateway:");
  Serial.print(gateway);
  Serial.println(" ");
}

program codeIgniter untuk monitoring data sensor arduino:

databaseArduino.php

<?php
class databaseArduino extends CI_Controller {
  public function __construct() {
    parent::__construct();
  }

  public function dariBrowser() {
    $this->load->model('Model_databaseArduino');
    $this->Model_databaseArduino->salinDataDariBrowser();
    $data['dataSensor'] = $this->Model_databaseArduino->ambilDataDariArduino();
    $data['dataParameter'] = $this->Model_databaseArduino->ambilDataDariBrowser();

    $this->load->view("data_sensor", $data);
  }

    public function dariArduino() {
    $this->load->model('Model_databaseArduino');
    $this->Model_databaseArduino->salinDataDariArduino();
    $data['dataParameter'] = $this->Model_databaseArduino->ambilDataDariBrowser();

    $this->load->view("data_parameter", $data);
  }
}
?>

Model_databaseArduino.php

<?php
class Model_databaseArduino extends CI_Model {

  public $title;
  public $content;
  public $date;

  public function ambilDataDariArduino()
  {
    $this->load->database();
    $query = $this->db->query("SELECT * FROM (
    SELECT * FROM `data_sensor` ORDER BY `nomor` DESC LIMIT 10
    ) sub
    ORDER BY `nomor` ASC");
    $this->db->close();  
    return $query->result();
  }

  public function salinDataDariArduino()
  {
    date_default_timezone_set('Asia/Jakarta'); # add your city to set local time zone
    $now = date('Y-m-d H:i:s');

    $this->load->database();
    $this->db->set('waktu', $now);
    $this->db->set('api', $this->input->get('Api'));
    $this->db->set('asap', $this->input->get('Asap'));
    $this->db->set('suhu', $this->input->get('Suhu'));
    $this->db->insert('data_sensor');
    $this->db->close();
  }

  public function ambilDataDariBrowser()
  {
    $this->load->database();
    $query = $this->db->query("SELECT * FROM `data_parameter` ORDER BY `nomor` DESC LIMIT 1");
    $this->db->close();  
    return $query->row();
  }

  public function salinDataDariBrowser()
  {
    date_default_timezone_set('Asia/Jakarta'); # add your city to set local time zone
    $now = date('Y-m-d H:i:s');

    $this->load->database();
    $this->db->set('waktu', $now);
    $this->db->set('setSuhu', $this->input->post('setSuhu'));
    $this->db->insert('data_parameter');
    $this->db->close();  
  }
}
?>

data_sensor.php

<?php
  defined('BASEPATH') OR exit('No direct script access allowed');

  if(isset($dataParameter))
  {
    echo "setSuhu=";
    echo $dataParameter->setSuhu;
  }
?>

data_parameter.php

<?php
defined('BASEPATH') OR exit('No direct script access allowed');
?>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Welcome to CodeIgniter</title>

  <style type="text/css">

  ::selection { background-color: #E13300; color: white; }
  ::-moz-selection { background-color: #E13300; color: white; }

  body {
    background-color: #fff;
    margin: 40px;
    font: 13px/20px normal Helvetica, Arial, sans-serif;
    color: #4F5155;
  }

  a {
    color: #003399;
    background-color: transparent;
    font-weight: normal;
  }

  h1 {
    color: #444;
    background-color: transparent;
    border-bottom: 1px solid #D0D0D0;
    font-size: 19px;
    font-weight: normal;
    margin: 0 0 14px 0;
    padding: 14px 15px 10px 15px;
  }

  code {
    font-family: Consolas, Monaco, Courier New, Courier, monospace;
    font-size: 12px;
    background-color: #f9f9f9;
    border: 1px solid #D0D0D0;
    color: #002166;
    display: block;
    margin: 14px 0 14px 0;
    padding: 12px 10px 12px 10px;
  }

  #body {
    margin: 0 15px 0 15px;
  }

  p.footer {
    text-align: right;
    font-size: 11px;
    border-top: 1px solid #D0D0D0;
    line-height: 32px;
    padding: 0 10px 0 10px;
    margin: 20px 0 0 0;
  }

  #container {
    margin: 10px;
    border: 1px solid #D0D0D0;
    box-shadow: 0 0 8px #D0D0D0;
  }
  </style>
</head>
<body>



<!--
<form action="/form/data_submitted" method="get">
User Name: <input type="text" name="u_name" placeholder="Please Enter User Name" class="input_box">
<br>
User email: <input type="text" name="u_email" placeholder="Please Enter Email Address" class="input_box">
<input type="submit" value="Submit" class="submit">
</form>
-->

<?php

// echo $this->input->post('setSuhu');

echo form_open('databaseArduino/dariBrowser');
if(isset($dataParameter->setSuhu))
{
  echo form_input('setSuhu', $dataParameter->setSuhu);
}
else
{
  echo form_input('setSuhu', '30.0');
}
echo form_submit('suhuSubmit', 'Set Temperatur');
echo form_close();

echo "<br>";
echo "<strong>Data pembacaan sensor</strong>";
echo "<br>";
echo "<table>";

  echo "<tr>";
  echo "<td width='50'>Nomor</td>";
  echo "<td width='200'>Waktu</td>";
  echo "<td width='50'>Api</td>";
  echo "<td width='50'>Asap</td>";
  echo "<td width='50'>Suhu</td>";
  echo "</tr>";

  foreach ($dataSensor as $row)
{
  echo "<tr>";
  echo "<td>".$row->nomor."</td>";
  echo "<td>".$row->waktu."</td>";
  echo "<td>".$row->api."</td>";
  echo "<td>".$row->asap."</td>";
  echo "<td>".$row->suhu."</td>";
  echo "</tr>";
}
echo "</table>";
?>
</body>
</html>

file server : htdocs.zip

Trik pemograman arduino

Clock 1Hz

Clock 1Hz atau penanda detik tanpa rtc

long millisDetik;

void setup() {
  millisDetik = millis();
}

void loop() {
  if(millisDetik != millis() / 1000L)
  {
    millisDetik = millis() / 1000L;

    // Kode
    
  }
}

Menghemat RAM menggunakan flash string

Arduino memiliki memory ram yang kecil, salah satu langkah penghematan adalah dengan memasukkan konstanta text / string ke dalam flash seperti kode berikut:

  Serial.println(F("https://www.semesin.com/project"));

atau jika menggunakan alamat

const PROGMEM char text[] = "https://www.semesin.com/project";
void setup() {
  Serial.begin(9600);
  Serial.println((const __FlashStringHelper *)text);
}

Definisi nilai output

Nilai parameter fungsi digitalWrite relay (NC/NO) dan transistor/mosfet sering kali terbalik, untuk mempermudah pekerjaan nilainya LOW-nya lebih baik didefenisikan.

#define pinRelay   8
#define relayLOW   HIGH //relay dengan nilai kebalikan

void setup() {
  pinMode(pinRelay, OUTPUT);
}

void loop() {
  digitalWrite(pinRelay, relayLOW);//mati
  delay(1000);
  digitalWrite(pinRelay, !relayLOW);//hidup
  delay(1000);
}

Cast Float to Byte Array

Dalam komunikasi data berbentuk float, lebih baik mengirim data berupa byte array dari pada nilai string dari float

Serial.begin(9600);
float nilaiFloat = 0.15625;
byte *arrayByte;

arrayByte = (byte*)&nilaiFloat;
Serial.print(nilaiFloat);
Serial.print(" = ");
Serial.print(arrayByte[3],HEX);
Serial.print(' ');
Serial.print(arrayByte[2],HEX);
Serial.print(' ');
Serial.print(arrayByte[1],HEX);
Serial.print(' ');
Serial.println(arrayByte[0],HEX);

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.semesin.com/project/");
  Serial.println();

  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