Mengirim file di SDCard ke PC melalui ESP8266 dengan Arduino

Mengunduh file (download) dari web server melaui web browser biasa kita lakukan. protokol yang umum digunakan adalah FTP atau HTTP. Perancangan kali ini menggunakan Arduino sebagai file server. File-filenya disimpan dalam SDCard, dan untuk berkomunikasi dengan jaringan menggunakan modul wifi ESP8266.

Perancangan sistem file server arduino ini mampu menampilkan File SDCard ke web browser, daftar file file ini merupakan isi dari root direktori kartu memori.

Untuk mendownload file dari arduino, buka browser di halaman sesuai alamat IP Wifi ESP8266 kemudian klik file yang akan didownload, atau jika ingin mendownload secara langsung bisa melalui link http://(alamat ip ESP8266)/namafile.ext.

Komponen yang digunakan :

  1. Arduino Mega
  2. Modul MicroSD
  3. Modul ESP8266 ESP12E

skema download file dari kartu memori melalui ESP8266 :

Sebelum digunakan edit dulu bagian ini :

#include <SPI.h>
#include <SD.h>
#include "WiFiEsp.h"

char ssid[] = "Twim";
char pass[] = "12345678";
int status = WL_IDLE_STATUS;

WiFiEspServer server(80);
File FileSDCard;

void setup() {
  Serial.begin(115200);
  Serial.println("Mengirim file di SDCard ke PC melalui ESP8266 dengan Arduino");
  Serial.println("https://www.project.semesin.com");

  Serial1.begin(115200);
  WiFi.init(&Serial1);

  if (!SD.begin(53)) 
  {
    Serial.println("Gagal memulai kartu memori!");
    while (true);
  }
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("Modul ESP8266 tidak ditemukan");
    while (true);
  }

  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    status = WiFi.begin(ssid, pass);
  }

  Serial.println("You're connected to the network");
  printWifiStatus();
  server.begin();
}

void loop() {
  WiFiEspClient client = server.available();
  if (client) {
    boolean currentLineIsBlank = true;
    String request = "";

    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        request += c;
        if (c == '\n' && currentLineIsBlank) {
          break;
        }
        if (c == '\n') {
          currentLineIsBlank = true;
        }
        else if (c != '\r') {
          currentLineIsBlank = false;
        }
      }
    }
    client.println("HTTP/1.1 200 OK");
    client.println("Connection: close");

    Serial.println(request);
    Serial.println();
    Serial.println("Mengirim respon");

    int startFilename = request.indexOf('/') + 1;
    int endFilename = request.indexOf("HTTP/1.1") - 1;

    String fileRquest = request.substring(startFilename, endFilename);
    Serial.print("Permintaan file : ");
    Serial.println(fileRquest);

    if(fileRquest == "")
    {
      File root = SD.open("/");
      client.println("Content-Type: text/html");
      client.println();
      client.print("<!DOCTYPE HTML>");
      client.print("<html>");
      client.print("Daftar file:<br>");

      while (true) 
      {
        File entry =  root.openNextFile();
        if (! entry) {
          break;
        }
        client.print("<a href=\"");
        client.print(entry.name());
        client.print("\">");
        client.print(entry.name());
        client.print("</a><br>");
        entry.close();
      }
      client.print("</html>\r\n");
    }
    else if (!SD.exists(fileRquest)) 
    {
      client.println("Content-Type: text/html");
      client.println();
      client.print("<!DOCTYPE HTML>");
      client.print("<html>");
      client.print("Maaf file tidak ditemukan");
      client.print("</html>");
    } 
    else 
    {
      Serial.print("Mengirim file : ");
      Serial.println(fileRquest);
      FileSDCard = SD.open(fileRquest, FILE_READ);

      client.print("Content-Length:");
      client.println((String)FileSDCard.size());
      client.println("Content-Type: text/plain");
      client.print("Content-Disposition: attachment; filename=\"");
      client.print(fileRquest);
      client.println("\"");
      client.println("Content-Transfer-Encoding: binary");
      client.println();

      for(uint16_t i=0;i<FileSDCard.size();i++)
      {
        client.write(FileSDCard.read());
      }
      FileSDCard.close();
    }
    delay(10);
    client.stop();
    Serial.println("Client disconnected");
  }
}
void printWifiStatus()
{
  IPAddress ip = WiFi.localIP();
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());
  Serial.print("IP Address: ");
  Serial.println(ip);
  Serial.println();
  Serial.print("To see this page in action, open a browser to http://");
  Serial.println(ip);
  Serial.println();
}

keluaran serial monitor:

library yang digunakan :
WifiEsp.zip

Fakta menarik arduino

Fakta arduino yang menarik dicermati (arduino fun fact):

  1. Pin 13 pada Arduino terhubung ke onboard led ‘L’.
  2. Maksimum nilai delayMicroseconds(x) adalah x = 16383.
  3. Nilai millis() akan kembai ke angka 0 dalam 49.7 hari atau 49 hari 17 jam 2 menit dan 47 detik
  4. Pin digital mampu mengalirkan arus 40mA (200mA dalam satu port) dan mampu menghidupkan LED (menggunakan resistor sebagai pembatas arus).
  5. analogWrite(pin, value) hanya berlaku pada pin: UNO 3, 5, 6, 9, 10, dan 11; MEGA 2 – 13 dan 44 – 46.
  6. Memiliki fitur Capacitive touch sensing.
  7. Maximum frekuensi PWM 8.000.000 Hz (8MHz).
  8. Maksimal baud rate Serial = 2.000.000 (2Mbps). Arduino 101 57600bps
  9. Flash memory Mega 8KBytes; UNO 2KBytes
  10. RAM Mega 256KBytes; UNO 32KBytes
  11. EEPROM Mega 4KBytes; UNO 1KBytes
  12. Area kode program uno 32.256 byte, nano 30.720 byte, micro 28.672 byte, walaupun sama-sama menggunakan ATMega328
  13. EEPROM Mega 4KBytes; UNO 1KBytes
  14. Fitur virtual komunikasi serial bisa menggunakan Fungsi SoftewareSerial dengan ketentuan :
    • Jika menggunakan lebih dari satu SoftewareSerial maka hanya satu SoftewareSerial yang bisa menerima data (receive) dalam satu waktu.
    • Pada arduino mega hanya pin-pin berikut yang bisa digunakan sebagai pin RX: 10, 11, 12, 13, 14, 15, 50, 51, 52, 53, A8, A9, A10, A11, A12, A13, A14 , A15. Pada Leonardo hanya : 8, 9, 10, 11, 14, 15, 16. dan Arduino 101 selain pin 13.
  15. Nama lain/alias pin Arduino Uno
    • 0 = RX
    • 1 = TX
    • 10 = SS
    • 11 = MOSI
    • 12 = MISO
    • 13 = SCK
    • A0 = 14
    • A1 = 15
    • A2 = 16
    • A3 = 17
    • A4 = 18
    • A5 = 19
  16. Nama lain/alias pin Arduino mega
    • 0 = RX
    • 1 = TX
    • 50 = MISO
    • 51 = MOSI
    • 52 = SCK
    • 53 = SS
    • A0 = 54
    • A1 = 55
    • A2 = 56
    • A3 = 57
    • A4 = 58
    • A5 = 59
    • A6 = 60
    • A7 = 61
    • A8 = 62
    • A9 = 63
    • A10 = 64
    • A11 = 65
    • A12 =66
    • A13 = 67
    • A14 = 68
    • A15 = 69

Membaca file txt dari SDCard/kartu memori kedalam struct menggunakan Arduino (Aplikasi Bel Sekolah)

SDCard/microSD/kartu memori merupakan media penyimpanan yang banyak digunakan pada perangkat-perangkat elektronik untuk menyimpan foto, video, data dll. Dalam aplikasi arduino SDCard bisa dibaca/ditulis dalam protokol SPI, format yang didukung adalah FAT.

Dalam perancangan ini saya menggunakan file .txt yang tersusun sebagai database (contoh database jadwal bel sekolah), dan dibaca dengan arduino untuk kemudian dimasukkan kedalam tabel “jadwalBelajar”.

format yang digunakan adalah

Aktif | Waktu | Hari aktif | Minggu aktif | Kegiatan

Aktif
Sebagai status item/baris jadwal diaktifkan atau tidak(diabaikan)
isiannya Aktif, Tidak Aktif

Waktu
Format waktu jam:menit

Hari aktif
hari dimana item jadwal diaktifkan
isiannya: Senin, Selasa, Rabu, Kamis, Jum’at, Sabtu, Minggu atau kombinasinya dalam bentuk array didalam tanda ‘{‘ dan ‘}’

minggu aktif
isiannya kombinasi minggu bentuk array didalam tanda ‘{‘ dan ‘}’

Kegiatan
text Kegiatan

Skema:

sketch/program:

#include <avr/pgmspace.h>
#include <SPI.h>
#include <SD.h>

struct Waktu
{ 
  byte jam; 
  byte menit; 
};

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

const PROGMEM char kegiatan[][21] = 
{
  "Jam Pelajaran 1\0    ",
  "Jam Pelajaran 2\0    ",
  "Jam Pelajaran 3\0    ",
  "Jam Pelajaran 4\0    ",
  "Jam Pelajaran 5\0    ",
  "Jam Pelajaran 6\0    ",
  "Jam Pelajaran 7\0    ",
  "Jam Pelajaran 8\0    ",
  "Jam Pelajaran 9\0    ",
  "Jam Pelajaran 10\0   ",
  "Jam Pelajaran 11\0   ",
  "Jam Pelajaran 12\0   ",
  "Jam Pelajaran 13\0   ",
  "Jam Pelajaran 14\0   ",
  "Jam Pelajaran 15\0   ",
  "Masuk\0              ",
  "Upacara\0            ",
  "Istirahat\0          ",
  "Selesai Istirahat\0  ",
  "Kepramukaan\0        ",
  "Khusus\0             ",
  "Pulang\0             ",
  "Pulang Jumat\0       ",
  "Pulang Sabtu\0       "
};

const char namaHari[][7] PROGMEM = 
{
  "Minggu",
  "Senin\0",
  "Selasa",
  "Rabu\0 ",
  "Kamis\0",
  "Jum'at",
  "Sabtu\0"
};

const char Aktifasi[][12] PROGMEM = 
{
  "Aktif\0     ",
  "Tidak aktif"
};

#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


#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[150];
Waktu waktu;
File fileJadwal;
char buffer[21];

void setup() {
  Serial.begin(9600);
  Serial.println("Membaca file txt dari SDCard/kartu memori kedalam struct menggunakan Arduino");
  Serial.println("(Aplikasi Bel Sekolah)");
  Serial.println("https://www.project.semesin.com");
}

void loop() {
  if (!SD.begin(53)) 
  {
    Serial.println("Gagal memulai kartu memori!");
  }
  else
  {  
    if (!SD.exists("Jadwal.txt")) 
    {
      Serial.println("file Jadwal.txt tidak ditemukan");
    } 
    else 
    {
      char c;
      byte index;
      String str;
      fileJadwal = SD.open("Jadwal.txt", FILE_READ);
      while(1)
      {
        c = fileJadwal.peek();
        if(c == -1)
        {
          break;
        }
        else if(c == '/')
        {
          while(fileJadwal.read() != '\n');
          continue;
        }
        else if((c == '\r') || (c == '\n') || (c == '\t') || (c == ' '))
        {
          fileJadwal.read();
          continue;
        }
        jadwalBelajar[index].aktif = ambilCSVAktif(fileJadwal);
        ambilCSVWaktu(&jadwalBelajar[index].waktu.jam,&jadwalBelajar[index].waktu.menit, fileJadwal);
        jadwalBelajar[index].hariAktif = ambilCSVHari(fileJadwal);
        jadwalBelajar[index].mingguAktif = ambilCSVMinggu(fileJadwal);
        jadwalBelajar[index].kegiatan = ambilCSVKegiatan(fileJadwal);

        copyFlashString(buffer, &Aktifasi[jadwalBelajar[index].aktif][0]);
        Serial.print(buffer);
        Serial.print('\t');
        if(jadwalBelajar[index].waktu.jam < 10) Serial.print('0');
        Serial.print(jadwalBelajar[index].waktu.jam);
        Serial.print(':');
        if(jadwalBelajar[index].waktu.menit < 10) Serial.print('0');
        Serial.print(jadwalBelajar[index].waktu.menit);
        Serial.print('\t');
        Serial.print(jadwalBelajar[index].hariAktif,HEX);
        Serial.print('\t');
        Serial.print(jadwalBelajar[index].mingguAktif,HEX);
        Serial.print('\t');
        copyFlashString(buffer, &kegiatan[jadwalBelajar[index].kegiatan][0]);
        Serial.println(buffer);
      }
      fileJadwal.close();
    }
  }

  while(1);//***
}
byte ambilCSVAktif(File file)
{
  String str = ambilCSVDariFile(file);

  for(byte i=0;i<sizeof(Aktifasi)/sizeof(Aktifasi[0]);i++)
  {
    copyFlashString(buffer, &Aktifasi[i][0]);
    if(str.equalsIgnoreCase((String)buffer))
    {
      return i;
    }
  }
}
void ambilCSVWaktu(byte* jam, byte* menit, File file)
{
  String str = ambilCSVDariFile(file);
  byte pos1 = str.indexOf(':');
  *jam = str.substring(0,pos1).toInt();
  *menit = str.substring(pos1+1).toInt();
}
byte ambilCSVHari(File file)
{
  String str = ambilCSVDariFile(file);
  for(byte i=0;i<sizeof(namaHari)/sizeof(namaHari[0]);i++)
  {
    copyFlashString(buffer, &namaHari[i][0]);
    if(str.equalsIgnoreCase((String)buffer))
    {
      return 1<<(7-i);
    }
  }
  byte dataHari = 0;
  byte shift = 0x80;
  byte hari[7];

  ambilCSVDariString(hari, str);
  for(byte i=0;i<sizeof(hari);i++)
  {
    if(hari[i])
    {
      dataHari += shift;
    }
    shift >>= 1;
    
  }
  return dataHari;
}

byte ambilCSVMinggu(File file)
{
  byte dataMinggu = 0;
  byte shift = 0x80;
  byte minggu[5];
  String str = ambilCSVDariFile(file);

  ambilCSVDariString(minggu, str);
  for(byte i=0;i<sizeof(minggu);i++)
  {
    if(minggu[i])
    {
      dataMinggu += shift;
    }
    shift >>= 1;
  }
  return dataMinggu;
}

byte ambilCSVKegiatan(File file)
{
  String str = ambilCSVDariFile(file);
  for(byte i=0;i<sizeof(kegiatan)/sizeof(kegiatan[0]);i++)
  {
    copyFlashString(buffer, &kegiatan[i][0]);
    if(str.equalsIgnoreCase((String)buffer))
    {
      return i;
    }
  }
}
String ambilCSVDariFile(File file)
{
  String str = "";
  char terminator = ',';

  while(1)
  {
    char c = file.peek();
    if(c == '{')
    {
      terminator = '}';
      file.read();
    }
    else if(((c != terminator) && (c != '\r')))
    {
      str += (char)file.read();
    }
    else
    {
      file.read();
      if(terminator == '}')
      {
        terminator = ',';
      }
      else if((terminator == ',') || (c == '\r'))
      {
        break;
      }
    }
  }
  str.trim();
  return str;
}
void copyFlashString(char* buf, const char* alamat)
{
  char c;
  while(c = pgm_read_byte(alamat++))
  {
    *buf++ = c;
  }
  *buf = 0;
}
void ambilCSVDariString(byte *minggu, String str)
{
  while(1)
  {
    int pos = str.indexOf(',');
    *minggu++ = str.substring(0,pos).toInt();
    if(pos == -1)
    {
      break;
    }
    str = str.substring(pos+1);
  }
}

contok keluaran serial monitor:

Contoh file jadwal.txt:
Jadwal.txt

Bel sekolah bluetooth dengan kontrol android : disini

Bel Sekolah menggunakan tabel database fleksibel berbasis Arduino

Bel sekolah digital dimanfaatkan untuk menentukan waktu pertukaran kegiatan di sekolah-sekolah. Jadwal untuk sebuah sekolah berbeda dengan sekolah lainnya, baik perbedaan tingkat sekolah maupun zona waktunya. Selain itu terdapat juga perbedaan jadwal untuk masing masing hari, seperti senin pertama setiap bulan untuk upacara dan hari lainnya yang mempunyai kekhususan waktu. Bel sekolah digital berbasis arduino atau mikrokontroller lain memiliki kemampuan penjadwalan jam pelajaran tersebut. selain itu bel sekolah digital arduino juga mudah dalam pengembangan nantinya.

Dalam perancangan bel sekolah arduino ini dibatasi hanya sampai pengaplikasian tabel database arduino fleksibel yang bisa disesuaikan dengan kebutuhan. Bel sekolah berbasis arduino ini hanya berupa penerapan dasar saja yang bisa dikombinasikan dengan aplikasi Bel Sekolah fungsional lainnya.

Untuk pengembangannya bisa saja perangkat bel sekolah otomatis berbasis arduino ini ditambahkan sistem entri data dari bluetooth, wifi, PC dll. juga bisa ditambahkan sistem output multimedia seperti alarm, suara, running text, DMD dan lain-lain.

Kelebihan perancangan ini adalah:

  1. Tabel database jadwal fleksibel
  2. Pembacaan RTC dengan interupsi sehingga menghemat resource
  3. Pemanfaatan sleep mode

Komponen yang digunakan:

  1. Arduino Uno
  2. RTC DS3231

berikut skema / rangkaian bel sekolah dengan arduino:

dan program/sketch Arduino:

#include <DS3232RTC.h>
#include <avr/sleep.h>

#define SQWPin 12

struct Waktu
{ 
  byte jam; 
  byte menit; 
};

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

enum _kegiatan
{
  JamPelajaran1,
  JamPelajaran2,
  JamPelajaran3,
  JamPelajaran4,
  JamPelajaran5,
  JamPelajaran6,
  JamPelajaran7,
  JamPelajaran8,
  JamPelajaran9,
  JamPelajaran10,
  JamPelajaran11,
  JamPelajaran12,
  JamPelajaran13,
  JamPelajaran14,
  JamPelajaran15,
  Masuk,
  Upacara,
  Istirahat,
  SelesaiIstirahat,
  Kepramukaan,
  Khusus,
  Pulang,
  PulangJumat,
  PulangSabtu
};

volatile bool interupsiDetik;
byte indexMataPelajaran;

#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

String namaHari[] = {"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[35];
Waktu waktu;
tmElements_t tm;

void setup() {
  byte i = 0;

  //Senin
  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};

  indexMataPelajaran = i;

  Serial.begin(9600);
  Serial.println("Bel Sekolah menggunakan tabel database flexibel berbaasis Arduino");
  Serial.println("Bisa ditambahkan input bluetooth, wifi, PC, komputer dll");
  Serial.println("Bisa ditambahkan output multimedia seperti suara, alarm, running text, dmd dll");
  Serial.println("https://www.project.semesin.com");
  
  byte ControlRegister;
  RTC.readRTC(0x0E,&ControlRegister,1);
  ControlRegister &= ~(0x07<<2);
  RTC.writeRTC(0x0E, &ControlRegister,1);

  *digitalPinToPCMSK(SQWPin) |= bit (digitalPinToPCMSKbit(SQWPin));
  PCIFR  |= bit (digitalPinToPCICRbit(SQWPin));
  PCICR  |= bit (digitalPinToPCICRbit(SQWPin));
 
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();

////Set waktu sekali saja
//  tm.Day = 1;
//  tm.Month = 4;
//  tm.Year = CalendarYrToTm(2018);
//  tm.Hour = 15;
//  tm.Minute = 24;
//  tm.Second = 00;
//
//  time_t t = makeTime(tm);
//  tm.Wday = dayOfWeek(t);
//  RTC.write(tm);
//  TampilkanWaktu();
}

ISR (PCINT0_vect)
{
  if(digitalRead(SQWPin))
  {
    interupsiDetik = true;
  }
}  

void loop() {
  
  
  if(interupsiDetik)
  {
    interupsiDetik = false;
    RTC.read(tm);
    
    if(tm.Second == 0)
    {
      TampilkanWaktu();//***
      
      byte hariKeDiTanggal1 = (((tm.Wday + 8)  - (tm.Day % 7)) % 7);
      byte SeninKe = ((tm.Day + 7 - hariKeDiTanggal1) / 7);
      byte mingguKe = ((tm.Day + 8 - hariKeDiTanggal1) / 7) + 1;
      
      for(byte i=0; i<indexMataPelajaran ;i++)
      {
        if(jadwalBelajar[i].aktif)
        {
          if((jadwalBelajar[i].waktu.jam == tm.Hour) && 
          (jadwalBelajar[i].waktu.menit == tm.Minute) && 
          (jadwalBelajar[i].hariAktif & (1<<(8-tm.Wday))) &&
          (jadwalBelajar[i].mingguAktif & (1<<(8-SeninKe))))
          {
            TampilkanWaktu();
            //Bisa ditambahkan sistem output
            switch(jadwalBelajar[i].kegiatan)
            {
              case JamPelajaran1:
                Serial.println("Jam Pelajaran 1");
                break;
              case JamPelajaran2:
                Serial.println("Jam Pelajaran 2");
                break;
              case JamPelajaran3:
                Serial.println("Jam Pelajaran 3");
                break;
              case JamPelajaran4:
                Serial.println("Jam Pelajaran 4");
                break;
              case JamPelajaran5:
                Serial.println("Jam Pelajaran 5");
                break;
              case JamPelajaran6:
                Serial.println("Jam Pelajaran 6");
                break;
              case JamPelajaran7:
                Serial.println("Jam Pelajaran 7");
                break;
              case JamPelajaran8:
                Serial.println("Jam Pelajaran 8");
                break;
              case JamPelajaran9:
                Serial.println("Jam Pelajaran 9");
                break;
              case JamPelajaran10:
                Serial.println("Jam Pelajaran 10");
                break;
              case JamPelajaran11:
                Serial.println("Jam Pelajaran 11");
                break;
              case JamPelajaran12:
                Serial.println("Jam Pelajaran 12");
                break;
              case JamPelajaran13:
                Serial.println("Jam Pelajaran 13");
                break;
              case JamPelajaran14:
                Serial.println("Jam Pelajaran 14");
                break;
              case JamPelajaran15:
                Serial.println("Jam Pelajaran 15");
                break;
              case Masuk:
                Serial.println("Masuk");
                break;
              case Upacara:
                Serial.println("Upacara");
                break;
              case Istirahat:
                Serial.println("Istirahat");
                break;
              case SelesaiIstirahat:
                Serial.println("Selesai istirahat");
                break;
              case Kepramukaan:
                Serial.println("Kepramukaan");
                break;
              case Khusus:
                Serial.println("Khusus");
                break;
              case Pulang:
                Serial.println("Jam pelajaran telah selesai, sampai jumpa esok hari");
                break;
              case PulangJumat:
                Serial.println("Jam pelajaran telah selesai, sampai jumpa minggu depan");
                break;
              case PulangSabtu:
                Serial.println("Jam pelajaran telah selesai, sampai jumpa minggu depan");
                break;
              default:
                Serial.println("Lainnya");
                break;
            }
          }
        }
      }
    }
  }
  delay(100);//Selesaikan Serial nge print, hapus saja jika tidak diperlukan
  sleep_mode();
}
void TampilkanWaktu()
{
  Serial.print("Waktu = ");
  print2digits(tm.Hour);
  Serial.write(':');
  print2digits(tm.Minute);
  Serial.write(':');
  print2digits(tm.Second);
  Serial.print(", Tanggal = ");
  Serial.print(namaHari[tm.Wday-1]);
  Serial.write(' ');
  Serial.print(tm.Day);
  Serial.write('/');
  Serial.print(tm.Month);
  Serial.write('/');
  Serial.print(tmYearToCalendar(tm.Year));
  Serial.println();
}

void print2digits(int number) {
  if (number >= 0 && number < 10) {
    Serial.write('0');
  }
  Serial.print(number);
}

contoh Bel Sekolah Arduino Uno dengan output modul mini MP3 DFPlayer (library DFPlayer_Mini_Mp3.h) dan software serial : Bel_Sekolah_tanpa_interupsi.ino

Bel sekolah bluetooth dengan kontrol android : disini

Pengukuran kecepatan objek (dua arah) dengan Arduino dan photo dioda menggunakan metode interupsi serta timer

Manfaat mengukur kecepatan benda menggunakan arduino antara lain : pengukuran laju kendaraan/mobil, mencari kecepatan bandul, menentukan kecepatan benda jatuh, mengukur laju pukulan dan lain-lain.

Prinsip kerja menentukan kecepatan benda pada arduino adalah mendeteksi objek melewati dua sensor photodioda yang disinari dengan infra merah dari LED dengan jarak yang diketahui. Ketika sebuah objek melewati photodioda dan menghalangi sinar sumbernya, maka photodioda akan memberikan sinyal aktif ke arduino melalui jalur interupsi sebagai nilai awal, demikian pula ketika objek melewati photodioda kedua yang disimpan sebagai nilai akhir. Pembagian antara jarak sensor dengan waktu ini akan diperoleh kecepatan.

Pengukuran kecepatan dengan arduino (dalam meter/detik atau km/jam) mengharuskan perhitungan waktu yang efektif guna memperoleh hasil pengukuran yang akurat. Kesalahan 1 mili detik saja akan menghasilkan perbedaan kecepatan yang cukup jauh. untuk mengurangi resiko ini bisa dilakukan dengan menambah jarak sensor.

berikut skema perancangan mencari kecepatan objek dengan arduino:

catatan: photodioda dipasang terbalik (plus ke ground dan minus ke pin Arduino pullup) agar menghasilkan output berupa digital signifikan.

program atau sketch menentukan kecepatan benda menggunakan arduino :

#define sensor1 2
#define sensor2 3
#define jarakSensor 0.05 //meter
#define waktuTick 1/8000000 //detik
#define mpsKekmph 3.6

volatile uint16_t waktuA;
volatile uint16_t waktuB;
volatile uint16_t overflowTimer;
volatile bool dariA;
volatile bool dariB;
volatile bool sampaiA;
volatile bool sampaiB;
double Kecepatan;

void setup() {
  Serial.begin(9600);
  Serial.println("Pengukuran kecepatan objek (dua arah) dengan Arduino");
  Serial.println("dan photo dioda menggunakan metode interupsi serta timer");
  Serial.println("https://www.project.semesin.com");

  pinMode(sensor1, INPUT_PULLUP);
  pinMode(sensor2, INPUT_PULLUP);
  
  EIFR |= _BV(INTF1) |_BV(INTF0);
  TIMSK1 |= _BV(TOIE1);
  TCCR1A = 0;

  attachInterrupt(digitalPinToInterrupt(sensor1), objekMelewatiA, RISING);
  attachInterrupt(digitalPinToInterrupt(sensor2), objekMelewatiB, RISING);
}

void loop() {
  if(dariA && sampaiB)
  {
    Kecepatan = 1.0 * jarakSensor / ((double)waktuTick * ((uint32_t)waktuB + (overflowTimer * 65535)));
    Serial.print("Kecepatan = ");
    printDouble(Kecepatan, 6);
    Serial.print(" meter/detik (");
    printDouble(Kecepatan * mpsKekmph, 6);
    Serial.println(" km/jam) dari A ke B");
    dariA = false;  
    sampaiB = false;  
  }
  else if(dariB && sampaiA)
  {
    Kecepatan = 1.0 * jarakSensor / ((double)waktuTick * ((uint32_t)waktuA + (overflowTimer * 65535)));
    Serial.print("Kecepatan = ");
    printDouble(Kecepatan, 6);
    Serial.print(" meter/detik (");
    printDouble(Kecepatan * mpsKekmph, 6);
    Serial.println(" km/jam) dari B ke A");
    dariB = false;  
    sampaiA = false;  
  }
  else if(dariA || dariB)
  {
    if(overflowTimer > 5000)
    {
      dariA = false;
      dariB = false;
      TCCR1B = 0;
      Serial.println("Terlalu lambat");
    }
  }
}

void objekMelewatiA()
{
  if(dariB)
  {
    waktuA = TCNT1;
    TCCR1B = 0;
    sampaiA = true;
  }
  else
  {
    TCNT1 = 0;
    TCCR1B = _BV(CS10);
    TIFR1 |= _BV(TOV1);
    overflowTimer = 0;
    dariA = true;
  }
}
void objekMelewatiB()
{
  if(dariA)
  {
    waktuB = TCNT1;
    TCCR1B = 0;
    sampaiB = true;
  }
  else
  {
    TCNT1 = 0;
    TCCR1B = _BV(CS10);
    TIFR1 |= _BV(TOV1);
    overflowTimer = 0;
    dariB = true;
  }
}

ISR(TIMER1_OVF_vect)
{
  overflowTimer++;
}

void printDouble(double nilai, byte belakangKoma)
{
  if(nilai < 0.0)
  {
    Serial.print('-');
    nilai = -nilai;
  }
  
  Serial.print ((long)nilai);
  if( belakangKoma > 0) 
  {
    Serial.print(".");
    unsigned long frac;
    unsigned long mult = 1;
    byte padding = belakangKoma - 1;
    while(belakangKoma--)
    {
      mult *=10;
    }
    if(nilai >= 0)
    {
      frac = (nilai - int(nilai)) * mult;
    }
    else
    {
      frac = (int(nilai)- nilai ) * mult;
    }
    unsigned long frac1 = frac;
    while( frac1 /= 10 )
    {
      padding--;
    }
    while(  padding--)
    {
      Serial.print("0");
    }
    Serial.print(frac,DEC) ;
 }
}

Mengatur kecepatan motor DC 5V menggunakan PWM

Untuk menggerakkan motor DC diperlukan driver, driver motor adalah rangkaian elektronika yang mampu menghasilkan arus yang besar untuk belitan motor. driver yang umum digunakan adalah formasi jembatan (full bridge) seperti chip L293, L298.

Salah satu rangkaian favorit (sederhana) untuk menggerakkan motor
dc adalah rangkaian push pull atau rangkaian totem pole.

skema rangkaian totem pole (rangkaian tarik-ulur) menggunakan transistor:

kombinasi transistor yang bisa digunakan adalah NPN BD139 dan PNP BD140 yang mampu melewatkan arus hingga 1A. Rangkaian ini memiliki kekurangan yaitu tegangan keluaran sama dengan tegangan masukan (input) dikurangi tegangan Vbe sekitar 0.6v.

Agar rangkaian pushpull/totempole bisa menghasilkan tegangan output lebih besar misalnya 12 volt, bisa melengkapinya dengan opamp.

Skema rangkaian pushpull dengan opamp:

Jika dikombinasikan dengan PWM dari arduino, maka rangkaian ini berfungsi sebagai Pengatur kecepatan motor dc yang memiliki fungsi kecepatan, selain itu juga fungsi maju dan mundur.

Untuk mengatur kecepatan motor dc menggunakan arduino, rangkaian disusun menjadi dua sisi (untuk polaritas positif dan negatif) dan dua input pwm.

Skema pengontrol kecepatan motor dc dengan arduino:

berikut contoh sketch atau program yang bisa diaplikasikan.

#define pinMotorA 9
#define pinMotorB 10

bool motorMaju = false;
bool motorHidup = false;
char chrKecepatan[5];
byte Kecepatan;

void setup() {
  Serial.begin(9600);
  Serial.println("Mengatur kecepatan motor DC 5V menggunakan PWM dilengkapi aksi maju-mundur");
  Serial.println("dengan rangkaian sederhana menggunakan Arduino dan 4 transistor");
  Serial.println("entri [M] untuk maju");
  Serial.println("entri [m] untuk mundur");
  Serial.println("entri [Kxxx] untuk set kecepatan, xxx = 0 s/d 255");
  Serial.println("entri [+] untuk menambah kecepatan 10 angka");
  Serial.println("entri [-] untuk kurangi kecepatan 10 angka");
  Serial.println("entri [B] untuk berhenti");
  Serial.println("https://www.project.semesin.com");

  pinMode(pinMotorA, OUTPUT);
  pinMode(pinMotorB, OUTPUT);
}

void loop() {
  if(Serial.available())
  {
    char c = Serial.read();
    if(c == 'M')
    {
      motorMaju = true;
      Serial.println("Motor maju");
      motorHidup = true;
    }
    else if(c == 'm')
    {
      motorMaju = false;
      Serial.println("Motor mundur");
      motorHidup = true;
    }
    else if(toupper(c) == 'K')
    {
      delay(10);
      Serial.readBytesUntil('\n', chrKecepatan, sizeof(chrKecepatan));
      Kecepatan = String(chrKecepatan).toInt();
      Serial.print("Set kecepatan = ");
      Serial.println(Kecepatan);
    }
    else if(c == '+')
    {
      if(Kecepatan <= 245)
      {
        Kecepatan += 10;
      }
      Serial.print("Set kecepatan = ");
      Serial.println(Kecepatan);
    }
    else if(c == '-')
    {
      if(Kecepatan >= 10)
      {
        Kecepatan -= 10;
      }
      Serial.print("Set kecepatan = ");
      Serial.println(Kecepatan);
    }
    else if(toupper(c) == 'B')
    {
      digitalWrite(pinMotorA, LOW);
      digitalWrite(pinMotorB, LOW);
      motorHidup = false;
      Serial.println("Motor berhenti");
    }
  }
  if(motorHidup)
  {
    if(motorMaju)
    {
      digitalWrite(pinMotorB, LOW);
      analogWrite(pinMotorA, Kecepatan);
    }
    else
    {
      digitalWrite(pinMotorA, LOW);
      analogWrite(pinMotorB, Kecepatan);
    }
  }

}

jika terjadi kedala Arduino me-reset ketika running, bisa diakibatkan Arduino kekurangan catu daya (power supply).

fungsi analogWrite() dengan variabel duty cycle dan frekuensi (aplikasi PWM)

Perintah analogWrite() dengan variabel duty cycle dan frekuensi pada pin 9 atau 10 ini berguna untuk membangkitkan sinyal PWM (PWM Generator) yang sangat variatif dari 0.24 Hz hingga 160kHz duty cycle 0.0 ~ 100.0%,sedangkan frekuensi 160kHz hingga 8MHz dengan duty cycle terbatas.

Modifikasi analogWrite ini menggunakan timer1 sebagai penanda waktunya, sehinggga hanya akan mempengaruhi pin 9 dan 10 saja, dan tidak bisa dikombinasikan dengan fungsi PWM arduino biasa.

Parameter PWM yang digunakan:

  1. pin, hanya pin 9 dan 10
  2. dutyCyclePersen, nilai 0..100 adalah lebar pulsa on dalam persen
  3. frekuensi, nilai 0.24 hingga 8000000 (8Mhz)

berikut sketch/program fungsi analogWrite serbaguna

#define PWM_1 9
#define PWM_2 10

void setup() {
  Serial.begin(9600);
  Serial.println("fungsi analogWrite() dengan variabel duty cycle dan frekuensi pada pin 9 atau 10");
  Serial.println("Pada penggunaan kombinasi pin 9 dan 10,");
  Serial.println("maka frekuensi yang di set terakhir yang akan digunakan");
  Serial.println("https://www.project.semesin.com");
}

void loop() {
  //analogWrite(pin, dutyCyclePersen, frekuensi)
  analogWrite(PWM_1, 50, 1000);
  while(1);
}

void analogWrite(byte pin, double dutyCyclePersen, double frekuensi)
{
  if(frekuensi > 244.0)
  {
    TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);
    ICR1 = (16000000L/(frekuensi)) - 1;
  }
  else if(frekuensi > 30.0)
  {
    TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11);
    ICR1 = (16000000L/(frekuensi * 8)) - 1;
  }
  else if(frekuensi > 3.0)
  {
    TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11) | _BV(CS10);
    ICR1 = (16000000L/(frekuensi * 64)) - 1;
  }
  else if(frekuensi > 0.95)
  {
    TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12);
    ICR1 = (16000000L/(frekuensi * 256)) - 1;
  }
  else if(frekuensi > 0.23)
  {
    TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12) | _BV(CS10);
    ICR1 = (16000000L/(frekuensi * 1024)) - 1;
  }
  TCCR1A &= ~_BV(WGM10);
  TCCR1A |= _BV(WGM11);
  if(pin == 9)
  {
    pinMode(9, OUTPUT);
    TCCR1A |= _BV(COM1A1);
    OCR1A = (dutyCyclePersen/100) * (ICR1 + 1);
  }
  if(pin == 10)
  {
    pinMode(10, OUTPUT);
    TCCR1A |= _BV(COM1B1);
    OCR1B = (dutyCyclePersen/100) * (ICR1 + 1);
  }
}

Mengukur tegangan Vcc Arduino

Pengembangan perangkat arduino pintar salah satunya adalah mampu mendeteksi suplai tegangan utama-nya yati tegangan Vcc. Tujuannya adalah :

  1. mendeteksi secara dini kegagalan yang mungkin terjadi seperti kehilangan daya utama dan menyiapkan tenaga cadangan
  2. Menyimpan data-data penting kedalam EEPROM apabila ada indikasi tegangan akan drop

Cara mengukuran Vcc Arduino bisa dilakukan dengan dua cara :

Cara pertama

Menggunakan rangkaian pembagi tegangan sebagai input ke ADC yang menggunakan internal reference (1.1Volt)

resistor yang digunakan haruslah memiliki toleransi rendah / presisi tinggi (misal 1%).

skema pengukuran tegangan suplai arduino:

sketch / program menghitung tegangan catu daya arduino:

//pin
#define detektorBaterai A0

//Konstanta
#define resistorPlus 10000L
#define resistorGround 1000L
#define teganganReferesiAnalog 1.1

void setup() {
  Serial.begin(9600);
  Serial.println("Pengukuran tegangan Vcc Arduino");
  Serial.println("https://www.project.semesin.com");

  analogReference(INTERNAL1V1);
}

void loop() {
  uint16_t rawVcc = 0;
  for(int i=0;i<10;i++)
  {
    rawVcc += analogRead(detektorBaterai); 
  }
  rawVcc /= 10;
  
  double teganganPembagi = teganganReferesiAnalog * rawVcc / 1023;//Volt
  double teganganVcc = teganganPembagi * ((resistorPlus + resistorGround) / resistorGround);//Volt

  Serial.print("Tegangan Vcc = ");
  Serial.println(teganganVcc);

  delay(1000);
}

Cara kedua

Pengukuran tegangan internal bandgap (1.1V) untuk dibandingkan dengan nilain ADC dari tegangan bandgap ideal.

nilai ADC ideal tegangan bandgap adalah:

dengan menggunakan rumusan perbandingan :

metode ini tanpa komponen external dengan sketch:

//Konstanta
#define ADCBandgapIdeal 225.06
#define TeganganVccIdeal 5.0

void setup() {
  Serial.begin(9600);
  Serial.println("Pengukuran tegangan Vcc Arduino");
  Serial.println("https://www.project.semesin.com");
}

void loop() {
  uint16_t rawVcc = 0;
  for(int i=0;i<10;i++)
  {
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);//ADC channel bandgap
    ADCSRA |= _BV( ADSC );
    while( ( (ADCSRA & (1<<ADSC)) != 0 ) );
    rawVcc += ADC; 
  }
  rawVcc /= 10;
  
  double teganganVcc = ADCBandgapIdeal * TeganganVccIdeal / rawVcc;

  Serial.print("Tegangan Vcc = ");
  Serial.println(teganganVcc);

  delay(1000);
}

Arduino tenaga baterai dengan indikator baterai lemah

Board arduino (uno) membutuhkan tegangan nominal sebesar 5Volt yang dilengkapi dengan regulator 1117/7805 yang mampu meregulasi tegangan hingga 15Volt (max) menjadi 5Volt.

Penggunaan baterai sebagai catu daya arduino harus memiliki voltase diatas 6Volt (mengimbangi tegangan dropout) jiks menggunakan regulator 1117.

Dalam contoh ini saya menggunakan baterai kotak 9Volt. Untuk pembacaan tegangan baterai, sebagai penyetaraannya digunakan resistor pembagi tegangan dengan nilai 1KΩ dan 10KΩ (toleransi 1% dan kehilangan arus 0.8mA). Keluaran resistor pembagi tegangan menghasilkan tegangan 0.818 Volt jika tegangan baterai 9V.

Tegangan dari resistor pembagi tegangan ini menjadi input bagi ADC channel 0. untuk lebih memaksimalkan pembacaan ADC digunakan tegangan referensi analog internal 1.1Volt.

skema baterai arduino (arduino battery):

Sketch/program arduino dengan baterai:

//pin
#define detektorBaterai A0
#define indikatorBateraiLemah 13//internal LED

//Konstanta
#define resistorPlus 10000L
#define resistorGround 1000L
#define teganganReferesiAnalog 1.1

byte bateraiLemah;
bool statusIndikator;
long indikatorMillisMulai;
uint16_t delayIndikator = 1000;

void setup() {
  pinMode(indikatorBateraiLemah, OUTPUT);
  analogReference(INTERNAL);
  analogRead(detektorBaterai);
}

void loop() {
  uint16_t rawBaterai = 0;
  for(int i=0;i<10;i++)
  {
    rawBaterai += analogRead(detektorBaterai); 
  }
  rawBaterai /= 10;
  
  double teganganPembagi = teganganReferesiAnalog * rawBaterai / 1023;//Volt
  double teganganBaterai = teganganPembagi * ((resistorPlus + resistorGround) / resistorGround);//Volt

  if((teganganBaterai < 8.0) && (bateraiLemah != 2))
  {
    bateraiLemah = 2;
    indikatorMillisMulai = millis();
  }
  else if((teganganBaterai < 8.5) && (teganganBaterai > 8.1) && (bateraiLemah != 1))
  {
    bateraiLemah = 1;
    indikatorMillisMulai = millis();
  }
  if((teganganBaterai > 8.6) && bateraiLemah)//Hysteresis
  {
    bateraiLemah = 0;
    digitalWrite(indikatorBateraiLemah, LOW);
  }
  if(bateraiLemah && (millis() - indikatorMillisMulai > delayIndikator))
  {
    digitalWrite(indikatorBateraiLemah, statusIndikator);
    if(statusIndikator)
    {
      delayIndikator = 100;
    }
    else 
    {
      if(bateraiLemah == 1)
      {
        delayIndikator = 2000;
      }
      else if(bateraiLemah == 2)
      {
        delayIndikator = 200;
      }
    }
    statusIndikator = !statusIndikator;
    indikatorMillisMulai = millis();
  }
}

Memainkan suara 4-bit ADPCM langsung dari arduino

Rancangan bangun berbasis arduino kurang lengkap tanpa adanya suara. Fungsi suara tergantung kebutuhan, yang paling diutamakan adalah informasi audio. Perangkat audio arduino yang umum digunakan adalah mp3 shield, dan data suara mp3 disimpan dalam kartu memori/SDCard.

Untuk memainkan file suara dengan Arduino atau musik arduino memiliki permasalahan :

  1. Arduino memiliki memori flash yang kecil (Uno 32KB, Mega 256KB)
  2. Port yang hanya mampu mengeluarkan (source/sink) arus sebesar 40mA setiap pin.
  3. Kecepatan clock arduino yang hanya 16MHz, membatasi sampling rate dan ukuran sample.

Untuk menyimpan data suara lebih panjang kita bisa menggunakan data terkompresi, Salah satu sistem kompresi sederhana adalah 4-bit ADPCM (Adaptive differential pulse-code modulation), yaitu kompresi data suara kedalam 4-bit sehinggan file suara PCM yang umumnya 8-bit atau 16-bit bisa diperkecil menjadi 4-bit. Arduino adpcm juga bisa digunakan sebagai alternatif penggunaan modul suara WTV020SD yang juga bisa memainkan file dalam format 4-bit adpcm (.ad4)

Dalam contoh ini saya menggunakan sample rate 8KHz dan ukuran(size) sample 9 bit, alternatif yang disediakan 16KHz – 8 bit. Dengan properti 8KHz-9Bit, kompresi 4-bit ADPCM hanya menggunakan 4Kb setiap detiknya, sehingga uno mampu menyimpan 6 detik data suara (ruang memory lainnya digunakan oleh program), sedangkan mega mampu menampung 62 detik data suara.

untuk memaksimalkan arus port Arduino, saya menggunakan speaker 32Ohm.

Urusan memperhalus suara yang akan dihasilkan saya menggunakan metode bridge (tanpa kapasitor kopling) dengan menggunakan pin 9 dan 10 yang terhubung ke Timer1.

komponen:

  1. Arduino Uno
  2. Speaker 32ohm/head phone

skema:

Sketch / program :

 
#include "Selamat-datang-8000.h"
 
#define speakerA 9
#define speakerB 10
 
const int8_t IndexTable[16] = {
    -1, -1, -1, -1, 2, 4, 6, 8,
    -1, -1, -1, -1, 2, 4, 6, 8
};
int16_t StepSizeTable[89] = {
    7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
    19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
    50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
    130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
    337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
    876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
    2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
    5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
    15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};
struct AD4Header
{
  byte ad4Code;
  char ad4Sign[4];
  uint16_t ad4SampleRate;
};
#define ad4DataOffset 6
 
long predsample;
int8_t index;
uint16_t panjangSuara;
uint16_t sampleCounter;
byte *dataSuara;
bool PWMUpdate;
bool swap;
AD4Header ad4Header;
bool suaraDimainkan;
 
void setup(void)
{
  pinMode(speakerA, OUTPUT);
  pinMode(speakerB, OUTPUT);
 
  Serial.begin(9600);
  Serial.println("Memainkan suara 4-bit ADPCM dengan arduino");
  Serial.println("https://www.project.semesin.com/");
}
 
void loop(void)
{
  if(!suaraDimainkan)
  {
    mainkanSuara(dataSuaraSelamatDatang8000, sizeof(dataSuaraSelamatDatang8000));
  }
  else
  {
    lanjutkanSuara();
  }
}
 
void mainkanSuara(const byte *fileSuara, uint16_t ukuranFileSuara)
{
  dataSuara = (byte*)fileSuara;
   
  ad4Header.ad4Code = pgm_read_byte_near(dataSuara + 0);
  ad4Header.ad4Sign[0] = pgm_read_byte_near(dataSuara + 1);
  ad4Header.ad4Sign[1] = pgm_read_byte_near(dataSuara + 2);
  ad4Header.ad4Sign[2] = pgm_read_byte_near(dataSuara + 3);
  ad4Header.ad4Sign[3] = 0;
  ad4Header.ad4SampleRate = pgm_read_word_near(dataSuara + 4);
 
  Serial.println();
  Serial.println("Memainkan file suara");
  Serial.print("Sample Rate : ");
  Serial.print(ad4Header.ad4SampleRate);
  Serial.println("Hz");
  Serial.print("Ukuran : ");
  Serial.println(ukuranFileSuara);
 
  if(ad4Header.ad4SampleRate == 16000)
  {
    ICR1 = 512;
  }
  else if(ad4Header.ad4SampleRate == 8000)
  {
    ICR1 = 1024;
  }
 
  TCCR1A = _BV(COM1A1) | _BV(COM1B1);
  TCCR1B = _BV(WGM13) | _BV(CS10);
 
  panjangSuara = ukuranFileSuara;
  sampleCounter = ad4DataOffset;
  predsample = 0;
  index = 0;
  swap = false;
  suaraDimainkan = true;
}
 
void lanjutkanSuara()
{
  if(TIFR1 & _BV(TOV1))
  {
    TIFR1 |= _BV(TOV1);
    if (sampleCounter > panjangSuara)
    {
      stopPlayback();
    }
    else
    {
      byte data = pgm_read_byte_near(dataSuara + sampleCounter);
      if(!swap)
      {
        data >>= 4;
      }
      else
      {
        sampleCounter++;  
      }
      swap = !swap;
   
      int16_t sample;
      if(ad4Header.ad4SampleRate == 16000)
      {
        sample = (ADPCMDecoder(data & 0x0F)/128) + 256;
        OCR1B = 512 - sample;
      }
      else if(ad4Header.ad4SampleRate == 8000)
      {
        sample = (ADPCMDecoder(data & 0x0F)/64) + 512;
        OCR1B = 1024 - sample;
      }
      OCR1A = sample;    
    }            
  }
}
int16_t ADPCMDecoder(byte codeNibble)
{
  uint16_t sample;
  int16_t diffq;
  uint16_t step;
 
  step = StepSizeTable[index];
 
  diffq = step >> 3;
  if( codeNibble & 4 )
    diffq += step;
  if( codeNibble & 2 )
    diffq += step >> 1;
  if( codeNibble & 1 )
    diffq += step >> 2;
   
  if( codeNibble & 8 )
    predsample -= diffq;
  else
    predsample += diffq;
 
  index += IndexTable[codeNibble];
 
  if( index < 0 )
    index = 0;
  if( index > 88 )
    index = 88;
 
  if( predsample > 32767 )
    predsample = 32767;
  else if( predsample < -32768 )
    predsample = -32768;
 
  return predsample;
}
 
void stopPlayback()
{
  TIMSK1 &= ~_BV(OCIE1A);
  TCCR1B &= ~_BV(CS10);
  if(ad4Header.ad4SampleRate == 16000)
  {
    OCR1A = 256;
    OCR1B = 256;
  }
  else if(ad4Header.ad4SampleRate == 8000)
  {
    OCR1A = 512;
    OCR1B = 512;
  }
  digitalWrite(speakerA, LOW);
  digitalWrite(speakerB, LOW);
 
  suaraDimainkan = false;
}

contoh file suara (copy-kan ke folder sketch arduino)

Selamat datang 8000.h

untuk mengkonversi file .wav atau .mp3 ke file .ad4 bisa menggunakan software 4D-SOMO-Tool (tidak direkomendasikan menggunakan ad4Converter karena software ini tidak menyertakan header). kemudian file .ad4 tersebut dijadikan .h dengan software bin2c.

Kesalahan umum perancangan berbasis Arduino

Kendala perancangan arduino yang sering terjadi :

  1. Program berhenti ditengah jalan
  2. Contoh rangkaian dan sketch tidak jalan
  3. Tegangan referensi bersama
  4. Hasil pembacaan sensor analog tidak konsisten
  5. Jalannya program tersendat
  6. Pembacaan detik yang berulang pada RTC
  7. Modul serial tidak bisa berkomunikasi
  8. Data dari komunikasi melalui internet tidak lengkap
  9. Arduino kekurangan power
  10. Kesalaham umum sketch/program
  11. kondisi if..else tidak bekerja di dalam loop()
  12. Interupsi bertumpuk dan terabaikan
  13. Variabel tidak berubah didalam blok interupsi
  14. Arduino me-reset saat relay berubah keadaan
  15. Upload sketch gagal

Para perancang Arduino sering menghadapi masalah dalam mengembangkan sistem berbasis arduino, maka sebelum melakukan perakitan arduino pastikan hal sepele berikut :

  1. Pastikan kabel jumper tersambung antara ujung-ujungnya.
  2. Komponen tidak rusak/cacat secara fisik
  3. Power supply / baterai / adaptor tersambung dengan benar (sesuai polaritas dan tegangan)

Sebelum memulai merakit arduino Juga perlu diperhatikan jalur internal pin-pin di papan/board arduino yang terhubung secara fisik.

  1. Antara pin A0-A5 (A0 – A8 pada mega) dan pin 0  – 5 (0 – 8 pada mega) adalah berbeda.
  2. Pin TWI/I2C (SCL dan SDA) dan SPI (SCK, MOSI, MISO) terhubung seperti gambar berikut:

    Apabila salah satu pin telah digunakan, maka pasangannya tidak lagi bisa digunakan sebagai pin (kecuali keperluan sambungan tambahan).

berikut ini adalah beberapa permasalahan yang sering muncul dalam perancangan sistem otomatis berbasis arduino:

Program berhenti ditengah jalan

Perangkat berjalan normal tetapi terhenti setelah menyelesaikan tugasnya. dan tidak bisa menjalankan perintah lainnya. Hal ini terjadi karena didalam sketch terdapat baris berikut:

while(1);
atau
for(;;);

baris sketch ini akan menghentikan jalannya program (kecuali interrupt), biasanya disisipkan oleh perancang untuk melihat hasil dan membuat perangkat selesai melakukan tugasnya dan berhenti dititik itu.
dengan menghapus baris tersebut maka program akan kembali berjalan dan menjalankan baris perintah berikutnya.

Contoh rangkaian dan sketch tidak jalan

Rangkaian dan sketch dibuat serupa tanpa perubahan tetapi perangkat tidak berkerja.

Dalam merakit rangkaian, –utamakan– defenisi poisisi pin dari sketch yang akan di upload. contohnya kode berikut berarti pin sensor berada dan pin nomor 2:

#define sensor 2
int sensor = 2;

Tegangan referensi bersama

Groung/polaritas negatif yang tidak terhubung mengakibatkan intepretasi yang berbeda terhadap suatu nilai.

Membangun sebuah sistem digital atau sistem analog yang terhubung dengan beberapa perangkat lain harus memiliki tegangan referensi yang sama (biasanya ground/negatif) kecuali wireless.

Hasil pembacaan sensor analog tidak konsisten

Pembacaan sensor analog melalui pin A0-A5 (A0-A8 pada mega) menggunakan perintah analogRead(pin), hasil berubah-ubah padahal sensor mengukur sesuatu yang diam.

Pembacaan analog dengan Arduino menggunakan ADC. ADC pada Arduino sendiri memiliki spesifikasi yang harus dipenuhi seperti dalam datasheetnya, beberapa penyebab yang sering terjadi adalah:

  1. Tegangan sumber (power supply) ke Arduino dan sensor tidak stabil.
  2. Resistansi yang besar, bisa diakibatkan
  • Jarak sensor dan pin dengan kabel yang panjang.
  • kabel memiliki resistansi besar.
  • Koneksi konektor yang tidak bagus (tidak kontak sempurna) karena longgar atau karatan.
  • interferensi sinyal lain (sinyal yang bersebelahan, jala listrik, sinyal handphone dll)

Jalannya program tersendat

penyebab umum jalannya program arduino lambat dan tersendat:

  1. Delay sering digunakan, namun sebenarnya pengunaan perintah delay yang berlebihan sangat merugikan karena
    • Menyita kinerja CPU
    • Melewatkan adanya permintaan dari modul/komponen luar yang ingin segera ditanggapi.
  2. Interupsi yang berlangsung tanpa disadari seperti penggunaan library yang sebenarnya mengandung interupsi.
  3. Perulangan seperti for, do, while yang berlangsung terus menerus, sebaiknya lebih diefesien lagi dan keluar dengan perintah ‘break’ jika persyaratannya telah terpenuhi.

Pembacaan detik yang berulang pada RTC

RTC digunakan sebagai penedia data waktu real time, namun kesalahan yang sering terjadi adalah ketika menunggu adanya perubahan data dari RTC (detik), pembacaan dilakukan berulang-ulang ke RTC.

Jika dicermati RTC juga memiliki fitur interupsi yang memberii tahu CPU bahwa adanya perubahan data. Ketika CPU Arduino mendapat interupsi ini barulah dilakukan pembacaan data RTC.

Modul serial tidak bisa berkomunikasi

Beberapa baudrate yang direkomendasikan : 300, 1.200, 2.400, 4.800, 9.600, 19.200, 38.400, 57.600, 74.880, 115.200, 230.400, 250.000, 500.000, 1.000.000, 2.000.000. Pemilihan baudrate berdasarkan :

  1. Spesifikasi modul yang akan berkomunikasi dengan Arduino. beberapa modul memiliki fitur ‘auto Baudrate’
  2. Tingkat kesalahan data, hal ini berkaitan dengan frekuensi CPU arduino, batas yang dizinkan adalah 0.5%.
    yang dihitung menggunakan rumus :
    error(%) = ((baudrate sebenarnya/badudrate )- 1) * 100%
  3. Baud rate yang rendah akan menyebabkan waktu tunda (delay) yang lebih banyak, seringkali hal ini tidak disadari dan menyebabkan baris perintah lain terlambat untuk dieksekusi.
    untuk menghitung waktu yang dibutuhkan serial dalam mengirim data (1 start bit, 8 data, 1 parity, 1stop bit) digunakan formula:Waktu(detik) = (1/baudrate * 11) * jumlah datasebagai contoh untuk mengirim 100 karakter pada baud rate 9600 dalam mode asynchronous membutuhkan waktu ±1  detik

Data dari komunikasi melalui internet tidak lengkap

Akses Arduino bisa dilakukan menggunakan ethernet, Wifi, Modem. Kesalahan umumnya adalah data yang diterima tidak lengkap, terpotong, kacau. kemungkinan yang terjadi adalah :

  1. Koneksi terganggu, perbaiki pengkabelan dan periksa apakah koneksi ke internet berjalan dari perangkat lain.
  2. Upaya menampilkan hasil data yang diterima melalui komunikasi serial.
    • naikkan baudrate misal Serial.begin(115200) untuk mengurangi waktu bagi komunikasi serial
    • Simpan data yang diterima dalam variabel string, dan tampilkan melalui serial setelah komunikasi internet selesai.

Arduino kekurangan power

Arduino memiliki internal regulator tegangan 5V dan 3.3V, dengan kemampuan arus 0.8-1A. keluaran dari regulator ini digunakan oleh Arduino sendiri dan sisanya melalui pin power. Jika sumber tegangan ini juga digunakan oleh modul-modul diluar arduino, maka pastikan itu mencukupi (sesuai spesifikasi) untuk memperoleh kinerja maksimal.

Kesalaham umum sketch/program

Dalam membuat sketch/koding/program/listing program dengan IDE Arduino kita harus mengikuti gaya bahasa C++, dan aturan gaya bahasa serta logika program yang sering terlewatkan adalah :

  1. Tanda titik koma (semicolon) pada setiap akhir perintah.
  2. Tanda kurung (parentheses) dan kurung kurawal (curly bracket) haruslah berpasangan.
  3. Penamaan variabel pada C++ bersifat case-sensitive, yang berarti perbedaan kapitalisasi huruf berarti beda variabel.
  4. Variabel global dan variabel local dengan nama yang sama sebaiknya dihindari.
  5. Tipe data antara signed dan unsigned harus menjadi perhatian khusus, karena akan menghasilkan logika matematika berbeda pada operasi tertentu.
  6. Variabel dengan bilangan berkoma (floating point) akan tersimpan sebagai bilangan digital basis 2 yang kadang berbeda (dibulatkan).

kondisi if..else tidak bekerja di dalam loop()

void setup() {
  pinMode(12, INPUT_PULLUP);//Tombol
  pinMode(13, OUTPUT);//LED
}
void loop() {
  if(digitalRead(12))
  {
    digitalWrite(13, HIGH);
  }
  else
  {
    digitalWrite(13, HIGH);
    delay(1000);
    digitalWrite(13, LOW);
  }
}

dari sketch diatas kita mengharapkan ketika tombol di pin 12 ditekan maka LED akan hidup terus, dan ketika dilepas LED akan hidup selama 1 detik kemudian mati.

Namun setelah dirunning ternyata LED terus hidup dan tidak pernah mati. kondisi ini disebut dengan “false loop”, karena sebenarnya kondisi dalam blok else selalu dijalakan karena berada didalam operasi loop().

Interupsi bertumpuk

Interupsi akan memotong jalannya program biasa untuk mengeksekusi baris yang ada dalam blok interupsi dan tidak dapat diinterupsi lagi. Apabila terjadi interupsi lain yang berbeda sebelum sebuah interupsi selesai/keluar (reti) maka interupsi lain itu harus menunggu.

Apabila sebuah interupsi berjalan dan terjadi kejadian interupsi yang sama lagi maka interupsi kedua ini akan dibatalkan (selama flag interupsi masih aktif).

Untuk interupsi yang sering terjadi dan dalam jeda yang singkat, maka baris program yang ada di badan interupsi haruslah sesingkat mungkin, baris program dihitung dalam satuan baris assembler misalnya satu baris “Serial.println();” akan menghasilkan lebih dari satu baris assembler.

Jika badan interupsi harus memiliki baris program yang besar, maka sebaiknya badan interupsi hanya menghasilkan flag, dan flag ini akan dibaca oleh baris program reguler untuk dieksekusi lebih lanjut.

Variabel tidak berubah didalam blok interupsi

Apabila interupsi digunakan untuk merubah keadaan/nilai sebuah variabel, maka variabel tersebut harus dideklarasikan dengan properti volatile.

Proses compile sketch menjadi bahasa mikrokontroller juga menjalankan fungsi optimalisasi, artinya baris program yang menurut compiler tidak efektif/sia-sia akan diabaikan, kecuali variabel dengan properti volatile (tetap diperhitungkan).

Arduino me-reset saat relay berubah keadaan

Relay merupakan komponen elektro mekanik yang memiliki koil (lilitan) untuk menggerakkan tuas kontak. Dalam operasional relay menghasilkan spike (kejut listrik) yang dihasilkan oleh:

  1. Koil, memiliki induktansi yang berpengaruh terhadap laju arus/detik, saat koil diberi tegangan maka koil akan dialiri arus sesaat yang besar sedangkan ketika di nonaktifkan relay akan menghasilkan arus sesaat yang besar dalam arah berlawanan (minus).
    Arus sesaat yang besar ini bisa mengakibatkan terjadinya kejut listrik diseluruh rangkaian nya.
  2. Kontak listrik, berlaku seperti saklar (switch) dimana semakin besar arus beban yang diputus/disambungkan maka akan semakin besar pula kemungkinan terjadinya bunga api, yang juga berpotensi besar menghasilkan kejut listrik.

Upaya pemecahan permasahan kontak relay ini bisa dilakukan dengan cara:

  1. Gunakan kabel berkualitas
  2. batasi arus ke koil dengan penambahan hambatan (resistor) yang sesuai
  3. Gunakan power supply yang bagus (dayanya cukup saat beban max) dan dilengkapi EMI filter (perlindungan dari gangguan luar)
  4. Pemisahan power supply arduino dan relay serta beban-beban besar
  5. Relay dilengkapi flyback dioda (seperti 1N4048)
  6. Lindungi kabel power dari EMI (twist, shielding)
  7. Lengkapi decoupling kapasitor sedekat mungkin ke pin power.
  8. Lindungi kabel komponen sensitif dari EMI (twist yang rapi) kabel clock diutamakan.
  9. tambahkan decoupling/filter kapasitor (1-100nf) di clock komponen sensitif
  10. saat relay bekerja, komponen sensitif dalam keadaan read mode
  11. Pisahkan jalur kabel daya dan kontrol, beda ducting.
  12. kabel antara Arduino dan komponen sensitif sedekat mungkin.
  13. firmware trick.

Upload sketch gagal

Sebelum mengupload sketch ke arduino pastikan memlih board dan processor serta port (dalam menu Tools) yang sesuai dengan board Arduino yang terpasang. Pastikan juga kabel usb dalam keadaan baik.

Masalah yang kerap terjadi saat proses upload adalah:

  1. Bentrok dengan Serial, arduino melakukan proses upload memanggunakan pin Serial (pin 0 dan 1) apabila pin tersebut juga digunakan untuk keperluan lain dapat dipastikan akan terjadi interferensi data.
  2. Gangguan juga bisa ditimbulkan oleh komponen/modul lain yang bekerja/berubah keadaan dan menyebabkan tegangan tidak stabil.
  3. Port Serial di PC/laptop digunakan oleh program lain seperti processing, putty atau serial monitor dari arduino IDE yang tidak se-gruop.

Mengontrol hingga 8 buah servo (multi servo) menggunakan AVR ATmega32

Menggerakkan servo dengan mikrokontroler AVR/atmega biasanya menggunakan PWM (Timer) namun. Namun pada chip AVR seperti ATmega8 hanya memliki 3 pin OC (output dari comparator) juga ATmega16, 32 memiliki 4 pin OC,  ATmega328 5 pin OC.

Untuk menggerakkan servo lebih banyak (multi servo) dari ketersediaan pin OC, bisa digunakan metode dua timer interrupt, yaitu :

  1. Timer1 berfungsi membangkitkan frekuensi 50Hz (periode 2ms) yang ditetapkan spesifikasi servo umum.
  2. Timer0 berfungsi mengatur lebar pulsa untuk masing-masing servo.

pengaturan

Servo memiliki sensor resistansi (potensio) untuk mendeteksi posisi dari aktuator. Terkadang ada servo yang nilai resistansinya berbeda-beda. untuk itu perlu diatur nilai offset dan tick-nya. berikut ini bebera variabel yang harus diatur sebelum digunakan:

Mengatur servo dengan atmega / AVR menggunakan codevision/Atmel studio harus memperhatikan bilangan pecahan, jadi pastikan hasil perhitungan posisi servo tepat.

#define F_CPU 16000000L

#define jumlahServo 8		//Servo 1 = pin 0, servo 2 = pin 1
#define portServo PORTD

//kalibrasi
#define servoTickOffset 1200
#define servoTickMinimum 0
#define servoTickMaksimum 4000L

berikut listing programnya: (AVR Studio 6.2, ATmega32A)

#define F_CPU 16000000L

#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define jumlahServo 8    //Servo 1 = pin 0, servo 2 = pin 1
#define portServo PORTD

//kalibrasi
#define servoTickOffset 1200
#define servoTickMinimum 0
#define servoTickMaksimum 4000L

volatile uint16_t servo[jumlahServo];
#define servoPortMask 0xFF >> (8-jumlahServo)

ISR(TIMER1_COMPA_vect)
{
  portServo = 0xFF;
  TCCR0 = (1<<WGM01) | (1<<CS00); // CTC, prescale 1, top ocr1a
}

ISR(TIMER0_COMP_vect)
{
  uint8_t nilaiPort = 0x00;
  uint8_t byteNilai;
  for(uint8_t i=0;i<jumlahServo;i++)
  {
    uint16_t tick = servo[i] * (uint8_t)((servoTickMaksimum - servoTickMinimum) / 180);
    if(tick + servoTickOffset < TCNT1)
    {
      byteNilai = 0x00;
    }
    else
    {
      byteNilai = 0x80;
    }
    nilaiPort = byteNilai | (nilaiPort >> 1);
  }
  nilaiPort >>=  (8 - jumlahServo);
  nilaiPort &= servoPortMask;
  portServo = nilaiPort ;
  if(TCNT1 > servoTickOffset + servoTickMaksimum)
  {
    TCCR0 = 0;
  }
}

int main (void)
{
  (*(&portServo - 1)) = 0xFF; //port sebagai output
  
  OCR0 = 88 - 1;
  TIMSK = (1<<OCIE0);

  TCCR1A = 0;
  TCCR1B = (1<<WGM12) | (1<<CS11);  // CTC, prescale 8, top ocr1a
  OCR1A = 20000 - 1;//50Hz 
  TIMSK |= (1<<OCIE1A);
  sei();

  while(1)
  {
    for(uint8_t i=0;i<180;i++)
    {
      servo[7] = i;
      _delay_ms(10);
    }

    for(uint8_t i=180;i != 0;i--)
    {
      servo[7] = i;
      _delay_ms(10);
    }
  }
}

Membaca RTC DS1307 menggunakan interupsi SQW

RTC DS1307 memiliki pin keluaran/output SQW. Pin SQW berfungsi sebagai pembangkit sinyal digital yang frekuensinya bisa diatur pada 1, 4.096, 8.192, 32.768Hz.

Keluaran ini bisa dimanfaatkan oleh mikrokontroller untuk mendeteksi perubahan detik jika SQW diatur pada frekuensi 1Hz (saat pin SQW kondisi high).

penggunaan interupsi dalam mikrokontroller sangat bermanfaat karena:

  1. Menghentikan program yang sedang berjalan dan segera menjalankan baris program interupsi.
  2. Mengaktifkan mikrokontroller dari posisi sleep().
  3. Tidak memakai sumberdaya mikrokontroller, tidak seperti perintah delay() yang menyebabkan mikrokontroller melakukan proses perulangan di satu titik.

Kombinasi interupsi dan sleep() menjadikan kinerja mikrokontroler lebih efisien (hemat sumberdaya). Namun juga perlu diperhatikan bahwa penggunaan interupsi akan menghambat baris program lain, jadi pergunakan fungsi interupsi secepat mungkin.

fungsi interupsi SQW pada DS1307/interupsi SQW pada DS3231 adalah memliki register kontrol yang sama.

daftar interupsi pinChange:

  1. ISR (PCINT0_vect) pin change interrupt untuk D8 sampai D13
  2. ISR (PCINT1_vect) pin change interrupt untuk A0 sampai A5
  3. ISR (PCINT2_vect) pin change interrupt untuk D0 sampai D7

Skema RTC DS1307/DS3231 hemat energi dengan arduino:

berikut ini contoh sketch/program dari penggunaan RTC DS1307 menggunakan metode  interupsi:

#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <avr/sleep.h>

byte SQWPin = A3;
volatile bool interupsiDetik;

void setup() {
  Serial.begin(9600);
  Serial.println("Membaca DS1307 dengan metode interupsi SQW");
  Serial.println("https://www.project.semesin.com");

  //Mengaktifkan Pin SQW keluaran 1Hz pada DS1307
  Wire.beginTransmission(0x68);
  Wire.write((uint8_t)0x07);
  Wire.write(0x10);
  Wire.endTransmission();

  ////Mengaktifkan Pin SQW keluaran 1Hz pada DS3231
  //Wire.beginTransmission(0x68);
  //Wire.write((uint8_t)0x0E);
  //Wire.write(0x1C);
  //Wire.endTransmission();

  //Pendeteksi interupsi
  *digitalPinToPCMSK(SQWPin) |= bit (digitalPinToPCMSKbit(SQWPin));
  PCIFR  |= bit (digitalPinToPCICRbit(SQWPin));
  PCICR  |= bit (digitalPinToPCICRbit(SQWPin));

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
}

ISR (PCINT1_vect)
{
  if(digitalRead(SQWPin))
  {
    interupsiDetik = true;
  }
}  
 
void loop() {
  tmElements_t tm;
  if (interupsiDetik) 
  {
    interupsiDetik = false;

    RTC.read(tm);
    Serial.print("Time = ");
    print2digits(tm.Hour);
    Serial.write(':');
    print2digits(tm.Minute);
    Serial.write(':');
    print2digits(tm.Second);
    Serial.print(", Date (D/M/Y) = ");
    Serial.print(tm.Day);
    Serial.write('/');
    Serial.print(tm.Month);
    Serial.write('/');
    Serial.print(tmYearToCalendar(tm.Year));
    Serial.println();
  }

  sleep_mode();
}

void print2digits(int number) {
  if (number >= 0 && number < 10) {
    Serial.write('0');
  }
  Serial.print(number);
}

Pengaturan alarm dengan arduino dan RTC DS1307 melalui 4 tombol

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

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

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

breadboard:

komponen yang digunakan:

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

sketch/program:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <EEPROM.h>

//pin
byte tombolUp = 9;
byte tombolDn = 10;
byte tombolMinus = 11;
byte tombolPlus = 8;
byte buzzer = 13;

LiquidCrystal_I2C  lcd(0x3F, 16, 2);
byte detikTerakhir = 60;

int alarmJamAddr = 0;
int alarmMenitAddr = 1;
int alarmDetikAddr = 2;

byte alarmJam;
byte alarmMenit;
byte alarmDetik;

bool alarmStatus = false;
unsigned long millisMulai;
unsigned long millisAlarmMulai;

bool buzzStatus;

uint16_t jedaBuzzer = 1000;
uint16_t waktuAlarm = 10000;//Alarm 10 detik

byte menu = 0;
byte menuLCDPos = 8;
byte temp;

void setup()
{
  Serial.begin(9600);
  Serial.println("Setting alarm menggunakan Arduino dan RTC1307 melalui 4 push button");
  Serial.println("https://www.project.semesin.com");

  pinMode(buzzer, OUTPUT);
  pinMode(tombolUp, INPUT_PULLUP);
  pinMode(tombolDn, INPUT_PULLUP);
  pinMode(tombolMinus, INPUT_PULLUP);
  pinMode(tombolPlus, INPUT_PULLUP);

  Wire.begin();
  Wire.beginTransmission(0x3F);
  if (Wire.endTransmission())
  {
    lcd = LiquidCrystal_I2C(0x27, 16, 2);
  }
  lcd.begin();
  lcd.backlight();
  lcd.setBacklight(HIGH);

  lcd.setCursor (0, 0);
  lcd.print("Waktu:    :  :  ");
  lcd.setCursor(0, 1);
  lcd.print("Alarm:    :  :  ");

  readEEPROMDataWaktu();

  lcd.setCursor(8, 1);
  if (alarmJam < 10)lcd.print('0');
  lcd.print(alarmJam);
  lcd.setCursor(11, 1);
  if (alarmMenit < 10)lcd.print('0');
  lcd.print(alarmMenit);
  lcd.setCursor(14, 1);
  if (alarmDetik < 10)lcd.print('0');
  lcd.print(alarmDetik);
}

void loop()
{
  tmElements_t tm;

  do
  {
    lcd.setCursor(menuLCDPos, 1);
    switch (bacaTombol())
    {
      case '+':
        temp = EEPROM.read(menu - 1);
        if (((temp >= 23) && (menu == 1)) || ((temp >= 59) && (menu > 1)))
          temp = 0;
        else
          temp++;
        EEPROM.write(menu - 1, temp);
        if (temp < 10)lcd.print('0');
        lcd.print(temp);
        break;
      case '-':
        temp = EEPROM.read(menu - 1);
        if ((temp == 0) && (menu == 1))
          temp = 23;
        else if ((temp == 0) && (menu > 1))
          temp = 59;
        else
          temp--;
        EEPROM.write(menu - 1, temp);
        if (temp < 10)lcd.print('0');
        lcd.print(temp);
        break;
      case 'M':
        menu++;
        if (menu == 1)
        {
          menuLCDPos = 8;
          temp = alarmJam;
          lcd.blink();
        }
        else if (menu == 2)
        {
          menuLCDPos = 11;
        }
        else if (menu == 3)
        {
          menuLCDPos = 14;
        }
        if (menu == 4)
        {
          menu = 0;
          lcd.noBlink();
          readEEPROMDataWaktu();
        }
        break;
      case 'E':
        menu = 0;
        lcd.noBlink();
        readEEPROMDataWaktu();
        break;
    }
  }
  while (menu);

  if (alarmStatus)
  {
    if (millisMulai + jedaBuzzer < millis())
    {
      buzzStatus = !buzzStatus;
      digitalWrite(buzzer, buzzStatus);
      millisMulai = millis();
    }
    if (millisAlarmMulai + waktuAlarm < millis())
    {
      alarmStatus = false;
      digitalWrite(buzzer, LOW);
    }
  }

  if (RTC.read(tm)) {
    if (tm.Second != detikTerakhir)
    {
      lcd.setCursor(8, 0);
      if (tm.Hour < 10)lcd.print('0');
      lcd.print(tm.Hour);
      lcd.setCursor(11, 0);
      if (tm.Minute < 10)lcd.print('0');
      lcd.print(tm.Minute);
      lcd.setCursor(14, 0);
      if (tm.Second < 10)lcd.print('0');
      lcd.print(tm.Second);

      if ((alarmJam == tm.Hour) && (alarmMenit == tm.Minute) && (alarmDetik == tm.Second))
      {
        alarmStatus = true;
        millisMulai = millis();
        millisAlarmMulai = millisMulai;
      }

      detikTerakhir = tm.Second;
    }
  }
}

char bacaTombol()
{
  char tombol = ' ';
  if (!digitalRead(tombolUp))
  {
    tombol = '+';
  }
  else if (!digitalRead(tombolDn))
  {
    tombol = '-';
  }
  else if (!digitalRead(tombolMinus))
  {
    tombol = 'M';
  }
  else if (!digitalRead(tombolPlus))
  {
    tombol = 'E';//exit
  }
  while (!digitalRead(tombolUp));
  while (!digitalRead(tombolDn));
  while (!digitalRead(tombolMinus));
  while (!digitalRead(tombolPlus));
  delay(200);

  return tombol;
}

void readEEPROMDataWaktu()
{
  alarmJam = EEPROM.read(alarmJamAddr);
  alarmMenit = EEPROM.read(alarmMenitAddr);
  alarmDetik = EEPROM.read(alarmDetikAddr);
}

 

I2C scanner

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

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

alamat tulis I2C yang umum:

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

Library:

Linear-feedback shift register (LFSR) dengan arduino

LFSR (linear-feedback shift register) adalah metode matematis untuk menghasilkan sebuah bilangan yang linear dengan bilangan sebelumnya.

Fungsi matematis ini digunakan sebagai:

  1. menghasilkan bilangan acak (random generator)
  2. Menghasilkan bilangan tersusun yang berulang sehingga bisa dimanfaatkan sebagai pembangkit suara gangguan (noise) seperti di tv/radio dengan siaran acak ketika tidak siaran.

sketch/program arduino LFSR:

metode Fibonacci

uint16_t LFSRBuffer;

void setup() {
  Serial.begin(9600);
  Serial.println("LFSR metode Fibonacci");
  Serial.println("https://www.project.semesin.com");
  
  uint16_t nilaiAwal = analogRead(0);
  LFSRFibonacci(nilaiAwal);
  //LFSRFibonacci(100);
}

void loop() {
  uint16_t bilanganLFSR = LFSRFibonacci();
  Serial.println(bilanganLFSR);

  delay(1000);
}

void LFSRFibonacci(uint16_t nilaiAwal)
{
  LFSRBuffer = nilaiAwal;
}

uint16_t LFSRFibonacci()
{
    uint16_t bit;
    
    bit  = ((LFSRBuffer >> 0) ^ (LFSRBuffer >> 2) ^ (LFSRBuffer >> 3) ^ (LFSRBuffer >> 5)) & 1;
    LFSRBuffer =  (LFSRBuffer >> 1) | (bit << 15);
    return LFSRBuffer;
}

metode Galois

uint16_t LFSRBuffer;

void setup() {
  Serial.begin(9600);
  Serial.println("LFSR metode Galois");
  Serial.println("https://www.project.semesin.com");
  
  uint16_t nilaiAwal = analogRead(0);
  LFSRGalois(nilaiAwal);
  //LFSRGalois(100);
}

void loop() {
  uint16_t bilanganLFSR = LFSRGalois();
  Serial.println(bilanganLFSR);

  delay(1000);
}

void LFSRGalois(uint16_t nilaiAwal)
{
  LFSRBuffer = nilaiAwal;
}

uint16_t LFSRGalois()
{
    unsigned lsb = LFSRBuffer & 1;
    LFSRBuffer >>= 1;
    if (lsb)
    {
        LFSRBuffer ^= 0xB400;
    }
    return LFSRBuffer;
}

Trik menyimpan bilangan random dengan arduino

Dengan arduino kita bisa menghasilkan bilangan acak (random generator) dengan menggunakan sketch/program:

long randNumber;

void setup(){
  Serial.begin(9600);
  Serial.println("Menyimpan bilangan acak/random");
  Serial.println("https://www.project.semesin.com");

  randomSeed(analogRead(0));
}

void loop() {
  // print bilangan acak dari 0 hingga 299
  randNumber = random(300);
  Serial.println(randNumber);

  delay(1000);
}

Nilai acak/random yang dihasilkan oleh mesin digital seperi arduino tidaklah sepenuhnya acak, karena secara pandang digital diperlukan metode matematis (contohnya LFSR – linear-feedback shift register) dengan menerapkan suatu persamaan dimana bilangan berikutnya dihitung dari bilangan yang ada sekarang.

Dengan demikian nilai random yang dihasilkan merupakan perulangan terus menerus dan berulang. Agar deretan acak ini menghasilkan deretan berbeda setiap waktu, maka diberikan nilai awal (seed). salah satu contoh pepuler adalah perintah analogRead(pin):

  nilaiAwal = analogRead(0);
  randomSeed(nilaiAwal);

Untuk kembali ke bilangan sebelumnya kita bisa menyimpan deret bilangan ke dalam array, namun tidak efisien untuk jumlah bilangan yang besar. Untuk itu kita bisa menggunakan trik yaitu dengan melakukan perulangan dengan memberi nilai awal (seed) yang sama.

contohnya seperti sketch berikut:

long randNumber;
uint16_t nilaiAwal;

void setup(){
  Serial.begin(9600);

  nilaiAwal = analogRead(0);
  randomSeed(nilaiAwal);
}

void loop() {
  randNumber = ambilDeretAcak(nilaiAwal, 100, 300);
  Serial.println(randNumber);
  
  delay(1000);
}

uint16_t ambilDeretAcak(uint16_t nilaiawal, uint16_t urutan, uint16_t nilaiAcakMaksimal)
{
  uint16_t nilaiAcak;

  randomSeed(nilaiawal);
  for(uint16_t i=0;i<urutan;i++)
  {
    nilaiAcak = random(nilaiAcakMaksimal);
  }
  return nilaiAcak;
}

fungsi ambilDeretAcak akan menghasilkan bilangan yang berada di urutan tertentu dalam deret acak dengan nilai awal tertentu.

Konversi penanggalan Masehi ke Hijriah menggunakan Arduino

Penanggalan masehi mengacu pada peredaran matahari, satu tahun masehi terhitung sebesar 365,25 hari, sedangkan Kalender hijriah mengacu pada peredaran bulan, waktu 12 kali peredaran bulan terhitung sebesar 354,367068 hari.
Untuk konversi masehi ke hijriah mengikuti kaidah berikut ini :

  1. Basis perhitungan Masehi ditetapkan pada 18 April 1999.
  2. Basis perhitungan Hijriah ditetapkan pada 1 Muharram 1420.
  3. Jumlah hari dalam satu bulan Hijriah adalah: 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29(30).
  4. Berlaku dari jam 00.00 hingga waktu Maghrib karena penanggalan Hijriah berganti hari pada waktu Maghrib

penerapan dalam Arduino bisa menggunakan program/sketch berikut ini:

double tahunBulan = 354.367068;
double tahunMatahari = 365.25;

String namaBulanHijriah[] = {"Muharram","Safar","Rabiul awal","Rabiul akhir","Jumadil awal","Jumadil akhir","Rajab","Sya'ban","Ramadhan","Syawal","Dzulkaidah","Dzulhijjah"};
String namaBulanMasehi[] = {"Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","November","Desember"};
uint16_t jumlahHariPerBulanHijriah[] = {0,30,59,89,118,148,177,207,236,266,295,325,354};
uint16_t jumlahHariPerBulanMasehi[]={0,31,59,90,120,151,181,212,243,273,304,334};

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

void loop() {
  uint32_t jumlahHari;
  double jumlahHariMatahari;
  
  byte tanggalMasehi = 16;
  byte bulanMasehi = 3;
  uint16_t tahunMasehi = 2018;

  byte tanggalHijriah;
  byte bulanHijriah;
  uint16_t tahunHijriah;

  jumlahHari = jumlahHariDariTanggal(tanggalMasehi, bulanMasehi, tahunMasehi - 2000);
  masehiKeHijriah(jumlahHari, &tanggalHijriah, &bulanHijriah, &tahunHijriah);

  Serial.print(tanggalMasehi);
  Serial.print(" ");
  Serial.print(namaBulanMasehi[bulanMasehi-1]);
  Serial.print(" ");
  Serial.print(tahunMasehi);

  Serial.print(" == ");

  Serial.print(tanggalHijriah);
  Serial.print(" ");
  Serial.print(namaBulanHijriah[bulanHijriah-1]);
  Serial.print(" ");
  Serial.println(tahunHijriah);

  while(1);
}

//tanggal mulai dari 1
//bulan mulai dari 1
//tahun dua digit
//minggu = 1, sabtu = 7
//base 18 April 1999
uint32_t jumlahHariDariTanggal(byte tanggal, byte bulan, uint16_t tahun)
{
  uint32_t jumlahHari = (uint32_t)tahun * 365;
  for (uint16_t i = 0; i < tahun; i++) 
  {
    if (!(i%4))
    {
      jumlahHari++;
    }
  }
  jumlahHari += jumlahHariPerBulanMasehi[bulan-1];
  if ( (bulan >= 2) && !(tahun % 4)) 
  { 
    jumlahHari++;
  }
  jumlahHari+= tanggal;
  return jumlahHari + 259;// base 18 April 1999
}

void masehiKeHijriah(uint32_t HariMasehi, byte *tanggal, byte *bulan, uint16_t *tahun)
{
  uint16_t sisaHari;
  *tahun = floor(HariMasehi/tahunBulan);
  sisaHari = HariMasehi - (tahunBulan * *tahun);

  for(byte i=0;i<sizeof(jumlahHariPerBulanHijriah);i++)
  {
    if(sisaHari <= jumlahHariPerBulanHijriah[i])
    {
      sisaHari -= jumlahHariPerBulanHijriah[i-1];
      *bulan = i;
      break;
    }
  }
  *tanggal = sisaHari;
  *tahun += 1420;
}

hasil keluaran di serial monitor:

Catatan : metode ini memiliki kekurangan yaitu menghitung penanggalan Hijriah dengan jumlah bulan yang tetap (hanya bagus untuk mengenal prinsip konversi) untuk konversi yang lebih presisi bisa menggunakan metode Julian Date :
Konversi masehi ke Pasaran Jawa dan Hijriah dengan metode Julian Date

Komunikasi dua arah Arduino dengan Web Browser menggunakan MySQL, XAMPP/web hosting dan ESP8266

Untuk komunikasi jarak jauh atau komunikasi tanpa kabel menggunakan Arduino bisa menggunakan ESP8266 (wifi shield) yang akan menghubungkan arduino ke internet/jaringan lokal. Disamping itu harus pula dibangun server yang berfungsi sebagai penyedia layanan sehingga sistem komunikasi bisa berjalan timbal balik. Dalam server juga dibutuhkan media untuk merekam komunikasi tersebut, salah satu yang populer adalah database MySQL. Ini adalah salah satu cara menghubungkan arduino dengan database yang paling efisien.

Menghubungkan MySQL – Arduino dibutuhkan bahasa php sebagai penerjemah query (bahasa database), baik permintaan (GET request) dari arduino – MySQL dan web browser – MySQL. Jika skema ini sudah terbangun maka kita bisa mengkomunikasikan arduino – web browser (lokal / internet).

Menyimpan data dari arduino ke mysql esp8266 bisa dilaksanakan dengan cara ini, data dari arduino ditransmisikan melalui jaringan wifi dan diterima oleh web server. Data tersebut kemudian di proses oleh php dan juka diizinkan maka data tersebut akan disimpan dalam database.

Mengirim perintah dari browser ke arduino dengan esp8266 juga dapat dilakukan dengan cara sebaliknya.

Webserver yang digunakan dalam perancangan ini adalah XAMPP di PC lokal dan jasa server hosting.

Langkah perancangannya sebagai berikut:

  1. Merangkai Arduino + ESP8266
  2. Membuat sketch Arduino untuk mengirim dan menerima data dari database
  3. Konfigurasi koneksi ESP8266 ke access point wifi (AP)
  4. Install XAMPP server dan mengaktifkannya
  5. Untuk web server hosting, membuat database, user account dan priveleged-nya
  6. Membuat program php, dariArduino.php, keArduino.php, dariBrowser.php serta program php pendukung.

Arduino + ESP8266

Rangkaian yang digunakan:

Diagram:

Untuk menghubungkan arduino dengan jaringan, salah satu yang bisa digunakan adalah modul wifi shiel ESP8266. kali ini saya menggunakan library “WiFiEsp.h”.

Catatan, untuk terhubung ke web hosting, pastikan wifi terhubung ke internet (bridge connection)

sebelum menggunakannya konfigurasi dahulu modul tersebut, editlah bagian berikut:


char ssid[] = "ArduinoMySQL";        // Isi dengan nama profil Wifi
char pass[] = "12345678";            // password wifi
//char server[] = "192.168.123.1";     // alamat access point yang 
char server[] = "semesin.com";     // alamat server hosting 

sketch atau program berikut memiliki fitur:

  1. Menerima masukan string/text dari serial monitor dan mengirimkannya ke webserver untuk disimpan ke database MySQL.
  2. Menerima data perintah dari webserver dalam jangka waktu tertentu contoh kali ini setiap 5 detik.

sketch atau program lengkapnya sebagai berikut:

#include "WiFiEsp.h"

char ssid[] = "ArduinoMySQL";        // Isi dengan nama profil Wifi
char pass[] = "12345678";            // password wifi
//char server[] = "192.168.123.1";     // alamat access point yang telah terinstall XAMPP local host
char server[] = "semesin.com";     // alamat web hosting

char namaVariabel[] = "Variabel";
String text = "";
String Respon = "";
bool responDariServer = false;

bool statusKomunikasiWifi = false;
long waktuMulai;
long waktuMintaData = 5000; //minta data setiap 5000ms

WiFiEspClient client;
int status = WL_IDLE_STATUS;

void setup()
{
  Serial.begin(9600);
  Serial.println("Koneksi arduino dengan mySql menggunakan ESp8266 dan XAMPP");
  Serial.println("Ketik pesan yang akan dikirim (pastikan setting serial ke \"both NL & CR\")");
  Serial.println("https://www.project.semesin.com");
  Serial.println();

  Serial1.begin(115200);
  WiFi.init(&Serial1);

  // 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();
  waktuMulai = millis();
}

void loop()
{
  //tunggu imputan nilai dari untuk dikirim ke server
  while(Serial.available())
  {
    char c = Serial.read();
    if((c != '\r') && (c != '\n'))
    {
      text += c;
    }
    if(c == '\n')
    {
      statusKomunikasiWifi = kirimKeDatabase("dataDariSerial",text);
      text = "";
      waktuMulai = millis();
    }
  }

  if(waktuMintaData < millis() - waktuMulai)
  {
    statusKomunikasiWifi = ambilDatabase("perintah");
    waktuMulai = millis();
  }
  
  // periksa respon dari server
  if(statusKomunikasiWifi)
  {
    // if there are incoming bytes available
    // from the server, read them and print them
    while (client.available()) 
    {
      char c = client.read();
      Respon += c;
    }
  
    // if the server's disconnected, stop the client
    if (!client.connected()) {
      Serial.println("Disconnecting from server...");
      client.stop();
      statusKomunikasiWifi = false;
      responDariServer = true;
    }
  }

  // penanganan data yang diretima 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
      Serial.print(variabel);
      Serial.print(" = ");
      Serial.println(nilai);
    }
    Respon = "";
  }
}
bool ambilDatabase(String variabel)
{
  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 /arduino_mysql/keArduino.php?variabel=");
    client.print(variabel);
    client.println(" HTTP/1.1");
    client.print("Host: ");
    client.println(server);
    client.println("Connection: close");
    client.println();

    long _startMillis = millis();
    while (!client.available() and (millis() - _startMillis < 2000));

    return true;
  }
  return false;
}

bool kirimKeDatabase(String namaVariabel, String nilai)
{
  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

    // parameter 1
    client.print("GET /arduino_mysql/dariArduino.php?");
    client.print("variabel=");
    client.print(namaVariabel);
    
    // parameter 2 dan selanjutnya
    client.print("&");
    client.print("nilai=");
    client.print(nilai);
    
    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(" ");
}

tampilan serial monitor dari arduino:

 

XAMPP webserver + database MySQL

untuk membuat webserver di komputer lokal instal XAMPP (saya menggunakan v3.2.2) atau bundel webserver lain yang termasuk didalamnya MySQL.

untuk memulainya jalankan/aktifkan webserver dan MySQL

 

Web Hosting + Database MySQL

catatan: huruf besar/kecil berpengaruh (case-sensitif)

Server hosting umumnya memiliki fitur kemanan, untuk itu kita harus masuk ke cpanel kemudian membuat sebuah database dengan memilih menu ‘MySQL® Database Wizard‘ dan mengisi nama database, misalnya ‘arduino_mysql’.

selanjutnya ‘next step’ buatlah akun dengan memasukkan nama dan paswword (nama dan password ini akan dimasukkan ke ‘fungction.php’).

selanjutnya prilih privileges yang akan digunakan.

sebagai webserver dan database server yang akan berhubungan timbal balik dengan arduino, maka kita buat halaman web berbasis bahasa PHP dan memiliki fitur:

  1. Menerima data dari Arduino menggunakan metode ‘GET’ dengan parameter ‘variabel’, ‘nilai’, dan ‘status’.
  2. Menyimpan data dari entri data di webserver, dan akan mengirimkannya ke Arduino saat diminta.
  3. Menyedian halaman tampilan database ke halaman web.

fitur-fitur ini saya buatkan dalam beberapa program PHP, untuk menggunakannya extract file Arduino MySQL dan copy ke folder “/arduino_mysql” (untuk XAMPP root folder “C:\xampp\htdocs\arduino_mysql”, untuk web hosting “public_html/arduino_mysql”) dengan program berikut:

file php yang digunakan diuraikan disini:

function.php

sebelum menggunakan file ini edit dahulu bagian sesuai kebutuhan:

    $servername = "localhost";
    $username = "******_Arduino";
    $password = "Arduino";
    $database = "******_arduino_mysql";

kode lengkap function.php

  <?php

 function databaseConnect()
  {
    /* XAMPP
    $servername = "localhost";
    $username = "root";
    $password = "";
    $database = "arduino_mysql";
    */
    
    $servername = "localhost";
    $username = "******_Arduino";
    $password = "Arduino";
    $database = "******_arduino_mysql";
    
    // Create connection
    $conn = new mysqli($servername, $username, $password);

    // Check connection
    if ($conn->connect_error) {
      die("Connection failed: " . $conn->connect_error);
    }
    echoDebug("Connected successfully<br>");
    
    // Create database
    $sql = "CREATE DATABASE IF NOT EXISTS ".$database;
    if ($conn->query($sql) === TRUE) {
      echoDebug("Database created successfully<br>");
    } else {
      echoDebug("Error creating database: " . $conn->error);
    }
    
    // Connect to database
    $conn = new mysqli($servername, $username, $password, $database);
    // Check connection
    if ($conn->connect_error) {
      die("Database connection failed: " . $conn->connect_error);
    }
    echoDebug("Database connected successfully<br>");
    
    // sql to create table
    $sql = "CREATE TABLE IF NOT EXISTS arduino_data (
    id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    variabel VARCHAR(30) NOT NULL,
    nilai VARCHAR(30) NOT NULL
    )";
    
    if ($conn->query($sql) === TRUE) {
      echoDebug("Table arduino_data created successfully</br>");
    } else {
      echoDebug("Error creating table: " . $conn->error);
    }
    // sql to create table
    $sql = "CREATE TABLE IF NOT EXISTS browser_data (
    id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    variabel VARCHAR(30) NOT NULL,
    nilai VARCHAR(30) NOT NULL
    )";
    
    if ($conn->query($sql) === TRUE) {
      echoDebug("Table arduino_data created successfully</br>");
    } else {
      echoDebug("Error creating table: " . $conn->error);
    }
    return $conn;
  }
  function echoDebug($message) 
  {
    // hapus komen '//' jika ingin men-debug pesan
    //echo $message;
  }
?> 

index.php

 <?php
  include("function.php");
  
  $conn = databaseConnect();

  if(isset($_GET['message']))
  {
    echo $_GET['message'];
    echo "<br>";
    echo "<br>";
  }
  $sql = "SELECT * FROM arduino_data";
  $result = $conn->query($sql);

  echo "<html>";
  echo "<head>";
  echo "<meta http-equiv='refresh' content='10'>";
  echo "</head>";
  echo "<body>";
  echo "Arduino Data";
  echo "<table border='1'>";
  echo "<tr>";
  echo "<td width='50'>id</td><td width='100'>Variabel</td><td width='200'>Nilai</td>";
  echo "</tr>";
  if ($result->num_rows > 0) {
    // output data of each row
    while($row = $result->fetch_assoc()) {
      echo "<tr>";
      echo "<td>".$row["id"]."</td><td>".$row["variabel"]."</td><td>".$row["nilai"]. "</td>";
      echo "</tr>";
    }
  } else {
    echo "<td colspan='3'>";
    echo("tidak ada hasil");
    echo "</td>";
  }
  echo "</table>";
  
  echo "<form action='dariBrowser.php' method=GET>";
  echo "<input type='hidden' name='aksi' value='hapus'><br>";
  echo "<input type='submit' value='Hapus semua data'>";
  echo "</form>";

  echo "<br>Kirim data ke Arduino<br>";
  echo "<form action='dariBrowser.php' method=GET>";
  echo "Nama variabel:<br><input type='text' name='variabel'><br>";
  echo "Nilai:<br><input type='text' name='nilai'><br>";
  echo "<input type='submit' value='Kirim'>";
  echo "</form>";
  echo "</body>";
  echo "</html>";

  $conn->close();
?> 

keArduino.php

 <?php
  include("function.php");
  
  $conn = databaseConnect();
  
  // Kirim respon bila ada
  if(isset($_GET["variabel"]))
  {
    $variabel = $_GET["variabel"];
    $sql = "SELECT * FROM browser_data WHERE variabel='".$variabel."'";

    if($result = $conn->query($sql))
    {
      $row = $result->fetch_assoc();
      echo $row["variabel"]."=".$row["nilai"];
    }
  }
  
  $conn->close();
?> 

dariArduino.php

 <?php
  include("function.php");
  
  $conn = databaseConnect();
  
  if(isset($_GET["variabel"]) && isset($_GET["nilai"]))
  {
    // Simpan data yang diterima ke database
    $variabel = $_GET["variabel"];
    $nilai = $_GET["nilai"];

    $sql = "INSERT INTO arduino_data (variabel, nilai) VALUES ('".$variabel."', '".$nilai."')";

    if ($conn->query($sql) === TRUE) {
      echoDebug("New record created successfully</br>");
    } else {
      echoDebug("Error: " . $sql . "<br>" . $conn->error);
    }
  }
  $conn->close();
?> 

dariBrowser.php

 <?php
  include("function.php");
  
  $conn = databaseConnect();
  
  if(isset($_GET["variabel"]) && isset($_GET["nilai"]))
  {
    // Simpan data yang diterima ke database
    $variabel = $_GET["variabel"];
    $nilai = $_GET["nilai"];
    
    $sql = "SELECT * FROM browser_data WHERE variabel='".$variabel."'";
    $result = $conn->query($sql);

    if ($result->num_rows > 0)
    {
      $sql = "UPDATE browser_data SET nilai='".$nilai."' WHERE variabel='".$variabel."'";
      if ($conn->query($sql) === TRUE) {
        echoDebug("Record updated successfully</br>");
      } else {
        echoDebug("Error updating record: " . $sql . "<br>" . $conn->error);
      }
    }
    else
    {
      $sql = "INSERT INTO browser_data (variabel, nilai) VALUES ('".$variabel."', '".$nilai."')";
      if ($conn->query($sql) === TRUE) {
        echoDebug("New record created successfully</br>");
      } else {
        echoDebug("Error: " . $sql . "<br>" . $conn->error);
      }
    }
      echo "<script type='text/javascript'> document.location = 'index.php?message=Entri data berhasil'; </script>";
    exit();
  }
  else if(isset($_GET['aksi']))
  {
    if($_GET['aksi'] == "hapus")
    {
      // sql to delete a record
      $sql = "TRUNCATE arduino_data";

      if ($conn->query($sql) === TRUE) {
        echoDebug("Record deleted successfully");
      } else {
        echoDebug("Error deleting record: " . $conn->error);
      }
      echo "<script type='text/javascript'> document.location = 'index.php?message=database telah dikosongkan'; </script>";
      exit();
    }
  }
  $conn->close();
?> 

Cara penggunaan

  • Nyalakan wifi (pastikan terhubung ke internet)
  • Masukkan perintah dari serial monitor, data akan tercatat dalam tabel ‘arduino_data’, dalam contoh ini nama variabelnya adalah ‘dataDariSerial’ dengan nilai sesuai dengan entry dari Serial monitor (pastikan setting serial ke “both NL & CR”)
  • Buka halaman “localhost/arduino_mysql/” atau “**nama server**/arduino_mysql/” di browser untuk melihat data yang dikirim dari arduino.
  • Masukan perintah dari formulir di halaman web, dan klik kirim untuk mengirim data ke Arduino, (dalam contoh ini arduino membaca variabel ‘perintah’ jadi masukkan nama variabel sebagai ‘perintah’ serta isikan juga kotak ‘nilai’nya)
  • Arduino akan membaca perintah setiap 5 detik dan akan dilaporkan di Serial monitor.

tampilan halaman web:

library : WiFiEsp.zip

Koneksi Arduino dan Android melalui bluetooth dan MIT App Inventor

Bluetooth dan Arduino bisa digunakan untuk berkomunikasi dengan Android menggunakan MIT App Inventor, berikut langkahnya:

Arduino + bluetooth

Mempersiapkan rangkaian yang terdiri dari Arduino mega dan modul bluetooth HC-05.

Masukkan program atau sketch berikut

String text = "";
void setup() {
  Serial.begin(9600);
  Serial1.begin(9600);

  Serial.println("Koneksi arduino dengan app inventor melalui bluetooth");
  Serial.println("Ketik pesan yang akan dikirim (pastikan setting serial ke \"both NL &amp; CR\"");
  Serial.println("https://www.project.semesin.com");
}

void loop() {
  while(Serial.available())
  {
    char c = Serial.read();
    text += c;
    if(c == '\n')
    {
      Serial1.print(text);
      text = "";
    }
  }
  while(Serial1.available())
  {
    Serial.print(char(Serial1.read()));
  }
}

Sketch ini akan menerima text dari “Serial Monitor” dan meneruskannya ke sambungan bluetooth

Begitu juga apabila ada data yang masuk melalui hubungan bluetooth akan ditampilkan ke Serial monitor.

 

Android + App Inventor

Instal apk ini di android BluetoothConnection.apk

Atau bisa mengedit Design MIT App Inventor berikut BluetoothConnection.aia

Design:

Block :

Blok inisialisasi merupakan block yang pertama kali dipanggil ketika aplikasi dijalankan, Jika bluetooth tidak aktif maka blok ini akan memberikan peringatan dan jika bluetooth aktif maka daftar perangkat bluetooth yang terhubung dimasukkan dalam daftar (list)

Blok buttonConnect.Click memiliki dua fungsi yaitu menghubungkan bluetooth dengan perangkat arduino dan memutuskan koneksi bluetooth. Jika bluetooth belum terhubung maka fungsi ini akan memanggil fungsi ListPicker1 yang menampilkan daftar perangkat bluetooth yang terhubung ke android.

Blok ListPicker1.AfterPicking merupakan sebuah even yang berjalan ketika daftar perangkat bluetooth sudah dipilih. Kemudian android diperintahkan untuk membuat sambungan bluetooth dengan perangkat arduino.

Blok buttonSend.Click bekerja saat pengguna menekan tombol kirim dan akan mengirimkan data yang ditulis di textBox.

Blok Clock1.Timer bekerja setiap interval untuk memantau adanya pengiriman data dari perangkat arduino, kemudian akan ditampilkan di layar aplikasi.

Blok Screen1.ErrorOccured merupakan sistem penanganan apabila aplikasi bermasalah dan akan memutuhkan hubungan bluetoothnya.

Block lengkapnya:

Artikel terkait : https://www.project.semesin.com/2018/06/02/komunikasi-data-bluetooth-dengan-app-inventor/

Sistem kontrol PID dengan Arduino

Sistem kontrol adalah suatu sistem untuk mengendalikan keadaan sehingga diperoleh hasil bersesuaian dengan nilai yang diinginkan se-efektif mungkin.

diagram diatas merupakan sistem kontrol terbuka (open loop), dalam sistem ini tidak ada pemberitahuan (klarifikasi).

Dalam suatu sistem pasti terdapat gangguan dan variabel didalam dan dari luar sistem yang mengakibatkan ketidaksesuaian antara hasil yang diperoleh dan nilai yang diinginkan.

diagram diatas merupakan sistem kontrol tertutup (close loop), dengan adanya umpan balik maka sistem bisa melakukan koreksi.

Untuk menjadikan sistem se-efektif mungkin, perlu dilakukan pemodelan terhadap sistem yang dibangun yakni:

    1. Pemodelan masukan (input)
    2. Pemodelan sistem kontrol
      • hidup mati (on/off)
      • proporsional (P)
      • integral (I)
      • proporsional + integral (PI)
      • proporsional + derivative (PD)
      • proporsional + integral + derivative (PID)
    3. Pemodelan gangguan baik baik dari dalam maupun dari luar, dari dalam lebih kepada keandalan pelaksana/actuator (contohnya motor, pemanas, dll) dan gangguan dari luar adalah kondisi-kondisi yang dapat mempengaruhi kinerja pelaksana (contohnya penambahan beban pada motor, sumber panas lain pada pemanas)
    4. pemodelan pelaksana/actuator adalah karakteristik respon dari pelaksana terhadap perintah/sinyal yang diberikan (contohnya perubahan kecepatan motor terhadap setiap level sinyal yang diberikan)
    5. pemodelan objek/plant menyangkut sifat dan variasi perubahan yang akan terjadi pada objek.
    6. pemodelan sensor juga mencakup karakteristik pembacaan sensor terhadap perubahan dari perangkat pelaksana yang diukur.

Adanya model dari sistem yang dibangun akhirnya dapat diperoleh hasil akhir berikut ini:

Keluaran (objek) = Sistem kontrol x masukan

penyederhanaan ini bisa dilakukan dengan metode laplace. persamaan diatas adalah bagaimana hubungan antara masukan dan keluaran sehingga tergambar perilaku dinamik dari sistem kontrol.

apabila variabel-variabel sistem kontrol tidak memberikan respon yang efektif seperti yang diinginkan, maka dilakukan penyesuaian (tuning)

Sistem kontrol PID (Proportional–Integral–Derivative)

PID merupakan mekanisme pengambil keputusan dalam merespon adanya nilai kesalahan. Mekanisme ini memiliki 3 variabel (Kp, Ki, Kd) yang bisa disesuaikan guna memperoleh respon yang efektif.
Sistem kontrol itu berbasis dengan waktu, untuk memperoleh nilai kesalahan berbasis waktu digunakan :

 	kesalahan = input - sensor;
 	integralKesalahan += kesalahan;
 	derivatifKesalahan = kesalahan - kesalahanLalu;
 	kesalahanLalu = kesalahan;
  1. Proporsional adalah faktor kali dari nilai kesalahan saat ini (berbanding lurus), nilai ini berpengaruh langsung terhadap sensitifitas dan responsifitas suatu sistem. Jika nilai Kp dibuat lebih tinggi maka sistem menjadi lebih responsif terhadap nilai kesalahan, tetapi apabila nilai kesalahan terlalu tinggi maka sistem akan terlalu sensitif.
     	Respon = Kp * kesalahan;
    
  2. Integral adalah faktor kali dari nilai kesalahan sebelumnya, nilai Ki merupakan penjumlahan dari nilai-nilai kesalahan yang berfungsi untuk mempercepat sistem menuju nilai yang diinginkan. Jika nilai Ki terlalu tinggi maka dikhawatirkan sistem akan menjadi tidak stabil (overshoot)
     	Respon += Ki * integralKesalahan;
    
  3. Derivatif adalah faktor kali dari kemungkinan nilai kesalahan yang akan datang, nilai Kd berfungsi untuk memprediksi nilai kesalahan beikutnya disebut juga faktor redam karena nilai Kd akan memperlambat tercapainya nilai yang diinginkan namun membuat sistem lebih stabil
     	Respon += Kd * derivatifKesalahan;
    

sketch/program lengkapnya seperti berikut

float kesalahan;
float integralKesalahan;
float derivatifKesalahan;
float kesalahanLalu = 0;
float Kp, Ki, Kd;
float input;

//Pin
byte sensorPin = A0;
byte Pemanas = 2;

//konstanta
#define Ksensor 0.2 //faktor koreksi sensor panas

void setup() {
  pinMode(Pemanas, OUTPUT);
  Serial.begin(9600);
  Serial.println("Sistem kendali PID dengan Arduino");
  Serial.println("https://www.project.semesin.com");
  
  input = 100;
  Kp = 1;
  Ki = 0.5;
  Kd = -0.2;
}

void loop() {
  float sensor = 1.0 * analogRead(sensorPin) * Ksensor;
  
  float respon = hitungPID(input, sensor);
  analogWrite(Pemanas, respon);
  Serial.print(input);
  Serial.print(",");
  Serial.println(sensor);
}

float hitungPID(float input, float sensor)
{
  kesalahan = input - sensor;
  integralKesalahan += kesalahan;
  derivatifKesalahan = kesalahan - kesalahanLalu;
  kesalahanLalu = kesalahan;
  
  return (Kp * kesalahan) + (Ki * integralKesalahan) + (Kd * derivatifKesalahan);
}