Guten Tag,
ich sitze seit circa 3 Tagen am Stecken und basteln für meine neue Maschine. Diese soll verschiedene funktionen haben wie z. B. Förederband Roboterarm etc. Übungs projekt..
In dem Code wollte ich für den Anfang 5 States machen (z. B. Start, Stopp...) die ich abfragen kann und schauen kann was passiert wenn das Systeme sich in dem und dem Status befindet
Folgendes Problem
In meinem Sketch kommt immer eine Fehlermeldung beim decompilen.
'state' was not declared in this scope
Falls ihr für den Code ansich Vorschläge hättet zum Verbessern etc. Bitte schreiben.
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 21);
//Control Unit
enum StateSystem {Start, Stop, CostumerID, Login, Sleep};
int d[] {5000, 2000, 500, 500};
byte StateSystem = state;
enum StateMaschine {Start, Fehler, Temperatur, Sleep};
unsigned long int millisStart;
unsigned long int millisStop;
unsigned long int millisCostumerID;
unsigned long int millisLogin;
unsigned long int Fehler;
unsigned long int Temperatur;
byte StateSystem = Start
byte StateMaschine = Start
void setup() {
Serial.begin(9600);
lcd.begin();
millisStart = millis();
}
void loop() {
if(millis()- millisStart < d[0]){
Serial.println("Das System ist nun Einsatzbereit!");
state = CostumerID;
}else{
state = Start;
}
if(state == Start){
lcd.setCursor(4,0);
lcd.print("Das Systeme");
lcd.setCursor(2,1);
lcd.print("startet gerade..");
}else if(state == CostumerID){
lcd.setCursor(3,1);
lcd.print("Costumer ID...");
}else if(state == Login){
lcd.setCursor(4,1);
lcd.print("Checking...");
}else if(state == Sleep){
lcd.setCursor(5,1);
lcd.print("Sleeping...");
}else if(state == Stop){
lcd.setCursor(3,1);
lcd.print("Herunterfahren...");
}
}
Der darunterliegende Typ ist ein Byte, ja, aber den Typ der Zustandsvariablen sollten von dem Typ der Aufzählung sein
Damit kann man aber immer noch allerlei Unsinn machen und es gibt auch Probleme, weil die Aufzählung keinen eigenen Gültigkeitsbereich hat. Daher besser:
Das geht seit C++11. Damit sind keine direkten Zuweisungen an Integer-Datentypen mehr erlaubt. Und man kann auch mehrere Aufzählungen mit den gleichen Konstanten haben. z.B. zwei Zustandsmaschinen mit "Start" und "Sleep". Bei dir kannst du diese Zustände zwischen den zwei enums im Moment nicht unterscheiden. Die überschneiden sich praktisch. Man kann die zwei enums z.B. untereinander zuweisen obwohl das womöglich semantisch keinen Sinn hat
Mit enum class (strongly typed enums) sind StateMachine::Start und StateSystem::Start klar zwei verschiedene Dinge
Ich glaube es hängt gerade ein wenig bei mir aber WARUM geht es nicht?
Ich verstehe es nicht
Meldung: use of enum 'StateSystem' without previous declaration
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 21);
//Control Unit
enum class StateSystem byte : {Start, Stop, CostumerID, Login, Sleep};
int d[] {5000, 2000, 500, 500};
StateSystem currentState = StateMachine::Start;
enum StateMaschine {Start, Fehler, Temperatur, Sleep};
unsigned long int millisStart;
unsigned long int millisStop;
unsigned long int millisCostumerID;
unsigned long int millisLogin;
unsigned long int Fehler;
unsigned long int Temperatur;
StateSystem = Start
StateMaschine = Start
void setup() {
Serial.begin(9600);
lcd.begin();
millisStart = millis();
}
void loop() {
if(millis()- millisStart < d[0]){
Serial.println("Das System ist nun Einsatzbereit!");
state = CostumerID;
}else{
state = Start;
}
if(state == Start){
lcd.setCursor(4,0);
lcd.print("Das Systeme");
lcd.setCursor(2,1);
lcd.print("startet gerade..");
}else if(state == CostumerID){
lcd.setCursor(3,1);
lcd.print("Costumer ID...");
}else if(state == Login){
lcd.setCursor(4,1);
lcd.print("Checking...");
}else if(state == Sleep){
lcd.setCursor(5,1);
lcd.print("Sleeping...");
}else if(state == Stop){
lcd.setCursor(3,1);
lcd.print("Herunterfahren...");
}
}
Die Sache mit strongly typed enums ist das deren Gültigkeitsbereich eingeschränkt ist. Du musst immer mit dem Scope Operator :: angeben welches gemeint ist
Diese zwei Zeilen sind aber auch völlig sinnfrei. Du musst eine Variablen diesen Typs anlegen!
Zum Beispiel
enum class StateSystem : byte {Start, Stop, CostumerID, Login, Sleep};
enum class StateMaschine : byte {Start, Fehler, Temperatur, Sleep};
...
StateSystem systemState = StateSystem::Start;
StateMachine state = StateMachine::Start;
systemState ist vom Typ StateSystem und hat den Wert StateSystem::Start
Denke dir vielleicht mal bessere Namen aus. "StateMaschine" ist etwas zu allgemein wenn zwei Zustandsmaschinen existieren
Außerdem denke bitte etwas nach. Ich mag das in meinem Beispiel currentState genannt haben. Bei dir heißen die Zustandsvariablen anders. Das muss schon konsistent sein
enum class StateSystem : byte {Start, Stop, CostumerID, Login, Sleep};
Der darunterliegende Datentyp wird mit : byte angegeben. Nicht mit "byte :". Das ist auch optional. Wenn man das weglässt ist es halt int. Da man mit enum class aber anders als mit den klassischen C enums keine direkte Kompatibilität mit Integern hat, ist das auch nicht so wichtig.
War vielleicht mein Fehler. Ich hatte das einmal falsch hingeschrieben und dann ausgebessert
Wie gesagt das ist optional. Wenn man nicht angibt wird wie sonst auch immer int verwendet. Das sind dann halt zwei Bytes statt eins. Also erst mal egal
Das hier reicht:
enum class StateSystem {Start, Stop, CostumerID, Login, Sleep};
Auf der Seite die ich verlinkt habe steht doch die allgemeine Definition:
enum struct|class name : type { enumerator = constexpr , enumerator = constexpr , ... }
Achte auf die Position des Doppelpunkts. Das ist das gleiche wie bei anderen Verwendungen des Doppelpunkts in C++, auch wenn du diese vielleicht nicht kennst
Und dann baust du hier den nächsten Fehler ein:
byte StateSystem systemState = StateSystem::Start;
byte StateMachine state = StateMachine::Start;
Was soll das "byte" da? Tippst du da willkürlich Code ein?
Einfaches Beispiel:
int var = 5;
Eine Variable var vom Typ int wird deklarliert und mit 5 initialisiert
Analog hier:
StateMachine state = StateMachine::Start;
Eine Variable state vom Typ StateMaschine wird deklariert und mit StateMachine::Start initialisiert
Du hast einen eigenen Datentyp erzeugt. Daher gelten die gleichen Regeln wie für andere Datentypen.
Ganz ehrlich halte ich es nicht für zielführend, jemandem, der offensichtlich noch so am Anfang steht, derartige Konstrukte zu empfehlen.
Das ist so ähnlich, als wenn Du ihm ein Einführungsbuch zu C++ vorschlägst, aber ihm empfiehlst es von hinten nach vorn zu lesen.
Er hat schon enums verwendet und will Zustandsmaschinen. Man muss aber enum class nehmen, damit die Zustände sauber getrennt sind, sonst gibt es Probleme. Das zu umgehen ist nicht sicher und erfordert noch mehr Wissen was da eigentlich dahinter steht. Enum class dagegen ist viel freundlicher, gerade weil man eher Fehler bekommt wenn man was falsch macht.
Der Unterschied ist minimalst. Einmal steht "class" dort und wenn man die Zustande verwendet muss man mit :: angeben auf welche Aufzählung man sich bezieht. Das ist alles.
uwefed:
Du solltest Programmstrukturen nur benutzen wenn Du weiß wie dami umzugehen ist.
Grüße Uwe
Hallo Uwe, da muß ich widersprechen, denn wenn ich nur tun würde, was ich schon gut kann, würde ich nie etwas dazulernen. Aber unsere Spezies möchte lernen, entdecken, hingehen, wo noch nie ein Mensch zuvor gewesen ist.
Wir fangen als Baby damit an und sollten nie aufhören! Das weltweite Netz ist eine der vielen Möglichkeiten dazu. Ist doch toll, wie einfach ich mit jemanden, der in Italien wohnt, kommunizieren kann.
Ich glaube, dass das ein großes Problem vieler Einsteiger ist, dass sie durch Copy/Paste vielfach Dinge nutzen, die sie noch nicht wirklich verstehen. Die Fehler, die er nach deinem Vorschlag gemacht hat, deuten eher darauf hin, dass ihm das ganze Thema der selbstdefinierten Datentypen noch nicht wirklich klar ist.
Insofern stimme ich auch Uwe zu, und ich denke so hat er das auch gemeint: Nicht einfach Copy/Paste, sondern lernen was das ist und dann bewusst einsetzen.
Serenifly:
Der Unterschied ist minimalst.
Wenn man sich auskennt, und die Hintergründe kennt schon
uwefed:
Du solltest Programmstrukturen nur benutzen wenn Du weiß wie damit umzugehen ist.
Grüße Uwe
MicroBahner:
Ganz ehrlich halte ich es nicht für zielführend, jemandem, der offensichtlich noch so am Anfang steht, derartige Konstrukte zu empfehlen.
Das ist so ähnlich, als wenn Du ihm ein Einführungsbuch zu C++ vorschlägst, aber ihm empfiehlst es von hinten nach vorn zu lesen.
Ja, sehe ich genauso. Diese Erklärungen zu den Fehlern kann man nicht kapieren wenn man noch keine Berührungen mit diese Strukturen hatte. Ich habe auch kein Wort von diesen Erklärungen kapiert, gebe ich offen zu, weil ich noch nichts derartiges bearbeiten musste.
Aber ich mache eben das, was ich immer mache wenn ich was hier nicht kapiere. Ich versuche es in meinen Büchern nachzuvollziehen, dass ich das nächste mal weiß worum es hierbei geht.
Start ist 0, Warten ist 1. Aber welchen Wert hat Stop? Einmal ist es 1 und einmal 2
Die klassischen C enums sind nicht viel anders als das:
#define Start 0
#define Stop 1
Wenn du eine Konstante neu definierst sollte der Compiler eigentlich eine Warnung ausspucken. Die musst in den IDE Einstellungen auch aktivieren.
EDIT: Mal getestet und kommt sogar ein Fehler. Auch für Konstanten die eigentlich den gleichen Wert haben. Das konnte also nie so funktionieren
Das ganze nennt sich "Scope" oder Gültigkeitsbereich. Der ist in dem Fall global. Genau deshalb wurde mit den C++11 Standard enum class eingeführt. Damit haben die Konstanten nur noch den Gültigkeitsbereich der Aufzählung zu der sie gehören. Man kann so Stop zweimal haben, mit zwei verschiedenen Werten.