Pengontrolan tegangan menggunakan PWM pada arduino

Pengontrolan tegangan berfungsi untuk menjaga kestabilan tegangan keluaran ke beban sehingga beban bisa bekerja semestinya. Contohnya jika beban lampu yang intensitas cahayanya bergantung kepada tegangan, maka dengan tegangan yang stabil akan mengeluarkan cahaya yang stabil pula.

Hal yang menyebabkan ketidakstabilan tegangan diantaranya :

  1. Perubahan beban (penambahan dan pengurangan)
  2. Perubahan nilai masukan (input)
  3. Faktor luar seperti interferensi.

Faktor yang mempengaruhi keandalan pengontrolan tegangan :

  1. Kecepatan respon dari pengontrol tegangan terhadap perubahan yang terjadi, semakin cepat semakin baik.
  2. Sistem koreksi yang digunakan, seperti PID, fuzzy
  3. Karakteristik sensor dan beban yang digunakan

Pengontrolan tegangan dengan arduino

Pengendalian tegangan harus memiliki masukan sensor tegangan dan aktuator kontrol tegangan. Pada aplikasi arduino pembacaan tegangan menggunakan ADC dan aktuasi kontrol tegangan menggunakan PWM.

Dalam contoh ini, sistim koreksi tegangan menggunakan metode proporsional, yaitu semakin besar selisih tegangan dan input maka akan semakin besar pula penambahan nilai PWM.

skema sistem kontrol tegangan mengguakan arduino:

Sketch / koding Penngendalian Tegangan dengan PWM:

#define pinSensorTegangan     0
#define pinOutputPWM          9
#define setTegangan           2.5//volt
#define faktorProporsional    0.1

float keluaran;

void setup() {
  pinMode(pinOutputPWM, OUTPUT);
  
  Serial.begin(9600);
  Serial.println("Sumber tegangan stabil (automatic voltage regulator) menggunakan kontrol proporsional");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();

  keluaran = setTegangan;
}

void loop() {
  uint16_t adc = analogRead(pinSensorTegangan);
  float tegangan = map(adc, 0, 1024, 0, 500)/100.0;
  float selisih = setTegangan - tegangan;
  float proporsional = faktorProporsional * selisih;
  
  keluaran += proporsional;
  keluaran = constrain(keluaran, 0, 5);
  byte keluaranPWM = map(keluaran*100, 0, 5*100, 0, 255);
  analogWrite(pinOutputPWM, keluaranPWM);

  //Plot serial, hapus untuk menambah kecepatan
  Serial.print(tegangan);
  Serial.print(", ");
  Serial.print(keluaranPWM);
  Serial.println();
  delay(10);
}

Hasil Plot sinyal PWM (merah) dan tegangan keluaran (biru) terhadap perubahan beban.

Statistik nilai mean, median dan modus menggunakan arduino

Pembacaan nilai analog arduino sering memberikan hasil dengan simpangan dari nilai yang seharusnya. Untuk mengurangi kesalahan ini kita bisa mengambil nilai rata-rata dari beberapa sample. ada beberapa metode untuk mencari nilai pendekatan pembacaan sensor ini yaitu:

Mean (nilai rata-rata)

adalah nilai tengah dari kelompok data yaitu dengan menjumlahkan seluruh data individu dan kemudian membaginya dengan jumlah individu tersebut.

dalam sketch arduino nilai mean dituliskan:

  float Sampel[] = {75.2, 82.0, 83.7, 74.4, 80.1, 85.5, 82.0, 80.1};
  byte jumlahSampel = sizeof(Sampel) / sizeof(Sampel[0]);

  float jumlah = 0;
  for (int i = 0; i < jumlahSampel; i++)
  {
    jumlah += Sampel[i];
  }
  float mean = jumlah / jumlahSampel;
  Serial.print("mean = ");
  Serial.println(mean);

Median (nilai tengah)

adalah nilai yang letaknya ditengah-tengah dari data yang telah diurutkan dari yang terkecil.

contoh penggunaan median pada arduino:

  float Sampel[] = {75.2, 82.0, 83.7, 74.4, 80.1, 85.5, 82.0, 80.1};
  byte jumlahSampel = sizeof(Sampel) / sizeof(Sampel[0]);

  urutkanSampel(Sampel, jumlahSampel);
  float median = Sampel[(byte)(jumlahSampel / 2)];
  if (!(jumlahSampel % 2))
  {
    median += Sampel[(byte)(jumlahSampel / 2) - 1];
    median /= 2;
  }
  Serial.print("median = ");
  Serial.println(median);

Modus (Data paling sering)

Adalah data yang paling sering muncul (frekuensi tertinggi) dan tidak ada yang sama dengan frekuensi tersebut.

Contoh penggunaan mean, median dan modus pada arduino:

void setup() {
  Serial.begin(9600);
  Serial.println("Mean, media, modus dengan Arduino");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();
}

void loop() {
  float Sampel[] = {75.2, 82.0, 83.7, 74.4, 80.1, 85.5, 82.0, 80.1};

  byte jumlahSampel = sizeof(Sampel) / sizeof(Sampel[0]);
  float jumlah = 0;
  for (int i = 0; i < jumlahSampel; i++)
  {
    jumlah += Sampel[i];
  }
  float mean = jumlah / jumlahSampel;
  Serial.print("mean = ");
  Serial.println(mean);

  urutkanSampel(Sampel, jumlahSampel);
  float median = Sampel[(byte)(jumlahSampel / 2)];
  if (!(jumlahSampel % 2))
  {
    median += Sampel[(byte)(jumlahSampel / 2) - 1];
    median /= 2;
  }
  Serial.print("median = ");
  Serial.println(median);

  float modus;
  Serial.print("modus = ");
  if(cariModus(Sampel, jumlahSampel, &modus))
  {
    Serial.println(modus);
  }
  else
  {
    Serial.println("tidak ada");
  }
  
  while (1);
}

void urutkanSampel(float *dataArray, byte jumlahData)
{
  byte i = 0;
  for (byte i = 0; i < jumlahData; i++)
  {
    byte k;
    for (byte j = 1; j < jumlahData; j++)
    {
      if (dataArray[j - 1] > dataArray[j])
      {
        float temp = dataArray[j - 1];
        dataArray[j - 1] = dataArray[j];
        dataArray[j] = temp;
      }
    }
  }
}
bool cariModus(float *dataArray, byte jumlahData, float *modus)
{
  byte frekuensi;
  float frekuensiData;
  byte frekuensiMax = 0;
  byte frekuensiMaxCount = 0;
  *modus = 0;
  for (byte i = 0; i < jumlahData; ++i)
  {
    frekuensi = 0;
    for (byte j = 0; j < jumlahData; j++)
    {
      if (dataArray[i] == dataArray[j])
      {
        frekuensi++;
      }
    }
    if(frekuensiMax < frekuensi)
    {
      frekuensiMax = frekuensi;
      frekuensiData = dataArray[i];
      *modus = dataArray[i];
      frekuensiMaxCount = 0;
    }
    else if((frekuensiMax == frekuensi) && (frekuensiData != dataArray[i]))
    {
      frekuensiMaxCount++;
    }
  }
  if(!frekuensiMaxCount)
  {
    return true;
  }
  return false;
}

Fungsi map untuk bilangan pecahan/float pada Arduino

Fungsi map() pada arduino IDE akan mengkonversikan suatu bilangan dalam suatu rentang ke rentang bilangan lain. Namun pada Arduino IDE map yang tersedia hanya untuk bilangan integer.

Bagaimana jika kita menginginkan fungsi map untuk bilangan pecahan/float:

  1. Faktor kali, yaitu dengan mengalikan rentang tujuan sehingga menjadi integer misalnya dikalikan dengan 10, 100 dan kemudian dibagi lagi dengan bilangan pengali tersebut contohnya:
    float x1 = map(adc, 0, 1023, 87 * 100, 108 * 100) / 100.0;
    
  2. Dengan modifikasi fungsi map untuk mengakomodir bilangan pecahan, seperti terlihat pada contoh sketch dibawah.

Skema pengujian fungsi map pecahan:

contoh sketch/program/koding penggunaan fungsi map pecahan:

void setup() {
  Serial.begin(9600);
  Serial.println("Fungsi map untuk bilangan pecahan/float");
  Serial.println("https://www.project.semesin.com/");
}

void loop() {
  uint16_t adc = analogRead(A0);

//Range/bentang asal adc = 0..1023
//Range/bentang tujuan   = 87.00 - 108.00

  float x1 = map(adc, 0, 1023, 87 * 100, 108 * 100) / 100.0;
  float x2 = mapPecahan(adc, 0, 1023, 87, 108);
  
  Serial.print(adc);
  Serial.print(" => ");
  Serial.print(x1, 2);
  Serial.print(" == ");
  Serial.println(x2, 2);

  delay(1000);
}

float mapPecahan(long x, long fromLow, long fromHigh, float toLow, float toHigh)
{
  return (x - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow;
}

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