#include "ClassRotMenue.h"
#include <LiquidCrystal_I2C.h>
/*
ec2021 - Beispiel für die Anwendung der Klasse RotMenue
* String() Aufrufe durch C-kompatible Funktionen ersetzt
*/
// Verschiedene - zum Teil über den Encoder anwählbare -
// States einer beispielhaften State Machine
// a) Hauptmenü zur Auswahl der Submenüs
// - Ansteuerung einer grünen Led
// - Ansteuerung einer roten Led
// - Ansteuerung eines Buzzers
// b) In den Submenüs für die Leds
// - Einschalten der Led
// - Ausschalten der Led
// - Rückkehr ins Hauptmenü
// c) In dem Submenü für den Buzzer
// - Auslösen eines Tones 262 Hz für 250 ms
// - Rückkehr ins Hauptmenü
enum MenueStates{
MAIN, // Zustand Hauptmenü
SUBGREEN, // Submenü Grüne Led
SUBGREENON, // Einschalten der grünen Led -> automatisch zurück Submenü Grüne Led
SUBGREENOFF, // Ausschalten der grünen Led -> automatisch zurück Submenü Grüne Led
SUBRED, // Submenü Rote Led
SUBREDON, // Einschalten der Roten Led -> automatisch zurück Submenü Rote Led
SUBREDOFF, // Ausschalten der Roten Led -> automatisch zurück Submenü Rote Led
SUBBUZZER, // Submenü Buzzer
SUBBUZZERTONE, // Auslösen des Tones -> automatisch zurück zu SUBBUZZER
SUBLANGUAGE, // Menü zur Sprachauswahl, geht nach Sprachauswahl ins Hauptmenü zurück
SUBGERMAN, // Menü auf Deutsch umschalten
SUBENGLISH // Menü auf Englisch umschalten
};
// Pinbelegung
const int clkPin = 2; // CLK Anschluss des Encoders
const int dtPin = 3; // DT Anschluss des Encoders
const int swPin = 4; // SW Anschluss des Encoders
const int GreenLedPin = 5; // Grüne Led
const int RedLedPin = 6; // Rote Led
const int buzzerPin = 8; // Buzzer
int state = MAIN; // Startzustand der State Machine
// Instanziieren der verschiedenen Menüobjekte
RotMenue HauptMenue(clkPin,dtPin, swPin, "");
RotMenue GreenLedMenue(clkPin,dtPin, swPin, "");
RotMenue RedLedMenue(clkPin,dtPin, swPin, "");
RotMenue BuzzerMenue(clkPin,dtPin, swPin, "");
RotMenue LanguageMenue(clkPin,dtPin, swPin, "");
LiquidCrystal_I2C lcd(0x27,20,4); // Lcd address 0x27, 20 chars, 4 lines
void setup() {
Serial.begin(115200);
// lcd vorbereiten
lcd.init();
lcd.backlight();
lcd.clear();
// Startausgabe
lcd.setCursor(0,0);
lcd.print("Encoder Demo");
delay(1000);
pinMode(GreenLedPin, OUTPUT);
pinMode(RedLedPin, OUTPUT);
// Mit der init() Funktion werden die Pins des Rotary Encoders
// initialisert, soweit alle Menues/Submenues über den selben
// Encoder bedient werden, muss dies nur für eines der Menues
// durchgeführt werden, bei mehreren Encodern genau einmal
// pro Encoder
HauptMenue.init();
// Mit true oder false kann im Folgenden für jedes Menüs
// das Verhalten "endlos durchwählen" oder "Anschlag links/rechts"
// gewählt werden; Default wäre "true" also endloses Durchwählen
boolean Revolve = false;
HauptMenue.SetRevolving(Revolve);
// Hier werden die Einträge des "Hauptmenüs" eingegeben
// Der State neben der Bezeichnung legt fest, welcher Zustand
// vom Menü mit HauptMenue.ActMenueState() zurückgegeben wird.
HauptMenue.Add("",SUBGREEN);
HauptMenue.Add("",SUBRED);
HauptMenue.Add("",SUBBUZZER);
HauptMenue.Add("",SUBLANGUAGE);
// Submenü für die Grüne Led
// Hier wird (wie auch bei den weiteren Submenüs)
// mit dem Eintrag "Back" und MAIN die Möglichkeit
// gegeben, wieder ins Hauptmenü zu kommen
// Praktisch könnte man aber von hier aus aber auch
// in weitere Submenüs wechseln.
// Die Möglichkeiten dazu werden im Hauptsketch verwaltet,
// für das Objekt sind Zusammenhänge wie Haupt-, Sub- oder Subsub-Menü
// transparent.
GreenLedMenue.SetRevolving(Revolve);
GreenLedMenue.Add("",SUBGREENON);
GreenLedMenue.Add("",SUBGREENOFF);
GreenLedMenue.Add("",MAIN);
// Submenü für die Rote Led
RedLedMenue.SetRevolving(Revolve);
RedLedMenue.Add("",SUBREDON);
RedLedMenue.Add("",SUBREDOFF);
RedLedMenue.Add("",MAIN);
// Submenü für den Buzzer
BuzzerMenue.SetRevolving(Revolve);
BuzzerMenue.Add("",SUBBUZZERTONE);
BuzzerMenue.Add("",MAIN);
// Startzustand sicherheitshalber hier nochmals gesetzt
LanguageMenue.Add("",SUBGERMAN);
LanguageMenue.Add("",SUBENGLISH);
SetToEnglish();
state = MAIN;
}
void loop() {
StateMachine();
}
void StateMachine(){
switch(state){
case MAIN : ActMenue(HauptMenue);
break;
case SUBGREEN : ActMenue(GreenLedMenue);
break;
case SUBGREENON : digitalWrite(GreenLedPin,HIGH);
state = SUBGREEN;
break;
case SUBGREENOFF : digitalWrite(GreenLedPin,LOW);
state = SUBGREEN;
break;
case SUBRED : ActMenue(RedLedMenue);
break;
case SUBREDON : digitalWrite(RedLedPin,HIGH);
state = SUBRED;
break;
case SUBREDOFF : digitalWrite(RedLedPin,LOW);
state = SUBRED;
break;
case SUBBUZZER : ActMenue(BuzzerMenue);
break;
case SUBBUZZERTONE : tone(buzzerPin, 262, 250); // Freq 252, Dauer 250 msec
state = SUBBUZZER;
break;
case SUBLANGUAGE : ActMenue(LanguageMenue);
break;
case SUBENGLISH : SetToEnglish();
state = MAIN;
break;
case SUBGERMAN : SetToGerman();
state = MAIN;
break;
default : state = MAIN;
PrintMenue(HauptMenue);
break;
}
}
// Diese Funktion erledigt die Abfrage, ob der Encoder
// gedreht und ob der Taster betätigt wurde und gibt dies
// bei positivem Ergebnis über Serial aus. Hier würde man ggf.
// Routinen zur Ausgabe eines Displays einfügen.
//
// Außerden wird bei Bestätigung durch den Taster (confirmed())
// der Zustand der State Machine auf den Wert gesetzt, der dem
// aktuell ausgewählten Menüpunkt zugewiesen wurde.
// Auf diese Weise gelangt die State Machine dann ggf. in einen
// neuen Zustand (siehe die enums oben)
//
void PrintMenue(RotMenue &aMenue){
Serial.print("-> ");
Serial.print(aMenue.ActMenueNo());
Serial.print("\t");
Serial.println(aMenue.ActMenueName());
WriteLcd(aMenue.ActMenueName(),aMenue.ActMenueTitle());
}
void ActMenue(RotMenue &aMenue){
if (aMenue.StateHasChanged() || aMenue.Changed()) {
PrintMenue(aMenue);
}
if (aMenue.confirmed()) {
Serial.print(" Confirmed Menue\t");
char buf[20];
strcpy(buf,aMenue.ActMenueName());
Serial.println(buf);
state = aMenue.ActMenueState();
}
}
void SetToEnglish(){
HauptMenue.ChangeMenueTitle("Main Menue");
HauptMenue.ChangeMenueEntry(0,"Control Green");
HauptMenue.ChangeMenueEntry(1,"Control Red");
HauptMenue.ChangeMenueEntry(2,"Control Buzzer");
HauptMenue.ChangeMenueEntry(3,"Language");
GreenLedMenue.ChangeMenueTitle("Control Green");
GreenLedMenue.ChangeMenueEntry(0,"Green On");
GreenLedMenue.ChangeMenueEntry(1,"Green Off");
GreenLedMenue.ChangeMenueEntry(2,"Back");
RedLedMenue.ChangeMenueTitle("Control Red");
RedLedMenue.ChangeMenueEntry(0,"Red On");
RedLedMenue.ChangeMenueEntry(1,"Red Off");
RedLedMenue.ChangeMenueEntry(2,"Back");
BuzzerMenue.ChangeMenueTitle("Control Buzzer");
BuzzerMenue.ChangeMenueEntry(0,"Tone");
BuzzerMenue.ChangeMenueEntry(1,"Back");
LanguageMenue.ChangeMenueTitle("Language");
LanguageMenue.ChangeMenueEntry(0,"German");
LanguageMenue.ChangeMenueEntry(1, "English");
PrintMenue(LanguageMenue);
};
// Zur Darstellung der verwendeten Umlaute auf dem
// LCD-Display sind diese wie folgt zu codieren:
// \365 ü
// \341 ä
// \357 ö
void SetToGerman(){
HauptMenue.ChangeMenueTitle("Hauptmen\365");
HauptMenue.ChangeMenueEntry(0,"Gr\365ne LED");
HauptMenue.ChangeMenueEntry(1,"Rote LED");
HauptMenue.ChangeMenueEntry(2,"Tongeber");
HauptMenue.ChangeMenueEntry(3,"Sprache");
GreenLedMenue.ChangeMenueTitle("Gr\365ne LED");
GreenLedMenue.ChangeMenueEntry(0,"Einschalten");
GreenLedMenue.ChangeMenueEntry(1,"Ausschalten");
GreenLedMenue.ChangeMenueEntry(2,"Zur\365ck");
RedLedMenue.ChangeMenueTitle("Rote LED");
RedLedMenue.ChangeMenueEntry(0,"Einschalten");
RedLedMenue.ChangeMenueEntry(1,"Ausschalten");
RedLedMenue.ChangeMenueEntry(2,"Zur\365ck");
BuzzerMenue.ChangeMenueTitle("Tongeber");
BuzzerMenue.ChangeMenueEntry(0,"Einschalten");
BuzzerMenue.ChangeMenueEntry(1,"Zur\365ck");
LanguageMenue.ChangeMenueTitle("Sprache");
LanguageMenue.ChangeMenueEntry(0,"Deutsch");
LanguageMenue.ChangeMenueEntry(1, "Englisch");
PrintMenue(LanguageMenue);
};
void WriteLcd(char* MenueName, char* MenueTitle){
lcd.clear();
lcd.setCursor(0,0);
lcd.print(MenueTitle);
lcd.setCursor(0,1);
lcd.print(MenueName);
}