#include <TimerOne.h>
#include <Stepper.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <RTClib.h>
#include "IRremote.h"
#include <Servo.h>
// LED's
#define LED_1 3
#define LED_2 5
#define LED_3 6
#define LED_4 9
// LED_1
// LED_4 LED_2
// LED_3
int led_timer_inteval = 1000;
int quadrant_interval = 5000;
int led_max_value = 255;
enum led_quadrant {first, second, third, fourth};
led_quadrant current_quadrant = led_quadrant::first;
bool entry_show_time = true;
unsigned long delta_time;
bool dim_leds = false;
// Stepper-Motor
#define Stepper_A_minus 8
#define Stepper_A_plus 7
#define Stepper_B_plus 4
#define Stepper_B_minus 2
const int stepsPerRevolution = 16;
Stepper hour_hand(stepsPerRevolution,Stepper_B_minus,Stepper_B_plus,Stepper_A_plus,Stepper_A_minus);
bool turn_hour_hand = false;
int hour = 0;
int max_hour = 12;
int correction_steps = 8;
// I2C Display
#define I2C_ADDR 0x27
#define LCD_COLUMNS 16
#define LCD_LINES 2
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
// RTC - Module
RTC_DS1307 rtc;
// IR Remote
#define PIN_RECEIVER 11
IRrecv receiver(PIN_RECEIVER);
decode_results irRecvResults;
// Servo
#define SERVO_PIN 10
Servo myservo;
int pos = 0;
// States in the state diagram
enum set_alarm { overview, overview_2, info, overview_3, input_help, alarm_on, alarm_off};
set_alarm current_set_alarm = set_alarm::overview;
bool set_alarm_clock_entry = true;
unsigned long current_time = 0;
unsigned long acttime = 0;
// Main state machine
enum main_states {set_clock, show_time, set_alarm_time};
main_states current_main_state = main_states::show_time;
bool main_state_machine_entry = true;
//Function, which converts IR-Input in char output
char cmd;
char irdecode(IRrecv rcv) {
char cmd;
cmd = '\0';
int decoded = rcv.decode();
if (decoded) {
if (rcv.decodedIRData.command) {
Serial.println(rcv.decodedIRData.command);
switch (rcv.decodedIRData.command) {
case 162: cmd = 'P'; break; // Power
case 226: cmd = 'M'; break; // Menu
case 34: cmd = 'T'; break; // Test
case 176: cmd = 'C'; break; // C
case 2: cmd = '+'; break; // Plus
case 152: cmd = '-'; break; // Minus
case 168: cmd = 'L'; break; // Play
case 144: cmd = 'F'; break; // Forward
case 224: cmd = 'R'; break; // Rewind
case 194: cmd = 'B'; break; // Back
case 104: cmd = '0'; break;
case 48: cmd = '1'; break;
case 24: cmd = '2'; break;
case 122: cmd = '3'; break;
case 16: cmd = '4'; break;
case 56: cmd = '5'; break;
case 90: cmd = '6'; break;
case 66: cmd = '7'; break;
case 74: cmd = '8'; break;
case 82: cmd = '9'; break;
}
}
rcv.resume(); // Receive the next value
}
return cmd;
}
// Function to return the value of the alarm
///////Kuckuck kann auch wann anders getriggert werden, und halt zu den weckzeiten
int value_kuckkuck = 1200; // Um 12 Uhr kommt der Kuckuck
int value_alarmA = -1; // -1 = aus
int value_alarmB = -2; // -2 = aus
int get_alarm(int alarm, int column, int line){
if (alarm == -1 || alarm == -2) {
lcd.setCursor(column, line);
lcd.print("HH:MM");
} else {
String str_alarm = String(alarm);
if (str_alarm.length() == 4) {
String first_two_digits = str_alarm.substring(0, 2);
String last_two_digits = str_alarm.substring(2, 4);
lcd.setCursor(column, line);
lcd.print(first_two_digits);
lcd.setCursor(column + 2, line);
lcd.print(":");
lcd.print(last_two_digits);
lcd.setCursor(column + 4 , line);
}
}
}
// Function to choose alarm
bool alarmA = false;
bool alarmB = false;
int line0 = 0;
int line1 = 1;
void choose_blink(int on, int off){
if(on == line0 && off == line1){
alarmA = true;
alarmB = false;
lcd.noBlink();
lcd.setCursor(10, 0);
lcd.blink();
}
if(on == line1 && off == line0){
alarmA = false;
alarmB = true;
lcd.noBlink();
lcd.setCursor(10, 1);
lcd.blink();
}
}
// Function to set new alarm
int charCount = 0;
int start = 10;
int Cursor = start+charCount;
String input = "";
int input_new_alarm_value(int choose_alarm, int line){
cmd = irdecode(receiver);
if (cmd){
lcd.setCursor(Cursor, line);
if(Cursor == 12){
lcd.print(":");
++Cursor;
}
lcd.print(cmd);
input += cmd;
++charCount;
++Cursor;
if (input.length() == 4){
int inputasInt = input.toInt();
if(choose_alarm == value_alarmA){
value_alarmA = inputasInt;
}
if(choose_alarm == alarmB){
alarmB = inputasInt;
}
}
}
return charCount;
return choose_alarm;
}
// Function which converts rtc-time into int
int convertToInteger(DateTime time) {
int hour = time.hour();
int minute = time.minute();
int convertedTime = hour * 100 + minute;
return convertedTime;
}
// Function, which triggers the kuckuck
void kuckuck(){
DateTime now = rtc.now();
int currentHourMinute = convertToInteger(now);
if (currentHourMinute == value_kuckkuck || currentHourMinute == value_alarmA || currentHourMinute == value_alarmB) {
myservo.write(90);
}
else {
myservo.write(-90);
}
}
void Timer_led_ISR()
{
// Timer loops every second
//set_time_minutes();
}
void turn_hour_hand_func(){
hour_hand.step(stepsPerRevolution);
hour++;
if(hour == max_hour){
hour = 0;
hour_hand.step(correction_steps);
}
}
void show_time_display(){
DateTime now = rtc.now();
char buffer_date[20];
snprintf(buffer_date, sizeof(buffer_date), "%02d/%02d/%02d", now.day(), now.month(), now.year());
char buffer_time[20];
snprintf(buffer_time, sizeof(buffer_time), "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
lcd.setCursor(0, 0);
lcd.print("Date:");
lcd.setCursor(6,0 );
lcd.print(buffer_date);
lcd.setCursor(0, 1);
lcd.print("Time:");
lcd.setCursor(8, 1);
lcd.print(buffer_time);
}
void setup() {
/////////////////// debuggen
Serial.begin(9600);
// Display
lcd.init();
lcd.backlight();
// LED
pinMode(LED_1, OUTPUT);
pinMode(LED_2, OUTPUT);
pinMode(LED_3, OUTPUT);
pinMode(LED_4, OUTPUT);
// IR-Remote
receiver.enableIRIn();
// Servo
myservo.attach(SERVO_PIN);
myservo.write(-90);
// Steppermotor
hour_hand.setSpeed(40);
if (!rtc.begin()) {
Serial.println("RTC nicht gefunden!");
while (1);
}
if (!rtc.isrunning()) {
Serial.println("RTC wird gestartet.");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
//Timer with 100ms
Timer1.initialize(led_timer_inteval);
Timer1.attachInterrupt(Timer_led_ISR);
}
void loop() {
if(turn_hour_hand){
turn_hour_hand = !turn_hour_hand;
turn_hour_hand_func();
}
// ## Too much traffic, wenn man es bei jedem loop macht
acttime = millis();
main_state_machine();
}
void main_state_machine(){
switch (current_main_state){
case set_clock:
// Code für set_clock
Serial.print("Im set_clock Fall.");
break;
case show_time:
show_time_display();
kuckuck();
// scheiß name
// wie den richtigen Quadranten auswählen? Switch case bezüglich Quadrant
set_time_minutes();
cmd = irdecode(receiver);
if (cmd=='M') {
current_main_state = main_states::set_alarm_time;
digitalWrite(LED_1, LOW);
digitalWrite(LED_2, LOW);
digitalWrite(LED_3, LOW);
digitalWrite(LED_4, LOW);
lcd.clear();
break;
}
break;
case set_alarm_time:
if(main_state_machine_entry){
current_set_alarm = set_alarm::overview;
main_state_machine_entry =!main_state_machine_entry;
}
set_alarm_state_machine();
break;
}
}
void set_alarm_state_machine(){
switch (current_set_alarm) {
//___________________________________________________
case (overview):
if (set_alarm_clock_entry) {
current_time = acttime;
lcd.setCursor(0, 0);
lcd.print("Wecker A:");
lcd.setCursor(0, 1);
lcd.print("Wecker B:");
set_alarm_clock_entry = false;
get_alarm(value_alarmA, 11, 0);
get_alarm(value_alarmB, 11, 1);
}
cmd = irdecode(receiver);
if (cmd=='M') {
lcd.clear();
current_set_alarm = set_alarm::info;
set_alarm_clock_entry = true;
}
// EINGABE BEENDEN
if (cmd=='B') {
lcd.clear();
//state_wecker_stellen = set_wecker_stellen::-----VERLASSEN-----; //Muss angepasst werden
set_alarm_clock_entry = true;
current_main_state = main_states::set_alarm_time;
}
if (acttime - current_time > 2000) {
lcd.clear();
current_set_alarm = set_alarm::overview_2;
set_alarm_clock_entry = true;
}
break;
//___________________________________________________
case(overview_2):
if (set_alarm_clock_entry) {
current_time = acttime;
lcd.setCursor(0, 0);
lcd.print("IRF=[M] Exit=[B]");
lcd.setCursor(0, 1);
lcd.print("NodeRED=[Button]");
set_alarm_clock_entry = false;
}
cmd = irdecode(receiver);
if (cmd=='M') {
lcd.clear();
current_set_alarm = set_alarm::info;
set_alarm_clock_entry = true;
}
if (acttime - current_time > 2000) {
lcd.clear();
current_set_alarm = set_alarm::overview;
set_alarm_clock_entry = true;
}
break;
//___________________________________________________
case(info):
if (set_alarm_clock_entry) {
current_time = acttime;
lcd.setCursor(0, 0);
lcd.print("Eingabe mit IR-");
lcd.setCursor(0, 1);
lcd.print("Fernbedienung");
set_alarm_clock_entry = false;
}
if (acttime - current_time > 2000) {
lcd.clear();
current_set_alarm = set_alarm::overview_3;
set_alarm_clock_entry = true;
}
break;
//___________________________________________________
case(overview_3):
if (set_alarm_clock_entry) {
current_time = acttime;
lcd.setCursor(0, 0);
lcd.print("Wecker A:");
lcd.setCursor(0, 1);
lcd.print("Wecker B:");
set_alarm_clock_entry = false;
get_alarm(value_alarmA, 11, 0);
get_alarm(value_alarmB, 11, 1);
choose_blink(line0, line1);
}
//UP + DOWN
cmd = irdecode(receiver);
if (cmd=='+') {
choose_blink(line0, line1);
}
if (cmd=='-') {
choose_blink(line1, line0);
}
// EINGABE BEENDEN
if (cmd=='B') {
lcd.clear();
//state_wecker_stellen = set_wecker_stellen::-----VERLASSEN-----; //Muss angepasst werden
set_alarm_clock_entry = true;
current_main_state = main_states::show_time;
}
// Play Button to continue
if (cmd == 'L') {
lcd.noBlink();
lcd.clear();
current_set_alarm = set_alarm::input_help;
set_alarm_clock_entry = true;
}
break;
//___________________________________________________
case(input_help):
if (set_alarm_clock_entry) {
current_time = acttime;
lcd.setCursor(0, 0);
lcd.print("1: Einschalten");
lcd.setCursor(0, 1);
lcd.print("0: Ausschalten");
set_alarm_clock_entry = false;
}
cmd = irdecode(receiver);
if (cmd=='1') {
lcd.clear();
current_set_alarm = set_alarm::alarm_on;
set_alarm_clock_entry = true;
}
if (cmd=='0') {
lcd.clear();
current_set_alarm = set_alarm::alarm_off;
set_alarm_clock_entry = true;
}
break;
//___________________________________________________
case(alarm_on):
if (set_alarm_clock_entry) {
current_time = acttime;
if(alarmA){
lcd.setCursor(0, 0);
lcd.print("Wecker A: XX:XX");
}
if(alarmB){
lcd.setCursor(0, 1);
lcd.print("Wecker B: XX:XX");
}
set_alarm_clock_entry = false;
}
if(alarmA){
while (charCount < 4) {
input_new_alarm_value(value_alarmA, 0);
}
}
if(alarmB){
while (charCount < 4) {
input_new_alarm_value(value_alarmB, 1);
}
}
if (charCount == 4) {
charCount = 0;
Cursor = start+charCount;
input = "";
lcd.clear();
current_set_alarm = set_alarm::overview_3;
set_alarm_clock_entry = true;
}
break;
//___________________________________________________
case(alarm_off):
if (set_alarm_clock_entry) {
if(alarmA){
value_alarmA = -1;
}
if(alarmA){
value_alarmB = -2;
}
set_alarm_clock_entry = false;
}
if (!set_alarm_clock_entry) {
lcd.clear();
current_set_alarm = set_alarm::overview_3;
set_alarm_clock_entry = true;
}
break;
}
}
void set_time_minutes(){
unsigned long current_time = millis();
switch (current_quadrant){
case first:
if(entry_show_time){
entry_show_time =false;
digitalWrite(LED_1, HIGH);
delta_time = current_time;
break;
}
if (current_time - delta_time < quadrant_interval ){
int dim = (current_time - delta_time) * led_max_value / quadrant_interval;
analogWrite(LED_1, led_max_value-dim);
analogWrite(LED_2, dim);
break;
}
if(!entry_show_time && (current_time - delta_time >= quadrant_interval))
{
current_quadrant = led_quadrant::second;
entry_show_time = true;
digitalWrite(LED_1, LOW);
digitalWrite(LED_2, LOW);
break;
}
case second:
if(entry_show_time){
entry_show_time =false;
digitalWrite(LED_2, HIGH);
delta_time = current_time;
break;
}
if (current_time - delta_time < quadrant_interval ){
int dim = (current_time - delta_time) * led_max_value / quadrant_interval;
analogWrite(LED_2, led_max_value-dim);
analogWrite(LED_3, dim);
break;
}
if(!entry_show_time && (current_time - delta_time >= quadrant_interval))
{
current_quadrant = led_quadrant::third;
entry_show_time = true;
digitalWrite(LED_2, LOW);
digitalWrite(LED_3, LOW);
break;
}
case third:
if(entry_show_time){
entry_show_time =false;
digitalWrite(LED_3, HIGH);
delta_time = current_time;
break;
}
if (current_time - delta_time < quadrant_interval ){
int dim = (current_time - delta_time) * led_max_value / quadrant_interval;
analogWrite(LED_3, led_max_value-dim);
analogWrite(LED_4, dim);
break;
}
if(!entry_show_time && (current_time - delta_time >= quadrant_interval))
{
current_quadrant = led_quadrant::fourth;
entry_show_time = true;
digitalWrite(LED_3, LOW);
digitalWrite(LED_4, LOW);
break;
}
case fourth:
if(entry_show_time){
entry_show_time =false;
digitalWrite(LED_4, HIGH);
delta_time = current_time;
break;
}
if (current_time - delta_time < quadrant_interval ){
int dim = (current_time - delta_time) * led_max_value / quadrant_interval;
analogWrite(LED_4, led_max_value-dim);
analogWrite(LED_1, dim);
break;
}
if(!entry_show_time && (current_time - delta_time >= quadrant_interval))
{
current_quadrant = led_quadrant::first;
entry_show_time = true;
digitalWrite(LED_4, LOW);
digitalWrite(LED_1, LOW);
turn_hour_hand = true;
break;
}
}
}