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