Dimmable LED bulb

Pencahayaan merupakan aspek kebutuhan yang bisa di optimasi sehingga memberikan kenyamanan, ketelitian, keakuratan dan seterusnya. Bola lampu (bulb) merupakan sumber cahaya yang disediakan pabrikan dalam bermacam ukuran, secara awam diberikan dalam satuan Watt. Namun dalam pencahayaan, dibutuhkan besaran lumen yang mampu di hasilkan oleh titik bola lampu. Apabila bola lampu digunakan untuk menerangi bisang di depannya, maka rata-rata kecerahan bidang tersebut adalah jumlah lumen di bagi luas area yang terpapar dalam satuan lux.

Standar pencahayaan menetapkan beberapa ukuran yang diperlukan untuk menerangi area, diantaranya :

Ruangan Tingkat Pencahayaan
Teras 60
Ruang tamu 120 ̴ 150
Ruang makan 120 ̴ 250
Ruang kerja 120 ̴ 250
Kamar tidur 120 ̴ 250
Kamar mandi 250
Dapur 250
Garasi 60

Dalam beberapa aplikasi pencahayaan diperlukan kecerahan yang akurat, untuk keperluan itu dibutuhkan bola lampu yang bisa diatur keluaran intensitas cahaya/lumen-nya. Selain itu keuntungan lainnya menggunakan dimmer seperti bisa diatur tingkat kesilauan, rendering warna, panas, daya.

Projek kali ini akan mencoba membuat bola lampu LED yang bisa diredupkan. Karena jenis bola lampu ini tidak banyak dijual atau jarang tersedia di toko listrik, kita bisa memodifikasi bola lampu led biasa menjadi bola lampu yang bisa di redupkan/dimmable.

Komponen yang diperlukan adalah :

  • Bola lampu LED yang punya cukup ruang bebas di dalamnya
  • Transformator, ukuran ampere dan tegangannya disesuaikan
  • Dioda bridge
  • kapasitor

rakit komponen tersebut menurut skema berikut:

Rangkaian ini berupa penyearah pada umumnya yang dihubungkan dengan led pada bola lampu. Pada percobaan ini led-led dikelompokkan dalam 3 led seri dan dihubungkan dengan sumber tegangan dari trafo 12v–6v (~18v ac) disesuaikan dengan spesifikasi komponen yang digunakan.

berikut adalah contoh pemasangan lampu led dimmable:

 

Dimmer LED bulb ini bisa di kontrol dengan arduino menggunakan skema berikut:

 

Gunakan koding dimmer bola lampu led ini untuk menguji modifikasi lampu led yang dapat diredupkan:

#define pinPWM          3
#define pinZCD          2
#define frekuensi       50 //50 Hz
#define inputSerial     false

#define fCPU            16e6
#define prescaler       1024
#define lebarZCD        500e-6//s
#define freqDutyCycle   (fCPU / (1.0 * prescaler * 2 * frekuensi))
#define minTrigger      ((lebarZCD/2) / (prescaler / fCPU)) + 1
#define maxTrigger      (freqDutyCycle * 0.85)

volatile uint16_t dutyCycle;
volatile intptr_t *portPWM;
volatile byte bitPWM;

void setup() {
  Serial.begin(9600);
  Serial.println("Dimmer LED arduino");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();

  pinMode(pinPWM, OUTPUT);

  dutyCycle = setDutyCycle(0);

  TCCR2A = _BV(WGM21);
  TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20);// clk/1024
  OCR2A = maxTrigger - 1;
  OCR2B = dutyCycle;
  TIMSK2 = _BV(OCIE2B);

  portPWM = (volatile intptr_t *) portOutputRegister(digitalPinToPort(pinPWM));
  bitPWM = digitalPinToBitMask(pinPWM);

  attachInterrupt(digitalPinToInterrupt(pinZCD), zcdChange, RISING);

  Serial.print("minTrigger=");
  Serial.println((int)minTrigger);
  Serial.print("maxTrigger=");
  Serial.println((int)maxTrigger);
}

void loop() {
#if inputSerial

  if (Serial.available())
  {
    int data = Serial.parseInt();
    dutyCycle = setDutyCycle(data);

    while (Serial.available())
    {
      Serial.read();
      delay(2);
    }
  }

#else

  for (byte i = 1; i <= 100; i++)
  {
    dutyCycle = setDutyCycle(i);
    delay(20);
  }
  for (int8_t i = 99; i >= 0; i--)
  {
    dutyCycle = setDutyCycle(i);
    delay(20);
  }
  delay(1000);

#endif

}
uint16_t setDutyCycle(uint8_t dutyCycle)
{
  uint8_t trigger = map(dutyCycle, 0, 100, maxTrigger, minTrigger);

  //  Serial.print("dutyCycle=");
  //  Serial.print(dutyCycle);
  //  Serial.print(" trigger=");
  //  Serial.println(trigger);

  return trigger;
}

void zcdChange()
{
  TCNT2 = 0;
  OCR2B = dutyCycle;
}

ISR (TIMER2_COMPB_vect)
{
  *portPWM |= bitPWM;
  delayMicroseconds(10);
  *portPWM &= ~bitPWM;
}

Traffic lights (UK sequence) dengan arduino

Urutan traffic lights standar Inggris mengandung arti sebagai berikut :

  1. Lampu merah,
    Setiap kendaraan wajib berhenti tidak lebih dari garis batas
  2. Lampu merah dan lampu kuning,
    Setiap kendaraan wajib berhenti namun bersiap untuk melanjutkan perjalanan
  3. Lampu hijau,
    Boleh melintasi traffic lights
  4. Lampu kuning,
    Bersiap untuk berhenti di belakang garis batas, tetapi jika sudah melewati garis batas masih di perbolehkan jalan.

Jumlah keseluruhan lampu yang dibutuhkan untuk 4 simpang adalah 12 buah, untuk menghemat jalur pin arduino, maka bisa diterapkan sistem scan yang menggunakan 3 jalur lampu, (untuk merah, kuning, hijau), serta 4 jalur untuk masing-masing posisi trafic led, sehingga untuk 4 simpang diperlukan 7 jalur pengontrolan saja.

skema traffic lights berbasis arduino:

program Urutan traffic lights menggunakan arduino:

#define jumlahTrafficLight    4

byte pinDigit[] = {7, 6, 5, 4};
byte pinRYG[] = {10, 9, 8};

#define lampuMerah    (1 << 2)
#define lampuKuning   (1 << 1)
#define lampuHijau    (1 << 0)

byte hijauSekarang;
byte hijauSelanjutnya;
byte timingTraffic;

struct traffic {
  byte lampu;
  byte lampuSelanjutnya;
  uint32_t waktu;
};

traffic skemaTraffic[] = {
  {lampuHijau  , lampuMerah               , 5000},
  {lampuKuning , lampuMerah               , 1000},
  {lampuMerah  , lampuMerah | lampuKuning , 1000},
};

byte lampuTraffic[jumlahTrafficLight];
byte indexTraffic;
byte indexScan;
uint32_t millisTraffic;
byte jalurDarurat;

void setup() {

  for (byte i = 0; i < jumlahTrafficLight; i++)
  {
    pinMode(pinDigit[i], OUTPUT);
  }
  for (byte i = 0; i < 3; i++)
  {
    pinMode(pinRYG[i], OUTPUT);
  }

  Serial.begin(9600);

  for (byte i = 0 ; i < jumlahTrafficLight; i++)
  {
    lampuTraffic[i] = skemaTraffic[2].lampu;
  }
  hijauSekarang = 0;
  hijauSelanjutnya = 1;

}

void loop() {

  if (millisTraffic < millis())
  {
    lampuTraffic[hijauSekarang] = skemaTraffic[indexTraffic].lampu;
    lampuTraffic[hijauSelanjutnya] = skemaTraffic[indexTraffic].lampuSelanjutnya;
    millisTraffic += skemaTraffic[indexTraffic].waktu;

    indexTraffic++;
    if (indexTraffic == 3)
    {
      indexTraffic = 0;
      hijauSekarang = hijauSelanjutnya;
      hijauSelanjutnya = (hijauSekarang + 1) % jumlahTrafficLight;
    }
  }

  trafficScan();
}

void trafficScan()
{
  digitalWrite(pinDigit[indexScan], HIGH);
  indexScan++;
  indexScan %= jumlahTrafficLight;
  digitalWrite(pinRYG[0], lampuTraffic[indexScan] & lampuMerah);
  digitalWrite(pinRYG[1], lampuTraffic[indexScan] & lampuKuning);
  digitalWrite(pinRYG[2], lampuTraffic[indexScan] & lampuHijau);
  digitalWrite(pinDigit[indexScan], LOW);

}

 

 

SPWM fungsional tanpa tabel-sinusiodal dengan arduino

Sinusoidal Pulse width modulation (SPWM) adalah teknik membangkitkan sinyal sinusiodal dengan metode PWM (pulse width modulation). Sinyal PWM umum di gunakan pada perangkat digital,  sehingga memungkinkan perangkat digital untuk menghasilkan sinyal sinusiodal.

Metode SPWM yang umum digunakan diantaranya :

  1. Segitiga yaitu dengan membandingkan sinyal ramp dengan sinyal sinusiodal dan diperoleh deret duty cycle yang bisa di masukkan dalam tabel.
  2. delta, deret pwm dihasilkan melalui penggambaran slope dalam batas (atas dan bawah) sinyal sinusiodal target.
  3. delta sigma
  4. space vector
  5. direct torque
  6. time proportioning

kehandalan S-PWM di ukur berdasarkan spectrum, karena SPWM adalah sinyal digital yang menyerupai sinyal sinus, maka membutuhkann filter low-pass untuk mengurangi spectrum frekusnsi tinggi dari switching digital.

SPWM juga bisa digunakan sebagai regulator AC untuk keperluan pengontrolan tegangan output.

berikut tata letak pin yang digunakan :

H-bridge dalam skema adalah BTS7960, bisa di ganti dengan modul/rangkaian H-bridge lain

 

sketch program arduino spwm:

#define pinSPWM_A               9
#define pinSPWM_B               10

#define frekuensiOutput         50
#define frekuensiSPWM           1000

volatile byte indexSPWM;
volatile float output;
int limitOutput;

void setup() {
  pinMode(pinSPWM_A, OUTPUT);
  pinMode(pinSPWM_B, OUTPUT);

  Serial.begin(9600);
  Serial.println(F("SPWM fungsional dengan arduino"));
  Serial.println(F("https://www.project.semesin.com"));
  Serial.println();

  TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);

  long cycles = (F_CPU / frekuensiSPWM / 2);
  uint8_t clockSelectBits;

  if      (cycles        < 0xFF) clockSelectBits = _BV(CS10);
  else if ((cycles >>= 3) < 0xFF) clockSelectBits = _BV(CS11);
  else if ((cycles >>= 3) < 0xFF) clockSelectBits = _BV(CS11) | _BV(CS10);
  else if ((cycles >>= 2) < 0xFF) clockSelectBits = _BV(CS12);
  else if ((cycles >>= 2) < 0xFF) clockSelectBits = _BV(CS12) | _BV(CS10);
  else     cycles = 0xFF - 1,    clockSelectBits = _BV(CS12) | _BV(CS10);

  TCCR1B = (1 << WGM13) | clockSelectBits;
  ICR1 = cycles;
  TIMSK1 = _BV(TOIE1);
  OCR1A = 0;
  OCR1B = 0;

  limitOutput = cycles;
}

void loop() {

  output = limitOutput / 2;

}

ISR(TIMER1_OVF_vect)
{
  int duty = 1.0 * sin(2.0 * PI * indexSPWM / (frekuensiSPWM / frekuensiOutput)) * output;

  if (duty >= 0)
  {
    OCR1AL = duty;
    OCR1BL = limitOutput + 1;
  }
  else
  {
    OCR1AL = limitOutput + 1;
    OCR1BL = 0 - duty;
  }

  indexSPWM++;
  indexSPWM %= frekuensiSPWM / frekuensiOutput;
}

output logic analyzer

Meningkatkan presisi pembacaan sensor analog dengan metode Trimmed Mean

Sensor-sensor analog dibaca oleh arduino melalui pin analog menggunakan adc. Agar pembacaan adc oleh arduino memiliki presisi yang baik diperlukan perlakuan (treatment) sisi hardware  diantaranya:

  1. Jika impedansi keluaran (output) sensor besar (>10 kΩ) maka perbesar sampling and hold time (s/h) dengan cara mengganti nilai ADCSRA,
  2. Gunakan tegangan referensi yang sesuai dengan tegangan maksimal keluaran sensor menggunakan perintah analogReference(),
  3. Gunakan kabel sependek mungkin antara pin output sensor dan pin adc arduino dan terselubung ground,
  4. Jauhkan kabel dan sensor dari komponen yang bisa menimbulkan interferensi elektromagnetik,
  5. Jika menggunakan tegangan referensi luar, tambakan dengan filter LC,
  6. Pin-pin analog yang tidak digunakan sebaiknya tidak dioperasikan saat proses pembacaan sensor.
  7. Tegangan power supply yang stabil dan terproteksi dari gangguan luar.

Pada sisi software bisa dilakukan peningkatan presisi diataranya menggunakan metode rata-rata seperti Mean, Trimmed Mean, Truncated mean, Winsorizing, Interquartile mean dan sebagainya.

Berikut contoh penggunaan metode Trimmed Mean untuk meningkatkan presisi pembacaan sensor analog :

#define faktorTrim        0.2 //20%
#define jumlahSampel      10

#define pinSensor         A0

int buffer[jumlahSampel], temp, rataRata;

void setup() {
  Serial.begin(9600);
  Serial.println(F("Meningkatkan presisi pembacaan sensor analog dengan metode Trimmed Mean"));
  Serial.println(F("https://www.project.semesin.com"));
  Serial.println();
}

void loop() {
  for (int i = 0; i < jumlahSampel; i++)
  {
    buffer[i] = analogRead(pinSensor);
    delay(10);
  }

  //proses pengurutan (sorting)
  for (int i = 0; i < jumlahSampel - 1; i++)
  {
    for (int j = i + 1; j < jumlahSampel; j++)
    {
      if (buffer[i] > buffer[j])
      {
        temp = buffer[i];
        buffer[i] = buffer[j];
        buffer[j] = temp;
      }
    }
  }

  //rata-rata (trimmed mean)
  rataRata = 0;
  for (int i = jumlahSampel * faktorTrim; i < jumlahSampel * (1 - faktorTrim); i++)
  {
    rataRata += buffer[i];
  }

  rataRata /= jumlahSampel * (1- (faktorTrim*2));

  Serial.print("sensor = ");
  Serial.println(rataRata);

  delay(500);
}

JWS Universal (Jadwal Waktu Shalat) 1 – 8 panel

Jadwal Waktu Shalat (JWS) adalah media informasi di rumah ibadah Masjid/Mushalla/Surau yang menampilkan informasi waktu-waktu Shalat.

Dalam perkembangannya Jam Waktu Shalat berfungsi sebagai :

  1. Menampilkan Jadwal / Waktu Shalat wajib dan sunat
  2. Menampilkan informasi tanggal Masehi, Hijriah, Pasaran Jawa, dan penanggalan khusus seperti penanggalan Minang
  3. Menampilkan Ayat Al-Quran, Doa
  4. Informasi Tartil, Tarhim, Azan, Iqamah
  5. Pengingat / alarm waktu masuk Shalat dan selesai Iqamah
  6. Memutar Suara/Musik Tartil, Tarhim disertai kontrol hidup/mati amplifier
  7. Perhitungan jadwal berdasarkan posisi matahari, masukan manual, data jadwal online harian.

Diagram alir Jawdal waktu-waktu Shalat :

 

Skema Jam waktu shalat universal:

 

Tampilan aplikasi (apk) JWS Semesin Universal:

 

contoh tampilan jadwal shalat 3 panel :

 

Koding / sketch JWS semesin:

/*
   JWS Semesin 1 - 8 panel

   Fitur yang tidak aktif
   1. Tampilan tanggal Hijriah
   2. Tampilan tanggal Jawa
   3. Tampilan tanggal Minang
   4. Pesan selama tartil
   5. Pesan selama tarhim
   6. Pesan selama Iqamah

*/

#define serialDebug                       false
#define modeDemo                          false

#define namaMesjid                        "JWS"

//defenisi pin
#define pinMP3Busy                        2
#define pinRelayAmpli                     3
#define pinBuzzer                         4
#define RTCDetikIRQ                       A3

#define relayOn                           LOW

#define I2CEEPROM_ADDRESS                 0x57

//setting
#define periodaAlarmWaktuShalatMasuk      100//milidetik
#define periodaAlarmWaktuAkhirIqamah      300//milidetik

#define pixelLebarPanel                   32
#define pixelTinggiPanel                  16

//variabel Setting
#define kecepatanMinimal                  10
#define skalaKecepatan                    10

#define tokenSetting                      32
#define lamaAksesApk                      10L * 60 * 1000

#define jumlahNyalaPadam                  5
#define jumlahTextInformasi               10

#define fontNamaMesjid                    Arial14
#define fontJamDanMenitUtama              angka6x14
#define fontSimbolGambar                  simbolGambar

#include <Wire.h>
#include <EEPROM.h>
#include <DMD_Semesin.h>
#include <RTC_Semesin.h>
#include <DFPlayer_Mini_Mp3.h>
#include <BluetoothApk.h>

#include <fonts/angka6x14.h>
#include <fonts/SystemFont5x7Ramping.h>
#include <fonts/Arial14.h>
#include <fonts/simbolGambar.h>

#include "fungsi.h"
#include "definisi.h"
#include "konstanta.h"
#include "setting.h"
#include "WaktuShalat.h"

const uint8_t *alamatFont[] = {
  Arial14,
  SystemFont5x7Ramping,
};

SPIDMD dmd(8, 1);//max jumlah panel = 8
RTC_DS3231 rtc;
DateTime now;

//Status
bool RTCValid = true;
byte modeOperasi = modeInformasi;
bool statusAlarm;
bool tampilJamMenitDetik = false;
long millisRunningText;
uint8_t kecepatanRunningText;
uint8_t kecepatanRunningEfek;

uint16_t hitungMundurTartil;
uint16_t hitungMundurTarhim;
uint16_t hitungMundurAzan;//terhitung sejak waktu masuk
uint16_t hitungMundurIqamah;//terhitung sejak selesai azan

bool pesanSetelahAzan;
bool pesanSebelumShalat;
uint16_t hitungMundurPeringatanSimbol;
uint16_t hitungMundurShalat;

bool initHitungMundurTartil;
bool initHitungMundurTarhim;
bool initHitungMundurAzan;
bool initHitungMundurIqamah;
bool initHitungMundurPeringatanSimbol;
bool initHitungMundurShalat;

uint8_t hitungMundurAlarmIqamah;//kali

int8_t indexWaktuIbadah;
int8_t indekInformasi;

//efek
uint16_t lamaTampilText;
bool initTampil;

bool tampilanInformasiSambung;

byte indekTampilan = jumlahInformasi;
EfekMarque efekMarque;

uint16_t lebarText;
uint8_t tinggiText;

byte detikSebelumnya = 60;
byte menitSebelumnya = 60;
byte tanggalSebelumnya = 0;

long millisAlarm;

bool status;

BluetoothApk bluetoothApk(&Serial, "JWS2 SEMESIN.COM");
long millisAksesApk;
bool aksesApk;

uint16_t counterAlamat = 0;
uint16_t counterAlamatSebelumnya = 0;
uint16_t parameterSebelumnya = 0;

byte lebarJamUtama = 32;

byte lebarJadwalShalat = 32;
byte modeTampilanJadwal;
char buffer[72];
uint16_t offsetJadwalEEPROM;
bool runningTextAktif;

JamDanMenitJadwal jadwalBerikutnya;
JamDanMenitAlarm alarmBerikutnya;
int8_t hitungMundurAlarm;
bool initHitungMundurAlarm;
bool statusRelayAmpli;

void setup() {

  pinMode(pinBuzzer, OUTPUT);
  digitalWrite(pinRelayAmpli, !relayOn);
  pinMode(pinRelayAmpli, OUTPUT);
  pinMode(pinMP3Busy, INPUT_PULLUP);

  Serial.begin(9600);
  Serial.println(F("JWS Semesin 1-8 panel"));
  Serial.println(F("https://www.project.semesin.com"));

  mp3_set_serial (Serial);
  mp3_set_volume (15);


#if serialDebug
  Serial.println(F("Inisialisasi"));
#endif

  rtc.begin();

  if (rtc.lostPower())
  {
#if serialDebug
    Serial.println(F("RTC tidak jalan"));
#endif
    write_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG, 0x00);
  }
  write_i2c_register(DS3231_ADDRESS, DS3231_CONTROL, DS3231_SquareWave1Hz);

  if (EEPROM.read(offsetof(Setting, token)) != tokenSetting)
  {
    pengaturanAwal();
  }

  updateUkuranTampilan();
  dmd.waitInterruptOver = false;
  dmd.begin();
  dmd.clearScreen();
  dmd.setBrightness(25.5 * EEPROM.read(offsetof(Setting, kecerahanStandar)));

  kecepatanRunningEfek = skala2kecepatanRunning(EEPROM.read(offsetof(Setting, kecepatanEfek)));

#if serialDebug
  Serial.println(F("Sistem mulai"));
#endif

  dmd.selectFont(fontNamaMesjid);
  dmd.drawString(1, 1, namaMesjid);

  delay(3000);
  dmd.clearScreen();

  statusRelayAmpli = digitalRead(pinMP3Busy);
}


void loop() {

....... file lengkap bisa didownload melalui link dibawah

file JWS universal:

JWS Semesin v2.1

catatan:
* untuk RTC DS3231, pin-SQW harus terpasang pada pin-A3 (arduino)
* jika menggunakan pcb jws (versi manapun) ada kemungkinan tidak cocok dan perlu modifikasi

Mengirim data melalui jaringan Wifi menggunakan ESP01 dengan metode POST (kasus IOT ThingSpeak)

ESP8266 adalah modul Wi-Fi dengan fitur TCP/IP yang lengkap dan bisa dihubungkan dengan mikrokontroler melalui protokon AT-Command. TCP/IP (Transmission Control Protocol / Internet Protocol)  adalah protokol komunikasi antara dua perangkat yang terhubung dengan sebuah metode paket termasuk metode pengalamatan dan metode transmisi dalam jaringan internet.

Dalam komunikasi TCP/IP dikenal beberapa metode request/permintaan yaitu : HEAD, GET, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH.

Metode POST  sering digunakan untuk mengirimkan data-data ter-enkripsi dan langsung ke server sehingga lebih menjamin kerahasiaan data. berbeda dengan metode GET yang mengirimkan requeat dalam bentuk URL.

Skematik ESP8266/ESP01 (+Arduino Uno) request metode post:

Sketch/koding pemrograman komunikasi ESP01 + arduino dengan server menggunakan metode POST :

char ssid[]             = "xxxx";            // your network SSID (name)
char pass[]             = "xxxxxxxx";        // your network password

char server[]           = "api.thingspeak.com";
byte port               = 80;
char APIKey[]           = "XXXXXXXXXXXXXX";
uint32_t periodeKirim   = 20000;

#include <WiFiEsp.h>
#include <SoftwareSerial.h>

SoftwareSerial SerialEsp(10, 11);
WiFiEspClient client;
int status = WL_IDLE_STATUS;     // the Wifi radio's status
uint32_t millisKirim;
bool statusKirim;

void setup()
{
  Serial.begin(9600);
  Serial.println("Mengirim data melalui jaringan Wifi menggunakan ESP01 dengan metode POST");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();

  SerialEsp.begin(115200);
  SerialEsp.println("AT+UART_DEF=9600,8,1,0,0");
  delay(500);
  SerialEsp.begin(9600);

  WiFi.init(&SerialEsp);

  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
  }
  millisKirim = millis();
}

void loop()
{
  if (millisKirim < millis())
  {
    millisKirim = millis() + periodeKirim;
    
    // hubungkan ke jaringan wifi jika belum tersambung atau terputus (auto reconnect)
    if (WiFi.status() != WL_CONNECTED)
    {
      Serial.print("Menghubungkan ke jaringan SSID: ");
      Serial.println(ssid);
      while (WiFi.status() != WL_CONNECTED)
      {
        WiFi.begin(ssid, pass);
        Serial.print(".");
        delay(5000);
      }
      printWifiStatus();
      Serial.println("Berhasil terhubung ke jaringan");
    }


    if (WiFi.status() == WL_CONNECTED)
    {
      Serial.println();
      Serial.println("Menghubungkan dengan server...");
      if (client.connect(server, port))
      {
        Serial.println();
        Serial.println("Terhubung dengan server.");

        char content[30];
        sprintf(content, "field1=%d", millis() % 100);

        client.println("POST /update HTTP/1.1");
        client.println("Host: api.thingspeak.com");
        client.println("User-Agent: tslib-arduino/1.5.0");
        client.print("X-THINGSPEAKAPIKEY: ");
        client.println(APIKey);
        client.println("Content-Type: application/x-www-form-urlencoded");
        client.print("Content-Length: ");
        client.println(strlen(content));
        client.println("Connection: close");
        client.println();

        client.print(content);

        statusKirim = true;
      }
    }
  }

  if (statusKirim)
  {
    while (client.available())
    {
      char c = client.read();
      Serial.write(c);
    }

    if (!client.connected()) 
    {
      Serial.println();
      Serial.println("Memutuskan hubungan dengan server...");
      delay(10);
      client.stop();

      statusKirim = false;
    }
  }
}


void printWifiStatus()
{
  // print the SSID of the network you're attached to
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength
  long rssi = WiFi.RSSI();
  Serial.print("Signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

Library :

 

Hex Serial Monitor dengan Visual C#

Serial monitor seperti pada putty, hyper terminal, arduino IDE sangat bermanfaat untuk memonitor atau mengontrol perangkat yang sedang dikembangkan/development. Serial monitor juga bisa digunakan untuk proses debug untuk mencari kesalahan algoritma, misalnya dengan memberikan informasi nilai variabel ataupun informasi step by step percabangan.

Penggunaan serial monitor untuk keperluan debug haruslah memberikan informasi detail. Proses debug perangkat digital sangat berhubungan dengan format hexadesimal (hex) dan komunikasi binary.

Fitur Hex Serial Monitor:

  1. Data Serial yang diterima di tampilkan dalam format tabel hexa, juga disertai string.
  2. Baris baru dengan pilihan 0x00 dan 0x0A
  3. Kirim data serial dalam format :
    • String
    • 2 karakter, contohnya 12AB34BC = 0x12, 0xAB, 0x34, 0xBC
    • separator spasi, contoh : 12 AB 34 BC
    • separator koma, contoh : 12, AB,34, BC
    • awalan 0x
    • awalan $, contoh : $12$AB $34  $BC

Fungsi utama software ini adalah untuk berkomunikasi melalui serial port dalam mode binary dan ditampilkan dalam format hexa desimal.

tampilan Hexadecimal serial monitor menggunakan c#:

 

Kode software visual c# hex serial monitor:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;

namespace Hex_serial_monitor
{
    public partial class Form1 : Form
    {
        public int baris = 0;
        public int kolom = 0;
        String strKolom;


        public Form1()
        {
            InitializeComponent();
        }

        public delegate void AddDataDelegate();
        public AddDataDelegate delegateSerial;

        private void Form1_Load(object sender, EventArgs e)
        {
            string[] ports = SerialPort.GetPortNames();

            comboBoxCOMPort.Items.Clear();
            foreach (string port in ports)
            {
                comboBoxCOMPort.Items.Add(port);
            }
            if (comboBoxCOMPort.Items.Count > 0)
            {
                comboBoxCOMPort.SelectedIndex = 0;
            }

            this.delegateSerial = new AddDataDelegate(updateSerial);

            dataGridView1.Rows.Clear();
            dataGridView1.Rows.Add();
            dataGridView1.Rows[baris].Cells[0].Value = baris.ToString("X2");
            strKolom = "";

            radioButtonAscii.Checked = true;
            //checkBox0.Checked = true;

        }
        private void updateSerial()
        {
            while (serialPort1.BytesToRead > 0)
            {
                int data = serialPort1.ReadByte();
                dataGridView1.Rows[baris].Cells[kolom + 1].Value = data.ToString("X2");

                if ((data >= ' ') && (data < 128))
                {

                    strKolom += (char)data;
                }
                else
                {
                    strKolom += ' ';
                }


                dataGridView1.Rows[baris].Cells[17].Value = strKolom;

                kolom++;
                if (((data == 0) && (checkBox0.Checked)) ||
                    ((data == 0x0A) && (checkBox0A.Checked)) || 
                    (kolom == 16))
                {
                    kolom = 0;
                    baris++;
                    dataGridView1.Rows.Add();
                    dataGridView1.Rows[baris].Cells[0].Value = baris.ToString("X2");
                    strKolom = "";
                }
                if (checkBoxAutoScroll.Checked)
                {
                    dataGridView1.FirstDisplayedScrollingRowIndex = dataGridView1.RowCount - 1;
                }
            }
            textBoxKirim.Focus();
        }

        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            dataGridView1.Invoke(this.delegateSerial, new Object[] { });
        }

        private void buttonClear_Click(object sender, EventArgs e)
        {
            dataGridView1.Rows.Clear();
            kolom = 0;
            baris = 0;
            dataGridView1.Rows.Add();
            dataGridView1.Rows[baris].Cells[0].Value = baris.ToString("X2");
            strKolom = "";
        }

        private void buttonConnect_Click(object sender, EventArgs e)
        {
            if (buttonConnect.Text == "Connect")
            {
                serialPort1.PortName = comboBoxCOMPort.Text;
                serialPort1.Open();
                if (serialPort1.IsOpen)
                {
                    buttonConnect.Text = "Disconnect";
                }
            }
            else
            {
                if (serialPort1.IsOpen)
                {
                    serialPort1.Close();
                    buttonConnect.Text = "Connect";
                }

            }

        }

        private void buttonKirim_Click(object sender, EventArgs e)
        {
            if (serialPort1.IsOpen)
            {
                if (radioButtonAscii.Checked)
                {
                    serialPort1.WriteLine(textBoxKirim.Text + "\r\n");
                }
                else if (radioButton2Character.Checked)
                {
                    if (textBoxKirim.Text.Length % 2 == 0)
                    {
                        int jumlahHex = textBoxKirim.Text.Length / 2;
                        byte[] hex = new byte[jumlahHex];

                        for (int i = 0; i < jumlahHex; i++)
                        {
                            String str2Hex = textBoxKirim.Text.Substring(i * 2, 2);
                            hex[i] = (byte)int.Parse(str2Hex, System.Globalization.NumberStyles.HexNumber);

                        }
                        serialPort1.Write(hex, 0, jumlahHex);
                    }
                }
                else if (radioButtonSpace.Checked)
                {
                    String[] strHexSplit = textBoxKirim.Text.Split(' ');
                    byte[] hex = new byte[strHexSplit.Length];

                    for (int i = 0; i < strHexSplit.Length; i++)
                    {
                        String strHex = strHexSplit[i].Substring(0, 2);
                        hex[i] = (byte)int.Parse(strHex, System.Globalization.NumberStyles.HexNumber);
                    }
                    serialPort1.Write(hex, 0, strHexSplit.Length);
                }
                else if (radioButtonComma.Checked)
                {
                    String[] strHexSplit = textBoxKirim.Text.Split(',');
                    byte[] hex = new byte[strHexSplit.Length];

                    for (int i = 0; i < strHexSplit.Length; i++)
                    {
                        String strHex = strHexSplit[i].Substring(0, 2);
                        hex[i] = (byte)int.Parse(strHex, System.Globalization.NumberStyles.HexNumber);
                    }
                    serialPort1.Write(hex, 0, strHexSplit.Length);
                }
                else if (radioButton0x.Checked)
                {
                    String[] strHexSplit = textBoxKirim.Text.Split('x');
                    byte[] hex = new byte[strHexSplit.Length];

                    for (int i = 0; i < strHexSplit.Length; i++)
                    {
                        String strHex = strHexSplit[i].Substring(0, 2);
                        hex[i] = (byte)int.Parse(strHex, System.Globalization.NumberStyles.HexNumber);
                    }
                    serialPort1.Write(hex, 0, strHexSplit.Length);
                }
                else if (radioButtonDollar.Checked)
                {
                    String[] strHexSplit = textBoxKirim.Text.Split('$');
                    byte[] hex = new byte[strHexSplit.Length];

                    for (int i = 0; i < strHexSplit.Length; i++)
                    {
                        String strHex = strHexSplit[i].Substring(0, 2);
                        hex[i] = (byte)int.Parse(strHex, System.Globalization.NumberStyles.HexNumber);
                    }
                    serialPort1.Write(hex, 0, strHexSplit.Length);
                }
                
            }
        }
    }
}

 

File Hexa Serial monitor berbasis c#

 

Media pembelajaran polygon berbasis Delphi

Poligon adalah geometri dengan banyak sudut seperti segitiga, segi empat dan nonagon dan lainnya. poligon terdiri atas beberapa titik atau vertex yang terhubung menjadi sebuah bentuk geometri. Poligon memiliki penamaan khusus sesuai jumlah sudutnya.

Poligon merupakan geometri yang dapat dikembangkan secara luas sehingga digunakan di berbagai bidang.

Untuk memudahkan mengenal atau memperkenalkan polygon, salah satu media yang bisa digunakan adalah pc/laptop. Fitur dasar seperti create dan modify adalah sebagai langkah awal  untuk membangun sebuah media pembelajaran geometri. Selanjutnya  bisa dikembangkan menjadi lebih fungsional sesuai kebutuhan pengembangannya.

Screenshoot media pembelajaran polygin menggunakan delphi :

Kode delphi pengembangan media belajar geometri polygon :

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, Buttons, Grids;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    Image1: TImage;
    GroupBox1: TGroupBox;
    GroupBox3: TGroupBox;
    GroupBox2: TGroupBox;
    LabelPosisi: TLabel;
    ButtonClose: TButton;
    LabelVertex: TLabel;
    GroupBox4: TGroupBox;
    StringGrid1: TStringGrid;
    ButtonDelete: TButton;
    ButtonDraw: TButton;
    ButtonEdit: TButton;
    ButtonInsert: TButton;
    ButtonClear: TButton;
    ButtonSave: TButton;
    ButtonOpen: TButton;
    Label1: TLabel;
    procedure ButtonDrawClick(Sender: TObject);
    procedure ButtonEditClick(Sender: TObject);
    procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure Image1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ButtonCloseClick(Sender: TObject);
    procedure ButtonSaveClick(Sender: TObject);
    procedure ButtonLoadClick(Sender: TObject);
    procedure refreshCanvas();
    procedure StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer;
      var CanSelect: Boolean);
    procedure ButtonDeleteClick(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure ButtonInsertClick(Sender: TObject);
    procedure ButtonClearClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

type Point = record
 X : integer;
 Y : integer;
end;

var
  Form1: TForm1;
  modeOperasi : integer;
  posisi : Point;
  tinggiCanvas : integer;
  startPolygon : boolean;
  polygon : array[0..20] of Point;
  polygonCount : integer;
  editIndex : integer;

const
  modeIdle = 0;
  modeDraw = 1;
  modeEdit = 3;
  modeInsert = 4;

implementation

{$R *.dfm}

procedure TForm1.refreshCanvas();
var
  i : integer;
begin
  StringGrid1.RowCount := polygonCount + 1;

  i := 0;
  while true do
  begin
    if polygon[i].X = -1 then
      break;
    StringGrid1.Cells[0,i+1] := inttostr(i+1);
    StringGrid1.Cells[1,i+1] := inttostr(polygon[i].X);
    StringGrid1.Cells[2,i+1] := inttostr(polygon[i].Y);

    if i = 0 then
    begin
      image1.Canvas.rectangle(0,0,image1.width-1, image1.height-1);
      image1.Canvas.MoveTo(polygon[0].X, tinggiCanvas - polygon[0].Y);
    end
    else
    begin
      if (modeOperasi = modeInsert) and (editIndex = i) then
      begin
        Image1.Canvas.Pen.Color := clRed;
      end
      else
      begin
        Image1.Canvas.Pen.Color := clBlack;
      end;
      image1.Canvas.LineTo(polygon[i].X, tinggiCanvas - polygon[i].Y);
    end;

    i := i + 1;
    if i = 20 then
      break;

  end;

   if modeOperasi = modeEdit then
   begin
    image1.Canvas.Rectangle(polygon[editIndex].X-5, tinggiCanvas - polygon[editIndex].Y - 5,
      polygon[editIndex].X+5, tinggiCanvas - polygon[editIndex].Y + 5);
   end;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  StringGrid1.Cells[0,0] := 'No';
  StringGrid1.Cells[1,0] := 'X';
  StringGrid1.Cells[2,0] := 'Y';
end;


procedure TForm1.FormResize(Sender: TObject);
begin
  tinggiCanvas := Image1.Height;

end;

procedure TForm1.ButtonDrawClick(Sender: TObject);
begin
  buttonDraw.Font.Style := [fsBold];
  ButtonEdit.Font.Style := [];
  ButtonInsert.Font.Style := [];

  modeOperasi := modeDraw;
  startPolygon := true;
  polygonCount := 0;
  polygon[polygonCount].X := -1;

  refreshCanvas();
end;

procedure TForm1.ButtonCloseClick(Sender: TObject);
begin

  if (modeOperasi = modeDraw) and (polygonCount > 1) then
  begin
    polygon[polygonCount] := polygon[0];
    polygonCount := polygonCount + 1;

    polygon[polygonCount].X := -1;

    LabelVertex.Caption := 'Vertex ' + inttostr(polygonCount);

    refreshCanvas();

    buttonDraw.Font.Style := [];
    ButtonEdit.Font.Style := [];
    ButtonInsert.Font.Style := [];
    modeOperasi := modeIdle;
  end;

end;

procedure TForm1.ButtonEditClick(Sender: TObject);
begin
  buttonDraw.Font.Style := [];
  ButtonEdit.Font.Style := [fsBold];
  ButtonInsert.Font.Style := [];
  modeOperasi := modeEdit;
  editIndex := 1;
  refreshCanvas();
end;

procedure TForm1.ButtonInsertClick(Sender: TObject);
begin
  buttonDraw.Font.Style := [];
  ButtonEdit.Font.Style := [];
  ButtonInsert.Font.Style := [fsBold];
  modeOperasi := modeInsert;
  editIndex := 1;
  refreshCanvas();
end;


procedure TForm1.ButtonClearClick(Sender: TObject);
begin
  polygonCount := 0;
  polygon[polygonCount].X := -1;

  refreshCanvas();
end;

procedure TForm1.ButtonDeleteClick(Sender: TObject);
var
  i : integer;
begin
  if editIndex <> 0 then
  begin
    for i := editIndex to polygonCount do
    begin
      polygon[i].X := polygon[i+1].X;
      polygon[i].Y := polygon[i+1].Y;
    end;
    polygonCount := polygonCount - 1;
    refreshCanvas();
  end;
end;


procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  posisi.X := X;
  posisi.Y := tinggiCanvas - Y;
  LabelPosisi.Caption := 'Posisi ' + inttostr(posisi.X) + ', ' + inttostr(posisi.Y);
end;

procedure TForm1.Image1Click(Sender: TObject);
var
  i : integer;
begin
  if (modeOperasi = modeDraw) and (polygonCount < 20) then
  begin
    polygon[polygonCount].X := posisi.X;
    polygon[polygonCount].Y := posisi.Y;
    polygonCount := polygonCount + 1;
    polygon[polygonCount].X := -1;
    LabelVertex.Caption := 'Vertex ' + inttostr(polygonCount);

    refreshCanvas();
  end
  else if (modeOperasi = modeEdit) and (editIndex > 0)then
  begin
    if (editIndex = 1) or (editIndex = polygonCount-1) then
    begin
      polygon[1].X := posisi.X;
      polygon[1].Y := posisi.Y;
      polygon[polygonCount-1].X := posisi.X;
      polygon[polygonCount-1].Y := posisi.Y;
    end
    else if (editIndex > 0) then
    begin
      polygon[editIndex].X := posisi.X;
      polygon[editIndex].Y := posisi.Y;

    end;

    refreshCanvas();
  end
  else if (modeOperasi = modeInsert) and (editIndex > 0) and (polygonCount < 20) then
  begin
    for i := polygonCount downto editIndex do
    begin
      polygon[i+1].X := polygon[i].X;
      polygon[i+1].Y := polygon[i].Y;
    end;
    polygon[editIndex].X := posisi.X;
    polygon[editIndex].Y := posisi.Y;
    polygonCount := polygonCount + 1;
    refreshCanvas();
 end;

end;


procedure TForm1.ButtonSaveClick(Sender: TObject);
var
  Stream: TStream;
begin
  Stream:= TFileStream.Create('polygon.bin', fmCreate);
  Stream.WriteBuffer(polygonCount, SizeOf(integer));
  Stream.WriteBuffer(polygon, (polygonCount+1) * SizeOf(Point));
  Stream.Free;
end;

procedure TForm1.ButtonLoadClick(Sender: TObject);
var
  Stream: TStream;
begin
  Stream:= TFileStream.Create('polygon.bin', fmOpenRead);
  Stream.ReadBuffer(polygonCount, SizeOf(integer));
  Stream.ReadBuffer(polygon, (polygonCount+1) * SizeOf(Point));
  Stream.Free;

  refreshCanvas();
  LabelVertex.Caption := 'Vertex ' + inttostr(polygonCount);

end;

procedure TForm1.StringGrid1SelectCell(Sender: TObject; ACol,
  ARow: Integer; var CanSelect: Boolean);
begin
  editIndex := ARow - 1;
  refreshCanvas();
end;

end.

code lengkap media pengajaran geometri poligon:
library :

Penjadwalan pembacaan sensor berbasis RTC DS3231

Pembacaan sensor dengan arduino dapat dilakukan kapan saja dan dimana saja dalam sketch arduino sesuai kebutuhan. Penjadwalan diperlukan untuk mendapatkan nilai dari sensor dalam rentang tertentu atau pada waktu-waktu khusus misalnya baca sensor saat gerhana matahari. Jika menginginkan sensor dibaca hanya pada waktu-waktu tertentu saja berdasarkan RTC dapat menggunakan metode berikut:

  1. Metode pergantian waktu, yaitu aksi pembacaan sensor dilakukan ketika satuan waktu berubah, contohnya setiap detik, setiap menit dan seterusnya. contoh :
      if (detikSebelumnya != now.second() )
      {
        detikSebelumnya = now.second();
        bacaSensor();
      }
    
  2. Metode modulus waktu, yaitu pembacaan sensor dilakukan pada setiap kelipatan satuan waktu, seperti pembacaan sensor setiap 2 detik, setiap 30 detik dan seterusnya. Contohnya :
      if (!(now.second() % periodaBacaSensor) )
      {
        bacaSensor();
      }
    
  3. Metode rentang waktu, yaitu pembacaan sensor dilakukan selama waktu pada rentang waktu tertentu. Contohnya :
      if ((now.hour() >= 20) || (now.hour() < 3))
      {
        bacaSensor();
      }
    
  4. Metode Tabel, yaitu pembacaan sensor sesuai waktu yang ditetapkan berdasarkan tanggal dan waktu tertentu, (pembacaan hanya dilakukan 1 kali). Contohnya:
      if (now.getEpoch() == databaseJadwal[i].getEpoch())
      {
        bacaSensor();
      }
    

Rangkaian yang digunakan pada pembacaan sensor sesuai jadwal tertentu berbasis rtc:

berikut kode/sketch penjadwalan pembacaan sensor menggunakan rtc pada arduino:


#define pinSensor     A0

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

DateTime set = {2020, 2, 22, 7, 0, 0, 1};
DateTime now;
DateTime databaseJadwal[] = {
  {2020, 2, 22, 7, 0, 3, 1},
  {2020, 2, 23, 7, 0, 0, 1},
  {2020, 2, 24, 7, 0, 0, 1},
  {2020, 2, 25, 7, 0, 0, 1},
  {2020, 2, 26, 7, 0, 0, 1},
  {2020, 2, 27, 7, 0, 0, 1},
};
byte detikSebelumnya;
byte menitSebelumnya;

#define periodaBacaSensor   5//detik

void bacaSensor()
{
  analogRead(pinSensor);
}

void setup() {
  Serial.begin(9600);
  Serial.println("Penjadwalan pembacaan sensor berbasis RTC");
  Serial.println("https://www.project.semesin.com");
  Serial.println();

  Wire.begin();
  rtc.begin();
  now = rtc.now();
  //  if (now.year() == 2000)
  {
    rtc.setDateTime(set);
  }
}

void loop() {
  now = rtc.now();

  if (detikSebelumnya != now.second() )
  {

    char bufWaktu[32];
    sprintf(bufWaktu, "%02d:%02d:%02d %02d-%02d-%04d", now.hour(), now.minute(), now.second(), now.date(), now.month(), now.year());
    Serial.println(bufWaktu);

    //pembacaan setiap detik
    detikSebelumnya = now.second();
    Serial.println("setiap detik");
    bacaSensor();


    //pembacaan setiap 2 detik
    if (now.second() % 2)
    {
      Serial.println("setiap 2 detik");
      bacaSensor();
    }

    //pembacaan setiap detik ke-5
    if (now.second() == 5)
    {
      Serial.println("setiap detik ke-5");
      bacaSensor();
    }

    //pembacaan setiap n detik (n 1..60)
    if (!(now.second() % periodaBacaSensor) )
    {
      Serial.println("setiap n detik");
      bacaSensor();
    }

    //pembacaan setiap 937 detik (berlaku untuk x detik 1..~)
    if (!(now.getEpoch() % 937) )
    {
      Serial.println("setiap 937 detik");
      bacaSensor();
    }

    //pembacaan setiap menit
    if (menitSebelumnya != now.minute() )
    {
      Serial.println("setiap menit");
      menitSebelumnya = now.minute();
      bacaSensor();
    }

    //pembacaan setiap 3 jam = 00:00, 03:00, 06:00, 09:00, 12:00, 15:00, 18:00, 21:00
    if (!(now.hour() % 3) && !now.minute() && !now.second())
    {
      Serial.println("setiap 3 jam");
      bacaSensor();
    }

    //pembacaan dalam rentang waktu
    if ((now.hour() >= 7) && (now.hour() < 10))//jam 07:00 - jam 09:59
    {
      Serial.println("pembacaan dalam rentang waktu 1");
      bacaSensor();
    }
    else if ((now.hour() >= 11) && (now.hour() < 15))//jam 11:00 - jam 14:59
    {
      Serial.println("pembacaan dalam rentang waktu 2");
      bacaSensor();
    }
    else if ((now.hour() >= 20) || (now.hour() < 3))//jam 20:00 - jam 02:59
    {
      Serial.println("pembacaan dalam rentang waktu 3");
      bacaSensor();
    }

    //pembacaan berdasarkan tabel waktu
    for (byte i = 0; i < sizeof(databaseJadwal) / sizeof(DateTime); i++)
    {
      if (now.getEpoch() == databaseJadwal[i].getEpoch())
      {
        Serial.print("tabel waktu = ");
        Serial.println(i);
        bacaSensor();
      }
    }
  }

}

library :

DMD Jam, Timer, Countdown dengan arduino

Jam, timer/stopwatch, countdown seringkali di kombinasikan dalam sebuah perangkat, karena fitur2 ini memiliki kemiripan fungsi.

Fitur tampilan dapat dipilih melalui satu tombol, setiap tombol ditekan maka mode waktu akan berubah yaitu Watch – Timer – Countdown, fungsi masing-masing adalah :

Watch (Jam)

Ditandai dengan Karakter ‘W’ (watch) pada sudut kiri atas, berfungsi menampilan waktu rtc saat ini dalam format “hh:mm:dd” atau jam:menit:detik.

Timer (Hitung maju)

Ditandai dengan karakter ‘T’ (Timer) pada sudut kiri atas. Dalam mode ini pewaktu memulai hitungan dari 00:00:00 dan akan terus bertambah setiap detik. Apabila hitungan maju ini telah mencapai 86.400 detik atau 23:59:59 atau 1 hari penuh, maka hitungan akan dimulai lagi dari 0.

Countdown (Hitung mundur)

Ditandai dengan karakter ‘C’ (Count down) pada sudut kiri atas.berfungsi menampilkan waktu tersisa dari batas aawal yang bisa diatur dengan memodifikasi variabel ‘waktuHitungMundur’ seperti contoh :


#define waktuHitungMundur 5 * 60// 5 menit

Waktu hitung mungdur dapat diisi angka 1 – 86400 dalam satuan detik.

Apabila hitungan habis atau mencapai 0 (00:00:00) maka tampilan akan berubah ke mode watch dan menampilkan waktu rtc saat ini.

Komponen yang digunakan dalam perancangan jam, counter, countdown:

  1. Arduino uno
  2. 2x Panel P10 hub12
  3. RTC DS3231

berikut skema rakitan tampilan waktu, hitung maju dan hitung mundur:

Koding/sketch arduino tampilan jam, penghitung mundur dan pewaktu maju:

#define waktuHitungMundur     5 * 60// 5 menit

#define pinTombol             A0


#include "Sodaq_DS3231.h"
#include <DMD2.h>
#include <fonts/angka6x14.h>
#include <fonts/System_Min5x7.h>

enum Mode
{
  modeJam,
  modeHitungMaju,
  modeHitungMundur,
  jumlahMode,
};

DateTime now;
uint32_t hitungMundur;
uint32_t hitungMaju;
byte mode;
char buffer[10];
byte jam, menit, detik;
byte detikSebelumnya;

SPIDMD dmd(2, 1);

void setup() {

  pinMode(pinTombol, INPUT_PULLUP);

  Serial.begin(9600);
  Serial.println("Jam, Hitung maju, Hitung mundur (Watch, Timer, Count down)");
  Serial.println("https://www.project.semesin.com");

  rtc.begin();

  //Set rtc untuk keperluan testing
  DateTime dt(2020, 2, 20, 15, 30, 0, 1);
  rtc.setDateTime(dt);

  dmd.begin();

  setMode();
}

void loop() {
  now = rtc.now();
  if (detikSebelumnya != now.second())
  {
    detikSebelumnya = now.second();

    switch (mode)
    {
      case modeJam:
        sprintf(buffer, "%02d%s%02d%s%02d", now.hour(), now.second() % 2 ? ":" : " ", now.minute(), now.second() % 2 ? ":" : " ", now.second());
        dmd.drawString(8, 1, buffer);
        break;

      case modeHitungMaju:
        jam = hitungMaju / (3600L);
        menit = (hitungMaju % (3600L)) / 60;
        detik = hitungMaju % 60;
        sprintf(buffer, "%02d%s%02d%s%02d", jam, now.second() % 2 ? ":" : " ", menit, now.second() % 2 ? ":" : " ", detik);
        dmd.drawString(8, 1, buffer);
        hitungMaju++;
        if(hitungMaju == 86400L)
        {
          hitungMaju = 0; 
        }
        
        break;

      case modeHitungMundur:
        jam = hitungMundur / (3600L);
        menit = (hitungMundur % (3600L)) / 60;
        detik = hitungMundur % 60;
        sprintf(buffer, "%02d%s%02d%s%02d", jam, now.second() % 2 ? ":" : " ", menit, now.second() % 2 ? ":" : " ", detik);
        dmd.drawString(8, 1, buffer);

        hitungMundur--;
        if (!hitungMundur)
        {
          mode = modeJam;
          setMode();
        }
        break;
    }
  }
  if (!digitalRead(pinTombol))
  {
    delay(100);
    if (!digitalRead(pinTombol))
    {
      mode++;
      mode %= jumlahMode;
      setMode();

      while (!digitalRead(pinTombol));
    }
  }
}

void setMode()
{
  dmd.selectFont(System_Min5x7);
  switch (mode)
  {
    case modeJam:
      dmd.drawString(0, 0, "W");
      break;

    case modeHitungMaju:
      dmd.drawString(0, 0, "T");
      hitungMaju = 0;
      break;

    case modeHitungMundur:
      dmd.drawString(0, 0, "C");
      hitungMundur = waktuHitungMundur;
      break;
  }
  dmd.selectFont(angka6x14);
  dmd.fontHeader.fixedWidth = 2;
}

library :