Hallo, ich habe zu Hause eine selbstgebaute Balkonsolaranlage laufen, die manuell steuerbar ist über Kippschalter aber auch per Handyapp mit Wlan Relais. Sie spannt einen Webserver aus, der die Daten und Steuermöglichkeiten auf mein Handy abbildet. Alles in Javascript und HTML geschrieben, nichts wurde gekauft. Es gibt mehrere fertige Lösungen, ist mir bekannt. Darüber hinaus übernimmt sie Steuerung der Energieflüsse selbst, also ob in die Akkus eingespeist wird oder ins Netz. Wann die Akkus nachgeladen werden müssen usw. Auch ein Inverter kann anspringen, d.h. die Verbraucher laufen auch bei Stromausfall. Ein Mode ist zb Automatik, da ist das meiste blockiert. Das Ganze wird über einen ESp32 gesteuert der Relais bedient und Sensoren ausliest. Ich habe den über einen Portexpander aufgeblasen, dazu hat er AD Wandler Module usw. Jetzt ist aber die Logik doch recht komplex geworden, d.h. es gibt ne Menge Flags, die Systemzustände anzeigen. Sensoren und Aktoren. Prio 1 hat der User aber wenn die Steuerung auf Automatik steht kann er zb nicht mehr umschalten, das System verwaltet die Relais selbst. Bei leerem Akku wird alles blockiert, da kann er nur noch warten bis Akkus wieder voll sind. Ich habe Eingabe Systeme, Systemzustände, Warn Levels, Alarm Levels und ne Menge Relais, die das System steuern, also Sensor, Logik, Aktor Ketten. Bei Warn Levels oder Alarm müssen gewisse Eingaben blockiert werden, bis sie wieder entsperrt werden können Frage: Mit welcher Programmiertechnik verwaltet man sowas? Bisher habe ich alles mit if then else, teilweise geschachtelt und das sieht ziemlich wüst aus. Grüße, Thorsten
:
Bearbeitet durch User
Harry L. schrieb: > https://www.mikrocontroller.net/articles/Statemachine Nützt mir nichts, kenne ich auch schon. Es handelt sich um ein Logik Schaltwerk, keine Ablaufsteuerung. Zudem muss ich Prioritäten verwalten können. Auch Statemachine können ein if then else Grab werden, um die Abbruch Bedingungen der States zu verwalten.
:
Bearbeitet durch User
Allerdings sieht mir das nach einem Ansatz aus {ROT , GRUEN , 1, 10, OSTWEST_GELB}, // OSTWEST_GRUEN {ROT , GELB , 0, 1, ALLE_ROT_1}, // OSTWEST_GELB {ROT , ROT , 0, 3, NORDSUED_ROTGELB}, // ALLE_ROT_1 {ROTGELB , ROT , 0, 1, NORDSUED_GRUEN}, // NORDSUED_ROTGELB {GRUEN , ROT , 0, 10, NORDSUED_GELB}, // NORDSUED_GRUEN {GELB , ROT , 0, 1, ALLE_ROT_2}, // NORDSUED_GELB {ROT , ROT , 0, 3, OSTWEST_ROTGELB}, // ALLE_ROT_2 {ROT , ROTGELB , 0, 1, OSTWEST_GRUEN}}; // OSTWEST_ROTGELB Daraus liesse sich was machen. Wobei ich die States noch definieren müsste. Vermutlich wird jeder denkbaren Möglichkeit wie die Aktoren stehen können ein State zu geordnet. Ich muss nachdenken ....
Also ich mache so etwas immer in C mit boolschen Algebra: char E1,E2,E3,M1,A1; // E = Eingänge M=Merker A=Ausgänge A1 = (E1 && E2 || E3) && !M1;
:
Bearbeitet durch User
Thorsten M. schrieb: > Bisher habe ich alles mit if then else, teilweise geschachtelt und das > sieht ziemlich wüst aus. Eventuell hilft das switch statement. Oder du baust dir eine Entscheidungstabelle.
Beitrag #7620641 wurde von einem Moderator gelöscht.
Dirk F. schrieb: > char E1,E2,E3,M1,A1; // E = Eingänge M=Merker A=Ausgänge > > A1 = (E1 && E2 || E3) && !M1; Erinnert mich an die Minterm, Maxterm Geschichten aus der Vorlesung. Karnaugh Diagramme hiessen die glaube ich. Gute Idee, wird mit aufgenommen!
Julian L. schrieb: > Oder du baust dir eine > Entscheidungstabelle. Genau, darauf brachte mich dieser Statemachine hinweis. Die Software ist über 2 Jahre "gewachsen", d.h. ohne Konzept dahinter immer mehr und immer größer und es wird Zeit einen kompletten Schnitt zu machen. Nur die Basisfunktionen zu behalten aber den Überbau komplett neu aufzusetzen. Da ich fast keinerlei Hardware wie Timer etc benutze weil die ausgelagert auf externe Module ist sollte das klappen. Bei harwarenahen Cortex Projekten mit vielen Timern und Schnittstellen wäre das ein Drama.
:
Bearbeitet durch User
Ich bin jetzt auch nicht der erfahrene Programmierer. Aber ich denke du verstehst die Zusatndsmaschine falsch. Die wird letztlich auch in if then else oder als switch abgebildet, das ist aber nicht die Idee dahinter. Mit einer Zustandsmaschine kannst du das besser Strukturieren. Jeder Zustand hat eindeutige Ausgänge (Relais, ....) und der Wechsel zu einem anderen Zustand ist auch definiert. Sogar das nur bestimmt wechsel möglich sind (z.B. von Fehler nur zu Stopp). Du hast damit also nicht mehr einen großen Haufen von Bedingungen (if.. then) die alle gleichzeitig gültig sind. Das hilft vor allem der Ordnung im Kopf.
Benjamin K. schrieb: > Jeder Zustand hat eindeutige Ausgänge (Relais, ....) und der Wechsel zu > einem anderen Zustand ist auch definiert. Schon verinnerlicht. Ich arbeite dran das alles zu tabellieren und dann nur diese Tabelle abzuarbeiten.
Thorsten M. schrieb: > Ich habe Eingabe Systeme, Systemzustände, Warn Levels, Alarm Levels und > ne Menge Relais, die das System steuern, also Sensor, Logik, Aktor > Ketten. Bei Warn Levels oder Alarm müssen gewisse Eingaben blockiert > werden, bis sie wieder entsperrt werden können > Frage: Mit welcher Programmiertechnik verwaltet man sowas? Mein Ansatz wäre es, solche Konfigurationen inklusive ihrer logischer Zusammenhänge als reine Daten abzubilden, und dann einen "Interpreter" für diese Datenstruktur zu schreiben. LG, Sebastian
Thorsten M. schrieb: > Auch Statemachine können ein if then else Grab werden, um die > Abbruch Bedingungen der States zu verwalten. Der Trick ist einfach, den riesen Klopper von unwartbaren Code in einzelne kleine Statemachines runter zu brechen, die man jede für sich gut überblicken kann. Zwischen Eingabe und Ausgabe kann man ja beliebig viele Operationen ausführen. Es ergibt sich auch eine eindeutige Priorität, jede spätere Aktion kann vorherige überstimmen. Es bietet sich daher an, die Fehlertests ans Ende zu schreiben. Damit kurzzeitige Fehlerzustände nicht gleich alles zusammen brechen lassen, benutze ich meinen Scheduler, um Timeouts aufzusetzen. Ist ein Timeout noch nicht abgelaufen, gibt es nur einen Busy-Zustand, d.h. neue Aktionen werden solange angehalten. Die Aktionen erfolgen auf Bitvariablen (SBIT Macro), die zu Beginn von IO-Pins oder IO-Expander gelesen werden und am Ende dann ausgegeben werden. Die Signale für die Kontrol-LEDs werden bequemer Weise gleich als Zustandsvariable mit benutzt (muß man aber nicht). Da Relais ja nicht sofort schalten, wird das Einschalten verzögert, damit es keinen Kurzschluß gibt. Hier mal ein kleiner Codeausschnitt:
1 | void tlc_relays( void ) // switch TLC relays |
2 | {
|
3 | REL_SIMS = 0; |
4 | if ( LED_PLC_SIMS ) |
5 | REL_SIMS = 1; |
6 | if ( LED_STC ) // HFC/PLC -> STC |
7 | {
|
8 | if ( REL_DBM ) |
9 | {
|
10 | REL_DBM = 0; |
11 | SWtimer_add( rel_stc_on, DELAYED ); |
12 | }
|
13 | }
|
14 | else // STC -> HFC/PLC |
15 | {
|
16 | if ( REL_STC ) |
17 | {
|
18 | REL_STC = 0; |
19 | SWtimer_add( rel_dbm_on, DELAYED ); |
20 | }
|
21 | }
|
22 | }
|
23 | |
24 | void control( void ) |
25 | {
|
26 | key_scan(); // handle key press |
27 | stc_actions(); // stc functions |
28 | plc_actions(); // plc functions |
29 | hfc_actions(); // hfc functions |
30 | calib_select(); // select ADC for calibration |
31 | adc_select(); // select ADC input channel |
32 | hfc_relays(); // hfc relay control |
33 | tlc_relays(); // tlc relay control |
34 | }
|
Danke für deine Erkläerungen. Ich arbeite auch mit Timeouts von Sofwtaretimern bisher damit Flicker nichts bewirken. Bin aber schon dran, erstmal das Hirnschmalz in eine Excel Tabelle bringen, dann coden. Relais werden "Make before break" geschaltet, da ich Netzspannung drauf habe und mein PC nicht abstürzen soll beim Umschalten von Hausnetz auf Bordnetz. Hier mal die Anzeige im Arbeitszimmer mit Wettervorhersage von openweather-map
:
Bearbeitet durch User
Beitrag #7620901 wurde vom Autor gelöscht.
Thorsten M. schrieb: > Frage: Mit welcher Programmiertechnik verwaltet man sowas? Mit Zustandsautomaten. Und zwar geht das so weit, dass in der Mainloop quasi alles gemacht wird. Und deshalb muss die Mainloop schnellstmöglich durchlaufen werden, wenn es nichts zu tun gibt. Ein delay(4000) kommt darin nicht vor. Thorsten M. schrieb: > Harry L. schrieb: >> https://www.mikrocontroller.net/articles/Statemachine > Nützt mir nichts, kenne ich auch schon. Du verwendest es aber nicht. Sowas wie if (nowstate != zustand) ist keine FSM. Dein ganzer Ablauf ist "schon irgendwie ein wenig" ein Zusandsautomat, aber die Automaten sind über Flags kreuz und quer vernmischt und verkoppelt. Der KippManager() ist ein Killer mit seinen vielen return-Punkten. Never ever. SaveDataToFlash() ist natürlich auch völlig falsch, wenn damit dann auf das EEPROM geschrieben wird. Sowas wie Flags.IsNetzBetrieb = false; Flags.IsBatterieBetrieb = true; würde ich als Flags.Betriebsart = NETZ bzw. = BATTERIE codieren. Denn dann gibt es entweder Netzbetrieb oder Battereibetrieb aber nicht die Möglichkeit, keines davon oder gar beides gleichzeitig zu aktivieren.
Lothar M. schrieb: > SaveDataToFlash() ist natürlich auch völlig falsch, wenn damit dann auf > das EEPROM geschrieben wird. Und trotzdem funktioniert es? Beim Rest stimme ich dir zu, das ist alles kraut und rüben gewachsen.
Ich würde auch ganz klar zur FSM raten. Damit kann man solche Dinge sehr sauber abarbeiten. Aus meiner Erfahrung mit der Ansteuerung/Auswertung von kritischer Hardware (also einer, bei der es ziemlich teuer wird, wenn etwas nicht so funktioniert, wie es soll) heraus ist das für solche Zwecke die beste Art, um Fehler zu vermeiden. FSMs kannst Du übrigens auch sehr schön grafisch entwerfen und den eigentlichen Code dann automatisch generieren lassen. Das macht das Ganze nochmal übersichtlicher. Und wie immer gilt: VORHER Gedanken machen und die Zustandsänderungen so klein wie möglich halten. Die (durchaus auch komplexeren) Programme, die bei mir tatsächlich oft sofort komplett fehlerfrei laufen, sind in der Tat diese Zustandsautomaten. P.S.: Ich würde mit case/switch arbeiten - das ergibt besser lesbaren Code. Und benenne die Zustände vernünftig.
Ich persönlich finde, state machines sind ja gedanklich recht einfach zu entwerfen. Einfach einen Graphen auf Papier zeichnen. Die Umsetzung ist dann aber doch immer schwierig und vor allem fehleranfällig - gerade wenn man ab und zu was ändern muss. Daher würde ich, wenn es mir zu viel wird, ein tool suchen, das mir dabei hilft. Matlab kann sowas, ist aber mit code generierung teuer. Auf die Schnelle hab ich das hier gefunden https://github.com/StateSmith/StateSmith sieht recht umfangreich aus und die Tatsache dass der Entwurf grafisch passiert, hat Vor- und Nachteile. Vielleicht fänden es manche besser, wenn man eine andere Repräsentation hätte und dann nur grafisch prüfen müsste, ob alles passt. Geschmackssache
Chris D. schrieb: > FSMs kannst Du übrigens auch sehr schön grafisch entwerfen und den > eigentlichen Code dann automatisch generieren lassen. > Das macht das Ganze nochmal übersichtlicher. Ich frage mich gerade noch wieviele Zustände ich überhaupt habe? Die 24V Anlage kann - Batterie laden (2 Relais müssen geschaltet werden) - Netzeinspeisung aus Solar betreiben per Hoymiles Inverter (1 Relais) - 2x 100W Netzeinspeisung (China Kracher) aus Batterie betreiben (2 Relais) - Spannung aus Netz durchleiten (2 Relais) - Netzselbst bilden durch 1 kW Inverter Eingabegrößen sind 3 Kippschalter, parallel zu dazu Handy Knöpfe WLAN Relais Die Kippschalter sind nur für Netzausfall, d.h. normalerweise sind sie alle unten. Aber wenn die Cloud offline ist oder die Fritzbox soll es ja auch noch laufen. Dazu habe ich rund 10 Messwerte, die alle irgendwelche Schwellwerte darstellen oder Messgrößen. Diese werden ausgewertet und bewirken Anzeigen oder Schaltzustandsänderungen PS: Kein Wort über den Holzrahmen bitte! ;-)
:
Bearbeitet durch User
Bard oder ChatGPT spucken sowas aus
1 | enum State { |
2 | STATE_1, |
3 | STATE_2, |
4 | STATE_3
|
5 | };
|
6 | |
7 | enum Event { |
8 | EVENT_1, |
9 | EVENT_2, |
10 | EVENT_3
|
11 | };
|
12 | |
13 | void StateMachine(State currentState, Event event) { |
14 | switch (currentState) { |
15 | case STATE_1: |
16 | switch (event) { |
17 | case EVENT_1: |
18 | // Aktionen für STATE_1 und EVENT_1
|
19 | // ...
|
20 | currentState = STATE_2; |
21 | break; |
22 | case EVENT_2: |
23 | // Aktionen für STATE_1 und EVENT_2
|
24 | // ...
|
25 | currentState = STATE_3; |
26 | break; |
27 | default:
|
28 | break; |
29 | }
|
30 | break; |
31 | case STATE_2: |
32 | // ...
|
33 | case STATE_3: |
34 | // ...
|
35 | }
|
36 | }
|
37 | |
38 | int main() { |
39 | State currentState = STATE_1; |
40 | while (true) { |
41 | // Lesen der Kippschalter
|
42 | // ...
|
43 | Event event = GetEventFromSwitches(); |
44 | StateMachine(currentState, event); |
45 | }
|
46 | return 0; |
47 | }
|
Statemachines sind schon gut, aber pro Prozess. Wenn man nebenlaeufige Prozesse hat, dh solche bei denen parallel gewartet resp kommuniziert wird, muss man Multiprozess/Multitasking/Multithreading denken. Wenn man kein solches System hat, welches das von Haus aus unterstuetzt, kann man das durch mehrere Statemachines ersetzen. Das Warten muss dann eben auf alle Statemachines verteilt werden.
Thorsten M. schrieb: > Bard oder ChatGPT spucken sowas aus Ja, passt doch. Und dann wie gesagt zusätzlich zu dieser "Toplevel-FSM" noch viele kleine "Unter-FSM" zur Lösung "lokaler Aufgaben", wie z.B. das zeitgerechte Umschalten von Relais o.ä. Und alle diese "Unter-FSM" in den Unterprogrammen werden von der Toplevel-FSM, der FSM in der Mainloop aus aufgerufen. So werden alle FSM in jedem Mainloop-Zyklus durchlaufen. Und zwar schnellstmöglich und ohne Unterbrechung. In keiner Subroutine taucht ein delay() auf. Wenn ein Durchlauf der Mainloop länger als z.B. 10ms dauert, dann ist irgendwo im Programm noch ein Klopper, der in mehrere Arbeitsschritte aufgetrennt werden muss.
Hmm, schwierige Sache, Statemachine klingt gut, insbesondere mit Zerlegung in Substates (untergeordnete Statemachines). Aber bitte beachten, dass du das Zeug erstmal nur auf die Ist-Werte anwendest (und die Konsequenzen für die Sollwerte darauf ableitest) sonst kommst du vermutlich in Teufels Küche. Was du z.b. machen könntest: eine große struct als static oder meinetwegen globale Variable, die immer deine Ist-Zustände abbildet. In die kannst du dann auch unter-structs mit einbauen. z.B.
1 | struct State |
2 | { |
3 | struct SubState_Sensor |
4 | { |
5 | int value; |
6 | int pressure; |
7 | } thermometer; |
8 | } gState; |
und dann deine Statemachine, die eben die gState-Variable auswerten:
1 | void checkState(const struct State*); |
Du scheinst eher C zu benutzen, sonst hätte ich dir auch das State-Pattern in C++ nahegelegt, aber so könnte das gehen. Wie gesagt, im ersten Schritt keine Sollwerte mit reinbringen!
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.