#include "ClassRotMenue.h"
#include <LiquidCrystal_I2C.h>
/*
ec2021 - Beispiel für die Anwendung der Klasse RotMenue
*/
// 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
};
// 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, "Main Menue");
RotMenue GreenLedMenue(clkPin,dtPin, swPin, "Control Green");
RotMenue RedLedMenue(clkPin,dtPin, swPin, "Control Red");
RotMenue BuzzerMenue(clkPin,dtPin, swPin, "Control Buzzer");
LiquidCrystal_I2C lcd(0x27,16,2); // Lcd address 0x27, 20 chars, 2 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 true oder false kann im Folgenden für alle Menüs
// das Verhalten "endlos durchwählen" oder "Anschlag links/rechts"
// gewählt werden; Default wäre "true" also endloses Durchwählen
boolean Revolve = true;
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("Control Green",SUBGREEN);
HauptMenue.Add("Control Red",SUBRED);
HauptMenue.Add("Control Buzzer",SUBBUZZER);
// Submenü für die Grüne Led
// Hier wird (wie auch bei den weiteren Submenüs)
// mit dem Eintrag "Back to..." 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("Green On",SUBGREENON);
GreenLedMenue.Add("Green Off",SUBGREENOFF);
GreenLedMenue.Add("Back to Main",MAIN);
// Submenü für die Rote Led
RedLedMenue.SetRevolving(Revolve);
RedLedMenue.Add("Red On",SUBREDON);
RedLedMenue.Add("Red Off",SUBREDOFF);
RedLedMenue.Add("Back to Main",MAIN);
// Submenü für den Buzzer
BuzzerMenue.SetRevolving(Revolve);
BuzzerMenue.Add("Tone",SUBBUZZERTONE);
BuzzerMenue.Add("Back to Main",MAIN);
// Startzustand sicherheitshalber hier nochmals gesetzt
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;
default : state = MAIN;
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 ActMenue(RotMenue &aMenue){
if (aMenue.StateHasChanged() || aMenue.Changed()) {
Serial.print(aMenue.ActMenueNo());
Serial.print("\t");
Serial.println(aMenue.ActMenueName());
WriteLcd(aMenue.ActMenueName(),aMenue.ActMenueTitle());
}
if (aMenue.confirmed()) {
Serial.println(" Confirmed Menue\t"+aMenue.ActMenueName());
state = aMenue.ActMenueState();
}
}
void WriteLcd(String MenueName, String MenueTitle){
lcd.clear();
lcd.setCursor(0,0);
lcd.print(MenueTitle);
lcd.setCursor(0,1);
lcd.print(MenueName);
}