#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "RTClib.h"
// ====== OLED ======
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// ====== RTC ======
RTC_DS1307 rtc;
// ====== Pin ======
#define BUZZER 14
#define LED_R 25
#define LED_G 26
#define LED_B 27
#define BTN_PLUS 18 // atas/next
#define BTN_MINUS 19 // bawah/prev
#define BTN_OK 23 // toggle / confirm kecil
#define BTN_SET 5 // lanjut/selesai step
// ====== RGB Duty ======
uint8_t DUTY_ON = 150;
uint8_t DUTY_OFF = 0;
// ====== Hari (3 huruf untuk UI) ======
const char* HARI3[7] = {"Sen","Sel","Rab","Kam","Jum","Sab","Min"};
// ====== Jadwal ======
const int MAX_JADWAL = 3;
int setHour[MAX_JADWAL] = {8,13,20};
int setMinute[MAX_JADWAL] = {0,0,0};
int jadwalAktif = 1; // dipilih user 1..3
// ====== Hari aktif (dibintangi) ======
bool hariSelected[7] = {false,false,false,false,false,false,false};
// ====== State Setting ======
bool settingMode = false;
bool stepJumlahJadwal = false;
bool stepJam = false;
bool stepMenit = false;
bool stepHari = false;
int curSchedule = 0; // index jadwal saat set jam/menit
int curHariIndex = 0; // index highlight hari (0..6)
unsigned long lastBlink = 0;
bool blinkState = true;
// Debounce
unsigned long lastSetPress = 0;
const unsigned long debounceDelay = 200;
// =================== RGB Helper ===================
void setupRGB() {
pinMode(LED_R, OUTPUT);
pinMode(LED_G, OUTPUT);
pinMode(LED_B, OUTPUT);
}
void setRGB(uint8_t r, uint8_t g, uint8_t b) {
analogWrite(LED_R, r);
analogWrite(LED_G, g);
analogWrite(LED_B, b);
}
// =================== SETUP ===================
void setup() {
Serial.begin(115200);
Wire.begin(21,22);
if (!rtc.begin()) { Serial.println("RTC gagal!"); while(1); }
if (!rtc.isrunning()) { rtc.adjust(DateTime(__DATE__, __TIME__)); }
pinMode(BUZZER, OUTPUT);
pinMode(BTN_OK, INPUT_PULLUP);
pinMode(BTN_PLUS, INPUT_PULLUP);
pinMode(BTN_MINUS, INPUT_PULLUP);
pinMode(BTN_SET, INPUT_PULLUP);
setupRGB();
setRGB(DUTY_OFF,DUTY_OFF,DUTY_OFF);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println("OLED gagal!"); while(1); }
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println("Pill Reminder Box");
display.display();
delay(800);
}
// =================== LOOP ===================
void loop() {
DateTime now = rtc.now();
// Masuk setting ketika SET ditekan dari layar utama
if (digitalRead(BTN_SET)==LOW && millis()-lastSetPress>debounceDelay && !settingMode) {
lastSetPress = millis();
// reset flow
settingMode = true;
stepJumlahJadwal = true;
stepJam = false;
stepMenit = false;
stepHari = false;
curSchedule = 0;
}
if (settingMode) {
handleSetting();
} else {
// ===== layar utama =====
showHome(now);
// Alarm: cek hari & waktu
int dow = now.dayOfTheWeek(); // 0=Minggu
int idx = (dow==0)?6:dow-1; // 0=Senin .. 6=Minggu
if (hariSelected[idx]) {
for (int i=0;i<jadwalAktif;i++){
if (now.hour()==setHour[i] && now.minute()==setMinute[i] && now.second()==0) {
pillReminder(i);
}
}
}
}
}
// =================== Setting Flow ===================
void handleSetting(){
// Tombol SET untuk pindah step
if (digitalRead(BTN_SET)==LOW && millis()-lastSetPress>debounceDelay) {
lastSetPress = millis();
if (stepJumlahJadwal) {
stepJumlahJadwal = false;
stepJam = true;
curSchedule = 0;
}
else if (stepJam) {
stepJam = false;
stepMenit = true;
}
else if (stepMenit) {
stepMenit = false;
curSchedule++;
if (curSchedule >= jadwalAktif) {
stepHari = true;
curSchedule = 0; // gunakan untuk highlight hari
curHariIndex = 0;
} else {
stepJam = true;
}
}
else if (stepHari) {
stepHari = false;
settingMode = false;
}
}
// Gambar UI sesuai step
if (stepJumlahJadwal) {
uiJumlahJadwal();
return;
}
if (stepJam || stepMenit) {
uiSetJamMenit(stepJam, stepMenit);
return;
}
if (stepHari) {
uiPilihHariGrid();
return;
}
}
// ========== UI: Pilih Jumlah Jadwal ==========
void uiJumlahJadwal(){
if (digitalRead(BTN_PLUS)==LOW) { jadwalAktif++; if (jadwalAktif>MAX_JADWAL) jadwalAktif = 1; delay(200); }
if (digitalRead(BTN_MINUS)==LOW) { jadwalAktif--; if (jadwalAktif<1) jadwalAktif = MAX_JADWAL; delay(200); }
display.clearDisplay();
display.setCursor(0,0); display.println("JUMLAH JADWAL");
display.setCursor(0,20); display.printf("[%d]", jadwalAktif);
display.setCursor(0,50); display.println("SET=lanjut");
display.display();
}
// ========== UI: Set Jam & Menit ==========
void uiSetJamMenit(bool onJam, bool onMenit){
if (millis()-lastBlink>500){ blinkState=!blinkState; lastBlink=millis(); }
// Ubah nilai
if (digitalRead(BTN_PLUS)==LOW) {
if (onJam) setHour[curSchedule] = (setHour[curSchedule]+1)%24;
if (onMenit) setMinute[curSchedule] = (setMinute[curSchedule]+1)%60;
delay(180);
}
if (digitalRead(BTN_MINUS)==LOW) {
if (onJam) setHour[curSchedule] = (setHour[curSchedule]-1+24)%24;
if (onMenit) setMinute[curSchedule] = (setMinute[curSchedule]-1+60)%60;
delay(180);
}
display.clearDisplay();
display.setCursor(0,0);
display.printf("Jadwal %d", curSchedule+1);
// Tampilan horizontal: "Jam: 08 : 30" dengan blink pada field aktif
display.setCursor(0,24);
display.print("Jam: ");
if (onJam) { if (blinkState) display.printf("%02d", setHour[curSchedule]); else display.print(" "); }
else { display.printf("%02d", setHour[curSchedule]); }
display.print(" : ");
if (onMenit) { if (blinkState) display.printf("%02d", setMinute[curSchedule]); else display.print(" "); }
else { display.printf("%02d", setMinute[curSchedule]); }
display.setCursor(0,50);
display.println("SET=lanjut");
display.display();
}
// ========== UI: Pilih Hari (Grid 2 kolom × 4 baris) ==========
void uiPilihHariGrid(){
// Navigasi atas/bawah (muter 0..6)
if (digitalRead(BTN_PLUS)==LOW) { curHariIndex = (curHariIndex+1)%7; delay(160); }
if (digitalRead(BTN_MINUS)==LOW) { curHariIndex = (curHariIndex-1+7)%7; delay(160); }
// Toggle bintang dengan OK
if (digitalRead(BTN_OK)==LOW) { hariSelected[curHariIndex] = !hariSelected[curHariIndex]; delay(180); }
display.clearDisplay();
display.setCursor(0,0);
display.println("PILIH HARI (*=aktif)");
const int leftX = 0;
const int rightX = 70;
const int baseY = 18;
const int rowH = 12;
auto drawItem = [&](int idx, int x, int row){
int y = baseY + row*rowH;
display.setCursor(x, y);
if (curHariIndex == idx) display.print(">");
else display.print(" ");
display.setCursor(x+8, y);
display.print(HARI3[idx]);
display.print(" ");
display.print(hariSelected[idx] ? "*" : " ");
};
// kiri kolom
drawItem(0, leftX, 0);
drawItem(1, leftX, 1);
drawItem(2, leftX, 2);
drawItem(3, leftX, 3);
// kanan kolom
drawItem(4, rightX, 0);
drawItem(5, rightX, 1);
drawItem(6, rightX, 2);
display.display();
}
// =================== Layar Utama ===================
// =================== Layar Utama ===================
void showHome(DateTime now){
display.clearDisplay();
display.setCursor(0,0);
display.printf("Now %02d:%02d:%02d", now.hour(), now.minute(), now.second());
// Tampilkan jadwal
for(int i=0;i<jadwalAktif;i++){
display.setCursor(0, 12 + i*10);
display.printf("Jadwal %d: %02d:%02d", i+1, setHour[i], setMinute[i]);
}
// Baris ringkas hari aktif, selalu satu baris
int hariCount = 0;
for(int h=0; h<7; h++) if(hariSelected[h]) hariCount++;
display.setCursor(0, 12 + jadwalAktif*10 + 2); // baris tepat di bawah jadwal terakhir
if(hariCount==7){
display.println("Hari: setiap hari");
} else {
display.print("Hari: ");
for(int h=0; h<7; h++){
if(hariSelected[h]){
display.print(HARI3[h]);
display.print(" ");
}
}
}
display.display();
}
// =================== Alarm ===================
void pillReminder(int idx){
setRGB(DUTY_ON, DUTY_OFF, DUTY_OFF);
tone(BUZZER, 1000);
display.clearDisplay();
display.setCursor(0,0);
display.printf("Minum Obat %d!", idx+1);
display.display();
while(digitalRead(BTN_OK)==HIGH) { delay(60); }
noTone(BUZZER);
setRGB(DUTY_OFF, DUTY_ON, DUTY_OFF);
display.clearDisplay();
display.setCursor(0,0);
display.println("Obat sudah diminum");
display.display();
delay(1200);
setRGB(DUTY_OFF, DUTY_OFF, DUTY_ON);
}