#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <pitches.h> // install 'PlayRtttl' library
//version 29-2-2020
//https://www.youtube.com/watch?v=B5O9bT54BzI
//original with seperate start button https://www.youtube.com/watch?v=rzxisoU9D6c
LiquidCrystal_I2C lcd(0x27,16,2);
//LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address
//LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address
//LiquidCrystal_I2C lcd(0x38); // Set the LCD I2C address
#define Start 4 // start stop button D4
// possible without button
// use encoder longpress to start
#define BuzzerPin 11 // buzzer D11
//#define BACKLIGHT_PIN 13
int relay = 12;
int beepCounts = 0;
int hours = 0;
int minutes = 0;
int seconds = 0;
int sethours = 0;
int setminutes = 0;
int setseconds = 0;
boolean timeState = false;
#define encoderPinA 2 // encoder CLK right
#define encoderPinB 3 // encoder DT left
#define encoderButton 5 // encoder SW switch
int HMS = 1;
int encoderPos = 0; // a counter for the dial
unsigned int lastReportedPos = 1; // change management
static boolean rotating = false; // debounce management
boolean A_set = false;
boolean B_set = false;
void setup() {
pinMode(relay, OUTPUT);
digitalWrite(relay, LOW);
pinMode(Start, INPUT_PULLUP);
pinMode(encoderPinA, INPUT_PULLUP); //enabling pullups
pinMode(encoderPinB, INPUT_PULLUP);
pinMode(encoderButton, INPUT_PULLUP);
attachInterrupt(0, doEncoderA, CHANGE); //pin 2
attachInterrupt(1, doEncoderB, CHANGE); //pin 3
Serial.begin(9600); // output
//lcd.begin(16, 2); // initialize the lcd for 16 chars 2 lines, turn on backlight
lcd.init(); // initialize the lcd
lcd.clear();
lcd.backlight();
delay((500));
//lcd.print("UV EXPOSURE UNIT");
//lcd.setCursor(0, 1);
showSpalshScreen();
playStartupMelody();
delay(1100);
tone(BuzzerPin, NOTE_C4, 700);
delay(200); // Add a slight delay after the note
lcd.clear();
noTone(BuzzerPin); // Stop the tone
}
void loop() {
if (digitalRead(encoderButton) == LOW)
{
HMS = HMS + 1;
if (HMS == 4)
{
HMS = 1;
}
tone(BuzzerPin, 1000, 100);
int t = 80;
while (digitalRead(encoderButton) == LOW) {
delay(10); t = t - 1;
// if (t == 60) {tone(BuzzerPin, 200, 50);}
if (t == 40) {tone(BuzzerPin, 200, 50);}
if (t == 20) {tone(BuzzerPin, 200, 50);}
if (t <= 1) {
t = 1; timeState = true;
sethours = hours;
setminutes=minutes;
setseconds=seconds;
lcd.setCursor(0, 0); lcd.print("Ready! ");
tone(BuzzerPin, 1000, 100);
}
}
}
// lcd.setCursor(0, 0);
// lcd.print("COUNT DOWN TIMER");
// delay(500);
if (HMS == 1) {
lcd.setCursor(0, 0);
lcd.print("set HRS ");
lcd.setCursor(6, 2);
}
if (HMS == 2) {
lcd.setCursor(0, 0);
lcd.print("set MIN ");
}
if (HMS == 3) {
lcd.setCursor(0, 0);
lcd.print("set SEC ");
}
//delay(500);
rotating = true; // reset the debouncer
encoderPos = constrain(encoderPos, -1, 1);
if (lastReportedPos != encoderPos) {
if (HMS == 1) {
hours = hours + encoderPos;
hours = constrain(hours, 0, 48);
}
else if (HMS == 2) {
minutes = minutes + encoderPos;
minutes = constrain(minutes, 0, 60);
}
else if (HMS == 3) {
seconds = seconds + encoderPos;
seconds = constrain(seconds, 0, 60);
}
lcd.setCursor(4, 1);
if (hours <= 9)
{
lcd.print("0");
}
lcd.print(hours);
lcd.print(":");
if (minutes <= 9)
{
lcd.print("0");
}
lcd.print(minutes);
lcd.print(":");
if (seconds <= 9)
{
lcd.print("0");
}
lcd.print(seconds);
encoderPos = 0;
lastReportedPos = encoderPos;
}
if (digitalRead(Start) == LOW || timeState == true) { //start count down timer
lcd.setCursor(0, 0); lcd.print(" UV LIGHT- ON ");
timeState = true;
digitalWrite(relay, HIGH);
delay(500);
while (timeState == true) {
//delay(992); //maintime delay
int period = 1000;
unsigned long time_now = 0;
time_now = millis();
while(millis() < time_now + period){ //wait approx. [period] ms}
}
//seconds = seconds - 1;
if (seconds > 0) {
seconds--;
} else {
// If seconds reach zero, decrease one minute
if (minutes > 0) {
minutes--;
seconds = 59;
} else {
// If minutes reach zero, decrease one hour
if (hours > 0) {
hours--;
minutes = 59;
seconds = 59;
}
}
}
if (minutes == 0 && hours == 0 && seconds == 0) { //count down alarm
lcd.clear();
beepCounts = 0;
while (timeState == true) {
//lcd.clear();
digitalWrite(relay, LOW);
lcd.setCursor(0, 0);
lcd.print(" UV LIGHT- OFF ");
lcd.setCursor(5, 1);
lcd.print("FINISH!");
if (beepCounts < 5) {
tone(BuzzerPin, NOTE_G4, 400);
//tone(BuzzerPin, 1000); // Play a tone of 1000 Hz on the buzzer
delay(300);
noTone(BuzzerPin);
delay(300);
tone(BuzzerPin, NOTE_C5, 400); // Keep the tone for 200 milliseconds
//noTone(BuzzerPin); // Turn off the buzzer
delay(300);
noTone(BuzzerPin);
beepCounts++; // Increment the beep coun
delay(300); // Wait for half a second between beeps
}
if (digitalRead(Start) == LOW) { // turn alarm off with switch
timeState = false;
seconds = 1;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("COUNT DOWN TIMER");
lcd.setCursor(4, 1);
lcd.print("00:00:00");
hours = sethours;
minutes=setminutes;
seconds=setseconds;
break;
}
if (digitalRead(encoderButton) == LOW) { // turn alarm off with rotary encoder switch
timeState = false;
//seconds = 1;
lcd.clear();
//lcd.setCursor(0, 0);
//lcd.print("COUNT DOWN TIMER");
//lcd.setCursor(4, 1);
//lcd.print("Done!");
while(digitalRead(encoderButton) == LOW){delay(300);}//wait for release switch
tone(BuzzerPin, NOTE_C4, 700);
delay(200); // Add a slight delay after the note
noTone(BuzzerPin); // Stop the tone
while(digitalRead(encoderButton) == LOW){delay(1000);}//wait for release switch
hours = sethours;
minutes=setminutes;
seconds=setseconds;
break;
}
}
}
lcd.setCursor(4, 1);
if (hours <= 9)
{
lcd.print("0");
}
lcd.print(hours);
lcd.print(":");
if (minutes <= 9)
{
lcd.print("0");
}
lcd.print(minutes);
lcd.print(":");
if (seconds <= 9)
{
lcd.print("0");
}
lcd.print(seconds);
if (digitalRead(Start) == LOW) { // pauze countdown with start button
delay(1000);
digitalWrite(relay, LOW);
timeState = false;
break;
}
if (digitalRead(encoderButton) == LOW) { // pauze countdown with encoder button
delay(1000);
tone(BuzzerPin, 500, 100);
digitalWrite(relay, LOW);
timeState = false;
break;
}
}
}
}
// Interrupt on A changing state
void doEncoderA() {
// debounce
if ( rotating ) delay (10); // wait a little until the bouncing is done
// Test transition, did things really change?
if ( digitalRead(encoderPinA) != A_set ) { // debounce once more
A_set = !A_set;
// adjust counter + if A leads B
if ( A_set && !B_set )
encoderPos = 1;
rotating = false; // no more debouncing until loop() hits again
}
}
// Interrupt on B changing state
void doEncoderB() {
if ( rotating ) delay (10);
if ( digitalRead(encoderPinB) != B_set ) {
B_set = !B_set;
// adjust counter – 1 if B leads A
if ( B_set && !A_set )
encoderPos = -1;
rotating = false;
}
}
void playStartupMelody() {
// Define the notes and durations for the melody
int melody[] = { NOTE_C4, NOTE_E4, NOTE_G4, NOTE_C5 };
int durations[] = { 4, 4, 4, 4 }; // 4 denotes a quarter note
// Play the melody
for (int i = 0; i < sizeof(melody) / sizeof(melody[0]); i++) {
int noteDuration = 750 / durations[i];
tone(BuzzerPin, melody[i], noteDuration);
delay(noteDuration * 1.1); // Add a slight delay between notes
noTone(BuzzerPin); // Stop the tone
}
}
void showSpalshScreen() {
lcd.print("UV EXPOSURE UNIT");
lcd.setCursor(0, 1);
String message = "----------------";
for (byte i = 0; i < message.length(); i++) {
lcd.print(message[i]);
delay(20);
}
}