Forum: Mikrocontroller und Digitale Elektronik C Programmierung: Wie komplexe Schaltwerke realisieren`?


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Thorsten M. (cortex_user)


Angehängte Dateien:

Lesenswert?

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
von Harry L. (mysth)


Lesenswert?


von Thorsten M. (cortex_user)


Lesenswert?

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
von Thorsten M. (cortex_user)


Lesenswert?

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 ....

von Dirk F. (dirkf)


Lesenswert?

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
von Julian L. (rommudoh)


Lesenswert?

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.
von Thorsten M. (cortex_user)


Lesenswert?

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!

von Thorsten M. (cortex_user)


Lesenswert?

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
von Benjamin K. (bentschie)


Lesenswert?

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.

von Thorsten M. (cortex_user)


Lesenswert?

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.

von Sebastian W. (wangnick)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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
}

von Thorsten M. (cortex_user)


Angehängte Dateien:

Lesenswert?

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.
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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.

von Thorsten M. (cortex_user)


Lesenswert?

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.

von Chris D. (myfairtux) (Moderator) Benutzerseite


Lesenswert?

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.

von A. S. (rava)


Lesenswert?

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

von Thorsten M. (cortex_user)



Lesenswert?

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
von Thorsten M. (cortex_user)


Lesenswert?

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
}

von Purzel H. (hacky)


Lesenswert?

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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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.

von Falk S. (falk_s831)


Lesenswert?

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
Noch kein Account? Hier anmelden.