Memberi pakan hewan secara teratur merupakan hal penting, baik untuk hewan peliharaan di rumah maupun ternak skala kecil. Namun, kesibukan sehari-hari sering membuat jadwal pemberian pakan menjadi tidak konsisten. Untuk mengatasi hal tersebut, kita dapat memanfaatkan teknologi IoT.
Pada blog post ini, kita akan membahas cara membuat Alat Pemberi Pakan Otomatis menggunakan ESP8266 dan servo motor. Sistem ini mampu mengeluarkan pakan secara otomatis dan menampilkan informasi melalui LCD 16×2, serta dapat dipantau melalui dashboard web tanpa koneksi internet.
Seperti proyek-proyek sebelumnya, ESP8266 dijalankan dalam mode Access Point (AP) dan menggunakan Async Web Server, sehingga sistem tetap sederhana dan mudah diterapkan.
Alat pemberi pakan otomatis ini bekerja dengan menggerakkan servo motor untuk membuka dan menutup wadah pakan berdasarkan jadwal waktu tertentu. Penentuan waktu dilakukan menggunakan RTC (Real Time Clock) sehingga jadwal pemberian pakan tetap akurat meskipun ESP8266 dimatikan atau kehilangan daya. ESP8266 bertugas membaca waktu dari RTC, mengontrol servo sesuai jadwal, menampilkan status sistem pada LCD 16×2, serta menyediakan halaman web yang dapat diakses melalui HP atau laptop untuk memantau proses pemberian pakan.
Alat dan Bahan yang Dibutuhkan
Nama Komponen
Gambar Komponen
NodeMcu ESP8266
Servo SG-90
LCD 16×2 I2C
RTC DS1307
Kabel Jumper
Breadboard
Gambar Rangkaian
ESP8266
Servo
Pin D4
Kabel orange
Vin / Vu
Kabel Merah
GND
Kabel Cokelat
ESP8266
LCD I2C
D5
SCL
D6
SDA
Vin / VU
VCC
GND
GND
ESP8266
RTC DS1307
D5
SCL
D6
SDA
Vin / VU
VCC
GND
GND
Cara Kerja Sistem
Cara kerja alat pemberi pakan otomatis ini adalah sebagai berikut:
ESP8266 menginisialisasi sistem dan servo motor.
Servo berada pada posisi awal (tertutup).
Ketika sistem dijalankan, servo berputar untuk membuka wadah pakan.
Setelah beberapa detik, servo kembali ke posisi awal.
Status pemberian pakan ditampilkan pada LCD 16×2.
Informasi status juga ditampilkan pada halaman web.
Library yang Digunakan
Pastikan library berikut sudah terpasang pada Arduino IDE:
ESP8266WiFi.h
ESPAsyncWebServer.h
ESPAsyncTCP.h
LiquidCrystal_I2C.h
Servo.h
RTClib.h
Code
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>
#include <RTClib.h>
#define SERVO_PIN D4
LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo feederServo;
RTC_DS1307 rtc;
AsyncWebServer server(80);
const char* ssid = "SmartFeeder_AP";
const char* password = "12345678";
bool feeding = false;
bool alreadyFed = false;
int feedHour = 7; // jam pemberian pakan
int feedMinute = 0; // menit pemberian pakan
unsigned long feedStartMillis = 0;
const unsigned long feedDuration = 3000; // 3 detik
String feedingStatusText() {
String text = "";
if (feeding == true) {
text = "PAKAN AKTIF";
} else {
text = "SIAP";
}
return text;
}
String htmlPage() {
String html = "";
html += "<!DOCTYPE html><html><head>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
html += "<title>Smart Feeder</title>";
html += "</head><body>";
html += "<h1>Smart Feeder ESP8266</h1>";
html += "<p>Status: ";
html += feedingStatusText();
html += "</p>";
html += "<p>Jadwal: 07:00</p>";
html += "</body></html>";
return html;
}
void setup() {
lcd.init();
lcd.backlight();
feederServo.attach(SERVO_PIN);
feederServo.write(0);
rtc.begin();
// SET WAKTU SEKALI SAJA JIKA RTC BARU
// rtc.adjust(DateTime(2026, 2, 5, 7, 0, 0));
WiFi.mode(WIFI_AP);
WiFi.softAP(ssid, password);
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/html", htmlPage());
});
server.begin();
}
void loop() {
DateTime now = rtc.now();
/* === Trigger Pakan === */
if (feeding == false &&
alreadyFed == false &&
now.hour() == feedHour &&
now.minute() == feedMinute) {
feeding = true;
feedStartMillis = millis();
feederServo.write(90);
}
/* === Stop Servo === */
if (feeding == true && millis() - feedStartMillis >= feedDuration) {
feederServo.write(0);
feeding = false;
alreadyFed = true;
}
/* === Reset flag setelah menit berlalu === */
if (now.minute() != feedMinute) {
alreadyFed = false;
}
/* === LCD === */
lcd.setCursor(0, 0);
lcd.print("Time: ");
lcd.print(now.hour());
lcd.print(":");
if (now.minute() < 10) {
lcd.print("0");
}
lcd.print(now.minute());
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("Status: ");
lcd.print(feedingStatusText());
lcd.print(" ");
delay(1000);
}
Cara Menggunakan Sistem
Pasang servo motor pada wadah pakan sehingga dapat membuka dan menutup dengan baik.
Rangkai servo dan LCD sesuai dengan skema koneksi.
Unggah program ke ESP8266 menggunakan Arduino IDE.
Nyalakan sistem dan pastikan servo bergerak dengan benar.
Hubungkan HP atau laptop ke jaringan WiFi SmartFeeder_AP.
Buka browser dan akses alamat IP 192.168.4.1 untuk memantau status sistem.
Penyiraman tanaman tidak selalu harus bergantung pada kondisi tanah. Pada beberapa jenis tanaman, penyiraman justru lebih efektif jika dilakukan pada waktu-waktu tertentu, misalnya pagi dan sore hari. Oleh karena itu, pada artikel ini kita akan membahas cara membuat Sistem Penyiram Tanaman Terjadwal menggunakan ESP8266 dan pompa air.
Berbeda dengan sistem sebelumnya yang berbasis sensor kelembapan tanah, sistem ini akan menyalakan pompa air berdasarkan jadwal waktu yang telah ditentukan. ESP8266 tetap dijalankan dalam mode Access Point (AP) dan menggunakan Async Web Server, sehingga sistem dapat diakses tanpa koneksi internet.
Sistem penyiram tanaman terjadwal ini bekerja dengan memanfaatkan modul RTC DS1307 sebagai penentu waktu. RTC akan menyimpan informasi jam, menit, dan tanggal secara mandiri. ESP8266 membaca data waktu dari RTC, lalu mengaktifkan pompa air sesuai jadwal yang telah ditentukan.
Status pompa, waktu saat ini, dan jadwal penyiraman ditampilkan melalui LCD 16×2 serta dapat dipantau melalui halaman web menggunakan browser HP atau laptop.
Alat dan Bahan yang Dibutuhkan
Nama komponen
Gambar Komponen
NodeMcu esp8266
Modul RTC DS1307
Pompa DC 5V
Relay Module 1 Channel
LCD 16×2 I2C
Kabel Jumper
Breadboard
Gambar Rangkaian
NodeMcu esp8266
Relay
Pin D1
IN
Vin /VU
VCC (+)
GND
GND (-)
NodeMcu Esp8266
LCD I2C
Pin D5
SCL
Pin D6
SDA
Vin / Vu
VCC
GND
GND
NodeMcu Esp8266
DS1307
Pin D5
SCL
Pin D6
SDA
Vin / Vu
VCC
GND
GND
Cara Kerja Sistem
Cara kerja sistem penyiram tanaman terjadwal ini adalah sebagai berikut:
RTC DS1307 menyimpan dan menyediakan data waktu secara real-time.
ESP8266 membaca data jam dan menit dari RTC.
Sistem membandingkan waktu saat ini dengan jadwal penyiraman yang telah ditentukan.
Jika waktu sesuai dengan jadwal, relay diaktifkan sehingga pompa air menyala.
Pompa air akan bekerja selama durasi tertentu.
Setelah durasi penyiraman selesai, pompa dimatikan secara otomatis.
Waktu saat ini dan status pompa ditampilkan pada LCD dan halaman web.
Library yang Digunakan
Pastikan library berikut sudah terpasang pada Arduino IDE:
ESP8266WiFi.h
ESPAsyncWebServer.h
ESPAsyncTCP.h
RTClib.h
LiquidCrystal_I2C.h
Code
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <LiquidCrystal_I2C.h>
#include <RTClib.h>
#define RELAY_PIN D1
LiquidCrystal_I2C lcd(0x27, 16, 2);
RTC_DS1307 rtc;
const char* ssid = "SmartWaterRTC_AP";
const char* password = "12345678";
AsyncWebServer server(80);
bool pumpState = false;
int waterHour = 6;
int waterMinute = 0;
int wateringDuration = 10; // detik
unsigned long startMillis = 0;
bool wateringActive = false;
String pumpStatusText() {
String text = "";
if (pumpState == true) {
text = "ON";
} else {
text = "OFF";
}
return text;
}
String htmlPage(DateTime now) {
String html = "";
html += "<!DOCTYPE html><html><head>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
html += "<title>Smart Watering RTC</title>";
html += "</head><body>";
html += "<h1>Penyiram Tanaman Terjadwal</h1>";
html += "<p>Waktu Saat Ini: ";
html += String(now.hour());
html += ":";
html += String(now.minute());
html += "</p>";
html += "<p>Status Pompa: ";
html += pumpStatusText();
html += "</p>";
html += "<p>Jadwal Penyiraman: 06:00</p>";
html += "</body></html>";
return html;
}
void setup() {
lcd.init();
lcd.backlight();
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, HIGH);
rtc.begin();
WiFi.mode(WIFI_AP);
WiFi.softAP(ssid, password);
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
DateTime now = rtc.now();
request->send(200, "text/html", htmlPage(now));
});
server.begin();
}
void loop() {
DateTime now = rtc.now();
if (wateringActive == false && now.hour() == waterHour && now.minute() == waterMinute) {
digitalWrite(RELAY_PIN, LOW);
pumpState = true;
wateringActive = true;
startMillis = millis();
}
if (wateringActive == true && millis() - startMillis >= wateringDuration * 1000) {
digitalWrite(RELAY_PIN, HIGH);
pumpState = false;
wateringActive = false;
}
lcd.setCursor(0, 0);
lcd.print("Time: ");
lcd.print(now.hour());
lcd.print(":");
lcd.print(now.minute());
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("Pump: ");
lcd.print(pumpStatusText());
lcd.print(" ");
delay(1000);
}
Cara Menggunakan Sistem
Rangkai RTC DS1307, relay, pompa air, dan LCD sesuai dengan skema koneksi.
Unggah program ke ESP8266 menggunakan Arduino IDE.
Atur waktu RTC (sekali saja) sesuai dengan waktu saat ini.
Nyalakan sistem dan pastikan semua komponen bekerja dengan baik.
Hubungkan HP atau laptop ke jaringan WiFi SmartWaterRTC_AP.
Buka browser dan akses alamat IP 192.168.4.1 untuk memantau sistem.
Pada tutorial ini kita akan membuat sistem Monitoring Kualitas Udara menggunakan sensor MQ-135. Sistem ini mampu mendeteksi gas dan asap di lingkungan sekitar, kemudian menampilkan hasilnya melalui Web Browser dan LCD 16×2.
ESP8266 akan dijalankan dalam mode Access Point (AP) dan menggunakan Async Web Server, sehingga sistem dapat diakses langsung dari HP atau laptop tanpa koneksi internet dan tanpa router.
Pada tutorial ini kita akan membuat Smart Lamp sederhana menggunakan ESP8266 + Relay, yang bisa dikontrol langsung dari HP atau browser tanpa internet.
ESP8266 akan berjalan dalam mode Access Point (AP), sehingga HP/laptop bisa langsung terhubung ke WiFi dari ESP8266, lalu mengontrol lampu lewat Web Server.
Alat & Bahan
Nama Komponen
Gambar Komponen
NodeMcu esp8266
Relay Module 1 Channel
Bola Lampu
Fitting Lampu
Kabel Jumper
Breadboard
Gambar Rangkaian
ESP8266
Relay
pin D1
pin S
vin / vu
pin +
GND
pin –
Library yang digunakan
ESP8266WiFi.h
ESPAsyncWebServer.h
ESPAsyncTCP.h
Code
#include <ESP8266WiFi.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#define RELAY_PIN D1
const char* ssid = "SmartLamp_AP";
const char* password = "12345678";
AsyncWebServer server(80);
bool lampState = false;
String processor() {
String statusText = "";
if (lampState == true) {
statusText = "ON";
} else {
statusText = "OFF";
}
String html = "";
html += "<!DOCTYPE html><html><head>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
html += "<title>Smart Lamp</title>";
html += "<style>";
html += "body{font-family:Arial;text-align:center;margin-top:40px;}";
html += "button{padding:15px 30px;font-size:18px;margin:10px;}";
html += "</style>";
html += "</head><body>";
html += "<h1>Smart Lamp Control</h1>";
html += "<p>Status: <b>" + statusText + "</b></p>";
html += "<button onclick=\"fetch('/on')\">ON</button>";
html += "<button onclick=\"fetch('/off')\">OFF</button>";
html += "</body></html>";
return html;
}
void setup() {
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, HIGH);
WiFi.mode(WIFI_AP);
WiFi.softAP(ssid, password);
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/html", processor());
});
server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){
digitalWrite(RELAY_PIN, LOW);
lampState = true;
request->send(200, "text/plain", "ON");
});
server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request){
digitalWrite(RELAY_PIN, HIGH);
lampState = false;
request->send(200, "text/plain", "OFF");
});
server.begin();
}
void loop() {
// Async Web Server tidak membutuhkan handleClient()
}
Petunjuk Penggunaan
Unggah (upload) program Smart Lamp ke papan ESP8266 menggunakan Arduino IDE.
Setelah proses upload selesai, aktifkan fitur WiFi pada perangkat HP atau laptop.
Cari dan sambungkan perangkat ke jaringan WiFi dengan nama SmartLamp_AP.
Buka aplikasi browser, kemudian akses alamat IP 192.168.4.1.
Gunakan tombol ON atau OFF pada halaman web untuk mengendalikan kondisi lampu.
Sangat Perlu Untuk Diperhatikan
Pastikan sumber listrik PLN dalam kondisi mati sebelum melakukan pemasangan atau perubahan rangkaian.
Hindari menyentuh kabel AC, terminal relay, atau bagian logam lainnya saat rangkaian masih terhubung dengan sumber listrik.
Gunakan box atau casing relay untuk melindungi rangkaian dari sentuhan langsung serta mengurangi risiko korsleting.
Periksa kembali setiap sambungan kabel, pastikan terpasang dengan kuat dan tidak ada kabel yang terkelupas.
Setelah semua komponen terpasang dengan benar, barulah sambungkan rangkaian ke sumber listrik dan lakukan pengujian secara bertahap.
Pada tutorial ini, akan dibahas langkah-langkah pembuatan sistem kendali LED berbasis WiFi menggunakan ESP8266. ESP8266 akan kita konfigurasikan sebagai Access Point (AP) sehingga kita dapat terhubung langsung tanpa menggunakan router tambahan. Selain itu, sistem web server yang digunakan bersifat asynchronous, sehingga respon kontrol menjadi lebih cepat dan halaman web tidak perlu dimuat ulang setiap kali status LED berubah.
Setelah rangkaian dan program selesai dibuat, unggah kode ke papan ESP8266 menggunakan Arduino IDE. Saat ESP8266 sudah berhasil menyala, modul akan otomatis membuat jaringan WiFi sesuai dengan nama dan kata sandi yang telah ditentukan di dalam program (pastikan sudah dicatat). Selanjutnya, hubungkan laptop atau smartphone kamu ke jaringan WiFi tersebut.
Setelah terhubung, buka aplikasi browser dan masukkan alamat IP 192.168.4.1 pada bilah alamat. Halaman kontrol LED akan tampil dan menunjukkan status LED saat ini. Tekan tombol Toggle untuk mengubah kondisi LED dari mati menjadi menyala atau sebaliknya.
Assalamualikum, Halo, semuanya! Kali ini kita akan membahas tentang sensor gerak yang cukup populer, yaitu sensor PIR HC-SR501. Sensor ini bisa mendeteksi gerakan dengan cara menangkap perubahan radiasi inframerah dari objek di sekitarnya. Radiasi sinar inframerah ini biasanya berasal dari panas tubuh makhluk hidup seperti orang dan binatang sehingga sensor ini sangat cocok untuk digunakan sebagai alat keamanan di rumah kita. Kita akan belajar cara menghubungkannya dengan Arduino UNO dan membuat sistem sederhana yang bisa mendeteksi gerakan. Yuk, simak!
Cara Kerja Sensor PIR HC-SR501
Sensor PIR HC-SR501 ini punya cara kerja yang cukup sederhana dan menarik. Sensor ini dilengkapi dengan dua elemen yang sensitif terhadap radiasi inframerah. Ketika ada objek hangat, seperti tubuh manusia dan hewan, yang bergerak di dekatnya, sensor bisa mendeteksi perubahan radiasi ini. Ketika gerakan terdeteksi, sensor membandingkan sinyal yang diterima dari kedua elemen tersebut. Jika ada perbedaan yang signifikan—misalnya, saat seseorang bergerak dari satu sisi ke sisi lain—sensor akan memberikan sinyal output yang menunjukkan adanya gerakan.
Sinyal ini biasanya berupa sinyal HIGH (sekitar 5V) yang bisa digunakan untuk menghidupkan perangkat lain, seperti LED atau relay. Selain itu, sensor ini juga memiliki potensiometer yang memungkinkan kita untuk mengatur berapa lama sinyal HIGH tersebut aktif setelah gerakan terdeteksi. Jadi, kita bisa sesuaikan durasinya sesuai kebutuhan. Setelah periode waktu yang ditentukan berakhir, sensor akan kembali ke mode tidak aktif dan mengeluarkan sinyal LOW sampai ada gerakan baru yang terdeteksi. Dengan cara kerjanya yang sederhana dan efektif, sensor PIR HC-SR501 ini sangat populer, terutama dalam aplikasi keamanan, otomatisasi rumah, dan berbagai proyek DIY dengan Arduino.
Alat dan Bahan
Seperti biasa, kita butuh alat dan bahan untuk project ini, yaitu:
Alat / Bahan
Kebutuhan
Arduino UNO
1 Buah
Sensor gerak PIR HC-SR501
1 buah
Breadboard
1 buah
LED
1 buah
buzzer
1 buah
Kabel Jumper
Secukupnya
Gambar Rangkaian
Coding
int ledPin = 12; // pin yang terhubung dengan LED
int buzzPin = 11; //pin yang terhubung dengan speaker
int pirPin = 8; // pin yang terhubung dengan
int status= LOW; // anggap saja nggak ada gerakan
int val = 0; // pembacaan status pin
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(buzzPin, OUTPUT);
pinMode(pirPin, INPUT);
Serial.begin(9600);
}
void loop(){
val = digitalRead(inputPin);
if (val == HIGH)
{
digitalWrite(ledPin, HIGH);
if (pirState == LOW)
{
Serial.println("Motion detected!"); // print on output change
pirState = HIGH;
tone(buzzPin, 1000);
delay(500);
noTone(buzzPIN);
}
}
else
{
digitalWrite(ledPin, LOW); // turn LED OFF
if (pirState == HIGH)
{
Serial.println("Motion ended!"); // print on output change
pirState = LOW;
}
}
}
Setelah kode di-upload ke Arduino, buka Serial Monitor di Arduino IDE. Gerakkan tangan kalian di depan sensor dan lihat apakah pesan “Motion Detected” muncul. Jika LED nyala dan speaker berbunyi, berarti semua berfungsi dengan baik!
Sampai disini, Kita sudah berhasil belajar bagaimana cara menghubungkan dan menggunakan sensor gerak PIR HC-SR501 dengan Arduino UNO. Dengan pengetahuan ini, kalian bisa mengembangkan proyek lainnya yang lebih seru, seperti sistem keamanan otomatis atau kontrol perangkat berdasarkan keberadaan orang dan sebagainya. Selamat bereksperimen!
Halo, Semuanya!, bertemui lagi dengan saya yang udah lama nggak nulis karena libur sekolah. Dalam dunia pertanian, pemantauan kelembaban tanah menjadi sangat penting untuk meningkatkan menjaga kesehatan tanaman dan efisiensi penggunaan air . Disini kita kan menggunakan sensor kelembaban tanah kapasitif untuk melakukan pemantauan kondisi tanah tempat tanaman kita. Dalam postingan ini, kita akan membahas bagaimana cara membuat antarmuka sensor kelembaban tanah kapasitif menggunakan Arduino UNO. Project ini nantinya bisa dikembangkan sedemikain rupa untuk mendukung sistem pertanian yang modern. Yuk, kita mulai!
Sensor Kelembaban Tanah Kapasitif
Sensor kelembaban tanah kapasitif adalah alat yang digunakan untuk mengukur kadar kelembaban tanah dengan cara mengukur perubahan kapasitansi listrik disekitar tanah. Sensor ini lebih akurat dan tahan lama dibandingkan dengan sensor kelembaban resistif (yang memiliki 2 probe), sehingga sangat cocok untuk penggunaan jangka panjang.
Untuk dapat mengendalikan input dari sensor ini, kita akan menggunakan Arduino sebagai otaknya. Untuk itu kita perlu mempersiapkan alat, bahan, gambar rangkaian dan contoh codingnya.
Alat dan Bahan
Alat/Bahan
Kebutuhan
Arduino UNO
1 Buah
Sensor Kelembaban Tanah Kapasitif
1 buah
Layar LCD
1 buah
Breadboard
1 buah
kabel jumper / kabel biasa
secukupnya
Gambar Rangkaian
Instalasi Library LCD 16×2 I2C
untuk bisa mengendalikan sensor LCD 16×2 dengan mudah, kita perlu menginstal library untuk kedua modul tersebut dengan mengikuti langkah-langkah berikut ini:
Bukalah aplikasi Arduino IDE, lalu buka library manager yang terdapat disebelah kiri layar
Library yang akan kita instal adalah library LiquidCrystal_I2C. Gunakan kotak pencarian untuk mempermudah pencarian library yang dimaksud. Lewati langkah ini jika library sudah pernah diinstal sebelumnya.
Setelah library berhasil terinstal, maka kita bisa lanjut ke proses penulisan code program. yukk lanjut…
Coding
/* Gantilah nilai-nilai variabel dibawah ini sesuai dengan hasil pemantauan mu */
#define nilaiBasah 277 // nilai maksimal, kita anggap ini sebagai kondisi basah
#define nilaiKering 380 // nilai minimal, kita anggap sebagai kering
// pin yang terhubung ke sensor
#define sensorPin A0
//library LCD
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup() {
Serial.begin(9600);
lcd.init();
lcd.backlight();
lcd.setCursor(2, 0);
lcd.print("Please Wait");
//tunggu 10 detik sebelum sensor aktif
lcd.setCursor(0, 1);
for (int a = 10; a >= 0; a--) {
Serial.println(a);
lcd.print(">");
delay(1000);
}
lcd.clear();
Serial.print("Sensor Aktif");
lcd.setCursor(2, 0);
lcd.print("Sensor Aktif");
delay(1000);
lcd.clear();
}
void loop() {
// pembacaan input dari sensor
int lembab = analogRead(sensorPin);
//menampilkan hasil pembacaan
Serial.print("Analog output: ");
Serial.println(lembab);
// penentuan kondisi tanah
if (lembab < nilaiBasah) {
Serial.println("Status: tanah terlalu basah");
lcd.setCursor(0, 0);
lcd.print("Kondisi Tanah");
lcd.setCursor(0, 1);
lcd.print("terlalu Basah");
} else if (lembab >= nilaiBasah && lembab < nilaiKering) {
Serial.println("Status: sempurna");
lcd.setCursor(0, 0);
lcd.print("Kondisi Tanah");
lcd.setCursor(0, 1);
lcd.print("sempurna");
} else {
Serial.println("Status: Tanah terlalu kering, perlu disiram");
lcd.setCursor(0, 0);
lcd.print("Kondisi Tanah");
lcd.setCursor(0, 1);
lcd.print("terlalu Kering");
}
Serial.println();
delay(1000);
lcd.clear();
}
Upload kode diatas, cek dahulu pastikan tidak ada yang error. Setelah upload berhasil, kita bisa langsung uji coba dengan menggunakan sampel tanah basah dan kering.
Assalamualaikum, bertemu lagi di web saya yang sederhana ini. Kali ini saya akan berbagi cara membuat robot line follower dengan menggunakan Arduino, 5 buah sensor infra merah dan motor shield L293D. Robot ini akan berjalan mengikuti jalur berwarna hitam diatas lantai yang berwarna terang. OK, tanpa panjang lebar lagi kita sediakan bahan-bahannya:
ikutilah gambar rangkaian dibawah ini. Arduino tidak terlihat didalam gambar karena motorshield akan digabung dengan Arduino dan semua wiring dihubungkan ke motor shield. Saya menggunakan Arduino Mega sebagai mikrokontroler, namun kalian tetap bisa menggunakan Arduino UNO seperti pada gambar.
supaya pin A0 sampai A5 dapat digunakan, pasangkan pin header di barisan lubang dekat posisi pin analog in pada motor shield. Pemasangan pin header ini juga mempermudah kita mengakses 5V dan GND.
Code
#include <AFMotor.h> //Adafruit Motor Shield Library.
#include <QTRSensors.h> //Pololu QTR Sensor Library versi 3.1.0.
AF_DCMotor motor1(2); //konektor M2
AF_DCMotor motor2(1); //konektor M1
#define KP 0.2 //naikkan nilainya perlahan-lahan untuk menambah keakuraran gerakan robot
#define KD 0.7
#define M1_minumum_speed 200 //motor 1 min speed, perlu diatur masing-masing terutama jika kecepatan putaran roda tidak sama
#define M2_minumum_speed 200 //motor 2 min speed
#define M1_maksimum_speed 255 //motor 1 max speed
#define M2_maksimum_speed 255 //motor 2 max speed
#define NUM_SENSORS 5 //jumlah sensor
#define TIMEOUT 2500
#define EMITTER_PIN 2
#define DEBUG 1
const int buttonPin1 = A0;
int buttonPushCounter = 0;
int buttonState = 0;
int lastButtonState = 0;
QTRSensorsRC qtrrc((unsigned char[]){ A5, A4, A3, A2, A1 }, NUM_SENSORS, TIMEOUT, EMITTER_PIN);
unsigned int sensorValues[NUM_SENSORS];
void setup() {
Serial.begin(9600);
pinMode(buttonPin1, INPUT_PULLUP);
delay(3000);
}
int lastError = 0;
int last_proportional = 0;
int integral = 0;
void loop() {
buttonState = digitalRead(buttonPin1);
if (buttonState != lastButtonState) {
if (buttonState == LOW) {
buttonPushCounter++;
buttonState++;
Serial.println(buttonPushCounter);
Serial.println(buttonState);
} else {
Serial.println("off");
}
delay(100);
}
lastButtonState = buttonState;
if (buttonPushCounter == 1) {
Serial.println("calibrating");
manual_calibration();
Serial.println("Stop");
motor1.run(RELEASE);
motor2.run(RELEASE);
motor1.setSpeed(0);
motor2.setSpeed(0);
buttonPushCounter = 2;
}
if (buttonPushCounter == 3) {
unsigned int sensors[5];
int position = qtrrc.readLine(sensors);
int error = position - 2000;
Serial.print("error: ");
Serial.print(error);
Serial.print(" ");
int motorSpeed = KP * error + KD * (error - lastError); //1 x 2000 +
lastError = error;
int leftMotorSpeed = M1_minumum_speed + motorSpeed;
int rightMotorSpeed = M2_minumum_speed - motorSpeed;
Serial.print(motorSpeed);
Serial.print(", ");
Serial.print(leftMotorSpeed);
Serial.print(", ");
Serial.print(rightMotorSpeed);
set_motors(leftMotorSpeed, rightMotorSpeed);
}
if (buttonPushCounter == 4) {
Serial.println("Stop");
motor1.run(RELEASE);
motor2.run(RELEASE);
motor1.setSpeed(0);
motor2.setSpeed(0);
buttonPushCounter = 2;
}
}
void set_motors(int motor1speed, int motor2speed) {
if (motor1speed > M1_maksimum_speed) motor1speed = M1_maksimum_speed;
if (motor2speed > M2_maksimum_speed) motor2speed = M2_maksimum_speed;
if (motor1speed < 0) motor1speed = 0;
if (motor2speed < 0) motor2speed = 0;
motor1.setSpeed(motor1speed);
Serial.print("speed kiri: ");
Serial.print(motor1speed);
motor2.setSpeed(motor2speed);
Serial.print(" | speed kanan: ");
Serial.println(motor2speed);
motor1.run(FORWARD);
motor2.run(FORWARD);
}
void manual_calibration() {
int i;
for (i = 0; i < 250; i++) {
if (i <= 25 || i >= 75) {
motor1.run(FORWARD);
motor2.run(BACKWARD);
motor1.setSpeed(100);
motor2.setSpeed(100);
} else {
motor1.run(BACKWARD);
motor2.run(FORWARD);
motor1.setSpeed(100);
motor2.setSpeed(100);
}
qtrrc.calibrate(QTR_EMITTERS_ON);
delay(20);
}
if (DEBUG) {
Serial.begin(9600);
for (int i = 0; i < NUM_SENSORS; i++) {
Serial.print(qtrrc.calibratedMinimumOn[i]);
Serial.print(' ');
}
Serial.println();
for (int i = 0; i < NUM_SENSORS; i++) {
Serial.print(qtrrc.calibratedMaximumOn[i]);
Serial.print(' ');
}
}
}
Foto-Foto
untuk memeprmudah proses perakitan, saya lampirkan foto-foto robot dari segala sisi. Di sini saya menggunakan Arduino Mega sebagai mikrokontroler
Rumah BateraiSensor – Tampak bawahheader pin analog dan powerSensor – Tampak AtasKananAtasButtonKiriheader pin analog dan power
Selanjutnya, kita coba menjalankan robot di jalur garis yang terbuat dari isolasi listrik atau lakban hitam. Selamat mencoba
Assalamualaikum, Semoga kalian semua selalu dalam keadaan sehat wal afiat.
Lewat tulisan kali ini, saya akan memberikan tutorial singkat tentang cara membuat Jam Digital dengan menggunakan LED Matrix dan RTC. Untuk sistem kendalinya, saya akan menggunakan Arduino Nano. Jika kamu tidak memiliki Arduino Nano, kamu tetap bisa menggunakan Arduino jenis lainnya, misalnya Arduino Uno.
Proses visual dari tutorial ini bisa kamu lihat di channel youtube saya dibawah ini:
Bahan-bahan
Sebelum kita mulai proses perakitan, tentu saja kita harus mempersiapkan bahan-bahan untuk proyek kita kali ini.
Bahan
Kebutuhan
Arduino Nano
1 buah
RTC DS1307
1 buah
LED Matrix Max 7219 4 matrix
1 buah
tactile button
2 buah
Photo Resistor /LDR
1 buah
Resistor 10 K ohm
1 buah
Kabel
secukupnya
Gambar Rangkaian
Library
Sebelum kita memasukkan program / coding jam digitalnya ke Arduino nano, kita harus menginstal beberapa library terlebih dahulu. library-library yang dibutuhkan untuk alat yang kita buat sekarang ini bisa kamu download lewat link di bawah ini:
Setelah semua library berhasil di download, buka Arduino IDE lalu ke menu sketch >> include library >> add .ZIP library lalu pilih file library yang telah kita download sebelumnya.
Coding
Setelah semua library terinstal dengan baik, kita sudah bisa menuliskan program untuk jam digital ini di Arduino IDE. Coding nya bisa kamu lihat dibawah ini
//include libraries:
#include "LedControl.h" // For assigning LED's
#include <fontDigiClock.h> // Font library
#include <Wire.h> // DS1307 clock
#include "RTClib.h" // DS1307 clock, works also with DS3231 clock
#include <Button.h> // Button library by Alexander Brevig
#include <OneWire.h> // This library allows you to communicate with I2C
//define constants
#define NUM_DISPLAY_MODES 5 // Number of clock-modes (counting zero as the first mode)
#define NUM_SETTINGS_MODES 3 // Number of settings modes = 3 (conting zero as the first mode)
#define SLIDE_DELAY 55 // The time in milliseconds for the slide effect per character in slide mode. Make this higher for a slower effect
#define cls clear_display // Clear display
#define LIGHT A0 // Photoresistor (LDR) for steering brightness
#define ONE_WIRE_BUS 4 // Data wire is plugged into pin 4 on the Arduino
OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
// Setup LED Matrix
// pin 12 is connected to the DataIn (DIN) on the display
// pin 11 is connected to the CLK on the display
// pin 10 is connected to LOAD (CS) on the display
//sets the 3 pins as 12, 11 & 10 and then sets 4 displays (max is 8 displays)
LedControl lc = LedControl(12, 11, 10, 4);
//global variables
bool debug = true; // For debugging only, starts serial output (true/false)
bool show_intro = true; // Show intro at startup ? (true/false)
byte intensity = 0; // Startup intensity/brightness (0-15)
bool ampm = false; // Define 12 or 24 hour time. false = 24 hour. true = 12 hour
bool show_date = true; // Show date? - Display date approx. every 2 minutes (default = true)
bool circle = true; // Define circle mode - changes the clock-mode approx. every 2 minutes. Default = true (on)
byte clock_mode = 1; // Default clock mode.
// clock_mode 0 = basic mode
// clock_mode 1 = small mode
// clock_mode 2 = slide mode
// clock_mode 3 = smallslide mode
// clock_mode 4 = word clock
// clock_mode 5 = shift mode
// clock_mode 6 = setup menu
////________________________________________________________________________________________
//Please don't change the following variables:
byte old_mode = clock_mode; // Stores the previous clock mode, so if we go to date or whatever, we know what mode to go back.
short DN; // Returns the number of day in the year
short WN; // Returns the number of the week in the year
bool date_state = true; // Holds state of displaying date
int devices, dev; // Number of LED Matrix-Displays (dev = devices-1)
int rtc[7]; // Array that holds complete real time clock output
char tempi[4]; // Holds temperature-chars for displaying temp
char dig[7]; // Holds time-chars for shift-mode
char shiftChar[8]; // Holds chars to display in shift-mode
////________________________________________________________________________________________
//day array (The DS1307/DS3231 outputs 1-7 values for day of week)
char days[7][4] = {
"Ming", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"
};
char daysfull[7][9] = {
"Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"
};
char suffix[1] = {'.'}; //date suffix "." , used in slide, basic and jumble modes - e.g. date = 25.
//suffix in German is always "."
RTC_DS1307 ds1307; // Create RTC object - works also with DS3231
Button buttonA = Button(2, BUTTON_PULLUP); // Setup button A (using button library)
Button buttonB = Button(3, BUTTON_PULLUP); // Setup button B (using button library)
////////////////////////////////////////////////////////////////////////////////////////
void setup() {
digitalWrite(2, HIGH); // turn on pullup resistor for button on pin 2
digitalWrite(3, HIGH); // turn on pullup resistor for button on pin 3
pinMode(LIGHT, INPUT); // LDR for brightness
if(debug){
Serial.begin(9600); //start serial
Serial.println("Debugging activated ... ");
}
//initialize the 4 matrix panels
//we have already set the number of devices when we created the LedControl
devices = lc.getDeviceCount();
dev = devices-1;
//we have to init all devices in a loop
for (int address = 0; address < devices; address++) {
/*The MAX72XX is in power-saving mode on startup*/
lc.shutdown(address, false);
/* Set the brightness to a medium values */
lc.setIntensity(address, intensity);
/* and clear the display */
lc.clearDisplay(address);
}
//Setup DS1307/DS3231 RTC
#ifdef AVR
Wire.begin(); // start I2C communication
#else
Wire1.begin(); // Shield I2C pins connect to alt I2C bus on Arduino
#endif
ds1307.begin(); //start RTC Clock - works also with DS3231
/* if (! ds1307.isrunning()) {
Serial.println("RTC is NOT running!");
ds1307.adjust(DateTime(__DATE__, __TIME__)); // sets the RTC to the date & time this sketch was compiled
}
*/
//Show intro ?
if(show_intro){ intro(); }
wipeBottom();
// Show state of displaying date. toggleDateState() must! run once at startup, otherwise it shows opposite information.
toggleDateState();
} // end of setup
////////////////////////////////////////////////////////////////////////////////////////
void loop() {
//run the clock with whatever mode is set by clock_mode - the default is set at top of code.
switch (clock_mode){
case 0:
basic();
break;
case 1:
small();
break;
case 2:
slide();
break;
case 3:
smallslide();
break;
case 4:
word_clock();
break;
case 5:
shift();
break;
case 6:
setup_menu();
break;
}
} // end of loop
////////////////////////////////////////////////////////////////////////////////////////
// plot: plot a dot at positon xy with val 0/1
void plot (byte x, byte y, byte val) {
y=7-y;
//select which matrix depending on the x coord
byte address;
if (x >= 0 && x <= 7) {
address = 3;
}
if (x >= 8 && x <= 15) {
address = 2;
x = x - 8;
}
if (x >= 16 && x <= 23) {
address = 1;
x = x - 16;
}
if (x >= 24 && x <= 31) {
address = 0;
x = x - 24;
}
if (val == 1) {
lc.setLed(address, 7-y, x, true);
} else {
lc.setLed(address, 7-y, x, false);
}
}
////////////////////////////////////////////////////////////////////////////////////////
//clear screen
void clear_display() {
for (byte address = 0; address < 4; address++) {
lc.clearDisplay(address);
}
}
////////////////////////////////////////////////////////////////////////////////////////
// setBright: set the brightness to a value between 0 and 15 (= 16 steps, in dependence of LDR)
int setBright(){
// map LDR-values from 0 to 15 and set the brightness of devices
int brightness = map(analogRead(LIGHT), 0, 1023, 0, 15);
//we have to init all devices in a loop
for (int address = 0; address < devices; address++) {
lc.setIntensity(address, brightness);
}
return brightness;
}
////////////////////////////////////////////////////////////////////////////////////////
// fade_high: fade intensity from 0 to brightness (in dependence of LDR)
void fade_high() {
// map LDR-values from 0 to 15
int brightness = map(analogRead(LIGHT), 0, 1023, 0, 15);
//fade from intensity 0 to brightness and set the brightness of devices
for (byte f=0; f<=brightness; f++) {
for (byte address = 0; address < 4; address++) {
lc.setIntensity(address, f);
}
delay(120); //change this to alter fade-up speed
}
return;
}
////////////////////////////////////////////////////////////////////////////////////////
// fade_low: fade intensity from brightness (in dependence of LDR) to 0
void fade_low() {
// map LDR-values from 0 to 15
int brightness = map(analogRead(LIGHT), 0, 1023, 0, 15);
//fade from brightness to 1 and set the brightness of devices
for (byte f=brightness; f>0; f--) {
for (byte address = 0; address < 4; address++) {
lc.setIntensity(address, f);
}
delay(120); //change this to alter fade-low speed
}
for (byte address = 0; address < 4; address++) {
lc.setIntensity(address, 0); // set intensity to lowest level
}
return;
}
////////////////////////////////////////////////////////////////////////////////////////
//intro: show intro at startup
void intro() {
for (byte address = 0; address < 4; address++) {
lc.setIntensity(address, 3);
}
for(int i=0; i<2; i++){
wipeBottom();
wipeTop();
}
wipeOutside();
char ver_a[9] = " Helmy";
char ver_b[9] = " Aswan";
char ver_c[9] = " Daniel";
for (byte address = 0; address < 4; address++) {
lc.setIntensity(address, 0);
}
byte i = 0;
while (ver_a[i]) {
delay(80);
puttinychar((i * 4), 1, ver_a[i]);
i++;
}
fade_high();
delay(200);
fade_low();
delay(500);
wipeOutside();
i = 0;
while (ver_b[i]) {
delay(80);
puttinychar((i * 4), 1, ver_b[i]);
i++;
}
fade_high();
delay(200);
fade_low();
delay(500);
wipeMiddle();
i = 0;
while (ver_c[i]) {
delay(80);
puttinychar((i * 4), 1, ver_c[i]);
i++;
}
fade_high();
delay(200);
fade_low();
delay(500);
wipeOutside();
} // end of intro
////////////////////////////////////////////////////////////////////////////////////////
// puttinychar:
// Copy a 3x5 character glyph from the myfont data structure to display memory, with its upper left at the given coordinate
// This is unoptimized and simply uses plot() to draw each dot.
void puttinychar(byte x, byte y, char c){
byte dots;
if (c >= 'A' && c <= 'Z' || (c >= 'a' && c <= 'z') ) {
c &= 0x1F; // A-Z maps to 1-26
}
else if (c >= '0' && c <= '9') {
c = (c - '0') + 32;
}
else if (c == ' ') {
c = 0; // space
}
else if (c == '.') {
c = 27; // full stop
}
else if (c == ':') {
c = 28; // colon
}
else if (c == '\'') {
c = 29; // single quote mark
}
else if (c == '!') {
c = 30; // exclamation mark
}
else if (c == '?') {
c = 31; // question mark
}
else if (c == '-') {
c = 42; // hyphen
}
else if (c == '#') {
c = 43; // degree-symbol
}
else if (c == '>') {
c = 44; // selector-arrow
}
else if (c == '~') {
c = 45; // Ü
}
else if (c == '*') {
c = 46; // Ö
}
for (byte col = 0; col < 3; col++) {
dots = pgm_read_byte_near(&mytinyfont[c][col]);
for (char row = 0; row < 5; row++) {
if (dots & (16 >> row))
plot(x + col, y + row, 1);
else
plot(x + col, y + row, 0);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////
//putnormalchar:
//Copy a 5x7 character glyph from the myfont data structure to display memory
void putnormalchar(byte x, byte y, char c){
byte dots;
if (c >= 'A' && c <= 'Z' ) {
c &= 0x1F; // A-Z maps to 1-26
}
else if (c >= 'a' && c <= 'z') {
c = (c - 'a') + 41; // A-Z maps to 41-67
}
else if (c >= '0' && c <= '9') {
c = (c - '0') + 31;
}
else if (c == ' ') {
c = 0; // space
}
else if (c == '.') {
c = 27; // full stop
}
else if (c == '\'') {
c = 28; // single quote mark
}
else if (c == ':') {
c = 29; // colon
}
else if (c == '>') {
c = 30; // clock_mode selector arrow
}
else if (c == '=') {
c = 79; // equal sign
}
else if (c >= -80 && c <= -67) {
c *= -1;
}
for (char col = 0; col < 5; col++) {
dots = pgm_read_byte_near(&myfont[c][col]);
for (char row = 0; row < 7; row++) {
//check coords are on screen before trying to plot
//if ((x >= 0) && (x <= 31) && (y >= 0) && (y <= 7)){
if (dots & (64 >> row)) { // only 7 rows.
plot(x + col, y + row, 1);
} else {
plot(x + col, y + row, 0);
}
//}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////
// small(=mode 1): show the time in small 3x5 characters with seconds-dots at bottom-line
void small() {
char textchar[8]; // the 16 characters on the display
byte mins = 100; //mins
byte secs = rtc[0]; //seconds
byte old_secs = secs; //holds old seconds value - from last time seconds were updated o display - used to check if seconds have changed
cls();
//run clock main loop as long as run_mode returns true
while (run_mode()) {
get_time();
secs = rtc[0];
//check for button presses
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
// when in circle mode and minute=even and second=14, switch to word_clock (mode 4)
if(circle){
if(rtc[1] % 2 == 0 && rtc[0]==14){
wipeInside();
clock_mode =4; // switch to wordclock mode
return;
}
}
//if secs changed then update them on the display
if (secs != old_secs) {
bottomleds(secs); // plot seconds-dots at bottomline
// display date, when second=40 and date_state = true
if(rtc[0]==40 && date_state){
display_date();
return;
}
char buffer[3];
itoa(secs, buffer, 10);
//fix - as otherwise if num has leading zero, e.g. "03" secs, itoa coverts this to chars with space "3 ".
if (secs < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
puttinychar( 20, 1, ':'); //seconds colon
puttinychar( 24, 1, buffer[0]); //seconds
puttinychar( 28, 1, buffer[1]); //seconds
old_secs = secs;
}
//if minute changes change time
if (mins != rtc[1]) {
//reset these for comparison next time
mins = rtc[1];
byte hours = rtc[2];
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
//byte dow = rtc[3]; // the DS1307/DS3231 outputs 0 - 6 where 0 = Sunday0 - 6 where 0 = Sunday.
//byte date = rtc[4];
//set characters
char buffer[3];
itoa(hours, buffer, 10);
//fix - as otherwise if num has leading zero, e.g. "03" hours, itoa coverts this to chars with space "3 ".
if (hours < 10) {
buffer[1] = buffer[0];
//if we are in 12 hour mode blank the leading zero.
if (ampm) {
buffer[0] = ' ';
}
else {
buffer[0] = '0';
}
}
//set hours chars
textchar[0] = buffer[0];
textchar[1] = buffer[1];
textchar[2] = ':';
itoa (mins, buffer, 10);
if (mins < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
//set mins characters
textchar[3] = buffer[0];
textchar[4] = buffer[1];
//do seconds
textchar[5] = ':';
buffer[3];
secs = rtc[0];
itoa(secs, buffer, 10);
//fix - as otherwise if num has leading zero, e.g. "03" secs, itoa coverts this to chars with space "3 ".
if (secs < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
//set seconds
textchar[6] = buffer[0];
textchar[7] = buffer[1];
byte x = 0;
byte y = 0;
//print each char
for (byte x = 0; x < 6 ; x++) {
puttinychar( x * 4, 1, textchar[x]);
}
}
delay(50);
} // end of while run_mode
}
////////////////////////////////////////////////////////////////////////////////////////
// basic(= mode 0): simple mode shows the time in 5x7 characters
void basic(){
cls();
char buffer[3]; //for int to char conversion to turn rtc values into chars we can print on screen
byte offset = 0; //used to offset the x postition of the digits and centre the display when we are in 12 hour mode and the clock shows only 3 digits. e.g. 3:21
byte x, y; //used to draw a clear box over the left hand "1" of the display when we roll from 12:59 -> 1:00am in 12 hour mode.
//do 12/24 hour conversion if ampm set to 1
byte hours = rtc[2];
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
//do offset conversion
if (ampm && hours < 10) {
offset = 2;
}
else{
offset = 0;
}
//set the next minute we show the date at
//set_next_date();
// initially set mins to value 100 - so it wll never equal rtc[1] on the first loop of the clock, meaning we draw the clock display when we enter the function
byte secs = 100;
byte mins = 100;
int count = 0;
//run clock main loop as long as run_mode returns true
while (run_mode()) {
//get the time from the clock chip
get_time();
//check for button press
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
// display temp, when second=40 and minute=even and date_state=true
if(rtc[0]==40 && rtc[1] % 2 == 0 && date_state){
wipeBottom();
wipeTop();
return;
}
// display date, when second=40 and minute=odd and date_state = true
if(rtc[0]==40 && rtc[1] % 2 == 1 && date_state){
display_date();
return;
}
//draw the flashing colon on/off if the secs have changed.
if (secs != rtc[0]) {
secs = rtc[0]; //update secs with new value
//Blink "::"
if(secs % 2 == 0){
plot(14-offset, 4, 1);
plot(14-offset, 2, 0);
plot(16-offset, 4, 0);
plot(16-offset, 2, 1);
}
else {
plot(14-offset, 4, 0);
plot(14-offset, 2, 1);
plot(16-offset, 4, 1);
plot(16-offset, 2, 0);
}
}
//redraw the display if button pressed or if mins != rtc[1]
if (mins != rtc[1]) {
//update mins and hours with the new values
mins = rtc[1];
hours = rtc[2];
//adjust hours of ampm set to 12 hour mode
if (hours > 12) { hours = hours - ampm * 12; }
if (hours < 1) { hours = hours + ampm * 12; }
itoa(hours, buffer, 10);
//if hours < 10 the num e.g. "3" hours, itoa coverts this to chars with space "3 " which we dont want
if (hours < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
//print hours
//if we in 12 hour mode and hours < 10, then don't print the leading zero, and set the offset so we centre the display with 3 digits.
if (ampm && hours < 10) {
offset = 2;
//if the time is 1:00am clear the entire display as the offset changes at this time and we need to blank out the old 12:59
if ((hours == 1 && mins == 0) ) {
cls();
}
}
else {
//else no offset and print hours tens digit
offset = 0;
//if the time is 10:00am clear the entire display as the offset changes at this time and we need to blank out the old 9:59
if (hours == 10 && mins == 0) {
cls();
}
putnormalchar(1, 0, buffer[0]);
}
//print hours ones digit
putnormalchar(7 - offset, 0, buffer[1]);
//print mins
//add leading zero if mins < 10
itoa (mins, buffer, 10);
if (mins < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
//print mins tens and mins ones digits
putnormalchar(19 - offset, 0, buffer[0]);
putnormalchar(25 - offset, 0, buffer[1]);
} // end of if (mins != rtc[1]
} // end of while run_mode
}
////////////////////////////////////////////////////////////////////////////////////////
//Big-Slide mode (=mode 2): like basic-mode, but with sliding digits top-down
void slide() {
byte digits_old[4] = {99, 99, 99, 99}; //old values we store time in. Set to somthing that will never match the time initially so all digits get drawn wnen the mode starts
byte digits_new[4]; //new digits time will slide to reveal
byte digits_x_pos[4] = {25, 19, 7, 1}; //x pos for which to draw each digit at
char old_char[2]; //used when we use itoa to transpose the current digit (type byte) into a char to pass to the animation function
char new_char[2]; //used when we use itoa to transpose the new digit (type byte) into a char to pass to the animation function
//old_chars - stores the 5 day and date suffix chars on the display. e.g. "mon" and "st". We feed these into the slide animation as the current char when these chars are updated.
//We sent them as A initially, which are used when the clocl enters the mode and no last chars are stored.
//char old_chars[6] = "AAAAA";
cls();
// plot the clock colon on the display
// putnormalchar( 13, 0, ':');
byte old_secs = rtc[0]; //store seconds in old_secs. We compare secs and old secs. WHen they are different we redraw the display
//run clock main loop as long as run_mode returns true
while (run_mode()) {
get_time();
byte secs =rtc[0];
// display date, when second=40 and date_state = true
if(rtc[0]==40 && date_state){
display_date();
return;
}
// when in circle mode and minute=even and second=15, switch to shift mode (mode 5)
if(circle){
if(rtc[1] % 2 == 0 && rtc[0]==15){
wipeMiddle();
wipeTop();
clock_mode =5; // switch to shift mode
return;
}
}
//check for button press
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
//if secs have changed then update the display
if (rtc[0] != old_secs) {
//Blink "::"
if(old_secs % 2 == 0){
plot(14, 4, 1);
plot(14, 2, 1);
plot(16, 4, 0);
plot(16, 2, 0);
}
else {
plot(16, 4, 1);
plot(16, 2, 1);
plot(14, 4, 0);
plot(14, 2, 0);
}
old_secs = rtc[0];
//do 12/24 hour conversion if ampm set to 1
byte hours = rtc[2];
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
//split all date and time into individual digits - stick in digits_new array
//rtc[0] = secs //array pos and digit stored
//digits_new[0] = (rtc[0]%10); //0 - secs ones
//digits_new[1] = ((rtc[0]/10)%10); //1 - secs tens
//rtc[1] = mins
digits_new[0] = (rtc[1] % 10); //2 - mins ones
digits_new[1] = ((rtc[1] / 10) % 10); //3 - mins tens
//rtc[2] = hours
digits_new[2] = (hours % 10); //4 - hour ones
digits_new[3] = ((hours / 10) % 10); //5 - hour tens
//rtc[4] = date
//digits_new[6] = (rtc[4]%10); //6 - date ones
//digits_new[7] = ((rtc[4]/10)%10); //7 - date tens
//draw initial screen of all chars. After this we just draw the changes.
//compare digits 0 to 3 (mins and hours)
for (byte i = 0; i <= 3; i++) {
//see if digit has changed...
if (digits_old[i] != digits_new[i]) {
//run 9 step animation sequence for each in turn
for (byte seq = 0; seq <= 8 ; seq++) {
//convert digit to string
itoa(digits_old[i], old_char, 10);
itoa(digits_new[i], new_char, 10);
//if set to 12 hour mode and we're on digit 2 (hours tens mode) then check to see if this is a zero. If it is, blank it instead so we get 2.00pm not 02.00pm
if (ampm && i == 3) {
if (digits_new[3] == 0) {
new_char[0] = ' ';
}
if (digits_old[3] == 0) {
old_char[0] = ' ';
}
}
//draw the animation frame for each digit
slideanim(digits_x_pos[i], 0, seq, old_char[0], new_char[0]);
delay(SLIDE_DELAY);
}
}
}
//save digita array tol old for comparison next loop
for (byte i = 0; i <= 3; i++) {
digits_old[i] = digits_new[i];
}
}// end of secs/oldsecs
}// end of while run_mode
}
////////////////////////////////////////////////////////////////////////////////////////
//called by slide
//this draws the animation of one char sliding on and the other sliding off. There are 8 steps in the animation, we call the function to draw one of the steps from 0-7
//inputs are are char x and y, animation frame sequence (0-7) and the current and new chars being drawn.
void slideanim(byte x, byte y, byte sequence, char current_c, char new_c) {
// To slide one char off and another on we need 9 steps or frames in sequence...
// seq# 0123456 <-rows of the display
// | |||||||
// seq0 0123456 START - all rows of the display 0-6 show the current characters rows 0-6
// seq1 012345 current char moves down one row on the display. We only see it's rows 0-5. There are at display positions 1-6 There is a blank row inserted at the top
// seq2 6 01234 current char moves down 2 rows. we now only see rows 0-4 at display rows 2-6 on the display. Row 1 of the display is blank. Row 0 shows row 6 of the new char
// seq3 56 0123
// seq4 456 012 half old / half new char
// seq5 3456 01
// seq6 23456 0
// seq7 123456
// seq8 0123456 END - all rows show the new char
//from above we can see...
//currentchar runs 0-6 then 0-5 then 0-4 all the way to 0. starting Y position increases by 1 row each time.
//new char runs 6 then 5-6 then 4-6 then 3-6. starting Y position increases by 1 row each time.
//if sequence number is below 7, we need to draw the current char
if (sequence < 7) {
byte dots;
if (current_c >= 'A' && current_c <= 'Z' ) {
current_c &= 0x1F; // A-Z maps to 1-26
}
else if (current_c >= 'a' && current_c <= 'z') {
current_c = (current_c - 'a') + 41; // a-z maps to 41-66
}
else if (current_c >= '0' && current_c <= '9') {
current_c = (current_c - '0') + 31;
}
else if (current_c == ' ') {
current_c = 0; // space
}
else if (current_c == '.') {
current_c = 27; // full stop
}
else if (current_c == '\'') {
current_c = 28; // single quote mark
}
else if (current_c == ':') {
current_c = 29; //colon
}
else if (current_c == '>') {
current_c = 30; // clock_mode selector arrow
}
byte curr_char_row_max = 7 - sequence; //the maximum number of rows to draw is 6 - sequence number
byte start_y = sequence; //y position to start at - is same as sequence number. We inc this each loop
//plot each row up to row maximum (calculated from sequence number)
for (byte curr_char_row = 0; curr_char_row <= curr_char_row_max; curr_char_row++) {
for (byte col = 0; col < 5; col++) {
dots = pgm_read_byte_near(&myfont[current_c][col]);
if (dots & (64 >> curr_char_row))
plot(x + col, y + start_y, 1); //plot led on
else
plot(x + col, y + start_y, 0); //else plot led off
}
start_y++;//add one to y so we draw next row one down
}
}
//draw a blank line between the characters if sequence is between 1 and 7. If we don't do this we get the remnants of the current chars last position left on the display
if (sequence >= 1 && sequence <= 8) {
for (byte col = 0; col < 5; col++) {
plot(x + col, y + (sequence - 1), 0); //the y position to draw the line is equivalent to the sequence number - 1
}
}
//if sequence is above 2, we also need to start drawing the new char
if (sequence >= 2) {
//work out char
byte dots;
//if (new_c >= 'A' && new_c <= 'Z' || (new_c >= 'a' && new_c <= 'z') ) {
// new_c &= 0x1F; // A-Z maps to 1-26
//}
if (new_c >= 'A' && new_c <= 'Z' ) {
new_c &= 0x1F; // A-Z maps to 1-26
}
else if (new_c >= 'a' && new_c <= 'z') {
new_c = (new_c - 'a') + 41; // A-Z maps to 41-67
}
else if (new_c >= '0' && new_c <= '9') {
new_c = (new_c - '0') + 31;
}
else if (new_c == ' ') {
new_c = 0; // space
}
else if (new_c == '.') {
new_c = 27; // full stop
}
else if (new_c == '\'') {
new_c = 28; // single quote mark
}
else if (new_c == ':') {
new_c = 29; // clock_mode selector arrow
}
else if (new_c == '>') {
new_c = 30; // clock_mode selector arrow
}
byte newcharrowmin = 6 - (sequence - 2); //minimumm row num to draw for new char - this generates an output of 6 to 0 when fed sequence numbers 2-8. This is the minimum row to draw for the new char
byte start_y = 0; //y position to start at - is same as sequence number. we inc it each row
//plot each row up from row minimum (calculated by sequence number) up to 6
for (byte newcharrow = newcharrowmin; newcharrow <= 6; newcharrow++) {
for (byte col = 0; col < 5; col++) {
dots = pgm_read_byte_near(&myfont[new_c][col]);
if (dots & (64 >> newcharrow))
plot(x + col, y + start_y, 1); //plot led on
else
plot(x + col, y + start_y, 0); //else plot led off
}
start_y++;//add one to y so we draw next row one down
}
}
}
////////////////////////////////////////////////////////////////////////////////////////
//Small-Slide-mode (mode 3): like small-mode, but with sliding digits top-down
void smallslide() {
byte digits_old[6] = {99, 99, 99, 99, 99, 99}; //old values we store time in. Set to somthing that will never match the time initially so all digits get drawn wnen the mode starts
byte digits_new[6]; //new digits time will slide to reveal
byte digits_x_pos[6] = {29, 25, 17, 13, 5, 1}; //x pos for which to draw each digit at
char old_char[2]; //used when we use itoa to transpose the current digit (type byte) into a char to pass to the animation function
char new_char[2]; //used when we use itoa to transpose the new digit (type byte) into a char to pass to the animation function
byte old_secs = rtc[0]; //store seconds in old_secs. We compare secs and old secs. WHen they are different we redraw the display
cls();
//run clock main loop as long as run_mode returns true
while (run_mode()) {
get_time();
// when in circle mode and minute=odd and second=12, switch to slide mode (mode 2)
if(circle){
if(rtc[1] % 2 == 1 && rtc[0]==12){
wipeInside();
wipeTop();
clock_mode =2; // switch to slide mode
return;
}
}
//if secs have changed then update the display
if (rtc[0] != old_secs) {
old_secs = rtc[0];
//do 12/24 hour conversion if ampm set to 1
byte hours = rtc[2];
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
//split all date and time into individual digits - stick in digits_new array
//rtc[0] = secs //array pos and digit stored
digits_new[0] = (rtc[0]%10); //0 - secs ones
digits_new[1] = ((rtc[0]/10)%10); //1 - secs tens
//rtc[1] = mins
digits_new[2] = (rtc[1] % 10); //2 - mins ones
digits_new[3] = ((rtc[1] / 10) % 10); //3 - mins tens
//rtc[2] = hours
digits_new[4] = (hours % 10); //4 - hour ones
digits_new[5] = ((hours / 10) % 10); //5 - hour tens
//rtc[4] = date
//digits_new[6] = (rtc[4]%10); //6 - date ones
//digits_new[7] = ((rtc[4]/10)%10); //7 - date tens
//draw initial screen of all chars. After this we just draw the changes.
//compare digits 0 to 5 (secs, mins and hours)
for (byte i = 0; i <= 5; i++) {
//see if digit has changed...
if (digits_old[i] != digits_new[i]) {
//run 9 step animation sequence for each in turn
for (byte seq = 0; seq <= 8 ; seq++) {
//convert digit to string
itoa(digits_old[i], old_char, 10);
itoa(digits_new[i], new_char, 10);
//if set to 12 hour mode and we're on digit 5 (hours tens mode) then check to see if this is a zero. If it is, blank it instead so we get 2.00pm not 02.00pm
if (ampm && i == 5) {
if (digits_new[5] == 0) {
new_char[0] = ' ';
}
if (digits_old[5] == 0) {
old_char[0] = ' ';
}
}
//draw the animation frame for each digit
slideTyniAnim(digits_x_pos[i], 0, seq, old_char[0], new_char[0]);
//hold display but check for button presses
int counter = 35; // = slide animation-time
while (counter > 0){
//check for button press
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
delay(1);
counter--;
}
}
}
}
// plot the clock colon on the display
putnormalchar( 8, 0, ':');
putnormalchar( 20, 0, ':');
//save digita array tol old for comparison next loop
for (byte i = 0; i <= 5; i++) {
digits_old[i] = digits_new[i];
}
}//secs %2
}//end of while run_mode
}
////////////////////////////////////////////////////////////////////////////////////////
//called by smallslide_mode
//this draws the animation of one char sliding on and the other sliding off. There are 8 steps in the animation, we call the function to draw one of the steps from 0-7
//inputs are are char x and y, animation frame sequence (0-7) and the current and new chars being drawn.
void slideTyniAnim(byte x, byte y, byte sequence, char current_c, char new_c) {
// To slide one char off and another on we need 9 steps or frames in sequence...
// seq# 0123456 <-rows of the display
// | |||||||
// seq0 0123456 START - all rows of the display 0-6 show the current characters rows 0-6
// seq1 012345 current char moves down one row on the display. We only see it's rows 0-5. There are at display positions 1-6 There is a blank row inserted at the top
// seq2 6 01234 current char moves down 2 rows. we now only see rows 0-4 at display rows 2-6 on the display. Row 1 of the display is blank. Row 0 shows row 6 of the new char
// seq3 56 0123
// seq4 456 012 half old / half new char
// seq5 3456 01
// seq6 23456 0
// seq7 123456
// seq8 0123456 END - all rows show the new char
//from above we can see...
//currentchar runs 0-6 then 0-5 then 0-4 all the way to 0. starting Y position increases by 1 row each time.
//new char runs 6 then 5-6 then 4-6 then 3-6. starting Y position increases by 1 row each time.
//if sequence number is below 7, we need to draw the current char
if (sequence < 7) {
byte dots;
if (current_c >= 'A' && current_c <= 'Z' ) {
current_c &= 0x1F; // A-Z maps to 1-26
}
else if (current_c >= 'a' && current_c <= 'z') {
current_c = (current_c - 'a') + 41; // A-Z maps to 41-67
}
else if (current_c >= '0' && current_c <= '9') {
current_c = (current_c - '0') + 31;
}
else if (current_c == ' ') {
current_c = 0; // space
}
else if (current_c == '.') {
current_c = 27; // full stop
}
else if (current_c == '\'') {
current_c = 28; // single quote mark
}
else if (current_c == ':') {
current_c = 29; //colon
}
else if (current_c == '>') {
current_c = 30; // clock_mode selector arrow
}
// byte curr_char_row_max = 6 - sequence; //(6) the maximum number of rows to draw is 6 - sequence number
byte curr_char_row_max = 7 - sequence; //(6) the maximum number of rows to draw is 6 - sequence number
byte start_y = sequence; //y position to start at - is same as sequence number. We inc this each loop
//plot each row up to row maximum (calculated from sequence number)
for (byte curr_char_row = 0; curr_char_row <= curr_char_row_max; curr_char_row++) {
for (byte col = 0; col < 3; col++) {
dots = pgm_read_byte_near(&mytinyfont[current_c+1][col]);
if (dots & (64 >> curr_char_row))
plot(x + col, y + start_y, 1); //plot led on
else
plot(x + col, y + start_y, 0); //else plot led off
}
start_y++;//add one to y so we draw next row one down
}
}
//draw a blank line between the characters if sequence is between 1 and 7. If we don't do this we get the remnants of the current chars last position left on the display
if (sequence >= 1 && sequence <= 8) {
for (byte col = 0; col < 2; col++) {
plot(x + col, y + (sequence - 1), 0); //the y position to draw the line is equivalent to the sequence number - 1
}
}
//if sequence is above 2, we also need to start drawing the new char
if (sequence >= 2) {
//work out char
byte dots;
if (new_c >= 'A' && new_c <= 'Z' ) {
new_c &= 0x1F; // A-Z maps to 1-26
}
else if (new_c >= 'a' && new_c <= 'z') {
new_c &= 0x1F; // A-Z maps to 1-26
}
else if (new_c >= '0' && new_c <= '9') {
new_c = (new_c - '0') + 32;
}
else if (new_c == ' ') {
new_c = 0; // space
}
else if (new_c == '.') {
new_c = 27; // full stop
}
else if (new_c == ':') {
new_c = 28; // doppelpunkt
}
else if (new_c == '\'') {
new_c = 29; // clock_mode selector arrow
}
else if (new_c == '!') {
new_c = 30; // clock_mode selector arrow
}
byte newcharrowmin = 7 - (sequence - 2); //minimumm row num to draw for new char - this generates an output of 6 to 0 when fed sequence numbers 2-8. This is the minimum row to draw for the new char
byte start_y = 0; //y position to start at - is same as sequence number. we inc it each row
//plot each row up from row minimum (calculated by sequence number) up to 6
for (byte newcharrow = newcharrowmin; newcharrow <= 6; newcharrow++) {
for (byte col = 0; col < 3; col++) {
dots = pgm_read_byte_near(&mytinyfont[new_c][col]);
if (dots & (64 >> newcharrow))
plot(x + col, y + start_y, 1); //plot led on
else
plot(x + col, y + start_y, 0); //else plot led off
}
start_y++;//add one to y so we draw next row one down
}
}
}
////////////////////////////////////////////////////////////////////////////////////////
// word_clock (= mode4): show the time using words rather than numbers
void word_clock() {
//potentially 5 lines to display
char str_0[8];
char str_a[8];
char str_b[8];
char str_c[8];
char str_d[8];
char str_e[8];
//byte hours_y, mins_y; //hours and mins and positions for hours and mins lines
byte hours = rtc[2];
if (hours > 12) { hours = hours - ampm * 12; }
if (hours < 1) { hours = hours + ampm * 12; }
// get_time(); //get the time from the clock chip
//run clock main loop as long as run_mode returns true
while (run_mode()) {
get_time(); //get the time from the clock chip
// when in circle mode and minute=odd and second is between 14 and 30, switch to smallslide-mode (mode 3)
if(circle){
if(rtc[1] % 2 == 1 && (rtc[0] >= 14 && rtc[0] <=30 )){
clock_mode =3; // switch to smallslide-mode (mode 3)
return;
}
else{
cls();
//hold display but check for button presses
int counter = 20;
while (counter > 0){
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
delay(1);
counter--;
}
}
}
get_time();
// display date when second is between 35 and 45 and date_state = true
if((rtc[0] >= 35 && rtc[0] <= 45) && date_state){
display_date();
return;
}
else{
wipeOutside();
}
get_time();
byte mins = rtc[1]; //get mins
hours = rtc[2];
//make hours into 12 hour format
if (hours > 12) { hours = hours - 12; }
if (hours == 0) { hours = 12; }
byte len = 0;
int setengah=0;
if (mins >= 5 && mins <= 9) { strcpy (str_a, "Lima"); strcpy (str_b, "Lewat"); strcpy (str_c, ""); }
else if (mins >= 10 && mins <= 14){ strcpy (str_a, "sepuluh"); strcpy (str_b, "Lewat"); strcpy (str_c, ""); }
else if (mins >= 15 && mins <= 19){ strcpy (str_a, "Limabls"); strcpy (str_b, "Lewat"); strcpy (str_c, ""); }
else if (mins >= 20 && mins <= 24){ strcpy (str_a, "sepuluh"); strcpy (str_b, "Kurang"); strcpy (str_c, "setengah"); setengah = 1; }
else if (mins >= 25 && mins <= 29){ strcpy (str_a, "Lima"); strcpy (str_b, "Kurang"); strcpy (str_c, "setengah"); setengah = 1; }
else if (mins >= 30 && mins <= 34){ strcpy (str_a, ""); strcpy (str_b, ""); strcpy (str_c, "setengah"); setengah = 1; }
else if (mins >= 35 && mins <= 39){ strcpy (str_a, "Lima"); strcpy (str_b, "Lewat"); strcpy (str_c, "setengah"); setengah = 1; }
else if (mins >= 40 && mins <= 44){ strcpy (str_a, "sepuluh"); strcpy (str_b, "Lewat"); strcpy (str_c, "setengah"); setengah = 1; }
else if (mins >= 45 && mins <= 49){ strcpy (str_a, "limabls"); strcpy (str_b, "Kurang"); strcpy (str_c, ""); setengah = 1; }
else if (mins >= 50 && mins <= 54){ strcpy (str_a, "sepuluh"); strcpy (str_b, "Kurang"); strcpy (str_c, ""); setengah = 1; }
else if (mins >= 55 && mins <= 59){ strcpy (str_a, "Lima"); strcpy (str_b, "Kurang"); strcpy (str_c, ""); setengah = 1; }
int wordHour = hours + setengah;
if(wordHour > 12){ wordHour = 1;}
if ( wordHour == 1 ) {strcpy (str_d, "SATU");}
else if ( wordHour == 2 ) { strcpy (str_d, "DUA"); }
else if ( wordHour == 3 ) { strcpy (str_d, "TIGA"); }
else if ( wordHour == 4 ) { strcpy (str_d, "EMPAT"); }
else if ( wordHour == 5 ) { strcpy (str_d, "LIMA"); }
else if ( wordHour == 6 ) { strcpy (str_d, "ENAM"); }
else if ( wordHour == 7 ) { strcpy (str_d, "TUJUH"); }
else if ( wordHour == 8 ) { strcpy (str_d, "DELAPAN"); }
else if ( wordHour == 9 ) { strcpy (str_d, "SEMBILAN"); }
else if ( wordHour == 10) { strcpy (str_d, "SEPULUH"); }
else if ( wordHour == 11) { strcpy (str_d, "SEBELAS"); }
else if ( wordHour == 12) { strcpy (str_d, "DUABLAS"); }
if (mins <= 4){
strcpy (str_a, "");
strcpy (str_b, "");
strcpy (str_c, "");
strcpy (str_e, "TEPAT");
}
else{
strcpy (str_e, "");
}
//end working out time
//run in a loop
setBright(); // set brightness of devices
int delayChar = 60; // delay between displaying next char
String dstring(str_d);
String estring(str_e);
//print line_0 / this line is always shown
strcpy (str_0, "JAM");
len = 0;
while (str_0[len]) {
len++;
} //get length of message
byte offset_top = (31 - ((len - 1) * 4)) / 2; //
byte i = 0;
while (str_0[i]) {
puttinychar((i * 4) + offset_top, 1, str_0[i]);
delay(delayChar);
i++;
}
//hold display but check for button presses
int counter = 900;
while (counter > 0){
//check for button press
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
delay(1);
counter--;
}
cls();
// Check minutes-LEDS at bottom-line
if ((mins-(mins/5)*5)==1)
{plot(13,7,1);
plot(15,7,0);
plot(17,7,0);
plot(19,7,0);
}
else if((mins-(mins/5)*5)==2)
{plot(13,7,1);
plot(15,7,1);
plot(17,7,0);
plot(19,7,0);
}
else if((mins-(mins/5)*5)==3)
{plot(13,7,1);
plot(15,7,1);
plot(17,7,1);
plot(19,7,0);
}
else if((mins-(mins/5)*5)==4)
{plot(13,7,1);
plot(15,7,1);
plot(17,7,1);
plot(19,7,1);
}
else {plot(13,7,0);
plot(15,7,0);
plot(17,7,0);
plot(19,7,0);
}
//print line c, if not empty / "setengah"
char cl = str_c[0];
if(cl>1){
len = 0;
while (str_c[len]) {
len++;
} //get length of message
byte offset_top = (31 - ((len - 1) * 4)) / 2;
byte i = 0;
while (str_c[i]) {
puttinychar((i * 4) + offset_top, 1, str_c[i]);
delay(delayChar);
i++;
}
//hold display but check for button presses
counter = 900;
while (counter > 0){
//check for button press
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
delay(1);
counter--;
}
cls();
}
//print line d, if not empty / Hours
char dl = str_d[0];
if(dl>1){
len = 0;
while (str_d[len]) {
len++;
} //get length of message
byte offset_top = (31 - ((len - 1) * 4)) / 2;
byte i = 0;
while (str_d[i]) {
puttinychar((i * 4) + offset_top, 1, str_d[i]);
delay(delayChar);
i++;
}
//hold display but check for button presses
counter = 870;
while (counter > 0){
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
delay(1);
counter--;
}
if(estring.length() != 0 ){
cls();
}
}
cls();
//print line b, if not empty / "Lewat"/"Kurang"/""
char bl = str_b[0];
if(bl>1){
len = 0;
while (str_b[len]) {
len++;
} //get length of message
byte offset_top = (31 - ((len - 1) * 4)) / 2;
byte i = 0;
while (str_b[i]) {
puttinychar((i * 4) + offset_top, 1, str_b[i]);
delay(delayChar);
i++;
}
//hold display but check for button presses
counter = 900;
while (counter > 0){
//check for button press
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
delay(1);
counter--;
}
cls();
}
//print line a / 5minute-intervall
char al = str_a[0];
if(al>1){
len = 0;
while (str_a[len]) {
len++;
} //get length of message
byte offset_top = (31 - ((len - 1) * 4)) / 2; //
byte i = 0;
while (str_a[i]) {
puttinychar((i * 4) + offset_top, 1, str_a[i]);
delay(delayChar);
i++;
}
//hold display but check for button presses
counter = 900;
while (counter > 0){
//check for button press
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
delay(1);
counter--;
}
cls();
}
//print line e, if not empty / "Uhr/""
char el = str_e[0];
if(el>1){
len = 0;
while (str_e[len]) {
len++;
} //get length of message
byte offset_top = (31 - ((len - 1) * 4)) / 2;
byte i = 0;
while (str_e[i]) {
puttinychar((i * 4) + offset_top, 1, str_e[i]);
delay(delayChar);
i++;
}
//hold display but check for button presses
counter = 900;
while (counter > 0){
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
delay(1);
counter--;
}
if(estring.length() == 0 ){ cls(); }
}
//wipe out devices
wipeMiddle();
} // end of while run-mode
} //end of wordclock
////////////////////////////////////////////////////////////////////////////////////////
/// shift-mode (=mode5): shift time-chars from right to left and back
void shift() {
while (run_mode()) {
setBright();
cls();
get_time();
// when in circle mode and minute=odd and second is between 4 and 15, switch to small mode (mode 1)
if(circle){
if(rtc[1] % 2 == 1 && (rtc[0] >= 4 && rtc[0] <=15 )){
wipeMiddle();
wipeTop();
clock_mode =1; // switch to small mode
return;
}
}
bool secflag= false;
get_shiftTime(secflag);
shiftChar[0] = dig[5];
shiftChar[1] = dig[4];
shiftChar[2] = ':';
shiftChar[3] = dig[3];
shiftChar[4] = dig[2];
shiftChar[5] = ':';
shiftChar[6] = dig[1];
shiftChar[7] = dig[0];
//________________________________________________
// shift chars from right to left inside
int x;
int y=1;
int nr=0;
int w=0;
do{
for(x=34; x>=w; x--){
puttinychar(x+w, y, shiftChar[nr]);
for (byte yy = 0 ; yy < 7; yy ++) {
plot(x+w + 3, yy, 0);
}
}
w=w+2;
nr++;
} while(w<=28);
//________________________________________________
setBright();
get_time();
// when in circle mode and minute=odd and second is between 4 and 15, switch to small mode (mode 1)
if(circle){
if(rtc[1] % 2 == 1 && (rtc[0] >= 4 && rtc[0] <=15 )){
wipeMiddle();
wipeTop();
clock_mode =1; // switch to small mode
return;
}
}
// get 5x new chars to display, when there is no shifting
for (int i=0; i<5; i++) {
setBright();
bool secflag= true; // -4 seconds, because the shifting of previous chars was approx. 4 seconds in the past
get_shiftTime(secflag);
shiftChar[0] = dig[5];
shiftChar[1] = dig[4];
shiftChar[2] = ':';
shiftChar[3] = dig[3];
shiftChar[4] = dig[2];
shiftChar[5] = ':';
shiftChar[6] = dig[1];
shiftChar[7] = dig[0];
puttinychar( 0, 1, shiftChar[0]);
puttinychar( 4, 1, shiftChar[1]);
puttinychar(12, 1, shiftChar[3]);
puttinychar(16, 1, shiftChar[4]);
puttinychar(24, 1, shiftChar[6]);
puttinychar(28, 1, shiftChar[7]);
//hold display but check for button presses
int counter = 860; // while in for-loop, stop 860ms before displaying next char
while (counter > 0){
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
delay(1);
counter--;
}
}
//________________________________________________
// shift chars from left to right outside
x=0;
nr=7;
for(int w=28; w>=0; w-=4){
do{
puttinychar(x+w, y, shiftChar[nr]);
for(byte yy = 0 ; yy < 7; yy ++) {
plot(x+w - 1, yy, 0);
}
x=x+1;
} while(x<=34);
x=0;
nr--;
}
//________________________________________________
//hold display but check for button presses
int counter = 300;
while (counter > 0){
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
delay(1);
counter--;
}
setBright();
get_time();
secflag=false;
get_shiftTime(secflag);
shiftChar[0] = dig[5];
shiftChar[1] = dig[4];
shiftChar[2] = ':';
shiftChar[3] = dig[3];
shiftChar[4] = dig[2];
shiftChar[5] = ':';
shiftChar[6] = dig[1];
shiftChar[7] = dig[0];
// when in circle mode and minute=odd and second is between 4 and 15, switch to small mode (mode 1)
if(circle){
if(rtc[1] % 2 == 1 && (rtc[0] >= 4 && rtc[0] <=15 )){
wipeMiddle();
wipeTop();
clock_mode =1; // switch to small mode
return;
}
}
//________________________________________________
// shift chars from right to left inside
//int x;
//int y=1;
nr=0;
w=0;
do{
for(x=34; x>=w; x--){
puttinychar(x+w, y, shiftChar[nr]);
for (byte yy = 0 ; yy < 7; yy ++) {
plot(x+w + 3, yy, 0);
}
}
w=w+2;
nr++;
} while(w<=28);
//________________________________________________
setBright();
get_time();
// when in circle mode and minute=odd and second is between 4 and 15, switch to small mode (mode 1)
if(circle){
if(rtc[1] % 2 == 1 && (rtc[0] >= 4 && rtc[0] <=15 )){
wipeMiddle();
wipeTop();
clock_mode =1; // switch to small mode
return;
}
}
// get 5x new chars to display, when there is no shifting
for (int i=0; i<5; i++) {
setBright();
secflag=true; // -4 seconds, because the shifting of previous chars was approx. 4 seconds in the past
get_shiftTime(secflag);
shiftChar[0] = dig[5];
shiftChar[1] = dig[4];
shiftChar[2] = ':';
shiftChar[3] = dig[3];
shiftChar[4] = dig[2];
shiftChar[5] = ':';
shiftChar[6] = dig[1];
shiftChar[7] = dig[0];
puttinychar( 0, 1, shiftChar[0]);
puttinychar( 4, 1, shiftChar[1]);
puttinychar(12, 1, shiftChar[3]);
puttinychar(16, 1, shiftChar[4]);
puttinychar(24, 1, shiftChar[6]);
puttinychar(28, 1, shiftChar[7]);
//hold display but check for button presses
counter = 860; // while in for-loop, stop 860ms before displaying next char
while (counter > 0){
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
delay(1);
counter--;
}
}
//________________________________________________
// shift chars from left to left outside:
nr=0;
w=-28;
do{
for(x=28; x>=w-3; x--){
puttinychar(x+w, y, shiftChar[nr]);
for (byte yy = 0 ; yy < 7; yy ++) {
plot(x+w + 3, yy, 0);
}
}
w=w+4;
nr++;
} while(w<=0);
//________________________________________________
//hold display but check for button presses
counter = 150;
while (counter > 0){
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
delay(1);
counter--;
}
} // end of while run-mode
}
// end of shift-mode
////////////////////////////////////////////////////////////////////////////////////////
// Create time-chars for shift-mode
void get_shiftTime(bool secflag){
get_time();
if (secflag){
if(rtc[0]>=4){
rtc[0] = rtc[0] -4; // -4 seconds, because the shifting of previous chars was approx. 4 seconds in the past
}
}
//split all time into individual digits - stick in dig array
char buffer_hours[3];
itoa( rtc[2], buffer_hours, 10);
char buffer_mins[3];
itoa( rtc[1], buffer_mins, 10);
char buffer_secs[3];
itoa( rtc[0], buffer_secs, 10);
if (rtc[0] < 10) {
buffer_secs[1] = buffer_secs[0];
buffer_secs[0] = '0';
}
dig[0] = buffer_secs[1]; //0 - secs ones
dig[1] = buffer_secs[0]; //1 - secs tens
if (rtc[1] < 10) {
buffer_mins[1] = buffer_mins[0];
buffer_mins[0] = '0';
}
dig[2] = buffer_mins[1]; //2 - mins ones
dig[3] = buffer_mins[0]; //3 - mins tens
if (rtc[2] < 10) {
buffer_hours[1] = buffer_hours[0];
buffer_hours[0] = '0';
}
dig[4] = buffer_hours[1]; //4 - hour ones
dig[5] = buffer_hours[0]; //5 - hour tens
// the string we want to shift:
//char shiftChar[8] = { dig[5], dig[4], ':', dig[3], dig[2], ':', dig[1], dig[0] };
} // end of get_shiftTime
//display date - show dayname, date, month, year, week of year in 4 steps
void display_date(){
int date_delay = 70; // delay between displaying next character
wipeBottom(); //wipe out devices
//read the date from the DS1307/DS3231
byte dow = rtc[3]; // day of week 0 = Sunday
byte date = rtc[4];
byte month = rtc[5] - 1;
byte year = rtc[6]-2000;
//array of month names to print on the display. Some are shortened as we only have 8 characters across to play with
// char monthnames[12][9] = {
// "Januar", "Februar", "Maerz", "April", "Mai", "Juni", "Juli", "August", "Septemb.", "Oktober", "November", "Dezember"
// };
char monthnames[12][4] = {
"Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Des"
};
//----------- print the day name ----------- //
//get length of text in pixels, that way we can centre it on the display by divindin the remaining pixels b2 and using that as an offset
byte len = 0;
while(daysfull[dow][len]) {
len++;
};
byte offset = (31 - ((len-1)*4)) / 2; //our offset to centre up the text
int i = 0;
while(daysfull[dow][i]){
puttinychar((i*4) + offset , 1, daysfull[dow][i]);
delay(date_delay);
i++;
}
//hold display but check for button presses
int counter = 1000;
while (counter > 0){
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); return; }
delay(1);
counter--;
}
cls();
//----------- print date numerals ----------- //
char buffer[3];
//if date < 10 add a 0
itoa(date,buffer,10);
if (date < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
offset = 5;
puttinychar(0+offset, 1, buffer[0]); //print the 1st date number
delay(date_delay);
puttinychar(4+offset, 1, buffer[1]); //print the 2nd date number
delay(date_delay);
puttinychar(8+offset, 1, suffix[0]); //print suffix - char suffix[1]={'.'}; is defined at top of code
delay(90);
//----------- print month name ----------- //
//get length of text in pixels, that way we can centre it on the display by divindin the remaining pixels b2 and using that as an offset
len = 0;
while(monthnames[month][len]) {
len++;
};
//offset = (31 - ((len-1)*4)) / 2; //our offset to centre up the text
offset = 17;
i = 0;
while(monthnames[month][i]){
puttinychar((i*4) +offset, 1, monthnames[month][i]);
delay(date_delay);
i++;
}
//hold display but check for button presses
counter = 1000;
while (counter > 0){
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); return; }
delay(1);
counter--;
}
cls();
//----------- print year ----------- //
offset = 9; //offset to centre text - e.g. 2016
char buffer_y[3] = "20";
puttinychar(0+offset , 1, buffer_y[0]); //print the 1st year number: 2
delay(date_delay);
puttinychar(4+offset , 1, buffer_y[1]); //print the 2nd year number: 0
delay(date_delay);
itoa(year,buffer,10); //if year < 10 add a 0
if (year < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
puttinychar(8+offset, 1, buffer[0]); //print the 1st year number
delay(date_delay);
puttinychar(12+offset, 1, buffer[1]); //print the 2nd year number
delay(1000);
cls();
//----------- print week of year ----------- //
offset = 1;
char buffer_w[6] = "Minggu";
puttinychar(0+offset , 1, buffer_w[0]); //print "W"
delay(date_delay);
puttinychar(4+offset , 1, buffer_w[1]); //print "o"
delay(date_delay);
puttinychar(8+offset , 1, buffer_w[2]); //print "c"
delay(date_delay);
puttinychar(12+offset , 1, buffer_w[3]); //print "h"
delay(date_delay);
puttinychar(16+offset , 1, buffer_w[4]); //print "e"
delay(date_delay);
itoa(WN,buffer,10); //if week < 10 add a 0
if (WN < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
puttinychar(23+offset, 1, buffer[0]); //print the 1st week number
delay(date_delay);
puttinychar(27+offset, 1, buffer[1]); //print the 2nd week number
//hold display but check for button presses
counter = 1000;
while (counter > 0){
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); return; }
delay(1);
counter--;
}
wipeTop(); //wipe out devices
} // end of display_date
////////////////////////////////////////////////////////////////////////////////////////
// toggleDateState: toggle Show date : On/Off
void toggleDateState(){
if (show_date == true ) {
show_date = false;
date_state = true;
if(debug){
Serial.println("Show date = On");
}
//cls();
wipeTop();
//display state of date
char dateOn[8] = "DATE:ON";
int len=7; // length of dateOn
byte offset_top = (31 - ((len - 1) * 4)) / 2;
byte i = 0;
while (dateOn[i]) {
puttinychar((i * 4) + offset_top, 1, dateOn[i]);
i++;
}
//hold display but check for button presses
int counter = 1000;
while (counter > 0){
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
delay(1);
counter--;
}
wipeBottom();
}
else{
show_date = true;
date_state = false;
if(debug){
Serial.println("Show date = Off");
}
//cls();
wipeTop();
//display state of date
char dateOff[9] = "DATE:OFF";
int len=8; // length of dateOn
byte offset_top = (31 - ((len - 1) * 4)) / 2;
byte i = 0;
while (dateOff[i]) {
puttinychar((i * 4) + offset_top, 1, dateOff[i]);
i++;
}
//hold display but check for button presses
int counter = 1000;
while (counter > 0){
if (buttonA.uniquePress()) { switch_mode(); return; }
if (buttonB.uniquePress()) { toggleDateState(); delay(1000); return; }
delay(1);
counter--;
}
wipeBottom();
}
}
////////////////////////////////////////////////////////////////////////////////////////
//display menu to change the clock-mode
void switch_mode() {
//remember mode we are in. We use this value if we go into settings mode, so we can change back from settings mode (6) to whatever mode we were in.
old_mode = clock_mode;
const char *modes[] = {
"Biasa", "Kecil", "Slide", "SL 2", "Kata", "Geser", "Setup",
};
byte next_clock_mode;
byte firstrun = 1;
//loop waiting for button (timeout after 35 loops to return to mode X)
for (int count = 0; count < 35 ; count++) {
//if user hits button, change the clock_mode
if (buttonA.uniquePress() || firstrun == 1) {
count = 0;
cls();
if (firstrun == 0) {
clock_mode++;
}
if (clock_mode > NUM_DISPLAY_MODES + 1 ) {
clock_mode = 0;
}
//print arrown and current clock_mode name on line one and print next clock_mode name on line two
char str_top[9];
//strcpy (str_top, "-");
strcpy (str_top, modes[clock_mode]);
next_clock_mode = clock_mode + 1;
if (next_clock_mode > NUM_DISPLAY_MODES + 1 ) {
next_clock_mode = 0;
}
byte i = 0;
while (str_top[i]) {
putnormalchar(i * 6, 0, str_top[i]);
i++;
}
firstrun = 0;
}
delay(50);
}
}
////////////////////////////////////////////////////////////////////////////////////////
//run clock main loop as long as run_mode returns true
byte run_mode() {
setBright(); //
return 1;
}
////////////////////////////////////////////////////////////////////////////////////////
// setup menu(=mode6): display menu to change the clock settings
void setup_menu() {
//char* set_modes[] = { //depecated
const char *set_modes[] = {
"Circl", "=24Hr","Set >", "Exit"};
if (ampm == 0) {
set_modes[1] = ("=12Hr");
}
byte setting_mode = 0;
byte next_setting_mode;
byte firstrun = 1;
//loop waiting for button (timeout after 35 loops to return to mode X)
for(int count=0; count < 35 ; count++) {
//if user hits button, change the clock_mode
if(buttonA.uniquePress() || firstrun == 1){
count = 0;
cls();
if (firstrun == 0) {
setting_mode++;
}
if (setting_mode > NUM_SETTINGS_MODES) {
setting_mode = 0;
}
//print arrown and current clock_mode name on line one and print next clock_mode name on line two
char str_top[9];
strcpy (str_top, set_modes[setting_mode]);
next_setting_mode = setting_mode + 1;
if (next_setting_mode > NUM_SETTINGS_MODES) {
next_setting_mode = 0;
}
byte i = 0;
while(str_top[i]) {
putnormalchar(i*6, 0, str_top[i]);
i++;
}
firstrun = 0;
}
delay(50);
}
//pick the mode
switch(setting_mode){
case 0:
set_circle();
break;
case 1:
set_ampm();
break;
case 2:
set_time();
break;
case 3:
//exit form menu
break;
}
//change the mode from mode 6 (=settings) back to the one it was in before
clock_mode=old_mode;
}
////////////////////////////////////////////////////////////////////////////////////////
//toggle circle mode: change clock-mode every 2 minutes? On/Off
void set_circle(){
cls();
char text_a[9] = "=Off";
char text_b[9] = "=On";
byte i = 0;
//if circle mode is on, turn it off
if (circle){
//turn circle mode off
circle = 0;
//print a message on the display
while(text_a[i]) {
putnormalchar((i*6), 0, text_a[i]);
i++;
}
} else {
//turn circlee mode on.
circle = 1;
//print a message on the display
while(text_b[i]) {
putnormalchar((i*6), 0, text_b[i]);
i++;
}
}
delay(1200); //leave the message up for a second or so
}
////////////////////////////////////////////////////////////////////////////////////////
//ampm: set 12 or 24 hour clock
void set_ampm() {
// AM/PM or 24 hour clock mode - flip the bit (makes 0 into 1, or 1 into 0 for ampm mode)
ampm = (ampm ^ 1);
cls();
}
////////////////////////////////////////////////////////////////////////////////////////
//set_time: set time and date
void set_time() {
cls();
//fill settings with current clock values read from clock
get_time();
byte set_min = rtc[1];
byte set_hr = rtc[2];
byte set_date = rtc[4];
byte set_mnth = rtc[5];
int set_yr = rtc[6];
//Set function - we pass in: which 'set' message to show at top, current value, reset value, and rollover limit.
set_date = set_value(2, set_date, 1, 31);
set_mnth = set_value(3, set_mnth, 1, 12);
set_yr = set_value(4, set_yr, 2013, 2099);
set_hr = set_value(1, set_hr, 0, 23);
set_min = set_value(0, set_min, 0, 59);
ds1307.adjust(DateTime(set_yr, set_mnth, set_date, set_hr, set_min));
cls();
}
//used to set min, hr, date, month, year values. pass
//message = which 'set' message to print,
//current value = current value of property we are setting
//reset_value = what to reset value to if to rolls over. E.g. mins roll from 60 to 0, months from 12 to 1
//rollover limit = when value rolls over
int set_value(byte message, int current_value, int reset_value, int rollover_limit){
cls();
//char messages[6][17] = {
char messages[6][9] = {
//"Set Mins", "Set Hour", "Set Day", "Set Mnth", "Set Year"};
"Menit >", "Jam >", "Tanggal >", "Bulan >", "Tahun >"};
//Print "set xyz" top line
byte i = 0;
while(messages[message][i])
{
puttinychar(i*4 , 1, messages[message][i]);
i++;
}
delay(999);
cls();
//print digits bottom line
char buffer[5] = " ";
itoa(current_value,buffer,10);
puttinychar(0 , 1, buffer[0]);
puttinychar(4 , 1, buffer[1]);
puttinychar(8 , 1, buffer[2]);
puttinychar(12, 1, buffer[3]);
delay(300);
//wait for button input
while (!buttonA.uniquePress()) {
while (buttonB.isPressed()){
if(current_value < rollover_limit) {
current_value++;
}
else {
current_value = reset_value;
}
//print the new value
itoa(current_value, buffer ,10);
puttinychar(0 , 1, buffer[0]);
puttinychar(4 , 1, buffer[1]);
puttinychar(8 , 1, buffer[2]);
puttinychar(12, 1, buffer[3]);
delay(150);
}
}
return current_value;
}
////////////////////////////////////////////////////////////////////////////////////////
// get_time: get the current time from the RTC
void get_time()
{
//get time
DateTime now = ds1307.now();
//save time to array
rtc[6] = now.year();
rtc[5] = now.month();
rtc[4] = now.day();
rtc[3] = now.dayOfTheWeek(); //returns 0-6 where 0 = Sunday
rtc[2] = now.hour();
rtc[1] = now.minute();
rtc[0] = now.second();
// Calculate day of year and week of year
DayWeekNumber(rtc[6],rtc[5],rtc[4],rtc[3]);
if(debug){
//print the time to the serial port - for debuging
Serial.print(" ");
Serial.print(rtc[2]);
Serial.print(":");
Serial.print(rtc[1]);
Serial.print(":");
Serial.print(rtc[0]);
Serial.print(" ");
Serial.print(rtc[4]);
Serial.print(".");
Serial.print(rtc[5]);
Serial.print(".");
Serial.print(rtc[6]);
Serial.print(" Wochentag: ");
Serial.print(rtc[3]);
Serial.print(" Tag ");
Serial.print(DN);
Serial.print(" in Woche ");
Serial.print(WN);
Serial.print(" in ");
Serial.print(rtc[6]);
Serial.print(" clock_mode: ");
Serial.println(clock_mode);
}
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
//DayWeekNumber: Calculate day of year and week of year
void DayWeekNumber(unsigned int y, unsigned int m, unsigned int d, unsigned int w){
int days[]={0,31,59,90,120,151,181,212,243,273,304,334}; // Number of days at the beginning of the month in a not leap year.
//Start to calculate the number of day
if (m==1 || m==2){
DN = days[(m-1)]+d; //for any type of year, it calculate the number of days for January or february
} // Now, try to calculate for the other months
else if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0){ //those are the conditions to have a leap year
DN = days[(m-1)]+d+1; // if leap year, calculate in the same way but increasing one day
}
else { //if not a leap year, calculate in the normal way, such as January or February
DN = days[(m-1)]+d;
}
// Now start to calculate Week number
if (w==0){
WN = (DN-7+10)/7; //if it is sunday (time library returns 0)
}
else{
WN = (DN-w+10)/7; // for the other days of week
}
}
////////////////////////////////////////////////////////////////////////////////////////
// bottomleds: plot seconds-dots at bottomline
void bottomleds(byte secs){
//switch on bottomleds from 1 to 30
if(secs >=1 && secs <=30){
for(int i=0; i<=secs-1; i++){
plot(i, 7, 1);
}
}
//switch off bottomleds from 30 to 1
if(secs>=31){
for(int i=0; i<=(30-(secs-30)); i++){
plot(i, 7, 1);
}
plot(30-(secs-30), 7, 0);
}
//switch off bottomled 1
if(secs == 0){
plot(0, 7, 0);
}
}
////////////////////////////////////////////////////////////////////////////////////////
//wipeRight: wipe-effect from right to left
void wipeRight(){
//left to right
for(int c=0; c<32; c++){
for(int r=7; r>=0; r--){
plot (c, r, 1);
}
delay(15);
for(int r=7; r>=0; r--){
plot (c, r, 0);
}
}
} // end of wipeRight
////////////////////////////////////////////////////////////////////////////////////////
//wipeLeft: wipe-effect from left to right
void wipeLeft(){
//right to left
for(int c=32; c>=0; c--){
for(int r=7; r>=0; r--){
plot (c, r, 1);
}
delay(15);
for(int r=7; r>=0; r--){
plot (c, r, 0);
}
}
} // end of wipeLeft
////////////////////////////////////////////////////////////////////////////////////////
//wipeTop: wipe-effect from top to bottom
void wipeTop(){
for(int r=0; r<=8; r++){
for(int c=0; c<32; c++){
plot (c, r, 1);
plot (c, r-1, 0);
}
}
} // end of wipeTop
////////////////////////////////////////////////////////////////////////////////////////
//wipeBottom: wipe-effect from bottom to top
void wipeBottom(){
//bottom to top
for(int r=7; r>=(-1); r--){
for(int c=0; c<32; c++){
plot (c, r, 1);
plot (c, r+1, 0);
}
}
} // end of wipeBottom
////////////////////////////////////////////////////////////////////////////////////////
//wipeMiddle: wipe-effect from left and right to the middle
void wipeMiddle(){
for(int c=0; c<=31; c++){
for(int r=7; r>=0; r--){
plot (c, r, 1);
plot (32-c, r, 1);
}
delay(10);
for(int r=7; r>=0; r--){
plot (c, r, 0);
if(c != 16){
plot (32-c, r, 0);
}
else{
plot (c, 0, 0); delay(50);
plot (c, 7, 0); delay(50);
plot (c, 1, 0); delay(50);
plot (c, 6, 0); delay(50);
plot (c, 2, 0); delay(50);
plot (c, 5, 0); delay(50);
plot (c, 3, 0); delay(50);
plot (c, 4, 0); delay(600);
return;
}
}
}
} // end of wipeMiddle
////////////////////////////////////////////////////////////////////////////////////////
//wipeOutside: wipe-effect from both sides over the middle to the other sides
void wipeOutside(){
for(int c=0; c<32; c++){
for(int r=7; r>=0; r--){
plot (c, r, 1);
plot (32-c, r, 1);
}
delay(5);
for(int r=7; r>=0; r--){
plot (c, r, 0);
if(c != 16){
plot (32-c, r, 0);
}
}
}
delay(300);
} // end of wipeOutside
////////////////////////////////////////////////////////////////////////////////////////
// wipeInside - looks like random-clearing of dots
// (for testing set all dots to 1)
void wipeInside(){
int verz=5; // delay between plotting each dot
int rh=7;
int rl=0;
for(int row=0; row<4; row++){
for(int col=0; col<8; col++){
plot(col, rh, 0); delay(verz);
plot(col, rl, 0); delay(verz);
plot(31-col, rh, 0); delay(verz);
plot(31-col, rl, 0); delay(verz);
}
rh--;
rl++;
}
rh=7;
rl=0;
for(int row=0; row<4; row++){
for(int col=0; col<8; col++){
plot(8+col, rh, 0); delay(verz);
plot(8+col, rl, 0); delay(verz);
plot(23-col, rh, 0); delay(verz);
plot(23-col, rl, 0); delay(verz);
}
rh--;
rl++;
}
delay(300);
} // end of wipeInside
////////////////////////////////////////////////////////////////////////////////////////
/*
/// scroll: scroll text from right to left - not used at present - too slow.
void scroll() {
char message[] = {"ABCDEFGH "};
cls();
byte p = 6; //current pos in string
byte chara[] = {0, 1, 2, 3, 4, 5, 6, 7}; //chars from string
int x[] = {0, 6, 12, 18, 24, 30, 36, 42}; //xpos for each char
byte y = 0; //y pos
// clear_buffer();
while (message[p] != '\0') {
//draw all 8 chars
for (byte c = 0; c < 8; c++) {
putnormalchar(x[c],y,message[ chara[c] ]);
//draw a line of pixels turned off after each char,otherwise the gaps between the chars have pixels left in them from the previous char
for (byte yy = 0 ; yy < 8; yy ++) {
plot(x[c] + 5, yy, 0);
}
//take one off each chars position
x[c] = x[c] - 1;
}
//reset a char if it's gone off screen
for (byte i = 0; i <= 5; i++) {
if (x[i] < -5 ) {
x[i] = 31;
chara[i] = p;
p++;
}
}
}
}
*/
////////////////////////////////////////////////////////////////////////////////////////
Coding asli untuk jam digital ini saya dapatkan dari situs http://arduino.joergeli.de/digiclock/digiclock.php. dan saya telah modifikasi sesuai kebutuhan. file coding diatas yang telah dimodifikasi dapat di download lewat link dibawah ini:
Keseluruhan proses pembuatan jam digital ini dari awal sampai akhir bisa ditonton langsung lewat channel youtube saya dibawah ini. Jangan lupa di subscribe ya……
Sampai disini dulu tulisan tutorial ini saya buat, sampai jumpa di kesempatan lainnya.
Assalamualaikum, Selamat datang bagi para pembaca website ini.
Salah satu sumber kecelakaan di lingkungan rumah tangga adalah kebocoran gas LPG yang tidak langsung terdeteksi. kebocoran gas ini seringkali menjadi sumber kebakaran di rumah-rumah yang menggunakan gas LPG sebagai sumber bahan bakar untuk memasak.
Pada tulisan ini, kita akan mempelajari cara membuat sebuah sistem pemantau kebocoran gas LPG dengan menggunakan Arduino dan sensor gas MQ-2. Untuk output nya kita akan menggunakan speaker piezo buzzer dan LCD 16×2. Alat ini akan bekerja secara terus menerus memantau kondisi udara dan akan langsung mengeluarkan bunyi pertanda adanya kebocoran gas LPG di sekitar alat.
Yuk, langsung saja kita masuk ke menu utama…
Alat dan Bahan
Nama
Kebutuhan
Arduino UNO
1 Buah
Sensor MQ-2
1 buah
Buzzer
1 buah
LCD 16×2 I2C
1 buah
Breadboard
1 buah
Kabel jumper
secukupnya
Gambar Rangkaian
Instalasi Library LCD 16×2 I2C
untuk bisa mengendalikan sensor LCD 16×2 dengan mudah, kita perlu menginstal library untuk kedua modul tersebut dengan mengikuti langkah-langkah berikut ini:
Bukalah aplikasi Arduino IDE, lalu buka library manager yang terdapat disebelah kiri layar
Library yang akan kita instal adalah library LiquidCrystal_I2C. Gunakan kotak pencarian untuk mempermudah pencarian library yang dimaksud. Lewati langkah ini jika library sudah pernah diinstal sebelumnya.
Setelah library berhasil terinstal, maka kita bisa lanjut ke proses penulisan code program. yukk lanjut…
Upload kode diatas, cek dahulu pastikan tidak ada yang error. Setelah upload berhasil, kita bisa langsung uji coba dengan menggunakan gas dari mancis atau dengan menggunakan gas lainnya.
Versi video dari tulisan ini bisa dilihat di channel youtube dibawah ini
Baiklah, sampai disini dulu tulisan ini saya buat, selamat mencoba