webserver + ajax dengan Nodemcu (esp8266) (aplikasi monitoring suhu dan kelembaban)

Monitoring atau kegiatan pemantauan bisa dilakukan dari jarak jauh melalui sistem komunikasi internet maupun metode komunikasi lainnya. Salah satu unsur monitoring data adalah display atau tampilan dimana beberapa aspek yang perlu diperhatikan adalah :

  1. Informatif, data yang ditampilkan mampu memberikan perspektif kondisi yang ada, sebagai contoh tampilan grafik: selain menunjukkan data juga akan memberikan gambaran pergerakan dari data-data sebelumnya (naik/turun).
  2. Real time. yaitu datayang tampil merupakan data lapangan saat ini dan terus diperbaharui dalam frekuensi tertentu dimana semakin besar akan semakin realtime.
  3. Bandwidth, adalah besarnya lalu lintas data yang terpakai oleh aktifitas monitoring, jadi sistem yang baik perlu mempertimbangkan metode lalu lintas data sekecil mungkin tanpa mengurangi aspek lainnya.
  4. Kenyamanan, berkaitan dengan design dan pola dinamik tampilan saat perubahan data.

Ajax

Asynchronous Javascript and XML (Ajax) adalah metode lalu lintas data web secara asynchronous (dibelakang layar) antara interface web (browser) dengan server yang bertujuan memberikan fitur interaktif kepada pengguna.

penggunaan ajax memungkinkan update data fleksibel tanpa perlu melakukan aksi (menekan tombl update) atau refresh halaman web.

Tampilan monitoring suhu dan kelembaban menggunakan nodemcu:

Komponen yang digunakan dalam perancangan monitoring suhu dan kelembaban berbasis webserver nodemcu :

  1. Nodemcu
  2. DHT11

Skema ajax webserver dengan nodemcu:

Sketch/program webserver nodemcu dengan ajax relatime:

#define jumlahData        5
#define hotspotSSID       "XXXXXXXX"
#define hotspotPassword   "XXXXXXXX"
#define pinDHT            D4

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>

#include <dht.h>

dht DHT;

ESP8266WebServer server(80);

struct Data
{
  uint32_t index;
  float temperatur;
  float kelembaban;
};

Data data[jumlahData];
byte ringIndex;
uint32_t indexDataDHT;
long millisBacaSensor;

void handleRoot() {
  char buffer[] =
    "<html><head>"
    "  <title>Temperatur dan Kelembaban</title>"
    "  <meta charset='UTF-8'>"
    "  <script src='https://code.highcharts.com/highcharts.js'></script>"
    "</head>"
    "<body>"
    "  <center>"
    "    <h1>Temperatur dan Kelembaban</h1>"
    "    <h2>Grafik Temperatur dan Kelembaban</h2>"
    "    <div id='grafik' style='height:400px;'></div>"
    "  </center>"
    "<script type='text/javascript'>"
    "  var timerId = setInterval('updateGrafik()', 1000);"
    "  var grafik = Highcharts.chart('grafik', {"
    "    title: {text: ''},"
    "    xAxis: {title: {text: 'index'},categories: [],rotation:90,},"
    "    yAxis: ["
    "      {labels: {format: '{value} °C',style: {color: Highcharts.getOptions().colors[1]}},title: {text: 'Temperatur'},},"
    "      {labels: {format: '{value} %',style: {color: Highcharts.getOptions().colors[1]}},title: {text: 'Kelembaban'},opposite: true,}, ],"
    "    credits: {enabled: false},"
    "    series: ["
    "      {name: 'Temperatur',yAxis: 0,data: []}, "
    "      {name: 'Kelembaban',yAxis: 1,data: []}, ]"
    "  });"
    "  function updateGrafik(){"
    "    var xhttp = new XMLHttpRequest();"
    "    xhttp.onreadystatechange = function() {"
    "      if (this.readyState == 4 && this.status == 200) {"
    "        eval(this.responseText.trim());}};"
    "    xhttp.open('GET', '/updateData', true);"
    "    xhttp.send(); }"
    "</script>"
    "</body></html>";
  server.send(200, "text/html", buffer);
}

void handleNotFound()
{
  server.send(404, "text/plain", "Alamat tidak ditemukan");
}

void updateData()
{
  char buffer[1000];
  byte index;

  strcpy(buffer, "grafik.xAxis[0].setCategories([");

  index = ringIndex;
  for (byte i = 0; i < jumlahData; i++)
  {
    sprintf(&buffer[strlen(buffer)], "%lu,", data[index].index);
    index++;
    index %= jumlahData;
  }
  strcpy(&buffer[strlen(buffer)], "]);");

  strcpy(&buffer[strlen(buffer)], "grafik.series[0].setData([");
  index = ringIndex;
  for (byte i = 0; i < jumlahData; i++)
  {
    dtostrf(data[index].temperatur, 8, 2, &buffer[strlen(buffer)]);
    strcpy(&buffer[strlen(buffer)], ",");
    index++;
    index %= jumlahData;
  }
  strcpy(&buffer[strlen(buffer)], "]);");

  strcpy(&buffer[strlen(buffer)], "grafik.series[1].setData([");
  index = ringIndex;
  for (byte i = 0; i < jumlahData; i++)
  {
    dtostrf(data[index].kelembaban, 8, 2, &buffer[strlen(buffer)]);
    strcpy(&buffer[strlen(buffer)], ",");
    index++;
    index %= jumlahData;
  }
  strcpy(&buffer[strlen(buffer)], "]);");

  server.send(200, "text/html", buffer);

}


void setup(void) {
  Serial.begin(9600);
  Serial.println("Web server dengan fitur ajax menggunakan nodemcu (aplikasi monitor suhu dan kelembaban)");
  Serial.println("https://www.semesin.com/project");
  
  WiFi.mode(WIFI_STA);
  WiFi.begin(hotspotSSID, hotspotPassword);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(hotspotSSID);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.on("/updateData", updateData);
  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");
}

void loop(void) {
  server.handleClient();

  if (millisBacaSensor < millis())
  {
    millisBacaSensor = millis() + 1000;

    if (DHT.read11(pinDHT) == DHTLIB_OK)
    {
      data[ringIndex].index = indexDataDHT;
      data[ringIndex].temperatur = DHT.temperature;
      data[ringIndex].kelembaban = DHT.humidity;

      indexDataDHT++;

      ringIndex++;
      ringIndex %= jumlahData;
    }
  }
}

Library:

Timbangan tampilan seven segment menggunakan arduino

Penggunaan timbangan secara umum membutuhkan akurasi dan presisi. Dalam skala lebih luas sebaiknya unit timbangan juga informatif sehingga dapat diketahui hasilnya secara luas. Untuk keperluan ini, unit timbangan  harus memiliki sarana display yang memadai seperti menggunakan display dot mattrix, seven segment dan sebagainya.

Fitur timbangan

Fitur/fasilitas timbangan bisa bermacam sesuai kebutuhan. Dalam perancangan timbangan 7-segment ini memasukkan fitur-fitur penting diantaranya :

    1. Kalibrasi

Kalibrasi adalah kegiatan untuk memastikan timbangan memiliki akurasi dan presisi yang baik. dalam perancangan ini prosedur kalibrasinya sebagai berikut:

    • Posisi perangkat timbangan dalam keadaan mati
    • Letakkan beban tera 1Kg
    • Tekan dan tahan tombol [hold]
    • Hidupkan perangkat timbangan (akan muncul tulisan ‘load 1’ di tampilan seven segment)
    • lepas tombol [hold]
    • Tunggu hingga muncul tulisan ‘load 2’ di 7-segment
    • Letakkan beban tera 2.5 Kg
    • Tekan dan lepas tombol [hold]
    • Tunggu hingga muncul tulisan ‘siap’ didisplay 7-segment
    • Kalibrasi selesai
  1. tombol tare
  2. tombol hold
  3. Tampilan seven segment

Seven Segmen

Seven segment memiliki kelebihan dari display lainnya, diantaranya :

  1. Intensitas cahaya lebih terang (akan berkurang jika menggunakan sistem scanning)
  2. Mudah terbaca dari jarak jauh (lebih jelas)
  3. Jika menggunakan mikrokontroler, lebih sedikit menggunakan resources (memory dan waktu proses)
  4. Menggunakan komponen lebih sedikit (non module)
  5. Perbandingan cahaya vs daya terpakai lebih sedikit sehingga lebih hemat.

Dalam proyek timbangan informatif dengan display 7-segment berbasis arduino ini menggunakan komponen sebagai berikut:

Komponen yang digunakan dalam perancangan timbangan sevent segment menggunakan arduino:

  1. Arduino uno
  2. modul hx711 dan loadcell 5Kg
  3. IC uln2003
  4. 5x 7-Segment CC
  5. 2x Tombol

Skema timbangan tampilan seven segmen:

source code/program arduino timbangan seven segment:

 

#include "HX711.h"
#include <TimerOne.h>
#include <EEPROM.h>

#define CCorCA                1//0 = CC, 1 = CA (rangkaian dengan resistor)
#define tareSaatMulai         1

#define beratKalibrasi1Tera   1.0f  //Kg
#define beratKalibrasi2Tera   1.00f  //Kg
#define beratMaksimal         2.50 //Kg
#define jumlahSampling        5     //kali
#define frekuensiUpdateData   20    //kali per detik

//pin
HX711 scale(3, 2); // (DT, SCK)

#define pin1    A0
#define pin2    A1
#define pin3    A2
#define pin4    A3
#define pin5    A4

#define pinA    4
#define pinB    5
#define pinC    6
#define pinD    7
#define pinE    8
#define pinF    9
#define pinG    10
#define pinDot  11

#define pinTare 12
#define pinHold 13

#define dotBlank 6
#define dotAll   5

byte pin7Segment[] = {pinA, pinB, pinC, pinD, pinE, pinF, pinG};
byte pin7SegmentCommon[] = {pin1, pin2, pin3, pin4, pin5};
byte posisiTitik = dotBlank;

const char angka[] = {
  0b00111111,
  0b00000110,
  0b01011011,
  0b01001111,
  0b01100110,
  0b01101101,
  0b01111100,
  0b00000111,
  0b01111111,
  0b01100111,
  0b00000000,//blank
  0b01000000,//strip
  0b00111000,//L
  0b01011100,//o
  0b01011111,//a
  0b01011110,//d
  0b01101101,//S
  0b00000100,//i
  0b01110011,//p
  
};
#define Seg_blank 10
#define Seg_stip 11
#define Seg_L 12
#define Seg_o 13
#define Seg_a 14
#define Seg_d 15
#define Seg_S 16
#define Seg_i 17
#define Seg_p 18

//volatile uint32_t beratBCD = 0;
volatile uint8_t beratBCD[5];
volatile byte lastScanIndex = 0;
volatile byte index7Segment = 0;

long lastMillis;
long kirimDataMillis;
byte modeKalibrasi = 0;
long beratKalibrasi1;
long beratKalibrasi2;
float beratTerukur;
bool tahanNilai = false;
byte timbangBeratCounter = 0;

#define alamatKalibrasiM 0
#define alamatKalibrasiC 4

void setup() {
  Serial.begin(9600);
  Serial.println(F("Timbangan tampilan seven segment menggunakan arduino"));
  Serial.println(F("https://www.semesin.com/project"));
  
  pinMode(pin1, OUTPUT);
  pinMode(pin2, OUTPUT);
  pinMode(pin3, OUTPUT);
  pinMode(pin4, OUTPUT);
  pinMode(pin5, OUTPUT);

  pinMode(pinA, OUTPUT);
  pinMode(pinB, OUTPUT);
  pinMode(pinC, OUTPUT);
  pinMode(pinD, OUTPUT);
  pinMode(pinE, OUTPUT);
  pinMode(pinF, OUTPUT);
  pinMode(pinG, OUTPUT);
  pinMode(pinDot, OUTPUT);

  pinMode(pinTare, INPUT_PULLUP);
  pinMode(pinHold, INPUT_PULLUP);

  Timer1.initialize(2000);
  Timer1.attachInterrupt( timerIsr );

  float m,c;

  scale.power_up();
  
  //Mode kalibrasi
  if(!digitalRead(pinHold))
  {
    delay(50);
    if(!digitalRead(pinHold))
    {
      beratBCD[4] = Seg_L;
      beratBCD[3] = Seg_o;
      beratBCD[2] = Seg_a;
      beratBCD[1] = Seg_d;
      beratBCD[0] = 1;

      while(!digitalRead(pinHold))//lepas
      {
        while(!digitalRead(pinHold))
        {
          delay(50);
        }
      }
      delay(1000);
      beratKalibrasi1 = scale.read_average(10);

      beratBCD[0] = 2;
      while(digitalRead(pinHold))//tekan
      {
        while(digitalRead(pinHold))
        {
          delay(50);
        }
      }
      delay(1000);
      beratKalibrasi2 = scale.read_average(10);
      
      m = 1.0 * (beratKalibrasi2 - beratKalibrasi1) / ( beratKalibrasi2Tera - beratKalibrasi1Tera);
      c = beratKalibrasi2 - (1.0 * m * beratKalibrasi2Tera);

      EEPROM.put(alamatKalibrasiM, m);
      EEPROM.put(alamatKalibrasiC, c);    

      beratBCD[4] = Seg_S;
      beratBCD[3] = Seg_i;
      beratBCD[2] = Seg_a;
      beratBCD[1] = Seg_p;
      beratBCD[0] = Seg_blank;

      delay(500);
    }
  }
  
  EEPROM.get(alamatKalibrasiM, m);
  EEPROM.get(alamatKalibrasiC, c);

  scale.set_scale(m);
  scale.set_offset(c);

#if tareSaatMulai
  scale.tare();
#endif
  
  scale.power_down();
  lastMillis = millis();
  kirimDataMillis = millis();
}

void loop() {

  if(!tahanNilai)
  {
    if(millis() - lastMillis > 10)
    {
      scale.power_up();
      delay(10);
      beratTerukur += scale.get_units(1);
      if(timbangBeratCounter++ == jumlahSampling)
      {
        beratTerukur = beratTerukur * 100 / jumlahSampling;
        if(beratTerukur < 0)
        {
          beratBCD[0] = 0;
          beratBCD[1] = 0;
          beratBCD[2] = 0;
          beratBCD[3] = Seg_stip;
          beratBCD[4] = Seg_blank;
          posisiTitik = 2;
        }
        else if(beratTerukur <= (beratMaksimal*100))
        {
          uint32_t BCD = Convert_IntToBCD32(beratTerukur);
          if((BCD & 0x0F) <= 5)
          {
            beratBCD[0] = 0;
          }
          else
          {
            beratBCD[0] = 5;
          }
          beratBCD[1] = (BCD >> 4) & 0x0F;
          beratBCD[2] = (BCD >> 8) & 0x0F;
          beratBCD[3] = (BCD >> 12) & 0x0F;
          beratBCD[4] = (BCD >> 16) & 0x0F;
          posisiTitik = 2;
        }
        beratTerukur = 0;
        timbangBeratCounter = 0;
      }
      scale.power_down();
      
      lastMillis = millis();
    }
  }

  //Kirim data setiap 100ms
  if(millis() - kirimDataMillis > (1000/frekuensiUpdateData))
  {
    if(beratTerukur <= 0)
    {
      Serial.println("0.00");
    }
    else
    {
      Serial.println(floor(beratTerukur)/100, 2);
    }

    kirimDataMillis = millis();
  }
  if(!digitalRead(pinTare))
  {
    delay(50);
    if(!digitalRead(pinTare))
    {
      beratBCD[0] = Seg_blank;
      beratBCD[1] = Seg_blank;
      beratBCD[2] = Seg_blank;
      beratBCD[3] = Seg_blank;
      beratBCD[4] = Seg_blank;
      posisiTitik = dotAll;
      scale.power_up();
      delay(10);
      scale.tare();
      scale.power_down();
      while(!digitalRead(pinTare));
    }
  }
  if(!digitalRead(pinHold))
  {
    delay(50);
    if(!digitalRead(pinHold))
    {
      tahanNilai = !tahanNilai;
      while(!digitalRead(pinHold));
    }
  }
}
void timerIsr()
{
  digitalWrite(pin7SegmentCommon[lastScanIndex], CCorCA?LOW:HIGH);
  drive7Segment(beratBCD[index7Segment]);
  if(posisiTitik == dotAll)
  {
    digitalWrite(pinDot, CCorCA?LOW:HIGH);
  }
  else if(posisiTitik == dotBlank)
  {
    digitalWrite(pinDot, CCorCA?HIGH:LOW);
  }
  else
  {
    digitalWrite(pinDot, (index7Segment == posisiTitik)?CCorCA?LOW:HIGH:CCorCA?HIGH:LOW);
  }
  digitalWrite(pin7SegmentCommon[index7Segment], CCorCA?HIGH:LOW);
  lastScanIndex = index7Segment++;
  if(index7Segment > 4)index7Segment = 0;
}
void drive7Segment(byte nilai)
{
  byte nilai7Segment = CCorCA?~angka[nilai]:angka[nilai];
  for(byte i=0;i<sizeof(pin7Segment);i++)
  {
    digitalWrite(pin7Segment[i], nilai7Segment & 0x01);
    nilai7Segment >>= 1;
  }
}
uint32_t Convert_IntToBCD32(uint32_t DecimalValue)
{
    uint32_t returnValue = 0;
    //uint32_t LSB_L = DecimalValue;
 
    while (DecimalValue >= 10000000L)
    {
        DecimalValue -= 10000000L;
        returnValue += 0x10000000;
    }
    while (DecimalValue >= 1000000L)
    {
        DecimalValue -= 1000000L;
        returnValue += 0x01000000;
    }
    while (DecimalValue >= 100000L)
    {
        DecimalValue -= 100000L;
        returnValue += 0x00100000;
    }
    while (DecimalValue >= 10000)
    {
        DecimalValue -= 10000;
        returnValue += 0x00010000;
    }
    while (DecimalValue >= 1000L)
    {
        DecimalValue -= 1000L;
        returnValue += 0x00001000;
    }
    while (DecimalValue >= 100)
    {
        DecimalValue -= 100;
        returnValue += 0x00000100;
    }
    while (DecimalValue >= 10)
    {
        DecimalValue -= 10;
        returnValue += 0x00000010;
    }
    return returnValue + DecimalValue;
}

Library yang digunakan dalam perakitan timbangan arduino:

  1. HX711.zip
  2. TimerOne.zip

Keamanan berlapis akses pintu menggunakan sandi keypad dan sidik jari menggunakan Code Vision

Sistem keamanan merupakan bagian sistem yang bertugas memberikan akses terhadap bagian-bagian yang dilindunginya. Jika unit yang dilindungi memiliki fungsi sangat vital yang hanya di boleh diakses oleh orang tertentu maka sistem keamanannya dibuat berlapis.

Infrastruktur sistem keamanan sudah sangat berkembang, beberapa yang sering digunakan pada aplikasi mikrokontroller diantaranya :

  1. Kata sandi dengan tombol/keypad/remot tv
  2. Kartu akses dengan RFID reader
  3. Sidik jari
  4. suara dengan voice recognition

Dalam perancangan dini menggunakan 2 lapis sistem keamanan yaitu kata sandi menggunakan keypad serta sidik jari.

Komponen yang digunakan:

  1. ATMega8535
  2. Keypad membrane 4×4
  3. Fingerprint dy50
  4. LCD I2c 16×2
  5. Solenoid doorlock
  6. Buzzer

Skema perancangan sistem keamanan berlapis (password dan fingerprint):
 

program code vision (cvavr) sistem keamanan menggunakan finger print dan keypad:

#include <mega8535.h>
#include <stdbool.h>
#include "fingerprint.h"
#include "lcdi2c.h"

// Declare your global variables here
#define password    "1234"
#define pinBuzzer   PORTD.3
#define pinKunci    PORTD.2

char buf[10];
uint8_t respon;

uint16_t timingFingerprintAktif;
uint16_t i;

char keypad;
char keypadBuffer[10];
bool statusPassword;
uint8_t keypadCnt;


#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)
#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<DOR)

// USART Receiver buffer
#define RX_BUFFER_SIZE 20
char rx_buffer[RX_BUFFER_SIZE];

#if RX_BUFFER_SIZE <= 256
unsigned char rx_wr_index=0,rx_rd_index=0;
#else
unsigned int rx_wr_index=0,rx_rd_index=0;
#endif

#if RX_BUFFER_SIZE < 256
unsigned char rx_counter=0;
#else
unsigned int rx_counter=0;
#endif

// This flag is set on USART Receiver buffer overflow
bit rx_buffer_overflow;

// USART Receiver interrupt service routine
interrupt [USART_RXC] void usart_rx_isr(void)
{
char status,data;
status=UCSRA;
data=UDR;
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
   {
   rx_buffer[rx_wr_index++]=data;
#if RX_BUFFER_SIZE == 256
   // special case for receiver buffer size=256
   if (++rx_counter == 0) rx_buffer_overflow=1;
#else
   if (rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0;
   if (++rx_counter == RX_BUFFER_SIZE)
      {
      rx_counter=0;
      rx_buffer_overflow=1;
      }
#endif
   }
}

#ifndef _DEBUG_TERMINAL_IO_
// Get a character from the USART Receiver buffer
#define _ALTERNATE_GETCHAR_
#pragma used+
char getchar(void)
{
char data;
while (rx_counter==0);
data=rx_buffer[rx_rd_index++];
#if RX_BUFFER_SIZE != 256
if (rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0;
#endif
#asm("cli")
--rx_counter;
#asm("sei")
return data;
}
#pragma used-
#endif


char scanning_keypad()
{
    PORTB = 0b01111111;
    delay_ms(20);
    if(PINB.0 == 0){while(PINB.0 == 0);return 'A';}
    if(PINB.1 == 0){while(PINB.1 == 0);return 'B';}
    if(PINB.2 == 0){while(PINB.2 == 0);return 'C';}
    if(PINB.3 == 0){while(PINB.3 == 0);return 'D';}
     
    PORTB = 0b10111111;
    delay_ms(20);
    if(PINB.0 == 0){while(PINB.0 == 0);return '3';}
    if(PINB.1 == 0){while(PINB.1 == 0);return '6';}
    if(PINB.2 == 0){while(PINB.2 == 0);return '9';}
    if(PINB.3 == 0){while(PINB.3 == 0);return '#';}
     
    PORTB = 0b11011111;
    delay_ms(20);
    if(PINB.0 == 0){while(PINB.0 == 0);return '2';}
    if(PINB.1 == 0){while(PINB.1 == 0);return '5';}
    if(PINB.2 == 0){while(PINB.2 == 0);return '8';}
    if(PINB.3 == 0){while(PINB.3 == 0);return '0';}
     
    PORTB = 0b11101111;
    delay_ms(20);
    if(PINB.0 == 0){while(PINB.0 == 0);return '1';}
    if(PINB.1 == 0){while(PINB.1 == 0);return '4';}
    if(PINB.2 == 0){while(PINB.2 == 0);return '7';}
    if(PINB.3 == 0){while(PINB.3 == 0);return '*';}    
    
    return 0;
     
}


// Standard Input/Output functions
#include <stdio.h>

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port A initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In 
DDRA=(0<<DDA7) | (0<<DDA6) | (0<<DDA5) | (0<<DDA4) | (0<<DDA3) | (0<<DDA2) | (0<<DDA1) | (0<<DDA0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T 
PORTA=(0<<PORTA7) | (0<<PORTA6) | (0<<PORTA5) | (0<<PORTA4) | (0<<PORTA3) | (0<<PORTA2) | (0<<PORTA1) | (0<<PORTA0);

// Port B initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In 
DDRB=(1<<DDB7) | (1<<DDB6) | (1<<DDB5) | (1<<DDB4) | (0<<DDB3) | (0<<DDB2) | (0<<DDB1) | (0<<DDB0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T 
PORTB=(0<<PORTB7) | (0<<PORTB6) | (0<<PORTB5) | (0<<PORTB4) | (1<<PORTB3) | (1<<PORTB2) | (1<<PORTB1) | (1<<PORTB0);

// Port C initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=Out Bit3=In Bit2=In Bit1=In Bit0=In 
DDRC=(0<<DDC7) | (0<<DDC6) | (0<<DDC5) | (1<<DDC4) | (0<<DDC3) | (0<<DDC2) | (0<<DDC1) | (0<<DDC0);
// State: Bit7=T Bit6=T Bit5=T Bit4=0 Bit3=T Bit2=T Bit1=T Bit0=T 
PORTC=(0<<PORTC7) | (0<<PORTC6) | (0<<PORTC5) | (0<<PORTC4) | (0<<PORTC3) | (0<<PORTC2) | (0<<PORTC1) | (0<<PORTC0);

// Port D initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In 
DDRD=(0<<DDD7) | (0<<DDD6) | (0<<DDD5) | (0<<DDD4) | (1<<DDD3) | (1<<DDD2) | (0<<DDD1) | (0<<DDD0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T 
PORTD=(0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (0<<PORTD0);

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=0xFF
// OC0 output: Disconnected
TCCR0=(0<<WGM00) | (0<<COM01) | (0<<COM00) | (0<<WGM01) | (0<<CS02) | (0<<CS01) | (0<<CS00);
TCNT0=0x00;
OCR0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer1 Stopped
// Mode: Normal top=0xFFFF
// OC1A output: Disconnected
// OC1B output: Disconnected
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=(0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (0<<WGM10);
TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (0<<WGM12) | (0<<CS12) | (0<<CS11) | (0<<CS10);
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=0xFF
// OC2 output: Disconnected
ASSR=0<<AS2;
TCCR2=(0<<WGM20) | (0<<COM21) | (0<<COM20) | (0<<WGM21) | (0<<CS22) | (0<<CS21) | (0<<CS20);
TCNT2=0x00;
OCR2=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=(0<<OCIE2) | (0<<TOIE2) | (0<<TICIE1) | (0<<OCIE1A) | (0<<OCIE1B) | (0<<TOIE1) | (0<<OCIE0) | (0<<TOIE0);

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
MCUCR=(0<<ISC11) | (0<<ISC10) | (0<<ISC01) | (0<<ISC00);
MCUCSR=(0<<ISC2);

// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud Rate: 57600
UCSRA=(0<<RXC) | (0<<TXC) | (0<<UDRE) | (0<<FE) | (0<<DOR) | (0<<UPE) | (0<<U2X) | (0<<MPCM);
UCSRB=(1<<RXCIE) | (0<<TXCIE) | (0<<UDRIE) | (1<<RXEN) | (1<<TXEN) | (0<<UCSZ2) | (0<<RXB8) | (0<<TXB8);
UCSRC=(1<<URSEL) | (0<<UMSEL) | (0<<UPM1) | (0<<UPM0) | (0<<USBS) | (1<<UCSZ1) | (1<<UCSZ0) | (0<<UCPOL);
UBRRH=0x00;
UBRRL=0x08;

// Analog Comparator initialization
// Analog Comparator: Off
// The Analog Comparator's positive input is
// connected to the AIN0 pin
// The Analog Comparator's negative input is
// connected to the AIN1 pin
ACSR=(1<<ACD) | (0<<ACBG) | (0<<ACO) | (0<<ACI) | (0<<ACIE) | (0<<ACIC) | (0<<ACIS1) | (0<<ACIS0);
SFIOR=(0<<ACME);

// ADC initialization
// ADC disabled
ADCSRA=(0<<ADEN) | (0<<ADSC) | (0<<ADATE) | (0<<ADIF) | (0<<ADIE) | (0<<ADPS2) | (0<<ADPS1) | (0<<ADPS0);

// SPI initialization
// SPI disabled
SPCR=(0<<SPIE) | (0<<SPE) | (0<<DORD) | (0<<MSTR) | (0<<CPOL) | (0<<CPHA) | (0<<SPR1) | (0<<SPR0);

// TWI initialization
// TWI disabled
TWCR=(0<<TWEA) | (0<<TWSTA) | (0<<TWSTO) | (0<<TWEN) | (0<<TWIE);

// Global enable interrupts
#asm("sei")

i2c_begin();
lcd_begin(0x27,16,2); // alamat lcd i2c
lcd_clear();
lcd_puts("Sistem Keamanan");
lcd_gotoxy(0,1);
lcd_puts("www.semesin.com");
delay_ms(3000);

lcd_clear();
//rx_wr_index = 15;
fingerPrintBegin((uint8_t*)&rx_buffer, &rx_wr_index);
respon = checkPassword();
//sprintf(buf, "%2X", respon);
lcd_gotoxy(0,0);
lcd_puts("Akses terbatas  ");

delay_ms(2000);

keypadCnt = 0;

while (1)
      {
      // Place your code here
            while(1)
            {      
                if(!statusPassword)
                {
                    keypad = scanning_keypad();
                    if(keypad)
                    {
                        if(keypad == '#')
                        {           
                            keypadBuffer[keypadCnt] = 0; 
                                         
                            lcd_clear();              
                            lcd_gotoxy(0,0);
                            if(strcmp(keypadBuffer, password) == 0)
                            {
                                statusPassword = 1;          
                                timingFingerprintAktif = 30000;
                                lcd_puts("Tempel sidikjari");
                            }
                            else    
                            {
                                statusPassword = 0;
                                lcd_puts("Password salah  ");
                                for(i=0;i<3;i++)
                                {
                                    pinBuzzer = 1;
                                    delay_ms(1000);
                                    pinBuzzer = 0;
                                    delay_ms(1000);
                                }
                                lcd_clear();
                                lcd_puts("Akses terbatas  ");
                                
                            }                      
                            keypadCnt = 0;
                    		
                        }
                        if((keypad >= '0') &&(keypad <= '9')) 
                        {            
                            if(keypadCnt == 0)
                            {
                                lcd_gotoxy(0,0);
                                lcd_puts("Password :       ");
                            }                     
                            if(keypadCnt < 4)
                            {
                                lcd_gotoxy(keypadCnt,1);
                                lcd_send_data(keypad);          
                                keypadBuffer[keypadCnt] = keypad;
                                keypadCnt++;
                            }  
                    	
                        }
                    	
                    }          
                }
                else
                {
                    delay_ms(1);
                    timingFingerprintAktif--;
                    if(!timingFingerprintAktif)
                    {
                        statusPassword = 0;
                        pinBuzzer = 1;
                        delay_ms(1000);
                        pinBuzzer = 0;    
                        lcd_clear();
                        lcd_puts("Panel           ");  
                        
                    }   
                    lcd_gotoxy(0,1);  
                    lcd_puts("Tempelkan jari  ");
                    respon = getImage();
                    if(respon != FINGERPRINT_OK)
                    {
                        break;
                    }  

                    lcd_gotoxy(0,1);  
                    lcd_puts("Konversi gambar ");
                    respon = image2Tz(1);
                    if(respon != FINGERPRINT_OK)
                    {
                        break;
                    }  
                    
                    lcd_gotoxy(0,1);  
                    lcd_puts("Mencari id      ");
                    respon = fingerFastSearch();
                    if(respon != FINGERPRINT_OK)
                    {
                        break;
                    }                     
                    lcd_clear();
                    sprintf(buf, "id = %2d", fingerID);
                    lcd_gotoxy(0,1);  
                    lcd_puts(buf);
                    lcd_gotoxy(0,0);
                    lcd_puts("Panel");
                                        
                    pinKunci = 1;
                    delay_ms(5000);
                    pinKunci = 0; 
                }
            }
      }
}

library:

  1. fingerprint.h
  2. lcdi2c.h

Mesin antrian dmd dilengkapi suara dengan arduino

Mesin antrian adalah sistem informasi antara meja layanan dan pengunjung untuk mengatur waktu pelayanan terhadap pengunjung sesuai urutan kedatangan. Sistem ini terdiri atas media, dan tombol panggil, bisa juga ditambahkan dengan tombol tiket (dengan cetak tiket). Media informasi seperti seven segment, running text (dmd), monitor, suara yang diletakkan di ruang tunggu yang terlihat/terdengar di kursi pengunjung yang sedang antri.

Mesin antrian dalam proyek ini memiliki fitur berikut :

  1. Tombol panggil 4 buah dengan fitur cascade yaitu masing-masing tombol dapat ditekan bersamaan tanpa harus menunggu proses panggilan meja operator lain.
  2. Tombol tiket 1 buah dan dilengkapi cetak tiket
  3. Tombol reset untuk mengembalikan ke keadaan awal
  4. Media dot matik P10 sebagai papan informasi.
  5. Suara panggil mp3 berupa nomor antrian dan nomor loket

Skema Mesin antrian arduino :

Komponen yang digunakan dalam membuat mesin antrian menggunakan dmd (dot matrix display) berbasis arduino :

  1. Arduino uno
  2. DMD P10
  3. Printer thermal
  4. DF Player mini mp3, speaker, resistor
  5. Tombol

Sketch / koding sistem antrian arduino :

#define pinTombolPanggil1   A0
#define pinTombolPanggil2   A1
#define pinTombolPanggil3   A2
#define pinTombolPanggil4   A3

#define pinTombolReset      A4
#define pinTombolTiket      A5

#define pinMP3Busy          2

#define belas               12 //belas.mp3
#define puluh               13 //puluh.mp3
#define seratus             14 //seratus.mp3
#define ratus               15 //ratus.mp3
#define seribu              16 //seribu.mp3
#define ribu                17 //ribu.mp3
#define koma                18 //Koma.mp3

#define antrianNomor        101
#define silahkanKeCS        102

#include <SPI.h>
#include <DMD_Semesin.h>
#include <fonts/angka6x14ABCD.h>
#include <DFPlayer_Mini_Mp3.h>
#include "Adafruit_Thermal.h"


byte nomorAntrianDaftar;
byte nomorAntrianPanggil;
byte nomorCSPanggil;

SPIDMD dmd(1, 1);
SoftwareSerial serialPrinter(4, 5);
Adafruit_Thermal printer(&serialPrinter);

struct DataAntrian
{
  uint16_t counterTiket;
  uint16_t counterPanggil;
  uint8_t CS;
  bool dataTombolTekan[4];
};

DataAntrian dataAntrian;
byte pinTombolPanggil[] = {pinTombolPanggil1, pinTombolPanggil2, pinTombolPanggil3, pinTombolPanggil4};
char buffer[20];

bool statusTombolTiket;
byte statusTombolPanggil[4];


void setup() {
  pinMode(pinTombolPanggil1, INPUT_PULLUP);
  pinMode(pinTombolPanggil2, INPUT_PULLUP);
  pinMode(pinTombolPanggil3, INPUT_PULLUP);
  pinMode(pinTombolPanggil4, INPUT_PULLUP);

  pinMode(pinTombolReset, INPUT_PULLUP);
  pinMode(pinTombolTiket, INPUT_PULLUP);

  Serial.begin(9600);

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

  mp3_set_serial (Serial);
  mp3_set_volume(30);
  Serial.println();

  serialPrinter.begin(9600);
  printer.begin();
  printer.sleep();

  dmd.clearScreen();
  dmd.setBrightness(255);
  dmd.selectFont(angka6x14ABCD);
  dmd.begin();
}

void loop() {

  bacaTombol();

  if (!digitalRead(pinTombolReset))
  {
    delay(100);
    if (!digitalRead(pinTombolReset))
    {
      dataAntrian.counterTiket = 0;
      dataAntrian.counterPanggil = 0;
      dataAntrian.CS = 0;
      dataAntrian.dataTombolTekan[0] = 0;
      dataAntrian.dataTombolTekan[1] = 0;
      dataAntrian.dataTombolTekan[2] = 0;
      dataAntrian.dataTombolTekan[3] = 0;

      dmd.clearScreen();
      Serial.println("Reset");
      while (!digitalRead(pinTombolReset));
    }
  }

  for (byte i = 0; i < sizeof(pinTombolPanggil); i++)
  {
    if (dataAntrian.counterTiket > dataAntrian.counterPanggil)
    {
      if (dataAntrian.dataTombolTekan[i])
      {
        dataAntrian.counterPanggil++;
        dataAntrian.CS = i + 1;

        sprintf(buffer, "%02d>%1d", dataAntrian.counterPanggil, dataAntrian.CS);
        dmd.drawString(2, 1, buffer);

        Serial.print("call=");
        Serial.println(dataAntrian.counterPanggil);
        Serial.print("cs=");
        Serial.println(dataAntrian.CS);

        mp3_play_and_wait(antrianNomor);
        suaraBilangan(dataAntrian.counterPanggil);
        mp3_play_and_wait(silahkanKeCS);
        suaraBilangan(dataAntrian.CS);

        Serial.println();
        dataAntrian.dataTombolTekan[i] = false;
      }
    }
  }
}

void suaraBilangan(uint32_t Bilangan)
{
  if (Bilangan < 100)
  {
    suaraPuluhan(Bilangan);
  }
  else if (Bilangan < 1000)
  {
    suaraRatusan(Bilangan);
  }
  else
  {
    suaraRibuan(Bilangan);
  }
}
void suaraPuluhan(uint8_t Bilangan)
{
  if (Bilangan < 12)
  {
    mp3_play_and_wait(Bilangan);
  }
  else if (Bilangan < 20)
  {
    mp3_play_and_wait(Bilangan - 10);
    mp3_play_and_wait(belas);
  }
  else
  {
    uint8_t puluhan = Bilangan / 10;
    mp3_play_and_wait(puluhan);
    mp3_play_and_wait(puluh);

    puluhan *= 10;
    if (Bilangan - puluhan != 0)
    {
      mp3_play_and_wait((Bilangan - puluhan));
    }
  }
}
void suaraRatusan(uint16_t Bilangan)
{
  uint8_t ratusan = (uint8_t)(Bilangan / 100);
  if (ratusan == 1)
  {
    mp3_play_and_wait(seratus);
  }
  else
  {
    mp3_play_and_wait(ratusan);
    mp3_play_and_wait(ratus);
  }
  if (Bilangan % 100)
  {
    suaraPuluhan(Bilangan - (ratusan * 100));
  }
}
void suaraRibuan(uint32_t Bilangan)
{
  uint16_t ribuan = (uint16_t)(Bilangan / 1000);
  if (ribuan == 1)
  {
    mp3_play_and_wait(seribu);
  }
  else if (ribuan < 100)
  {
    suaraPuluhan(ribuan);
    mp3_play_and_wait(ribu);
  }
  else
  {
    suaraRatusan(ribuan);
    mp3_play_and_wait(ribu);
  }
  if (Bilangan % 1000)
  {
    suaraRatusan(Bilangan - (ribuan * 1000));
  }
}

void mp3_play_and_wait(uint16_t num) {
  mp3_play (num);
  delay(200);
  while (!digitalRead(pinMP3Busy))
  {
    bacaTombol();
  }
}

void bacaTombol()
{
  if (!digitalRead(pinTombolTiket))
  {
    if (!statusTombolTiket)
    {
      delay(100);
      if (!digitalRead(pinTombolTiket))
      {
        dataAntrian.counterTiket++;
        printer.wake();
        printer.setDefault();

        printer.justify('C');
        printer.println("Nomor Antrian");
        printer.doubleHeightOn();
        printer.println(dataAntrian.counterTiket);
        printer.doubleHeightOff();
        printer.println("Terima kasih");
        printer.feed(2);
        printer.sleep();

        Serial.print("ambil tiket : ");
        Serial.println(dataAntrian.counterTiket);

        statusTombolTiket = true;
      }
    }
  }
  else
  {
    statusTombolTiket = false;
  }

  for (byte i = 0; i < sizeof(pinTombolPanggil); i++)
  {
    if (!digitalRead(pinTombolPanggil[i]))
    {
      if (!statusTombolPanggil[i])
      {
        delay(100);
        if (!digitalRead(pinTombolPanggil[i]))
        {
          dataAntrian.dataTombolTekan[i] = true;
          statusTombolPanggil[i] = true;

          Serial.print("TombolPanggil:");
          Serial.println(i);
        }
      }
    }
    else
    {
      statusTombolPanggil[i] = false;
    }
  }
}

library :

suara mp3

  1. mp3 mesin antrian arduino.zip

Pengisi galon otomatis menggunakan arduino

Depot pengisian air minum menggunakan galon sebagai tempat air. Tahap-tahap pengisian yang lazim di lakukan adalah: pembersihan, pengisian, pemasangan tutup dan perbersihan.

Dalam artikel ini hanya melakukan otomatisasi pada tahap pengisian, cara kerjanya sebagai berikut :

  1. Saat mulai, sensor ir/obstacle mendeteksi keberadaan galon diatas tempat pengisian galon. Jika ada galon maka sistem menginformasikan agar galon diambil terlebih dahulu.
  2. Sistem menunggu hingga galon kosong ditempatkan pada posisi pengisian galon.
  3. Sistem akan menghidupkan pompa air, kemudian menghidupkan solenoid.
  4. Sensor flow meter akan menghitung volume ait yang dialirkan kedalam galon.
  5. Jika volume telah mencukupi maka solenoid dan pompa dimatikan.
  6. Jika selama pengisian galon, posisi galon bergeser atau diambil, maka solenoid dan pompa akan dimatikan.
  7. Sistem akan menunggu galon diambil.
  8. Selesai.

Skema pengisian galon otomatis berbasis arduino:

Komponen yang digunakan dalam perancangan sistem pengisian galon arduino:

  1. Arduino uno
  2. LCD 16×2 lcd backpack
  3. sensor obstacle
  4. flow sensor
  5. solenoid
  6. pompa air
  7. relay 2 channel

Program pengisi galon menggunakan arduino:

#define kapasitasGalon      19//liter
#define pulsaPerLiter       450//sesuai spesifikasi sensor water flow

#define pinWaterFlow        2
#define pinSensorIR         A0
#define pinPompa            7
#define pinSelenoid         6

#define IRAktif             LOW
#define relayAktif          LOW

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <util/atomic.h>

LiquidCrystal_I2C lcd(0x3F, 16, 2);
volatile uint16_t pulseCount;

void setup() {
  digitalWrite(pinPompa, !relayAktif);
  digitalWrite(pinSelenoid, !relayAktif);
  pinMode(pinWaterFlow, INPUT);
  pinMode(pinSensorIR, INPUT);
  pinMode(pinPompa, OUTPUT);
  pinMode(pinSelenoid, OUTPUT);

  Serial.begin(9600);
  Serial.println(F("Pengisi galon otomatis"));
  Serial.println(F("https://www.semesin.com/project"));


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

  lcd.backlight();
  lcd.print("Pengisi galon");
  lcd.setCursor(0, 1);
  lcd.print("Semesin.com");
  delay(3000);


  lcd.setCursor(0, 0);
  lcd.print("ambil Galon     ");
  while (digitalRead(pinSensorIR) == IRAktif); //deteksi awal : jika ada galon kosongkan dahulu

  attachInterrupt(digitalPinToInterrupt(pinWaterFlow), pulseCounter, FALLING);
}

void loop() {
  lcd.setCursor(0, 0);
  lcd.print("Letakkan Galon  ");
  while (digitalRead(pinSensorIR) == !IRAktif);

  delay(1000);//memastikan galon diletakkan

  if (digitalRead(pinSensorIR) == IRAktif)
  {
    lcd.setCursor(0, 0);
    lcd.print("Mengisi Galon...");

    pulseCount = 0;
    digitalWrite(pinPompa, relayAktif);
    delay(1000);
    digitalWrite(pinSelenoid, relayAktif);

    lcd.setCursor(0, 1);
    lcd.print("                ");
    uint16_t jumlahPulsa;

    while ((jumlahPulsa < kapasitasGalon * pulsaPerLiter) && (digitalRead(pinSensorIR) == IRAktif))
    {
      ATOMIC_BLOCK(ATOMIC_FORCEON)
      {
        jumlahPulsa = pulseCount;
      }

      lcd.setCursor(0, 1);
      lcd.print((1.0 * jumlahPulsa / pulsaPerLiter));
      lcd.print(" liter");
    }

    digitalWrite(pinSelenoid, !relayAktif);
    delay(100);
    digitalWrite(pinPompa, !relayAktif);

    lcd.setCursor(0, 0);
    if (jumlahPulsa >= kapasitasGalon * pulsaPerLiter)
    {
      lcd.print("Galon penuh     ");
    }
    else if (digitalRead(pinSensorIR) != IRAktif)
    {
      lcd.print("Galon tidak ada ");
    }

    delay(1000);
    lcd.setCursor(0, 0);
    lcd.print("silahkan ambil  ");

    while (digitalRead(pinSensorIR) == IRAktif);
    delay(1000);//memastikan galon sudah diambil
    while (digitalRead(pinSensorIR) == IRAktif);
  }
}

void pulseCounter()
{
  pulseCount++;
}

Library:

Simulasi soft start motor induksi 3 phasa menggunakan simulink matlab

Pada motor induksi yang diam apabila tegangan normal diberikan ke stator maka akan ditarik arus yang besar oleh belitan primernya. Motor induksi saat dihidupkan secara langsung akan menarik arus 5 sampai 7 kali dari arus beban penuh dan hanya menghasilkan torsi 1,5 sampai 2,5 kali torsi beban penuh. Arus mula yang besar ini dapat mengakibatkan drop tegangan pada saluran sehingga akan mengganggu peralatan lain yang dihubungkan pada saluran yang sama. Untuk motor yang berdaya besar tentu arus starting juga akan semakin besar, sehingga untuk motor dengan daya diatas 30 atau 50 hp tidak dianjurkan menghidupkan motor secara langsung.

Beberapa metode starting tradisional motor induksi diantaranya adalah DOL (Direct On Line), Wye-Delta, auto-trafo, dan primary resistor, yang ternyata dalam pelaksanaannya masih menarik arus start yang besar, terlebih pada starting DOL. Pada starting Wye-Delta, perpindahan dari wye ke delta ternyata jugamenyebabkan hentakan yang cukup keras pada motor. Jika ini terus dilakukan, dikhawatirkan motor akan cepat mengalami kerusakan. Primary resistor adalah dengan menserikan tahanan dengan sumber tegangan dengan maksud untuk menahan atau mengurangi arus start yang masuk kedalam motor, tetapi jika ternyata terjadi lonjakan tegangan yang berlebih tahanan tidak cukup untuk membendung arus lebih yang lewat karena  tahanan (R) tidak otomatis bertambah nilainya seiring dengan naiknya tegangan.

Soft start

Metode soft starting, adalah mengalirkan tegangan masuk kedalam motor secara bertahap, sehingga Motor tidak menarik arus starting yang terlalu besar. Dalam percobaan ini menggunakan anti parallel SCR dengan pemicu pwm.

Komponen SCR memblokir aliran arus dalam satu arah tetapi meneruskan arus dalam arah yang lainnya setelah menerima sinyal triger atau “penyulut “ yang disebut pulsa gerbang. Enam buah SCR disusun dalam konfigurasi apa yang disebut konverter AC-AC anti paralel.

Dengan rangkaian kendali yang tepat dapat dicapai pengendalian arus motor atau waktu percepatan yaitu dengan mengenakan pulsa gerbang ke SCR pada waktuyang berbeda dalam setiap setengah siklus tegangan sumber.

Jika pulsa gerbang dikenakan lebih dulu dalam setengah siklus, keluaran SCR tinggi. Jika pulsa gerbang dikenakan agak lambat dalam setengah siklus, keluarannya rendah. Maka tegangan masukan ke motor dapat diubah dari nol sampai sepenuhnya selama periode start, sehingga motor melakukan percepatan dengan halus mulai dari nol sampai ke kepesatan penuh.

Simulink soft start

Tegangan soft start

Arus soft start motor induksi :

 

 

Sinyal PWM untuk pemicu SCR :

Kecepatan motor dengan metode soft start:

Kesimpulan:

Terjadi arus start yang besar selama periode pwm kecil daroi 80%, dan terjadi tegangan kejut yang besar akibat switching pwm.

Kompresi citra dengan metode DCT-Kuantisasi-Huffman

Citra merupakan informasi multimedia dengan jumlah data yang sangat besar di mana kualitasnya sering dikaitkan dengan resolusi citra. Resolusi citra berarti ukuran panjang dan lebar dari sebuah citra dalam satuan pikel. Kedalaman intensitas warna berarti banyaknya bit yang digunakan untuk tiap kode warna yang dinyatakan dalam satuan bit/piksel. Semakin tinggi resolusi citra, semakin banyak jumlah piksel dan kedalaman intensitas warna sehingga mengakibatkan semakin baik kualitas citranya. Tingginya resolusi citra dan kedalaman intensitas warna berarti jumlah bit yang ada semakin banyak sehingga mentransmisikan citra dengan resolusi yang tinggi membutuhkan penyimpanan data citra untuk jumlah bit yang ditransmisikan. Oleh karena itu, untuk meminimalkan jumlah bit yang ditransmisikan, digunakan suatu algoritma kompresi citra.

Kompresi citra dapat dilakukan dalam domain spasial maupun domain frekuensi. Pada kompresi citra dalam domain spasial, redundansi data yang relatif tinggi dalam citra dikodekan dengan menggunakan bit yang lebih kecil. Redundansi berarti banyaknya piksel dari citra memiliki kesamaan secara visual atau kesamaan nilai. Sementara pada kompresi citra dalam domain frekuensi, perlu dilakukan proses transformasi dari domain spasial ke domain frekuensi. Merode transformasi yang digunakan pada penelitian ini yaitu Discrete Cosine Transform (DCT) yang digunakan dalam kompresi JPEG 2000.

Kompresi citra terbagi menjadi dua teknik, yaitu lossy dan loseless. Teknik kompresi citra loseless bekerja dengan membuat kapasitas file dari citra sekecil mungkin yaitu dengan mengoptimalkan teknik pengkodean data redundan yang terdapat pada citra asli. Dalam hal tersebut, kompresi loseless terjadi tanpa ada perubahan data. Salah satu metode yang menerapkan teknik kompresi loseless yaitu metode Huffman. Teknik kompresi citra lossy bekerja dengan mengurangi jumlah bit pada informasi detil citra seperti luminance dan chrominance (warna). Salah satu metode yang menerapkan teknik kompresi lossy yaitu DCT.

Discrete Cosine Transform (DCT)

Metode Discrete Cosine Transform (DCT) mengubah data citra masukan ke dalam format yang dapat mengurangi redundansi piksel. Untuk memetakan nilai piksel ke dalam satu set koefisien, teknik perubahan pengkodean menggunakan reversibel dan linier matematika transformasi. Proses selanjutnya dibutuhkan proses kuantisasi dan pengkodean. Transformasi 2D-DCT dinyatakan dalam persamaan berikut:

Kuantisasi

Pada tahap kuantisasi, koefisien DCT yang tidak penting dihilangkan guna merekontruksi citra yang baru. Proses kuantisasi menggunakan teknik lossy dengan menyeleksi frekuensi yang tinggi untuk dihilangkan.

Pengkodean Huffman

Prinsip pengkodean Huffman yaitu mengkodekan koefisien hasil proses DCT dengan mengeliminasi nilai-nilai matriks bernilai nol sehingga kelebihan dari keluaran kuantisasi hilang secara zigzag sehingga dihasilkan citra yang sudah terkompresi atau dilakukan kompresi citra.

Program matlab kompresi DCT  – Quantization – Huffman

clear all
close all
clc

I=imread('football.jpg');

qy50 = [16 11 10 16 24 40 51 61;
    12 12 14 19 26 58 60 55;
    14 13 16 24 40 57 69 56;
    14 17 22 29 51 87 80 62;
    18 22 37 56 68 109 103 77;
    24 35 55 64 81 104 113 92;
    49 64 78 87 103 121 120 101;
    72 92 95 98 112 100 103 99];

qc50 = [ 17 18 24 47 99 99 99 99;
    18 21 26 66  99 99 99 99;
    24 26 56 99 99 99 99 99;
    47 66 99 99 99 99 99 99;
    99 99 99 99 99 99 99 99;
    99 99 99 99 99 99 99 99;
    99 99 99 99 99 99 99 99;
    99 99 99 99 99 99 99 99;];

zz =[1 2 6 7 15 16 28 29;
    3 5 8 14 17 27 30 43;
    4 9 13 18 26 31 42 44;
    10 12 19 25 32 41 45 54;
    11 20 24 33 40 46 53 55;
    21 23 34 39 47 52 56 61;
    22 35 38 48 51 57 60 62;
    36 37 49 50 58 59 63 64;];


% Konversi Warna Image RGB ke YCbCr
Iycbcr=rgb2ycbcr(I);
[MI,NI,~]=size(Iycbcr);
mb=mod(MI,8);
nb=mod(NI,8);
if nb>0
    Iycbcr(:,NI+1:NI+(8-nb),:)=0;
end
if mb>0
    Iycbcr(MI+1:MI+(8-mb),:,:)=0;
end
[m,n,o] = size(Iycbcr);

% Blok Citra
Imycbcr=Iycbcr;

ImDouble = double(Imycbcr);

% DCT
ImDCT = dct(ImDouble);

% Kuantisasi
yDCT = ImDCT(:,:,1);
cbDCT = ImDCT(:,:,2);
crDCT = ImDCT(:,:,3);

y_kuantisasiBlock  = @(block_struct) round(block_struct.data ./ qy50) ;
c_kuantisasiBlock  = @(block_struct) round(block_struct.data ./ qc50) ;
y_kuantisasi =  blockproc(yDCT ,[8 8] , y_kuantisasiBlock) ;
cb_kuantisasi =  blockproc(cbDCT ,[8 8] , c_kuantisasiBlock) ;
cr_kuantisasi =  blockproc(crDCT ,[8 8] , c_kuantisasiBlock) ;

% Zig-Zag encoding
y_ZigZag = zeros(m*n,1);
cb_ZigZag = zeros(m*n,1);
cr_ZigZag = zeros(m*n,1);
idx = 0;
for i=1:8:m
    for j=1:8:n
        y_ZigZag(zz+idx) = y_kuantisasi(i:i+7, j:j+7);
        cb_ZigZag(zz+idx) = cb_kuantisasi(i:i+7, j:j+7);
        cr_ZigZag(zz+idx) = cr_kuantisasi(i:i+7, j:j+7);
        idx = idx + (8*8);
    end
end

zigZagCoding = [y_ZigZag; cb_ZigZag; cr_ZigZag];

% Huffman encoding
minDinput=min(zigZagCoding);
if minDinput<=0
    faktor=minDinput*-1+1;
else
    faktor=0;
end
zigZagCoding=zigZagCoding+faktor;
Hist=zeros(1,max(zigZagCoding));
L=length(zigZagCoding);
DinputNum=zeros(1,L);

for i=1:L
    DinputNum(i)=zigZagCoding(i);
    Hist(DinputNum(i))=Hist(DinputNum(i))+1;
end
PHist=Hist/L;
[PHist, symbols]=sort(PHist,'descend');
eobi=max(find(PHist~=0));
PHist=PHist(1:eobi);
symbols=symbols(1:eobi);

% Proses Coding
dict=huffmandict(symbols,PHist);
ImHuffmanEncoder=huffmanenco(zigZagCoding,dict);

imKirim = ImHuffmanEncoder;

%% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

imTerima = imKirim;

%Huffman decoding
ImHuffmanDecoder=huffmandeco(imTerima,dict) - faktor;

ImDecoder = reshape(ImHuffmanDecoder, m, n, o);

y = ImDecoder(:,:,1);
cb = ImDecoder(:,:,2);
cr = ImDecoder(:,:,3);

% Zig-Zag decoding
y_DeZigZag = zeros(m,n);
cb_DeZigZag = zeros(m,n);
cr_DeZigZag = zeros(m,n);
idx = 0;
for i=1:8:m
    for j=1:8:n
        y_DeZigZag(i:i+7, j:j+7) = y(zz+idx);
        cb_DeZigZag(i:i+7, j:j+7) = cb(zz+idx);
        cr_DeZigZag(i:i+7, j:j+7) = cr(zz+idx);
        idx = idx + (8*8);
    end
end


% De Kuantisasi
y_DekuantisasiBlock  = @(block_struct) round(block_struct.data .* qy50) ;
c_DekuantisasiBlock  = @(block_struct) round(block_struct.data .* qc50) ;
y_Dekuantisasi =  blockproc(y_DeZigZag ,[8 8] , y_DekuantisasiBlock) ;
cb_Dekuantisasi =  blockproc(cb_DeZigZag ,[8 8] , c_DekuantisasiBlock) ;
cr_Dekuantisasi =  blockproc(cr_DeZigZag ,[8 8] , c_DekuantisasiBlock) ;

ImDeKuantisasi = cat(3, y_Dekuantisasi, cb_Dekuantisasi, cr_Dekuantisasi);

% De DCT
ImDeDCT = idct(ImDeKuantisasi);
ImDeDouble = uint8(ImDeDCT);
IDeycbcr = ycbcr2rgb(ImDeDouble);

figure('name',['SNR=',num2str(1)]);
subplot(1,2,1);
imshow(I);
title('citra yang dikirim')

subplot(1,2,2);
imshow(IDeycbcr);
title('citra yang diterima')

hasil kompres-dekompres dengan metode DCT Kuantisasi Zigzag Huffman :

 

Antar muka (interface) kalibrasi sensor PH melalui serial monitor Arduino

Sensor PH adalah instrumen untuk mengukur konsentrasi hidrogen dalam sebuah larutan. Baik sensor pH untuk air maupun untuk tanah perlu dikalibrasi berkala agar ke-akuratannya terjamin.

Untuk menjamin keakuratan sensor ph, diperlukan bahan buffer solution dengan pH diketahui dan akurat. buffer solution yang digunakan umumnya adalah dengan pH 4.0 dan pH 7.0.

Beberapa produsen sensor pH juga menyertakan instrumen untuk melakukan kalibrasi secara manual. Namun jika dihubungkan dengan arduino maka, arduino (instrumen baca) juga perlu dikalibrasi. Dalam atikel ini disertakan program interface kalibrasi sensor pH melalui serial monitor, yang merupakan pengembangan dari library sensor pH yang ada. Hasil kalibrasi akan tersimpan dalam EEPROM untuk digunakan dalam pengukuran normal.

Skema mengkalibrasi sensor pH dengan arduino:

Sktech program pengkalibrasian sensor ph pada arduino :

#define pinPH               A1
#define suhuPengukuran      25

#include "DFRobot_PH.h"

DFRobot_PH ph;

float voltagePH;
float phValue;

long millisBacaSensor;

void setup()
{
  Serial.begin(9600);
  Serial.println("Kalibrasi sensor PH melalui serial monitor");
  Serial.println("Buka serial monitor dan kirim (send) sebarang karakter");
  Serial.println("https://www.semesin.com/project");
  Serial.println();

  ph.begin();
}

void loop()
{
  if (millisBacaSensor < millis())
  {
    millisBacaSensor = millis() + 3000;

    voltagePH = analogRead(pinPH) / 1024.0 * 5000;          // read the ph voltage
    phValue    = ph.readPH(voltagePH, suhuPengukuran);      // convert voltage to pH with temperature compensation

    byte PH = round(phValue);

    Serial.println();
    Serial.print("voltagePH = ");
    Serial.println(voltagePH);
    Serial.print("phValue = ");
    Serial.println(phValue);
    Serial.print("PH = ");
    Serial.println(PH);
  }

  if (Serial.available())
  {
    Serial.readString();

    Serial.println();
    Serial.println("Proses kalibrasi, ikuti petunjuk di serial monitor");
    
    ph.calibration(voltagePH, suhuPengukuran, "ENTERPH");

    while (!Serial.available());
    Serial.readString();
    
    voltagePH = analogRead(pinPH) / 1024.0 * 5000;      // read the ph voltage
    ph.calibration(voltagePH, suhuPengukuran, "CALPH");

    ph.calibration(voltagePH, suhuPengukuran, "EXITPH");
  }
}

library : DFRobot_PH.zip

 

Penjadwalan dan durasi aktif relay dengan setting melalui tombol berbasis arduino

Pengontrolan waktu hidup dan mati peralatan sebagai otomasi yang mengurangi ketergantungan dari kontrol operator, bisa dilakukan dengan penjadwalan beban/peralatan. Contohnya dalam pengaturan beban dalam rumah tangga, misalkan terdapat beban yang akan dikontrol seperti berikut :

  1. lampu teras
  2. lampu taman
  3. pompa pengisi air tandon
  4. kran/solenoid Penyiram tanaman
  5. electric water heater

adalah beban-beban listrik yang biasanya hidup dan mati-nya terjadwal.

Untuk mengatur jadwalnya, pada contoh ini menggunakan 3 tombol yaitu set, up dan down. adapun komponen yang digunakan adalah :

  1. Arduino uno
  2. rtc ds3231
  3. lcd 16×02 i2c
  4. relay 8 channel

Skema penjadwalan relay menggunakan arduino :

koding arduino penjadwalan relay :


#define pinTombolSet        A0
#define pinTombolUp         A1
#define pinTombolDown       A2

#define pinRelay1           2
#define pinRelay2           3
#define pinRelay3           4
#define pinRelay4           5
#define pinRelay5           6
#define pinRelay6           7
#define pinRelay7           8
#define pinRelay8           9

#define relayOn             LOW
#define jumlahRelay         8

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "Sodaq_DS3231.h"
#include "EEPROM.h"

LiquidCrystal_I2C lcd(0x3F, 16, 2);//coba juga 0x27

byte setting[jumlahRelay * 4];
byte menu = 0;
char buf[32];
DateTime now;
byte detikSebelumnya = 60;
byte pinRelay[] = {pinRelay1, pinRelay2, pinRelay3, pinRelay4, pinRelay5, pinRelay6, pinRelay7, pinRelay8};

void setup()
{
  pinMode(pinTombolSet, INPUT_PULLUP);
  pinMode(pinTombolUp, INPUT_PULLUP);
  pinMode(pinTombolDown, INPUT_PULLUP);

  for (byte i = 0; i < jumlahRelay; i++)
  {
    digitalWrite(pinRelay[i], !relayOn);
    pinMode(pinRelay[i], OUTPUT);
  }

  Serial.begin(9600);
  Serial.println(F("Penjadwalan dan durasi aktif relay dengan setting melalui tombol berbasis arduino"));
  Serial.println(F("https://www.semesin.com/project"));
  Serial.println();
  
  Wire.begin();
  rtc.begin();
  DateTime dt(2011, 11, 10, 15, 18, 0, 5); // set tanggal dan waktu (format): tahun, bulan tanggal, jam, menit, detik, hari (1=minggu, 7=sabtu)
  rtc.setDateTime(dt);

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

  lcd.backlight();
  lcd.print("Jadwal relay");
  lcd.setCursor(0, 1);
  lcd.print("www.Semesin.com");
  delay(3000);

  lcd.clear();

  if (EEPROM.read(0) != 12)
  {
    EEPROM.update(0, 12);
    for (byte i = 0; i < jumlahRelay * 4; i++)
    {
      setting[i] = 0;
    }
    EEPROM.put(1, setting);
  }
  EEPROM.get(1, setting);

  Serial.println("Sistem mulai");

}


void loop()
{
  now = rtc.now();
  if (detikSebelumnya != now.second())
  {
    detikSebelumnya = now.second();
    if (now.second() == 0)
    {
      for (byte i = 0; i < jumlahRelay; i++)
      {
        uint16_t unixNow = (now.hour() * 60) +  now.minute();
        uint16_t unixWaktu = ((setting[(i * 4) + 0] * 60) +  setting[(i * 4) + 1]);
        uint16_t unixDurasi = ((setting[(i * 4) + 2] * 60) +  setting[(i * 4) + 3]);
        if (unixNow == unixWaktu)
        {
          digitalWrite(pinRelay[i], LOW);
          Serial.print("Relay ");
          Serial.print(i + 1);
          Serial.println(" aktif");
        }
        if (unixNow == unixWaktu + unixDurasi)
        {
          digitalWrite(pinRelay[i], HIGH);
          Serial.print("Relay ");
          Serial.print(i + 1);
          Serial.println(" tidak aktif");
        }
      }
    }
    if (!menu)
    {
      sprintf(buf, "%02d/%02d/%04d", now.date(), now.month(), now.year());
      lcd.setCursor(3, 0);
      lcd.print(buf);
      sprintf(buf, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
      lcd.setCursor(4, 1);
      lcd.print(buf);
    }
  }

  if (menu)
  {
    if (!digitalRead(pinTombolUp))
    {
      delay(50);
      while (!digitalRead(pinTombolUp))
      {
        setting[menu - 1]++;
        if ((menu - 1) % 2)
        {
          if (setting[menu - 1] >= 60)
          {
            setting[menu - 1] = 0;
          }
        }
        else
        {
          if (setting[menu - 1] >= 24)
          {
            setting[menu - 1] = 0;
          }
        }

        tampilanMenu();
        delay(100);
      }
    }
    if (!digitalRead(pinTombolDown))
    {
      delay(50);
      while (!digitalRead(pinTombolDown))
      {

        if ((menu - 1) % 2)
        {
          if (setting[menu - 1] == 0)
          {
            setting[menu - 1] = 59;
          }
          else
          {
            setting[menu - 1]--;
          }
        }
        else
        {
          if (setting[menu - 1] == 0)
          {
            setting[menu - 1] = 23;
          }
          else
          {
            setting[menu - 1]--;
          }
        }

        tampilanMenu();
        delay(100);
      }
    }
  }

  if (!digitalRead(pinTombolSet))
  {
    delay(50);
    if (!digitalRead(pinTombolSet))
    {
      menu++;

      if (menu == jumlahRelay * 4 + 1)
      {
        menu = 0;
        lcd.clear();
        lcd.noCursor();
        EEPROM.put(1, setting);
      }
      else
      {
        lcd.clear();
        lcd.setCursor(2, 0);
        lcd.print("Set Relay ");
        lcd.print(((menu - 1) / 4) + 1);
        tampilanMenu();
      }
      
      long miliisTekanPanjang = millis() + 3000;
      while (!digitalRead(pinTombolSet))
      {
        if (miliisTekanPanjang < millis())
        {
          menu = 0;
          lcd.clear();
          lcd.noCursor();
          EEPROM.put(1, setting);

        }
      }
    }
  }
}
void tampilanMenu()
{
  sprintf(buf, "%s : %02d:%02d", ((menu - 1) % 4) / 2 ? "Durasi" : "Waktu ", setting[(menu - 1) & 0xFE], setting[((menu - 1) & 0xFE) + 1]);
  lcd.setCursor(0, 1);
  lcd.print(buf);

  lcd.setCursor(((menu - 1) % 2) ? 12 : 9, 1);
  lcd.cursor();
}
  1. LiquidCrystal-I2C.zip
  2. Sodaq_DS3231.zip

Remot kontrol android untuk mobil arduino melalui bluetooth

Remot kontrol adalah instrumen jarak jauh (remot) yang mengendalikan perangkat untuk melaksanakan fungsi-fungsi sesuai perintah. Komunikasi antara remot kontrol dan perangkat (rtu) bisa menggunakan kabel atau tanpa kabel (bluetooth, wifi, radio).

Android bisa dimanfatkan sebagai remot kontrol dengan memanfatkan fasilitas sensor-sensor yang dimilikinya. Dalam contoh ini (pengontrolan mobil arduino) memanfatkan fitur berikut :

  1. Bluetooth
  2. Sensor orientasi/kemiringan
  3. Touch screen

Pengendalian remot kontrol bisa dilakukan dalam 2 mode yaitu mode steer dan mode sensor orientasi.

Prototipe mobil arduino dibuat dari komponen berikut :

  1. Arduino uno
  2. Driver motor L298
  3. Motor DC
  4. Bluetooth HC-05 dan resistor pembagi tegangan
  5. Rangka miniatur mobil

Fokus dalam perancangan mobil arduino ini adalah pengendalian kecepatan motor kiri dan kanan sehingga diperoleh gerakan yang mulus/smooth. jadi tidak seperti kendali steer mobil pada umumnya yang menggunakan metode putaran poros roda untuk melakukan belokan. Untuk itu digunakan formula gerakan menggunakan pwm (pulse width modulation) dan pengaturan kecepatan roda kiri dan kanan sehingga dihasilkan gerakan yang halus.

berikut skema pengendalian gerakan mobil-mobilan arduino melalui android :

sketch arduino untuk mobil remot dengan gerakan yang halus:

#include <SoftwareSerial.h>

#define pinMotorKiriPlus            5
#define pinMotorKiriMinus           6
#define pinMotorKananPlus           10
#define pinMotorKananMinus          11

SoftwareSerial bluetooth(2, 3);

int x = 128;
int y = 128;
byte kiri;
byte kanan;
char buffer[32];
bool statusBerhenti;

void setup() {
  pinMode(pinMotorKiriPlus, OUTPUT);
  pinMode(pinMotorKiriMinus, OUTPUT);
  pinMode(pinMotorKananPlus, OUTPUT);
  pinMode(pinMotorKananMinus, OUTPUT);

  Serial.begin(9600);
  Serial.println(F("Remot kontrol android untuk mobil arduino melalui bluetooth"));
  Serial.println(F("https://www.semesin.com/project"));
  Serial.println();
  
  bluetooth.begin(9600);

  Serial.println("Sistem mulai");

}

void loop() {

  while (bluetooth.available())
  {
    char c = bluetooth.read();
    switch (c)
    {
      case 'x':
        x = bluetooth.parseInt();
        break;
      case 'y':
        y = bluetooth.parseInt();
        break;
    }
  }

  if ((y > 96) && (y < 160))
  {
    if (!statusBerhenti)
    {
      digitalWrite(pinMotorKiriPlus, LOW);
      digitalWrite(pinMotorKiriMinus, LOW);
      digitalWrite(pinMotorKananPlus, LOW);
      digitalWrite(pinMotorKananMinus, LOW);
      Serial.println("berhenti");
      statusBerhenti = true;
    }

  }
  else
  {
    float rasioKiri = 1.0 * x / 255;
    float rasioKanan = 1.0 * (255 - x) / 255;
    byte kecepatan = abs(y - 128) * 2;

    if (rasioKiri > rasioKanan)
    {
      rasioKanan += 1 - rasioKiri;
      rasioKiri = 1;
    }
    else
    {
      rasioKiri += 1 - rasioKanan;
      rasioKanan = 1;
    }

    kiri = constrain(rasioKiri * kecepatan, 0 , 255);
    kanan = constrain(rasioKanan * kecepatan, 0 , 255);

    sprintf(buffer, "%s : kiri: %d, kanan: %d", y < 128 ? "Maju" : "mundur", kiri, kanan);
    Serial.println(buffer);

    if (y < 128)
    {
      analogWrite(pinMotorKiriPlus, kiri);
      analogWrite(pinMotorKananPlus, kanan);
      digitalWrite(pinMotorKiriMinus, LOW);
      digitalWrite(pinMotorKananMinus, LOW);
    }
    else
    {
      analogWrite(pinMotorKiriMinus, kiri);
      analogWrite(pinMotorKananMinus, kanan);
      digitalWrite(pinMotorKiriPlus, LOW);
      digitalWrite(pinMotorKananPlus, LOW);
    }
    statusBerhenti = false;
  }
}

block app inventor remot kontrol android:

screenshoot Remot kontrol arduino pengendali mobil-mobilan:

File app inventor remot kontrol:

1. Setir_Android.apk
2. Setir_Android.aia

Sistem kontrol PID close loop – stabilizer tegangan – menggunakan arduino melalui simulink matlab

Sistem kontrol PID (Proportional–Integral–Derivative controller) bertujuan memperoleh hasil optimum yang menggunakan mekanisme umpan balik (sistem tertutup / close loop). sistem kontrol PID juga bisa diterapkan pada arduino. Untuk menambah kemampuan numerik pada arduino, bisa dikombinasikan dengan program matlab.

Program Matlab (Matrix Laboratory) yang memiliki fitur simulink dapat dapat digunakan untuk memprogram arduino, dan dengan kelebihan matlab bisa dengan mudah mengembangkan sistem kontrol (dan keperluan numerikal lain) kedepannya.

Untuk pemrograman arduino menggunakan matlab, diperlukan add-ons matlab berikut ini:

  1.  Simulink Support Package for Arduino Hardware
  2. MATLAB Support for MinGW-w64 C/C++ Compiler

Dalam contoh ini menggunakan plant pengatur tegangan keluaran pwm dengan beban kapasitor.

Skema / plant / rangkaian percobaan sistem kontrol pid loop tertutup berbasis arduino:

Model simulink matlab sistem kontrol tegangan

respon keluaran pengontrolan tegangan dengan arduino dan simulink

file simulink pengontrolan pid tegangan pwm:

pidArduino.slx

 

 

Input string melalui keypad menggunakan arduino

Keypad (dalam konteks arduino) sama halnya dengan keypad pada handphone lawas (tuts) yang terdiri dari setidaknya 12 tombol berupa angka 0 hingga 9 serta karakter * dan karakter #. Untuk menuliskan karakter huruf, maka tuts keypad harus ditekan beberapa kali hingga karakter yang diinginkan muncul.

String / text terdiri atas karakter-karakter penyusunnya, penggunaan keypad 3×4 (numerik) membutuhkan trik khusus dan terdapat bermacam-macam metode, dalam contoh ini menggunakan metode entry deret.

 

aturan pemakaian umum:

  1. Masing-masing tuts memiliki beberapa karakter, untuk memilih karakter maka tuts yang sama ditekan berulang-ulang hingga karakter yang diinginkan tampil.
  2. Jika tuts/tombol tidak ditekan selama 3 detik, maka karakter sebelumnya dimasukkan dalam memory dan sistem lanjut ke karakter berikutnya.
  3. Jika tuts yang ditekan berbeda dengan tuts sebelumnya, maka karakter terakhir akan dimasukkan dalam memory.
  4. tuts * berfungsi untuk menghapus karakter terakhir
  5. jika tuts * pada saat belum ada karakter yang dimasukkan, maka sistem akan kembali ke sistem normal (dalam contoh ini kembali ke keadaan awal).
  6. tuts # berfungsi seperti ‘enter’ untuk menyimpan string dan kembali ke sistem normal (dalam contoh ini kembali ke keadaan awal).

skema pemanfaatan keypad sebagai entry teks:

koding/sketch masukan keypad sebagai string / teks :

#define periodaKeypad         3000

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

LiquidCrystal_I2C lcd(0x3F, 16, 2);

const byte ROWS = 4;
const byte COLS = 3;
char keys[ROWS][COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};
byte rowPins[ROWS] = {11, 10, 9, 8};
byte colPins[COLS] = {7, 6, 5};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );


char pad[11][11] = {
  "0 ",
  "1?!*#:=/+-",
  "2AaBbCc",
  "3DdEeFf",
  "4GgHhIi",
  "5JjKkLl",
  "6MmNnOo",
  "7PpQqRrSs",
  "8TtUuVvWw",
  "9XxYyZz",
};

byte padCounter;
char padChar;
bool padDitekan;
byte charCounter;
byte keySebelumnya;
char bufferKeypad[17];
char *bufferKeypadPtr;

long millisKeypad;

void setup() {
  Serial.begin(9600);
  Serial.println(F("Input string malalui keypad menggunakan arduino"));
  Serial.println(F("https://www.semesin.com/project"));
  Serial.println();

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

  resetInput();
}

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

  if (key) {
    switch (key)
    {
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':

        millisKeypad = millis() + periodaKeypad;
        if ((key == keySebelumnya) || (keySebelumnya == 0))
        {
          padChar = pad[key - '0'][charCounter];
          keySebelumnya = key;
        }
        else if ((padDitekan) && (padCounter < sizeof(bufferKeypad) - 1))
        {
          *bufferKeypadPtr++ = padChar;
          keySebelumnya = key;
          charCounter = 0;
          padCounter++;
          padChar = pad[key - '0'][charCounter];
        }

        padDitekan = true;

        lcd.setCursor(padCounter, 1);
        lcd.print(padChar);
        lcd.setCursor(padCounter, 1);

        charCounter++;
        if (!pad[key - '0'][charCounter])
        {
          charCounter = 0;
        }
        break;

      case '*':
        if (padCounter)
        {
          if (keySebelumnya)
          {
            keySebelumnya = 0;
          }
          lcd.setCursor(padCounter, 1);
          lcd.print(' ');
          charCounter = 0;
          padCounter--;
          bufferKeypadPtr--;
          padChar = *bufferKeypadPtr;

          lcd.setCursor(padCounter, 1);
        }
        else
        {
          resetInput();
        }
        break;
      case '#':
        if ((padDitekan) && (padCounter < sizeof(bufferKeypad) - 1))
        {
          *bufferKeypadPtr++ = padChar;
        }
        *bufferKeypadPtr = 0;

        Serial.print("String input = ");
        Serial.println(bufferKeypad);

        lcd.clear();
        lcd.noBlink();
        lcd.setCursor(0, 0);
        lcd.print("String input = ");
        lcd.setCursor(0, 1);
        lcd.print(bufferKeypad);
        delay(3000);

        resetInput();
        break;
    }
  }

  if ((padDitekan) && (padCounter < sizeof(bufferKeypad) - 1))
  {
    if (millisKeypad < millis())
    {
      *bufferKeypadPtr++ = padChar;
      keySebelumnya = key;
      charCounter = 0;
      padCounter++;
      padDitekan = false;

      lcd.setCursor(padCounter, 1);
      lcd.print(' ');
      lcd.setCursor(padCounter, 1);
    }
  }
}

void resetInput()
{
  bufferKeypadPtr = bufferKeypad;
  charCounter = 0;
  padCounter = 0;
  keySebelumnya = 0;
  padDitekan = false;

  lcd.clear();
  lcd.print("Masukkan string");
  lcd.setCursor(padCounter, 1);
  lcd.blink();
}

library entry teks melalui keypad berbasis arduino :
LiquidCrystal-I2C.zip
Keypad.zip