Selamat Datang di website bersih tanpa iklan 👋

CODING. Jam dinding dan sensor suhu

 Jam Seven segment.

    jam digital dengan fitur menampilkan Suhu, Kelembapan, Alarm.
    hardware: Seven segment COM ANODA, DHT 11, RTC, ESP32, BUZZER.

Tampilan Suhu


Tampilan jam.
dengan membalik digit seven segment ke 3.
untuk menampilkan : (titik dua)





/*
HARDWARE: ESP32, RTC, DHT 11 , TOMBOL .
seven segment COM ANODA.
TR PNP 9012 X4 untuk trigger multiplex
TR NPN 2N2222 X2 untuk buzzer dan dot led.
*/

#include <Wire.h>
#include <RTClib.h>
#include <DHT.h>
#include <EEPROM.h>

#define SER_PIN   23
#define SRCLK_PIN 19
#define RCLK_PIN  18
#define DOT_PIN   2
#define BUZZER    33
#define MENU      26
#define SET       27
#define DHT_PIN       25
#define DHTTYPE DHT11
DHT dht(DHT_PIN, DHTTYPE);
RTC_DS3231 rtc;

#define NOTE_C4  523
#define NOTE_E4  659
#define NOTE_G4  784
#define NOTE_C5  1047
#define NOTE_E5  1319
#define NOTE_G5  1568

int melody[] = {
  NOTE_E5, NOTE_E5, 0,
  NOTE_E5, 0, NOTE_C5, NOTE_E5,
  NOTE_G5, 0, NOTE_G4
};

int noteDurations[] = {
  8, 8, 4,
  8, 4, 8, 4,
  4, 4, 4
};


uint8_t angkaAktif[4] = {8, 8, 8, 8};  
unsigned long lastSwitch = 0;
uint8_t step = 0;
uint8_t digitAktif = 0;
unsigned long lastMux = 0;
// Mode tampilan
enum Mode { MODE_JAM, MODE_SUHU, MODE_KELEMBAPAN };
Mode mode = MODE_JAM;
unsigned long lastModeSwitch = 0;
bool inSettingMode = false;
uint8_t setStep = 0; // 0: jam, 1: menit, 2: selesai
uint8_t setJam = 0, setMenit = 0;
unsigned long menuPressTime = 0;
bool menuHoldDetected = false;
bool menuReleasedAfterHold = false;
unsigned long lastAlarmCheck = 0;
int lastAlarmMinute = -1;  //  mendeteksi perubahan menit
bool inAlarmSettingMode = false;
uint8_t alarmSetStep = 0;
bool setHoldDetected = false;
bool setReleasedAfterHold = false;
unsigned long setPressTime = 0;
int jamAlarmAwal = 5;     // default 05.00
int jamAlarmAkhir = 21;   // default 21.00
int alarmTiap30Menit = 1; // default aktif

void setup() {
  Serial.begin(115200);
  Wire.begin();
  rtc.begin();
  dht.begin();
  EEPROM.begin(16);
  pinMode(SER_PIN, OUTPUT);
  pinMode(SRCLK_PIN, OUTPUT);
  pinMode(RCLK_PIN, OUTPUT);
  pinMode(DOT_PIN, OUTPUT);
  pinMode(MENU, INPUT_PULLUP);
  pinMode(SET, INPUT_PULLUP);
  pinMode(BUZZER, OUTPUT);

  digitalWrite(DOT_PIN, LOW); // Dot mati. PAKAI TRANSISTOR NPN
   if (rtc.lostPower()) {
    rtc.adjust(DateTime(2025, 1, 1, 0, 0, 0)); // Set default waktu jika RTC belum diset
    tone(BUZZER, 1000, 3000);    //Bunyikan buzzer peringatan RTC Jam bermasalah.
    for(int i = 0; i < 10; i++ ){
      tone(BUZZER, 200 + (i * 100), 200);
      delay(80);
    }
    for(int i = 0; i < 10; i++ ){
      tone(BUZZER, 200 + (i * 100), 200);
      delay(50);
    }
    noTone(BUZZER);
  }
  Serial.println("digital clock ready");
    // Tes tampilan angka dari 0000, 1111, ..., 9999
  for (int d = 0; d <= 9; d++) {
    angkaAktif[0] = d;
    angkaAktif[1] = d;
    angkaAktif[2] = d;
    angkaAktif[3] = d;

    unsigned long start = millis();
    while (millis() - start < 400) {  // tampilkan selama 200 ms per angka
      tampilDigit(digitAktif, angkaAktif[digitAktif]);
      digitAktif = (digitAktif + 1) % 4;
    }
  }

  tone(BUZZER, 1000, 100);
  noTone(BUZZER);
  jamAlarmAwal = EEPROM.read(0);
  if (jamAlarmAwal > 23) jamAlarmAwal = 5;
  jamAlarmAkhir = EEPROM.read(1);
  if (jamAlarmAkhir > 23) jamAlarmAkhir = 21;
  alarmTiap30Menit = EEPROM.read(2);
  if (alarmTiap30Menit > 1) alarmTiap30Menit = 1;
  Serial.print("Alarm Awal: "); Serial.println(jamAlarmAwal);
  Serial.print("Alarm Akhir: "); Serial.println(jamAlarmAkhir);
  Serial.print("30 Menit: "); Serial.println(alarmTiap30Menit);
}

void loop() {
  unsigned long now = millis();
  // Deteksi tahan SET untuk masuk mode setting alarm
  if (!inSettingMode && !inAlarmSettingMode) {
    if (digitalRead(SET) == LOW) {
      if (setPressTime == 0) setPressTime = now;
      if (now - setPressTime >= 5000 && !setHoldDetected) {
        tone(BUZZER, 1000, 200);
        inAlarmSettingMode = true;
        alarmSetStep = 0;
        setHoldDetected = true;
        setReleasedAfterHold = false;
        Serial.println("Masuk mode setting alarm");
      }
    } else {
      setPressTime = 0;
    }
  }
  if (inAlarmSettingMode) {
    // Tampilkan data alarm yang sedang disetel
    angkaAktif[0] = 10;
    angkaAktif[1] = 10;
    if (alarmSetStep == 0) {
      angkaAktif[2] = jamAlarmAwal / 10;
      angkaAktif[3] = jamAlarmAwal % 10;
    } else if (alarmSetStep == 1) {
      angkaAktif[2] = jamAlarmAkhir / 10;
      angkaAktif[3] = jamAlarmAkhir % 10;
    } else if (alarmSetStep == 2) {
      angkaAktif[2] = 0;
      angkaAktif[3] = alarmTiap30Menit;
    }

    if (!setReleasedAfterHold) {
      if (digitalRead(SET) == HIGH) {
        setReleasedAfterHold = true;
      }
    } else {
      if (digitalRead(SET) == LOW) {
        tone(BUZZER, 600, 100);
        delay(200);
        while (digitalRead(SET) == LOW);
        if (alarmSetStep == 0) jamAlarmAwal = (jamAlarmAwal + 1) % 24;
        else if (alarmSetStep == 1) jamAlarmAkhir = (jamAlarmAkhir + 1) % 24;
        else if (alarmSetStep == 2) alarmTiap30Menit = (alarmTiap30Menit + 1) % 2;
      }

      if (digitalRead(MENU) == LOW) {
        tone(BUZZER, 1200, 100);
        delay(200);
        while (digitalRead(MENU) == LOW);
        alarmSetStep++;
        if (alarmSetStep >= 3) {
          inAlarmSettingMode = false;
          setHoldDetected = false;

          // Simpan ke EEPROM
          EEPROM.write(0, jamAlarmAwal);
          EEPROM.write(1, jamAlarmAkhir);
          EEPROM.write(2, alarmTiap30Menit);
          EEPROM.commit();
          Serial.println("Selesai setting alarm");
          tone(BUZZER, 1100, 300);
          delay(200);
          tone(BUZZER, 1500, 500);
        }
      }
    }
  }


    // Jalankan alarm hanya jika tidak sedang masuk menu setting
  if (!inSettingMode && !inAlarmSettingMode && now - lastAlarmCheck > 1000) {
    lastAlarmCheck = now;
    DateTime t = rtc.now();
    int jam = t.hour();
    int menit = t.minute();
    if (jam >= jamAlarmAwal && jam <= jamAlarmAkhir) {
      // Cegah pemutaran ganda di menit yang sama
      if (lastAlarmMinute != menit) {
        lastAlarmMinute = menit;
        if (menit == 0) {          
          nadaCampina();          
        } else if (menit == 30 && alarmTiap30Menit == 1) {
          tone(BUZZER, 1000);
          delay(2000);
          noTone(BUZZER);
          mainkanMelodiMario();        
        }
      }
    }
  }



  // Deteksi tahan tombol MENU untuk masuk mode set waktu
  // === MODE SETTING WAKTU ===
  if (!inSettingMode && !inAlarmSettingMode) {
    if (digitalRead(MENU) == LOW) {
      if (menuPressTime == 0) menuPressTime = now;
      if (now - menuPressTime >= 5000 && !menuHoldDetected) {
        tone(BUZZER, 1500, 300);
        inSettingMode = true;  //masuk ke menu setting jam mode.
        setStep = 0;
        menuHoldDetected = true;
        menuReleasedAfterHold = false;

        DateTime nowTime = rtc.now();
        setJam = nowTime.hour();
        setMenit = nowTime.minute();

        Serial.println("Masuk mode set waktu");
      }
    } else {
      menuPressTime = 0;
    }
  }

  if (inSettingMode) {
    // Tampilkan nilai jam/menit yang sedang disetel
    angkaAktif[0] = 10;
    angkaAktif[1] = 10;
    if (setStep == 0) {
      angkaAktif[2] = setJam / 10;
      angkaAktif[3] = setJam % 10;
    } else if (setStep == 1) {
      angkaAktif[2] = setMenit / 10;
      angkaAktif[3] = setMenit % 10;
    }

    if (!menuReleasedAfterHold) {
      if (digitalRead(MENU) == HIGH) {
        menuReleasedAfterHold = true;
      }
    } else {
      if (digitalRead(MENU) == LOW) {
        tone(BUZZER, 1500, 300);
        delay(100);
        while (digitalRead(MENU) == LOW);
        setStep++;
        if (setStep == 2) {
          rtc.adjust(DateTime(2025, 1, 1, setJam, setMenit, 0));
          inSettingMode = false;
          menuHoldDetected = false;
          tone(BUZZER, 1100, 300);
          delay(200);
          tone(BUZZER, 1500, 500);
          Serial.println("Selesai set waktu");
        }
      }

      if (digitalRead(SET) == LOW) {
        tone(BUZZER, 500, 100);
        delay(100);
        while (digitalRead(SET) == LOW);
        if (setStep == 0) setJam = (setJam + 1) % 24;
        else if (setStep == 1) setMenit = (setMenit + 1) % 60;
      }
    }
  }



  // Ganti mode tampilan setiap 8 + 2 + 2 detik (total 12 detik siklus)
    if (!inSettingMode && !inAlarmSettingMode) {
  // MODE NORMAL: logika rotasi tampilan
  if (now - lastModeSwitch > 12000) {
    lastModeSwitch = now;
    mode = MODE_JAM;
  } else if (now - lastModeSwitch > 10000) {
    mode = MODE_KELEMBAPAN;
  } else if (now - lastModeSwitch > 8000) {
    mode = MODE_SUHU;
  } else {
    mode = MODE_JAM;
  }
  // Update angkaAktif[] berdasarkan mode
  if (mode == MODE_JAM) {
    DateTime nowTime = rtc.now();
    int jam = nowTime.hour();
    int menit = nowTime.minute();
    angkaAktif[0] = jam / 10;
    angkaAktif[1] = jam % 10;
    angkaAktif[2] = menit / 10;
    angkaAktif[3] = menit % 10;
  } else if (mode == MODE_SUHU) {
    float suhu = dht.readTemperature();
    int val = suhu * 100;
    angkaAktif[0] = (val / 1000) % 10;
    angkaAktif[1] = (val / 100) % 10;
    angkaAktif[2] = (val / 10) % 10;
    angkaAktif[3] = 10;
  } else if (mode == MODE_KELEMBAPAN) {
    float hum = dht.readHumidity();
    int val = hum * 100;
    angkaAktif[0] = (val / 1000) % 10;
    angkaAktif[1] = (val / 100) % 10;
    angkaAktif[2] = 10;
    angkaAktif[3] = 10;
    }
  }
 
 
 
  // Multiplex tiap 2ms
  if (now - lastMux >= 2) {
    lastMux = now;
    tampilDigit(digitAktif, angkaAktif[digitAktif]);
    digitAktif = (digitAktif + 1) % 4;
  }
}



// Fungsi menampilkan satu digit
void tampilDigit(uint8_t pos, uint8_t angka) {  
  if (angka > 9) {
    angka = 0x0F; // semua segmen mati
  }
  uint8_t bcd = angka & 0x0F;  
  uint8_t select = ~(1 << (4 + pos));  
  uint8_t data = (select & 0xF0) | (bcd & 0x0F);
  digitalWrite(RCLK_PIN, LOW);
  shiftOutStable(SER_PIN, SRCLK_PIN, MSBFIRST, data);
  digitalWrite(RCLK_PIN, HIGH);  
  // Logika pengaturan DOT
  if (mode == MODE_JAM && (pos == 1 || pos == 2)) {
    digitalWrite(DOT_PIN, HIGH); // titik jam antara HH:MM
  } else if (mode == MODE_SUHU && pos == 1) {
    digitalWrite(DOT_PIN, HIGH); // titik suhu di digit ke-2 sebagai koma
  } else {
    digitalWrite(DOT_PIN, LOW); // mati untuk kelembapan & lainnya
  }
}


void shiftOutStable(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val) {
  for (uint8_t i = 0; i < 8; i++)  {
    uint8_t bitVal = (bitOrder == LSBFIRST) ? (val >> i) & 1 : (val >> (7 - i)) & 1;
    digitalWrite(dataPin, bitVal);
    delayMicroseconds(2);
    digitalWrite(clockPin, HIGH);
    delayMicroseconds(2);
    digitalWrite(clockPin, LOW);
  }
}


void mainkanMelodiMario() {
  int panjang = sizeof(melody) / sizeof(melody[0]);
  for (int i = 0; i < panjang; i++) {
    int noteDuration = 1000 / noteDurations[i];
    if (melody[i] > 0)
      tone(BUZZER, melody[i], noteDuration);
    delay(noteDuration * 1.30); // ada jeda antar nada
    noTone(BUZZER);
  }
}


void nadaCampina() {  //CAMPINA
  int nada[] = {1568, 1568, 1568, 1760, 1760, 1760, 1568, 1760, 1396, 1568, 2094, 2094, 2094, 1976, 1760, 1568, 1568, 2348, 2094, 1320, 1396, 1396, 1320, 1176, 2348, 2094, 2094}
;
    int durasi[] = {80, 80, 80, 200, 80, 80, 150, 80, 80, 300, 80, 80, 80, 80, 80, 200, 80, 80, 200, 80, 200, 80, 80, 200, 80, 80, 400 };
    int jumlahNada = sizeof(nada) / sizeof(nada[0]);
    // Mainkan nada
    for (int i = 0; i < jumlahNada; i++) {
        tone(BUZZER, nada[i]);  
        delay(durasi[i]);        
        noTone(BUZZER);  
        delay(150);
      }      
}




Posting Komentar

Lebih baru Lebih lama