Ich entwickele zurzeit eine Arduino Bibliothek um Steuerungsaufgaben und
das Handling von Ein- und Ausgabe zu vereinfachen. Vielleicht habt Ihr
ein paar Anregungen bevor ich das Teil bei github veröffentlichen werde.
Dadurch das die Programmlogik von den Ein- und Ausgabefunktionen durch
die IO Controller strikt getrennt sind, läuft das untere Codebeispiel
ohne Änderungen sowohl auf einem Arduino als auch auf dem PC
(Visualstudio c++).
Aus diesem Grund habe ich z.B. Serial durch den Namen printStream
ersetzt. Auf dem Arduino wird dann die UART Ein-/Ausgabe genutzt und auf
dem Windows PC die Console. Die Bibliothek bildet auf dem Windows PC
einige Arduino Funktionen(Streams,Print,String e.t.c.) nach, falls diese
auf dem PC nicht verfügbar sind.
----------------------
Das folgende Beispiel benutzt die Bibliothek:
/*PLC Node Calls e.g.clock, ... must not be used within if statemnts or loops.
53
Only linear sequences are permitted !!!!
54
forbidden example:
55
if (condition)
56
{
57
a = clock(1000);
58
b = clock(2000);
59
}
60
*/
61
stopwatchTempValue=stopwatch(btn1);
62
if(fall(btn1))
63
{
64
measuredTime=stopwatchTempValue;
65
clockRun=true;
66
}
67
clockResult=clock(measuredTime);
68
clockTick=clockRun&clockResult;
69
}
70
}example;
71
voidsetup(){
72
printStream.begin(9600);
73
io=newPlcStdIO(&printStream);
74
example.registerIO(io);
75
machine.registerProcess(&example);
76
PlcStream*st;
77
PlcBugStream*m;
78
}
79
voidloop(){
80
machine.tick();
81
}
Die Klasse Example macht folgendes:
Wenn ein Button gedrückt und losgelassen wird, wird die Zeit zuwichen
den beiden Tastenzuständen gemessen und angezeigt (measured time=). Im
Anschluss gibt ein periodischer Timer das gemessene Zeitinterval als
High und Low Flanken aus (clock tick=1, clock tick=0 ..... siehe
Screenshot).
Um die Zeitmessung zu starten wird auf dem Windows PC in der Konsole
btn1=1 und btn1=0 hintereinander eingegeben, um den IO Port des Arduinos
zu simulieren.
Zusätzlich kann mit den Kommandos run und stop die Ausgabe gestartet und
beendet werden.
Wenn das Programm auf einem Arduino laufen soll, wird zusätzlich ein
HardwareIO Conroler registriert, welcher das Prozessabild der ein- und
Ausgänge auf die IO Pins abbildet. Das eigentliche Programm hat keine
Kenntnis darüber welche IO Hardware existieren.
-----------------------------------
Die virtuelle Funktion onRegister bindet Variablen an beliebige IO
Controller. Zudem wird gleichzeitig ein Name zugewiesen wie die Aus- und
Eingabe erfolgen soll. Propertys wie z.B. ReadOnly sind auch möglich.
Außerdem könne hier auch Kommandos registriert werden.
Zurzeit existieren IO Controller für die Kommandozeilenein-/ausgabe und
die Hardware IO Pin Ein/Ausgabe. Es folgen noch weitere für ModBus, I2C
und mqtt.
Die virtuelle Funktion onMessage reagiert auf Änderungen von gebunden
Variablen und Kommandos.
Die virtuelle Funktion onTask bildet SPS/PLC Funktionalität ab. Es wird
im Hintergrund das Prozessabbild der Ein/-Ausgänge ausgewertet und die
daraus erfolgten Variablenänderungen an die registrierten IOController
weitergeleitet.
Es sind folgende SPS/Plc Standardfunktionen implementiert.
1
boolrs(boolset,boolreset);
2
Int32ctu(booltrigger,boolreset,Int32pv,bool&q);
3
boolsr(boolset,boolreset);
4
// Ausschaltverzögerung
5
booltof(booltrigger,Int32duration);
6
// Einschaltverzögerung
7
boolton(booltrigger,Int32duration);
8
/// Impulsbildung
9
booltp(booltrigger,Int32duration);
10
boolclock(Int32interval);
11
boolgateBool(boolopen,boolvalue);
12
Int32gate(boolopen,Int32value);
13
Int32stopwatch(boolrun);
Bei jedem Funktionsaufruf werden falls notwendig, im Hintergrund
automatisch Merker zugewiesen und dessen Zustände beim nächsten
Funktionsaufruf abgerufen. Es können z.B. beliebig viele clock Aufrufe
mit unterschiedlichen Zeiten verwendet werden. Alles läuft asynchron und
das Hauptprogramm wird nicht blockiert wie es z.B. bei delay Aufrufen
der Fall wäre
-------------------
die setup Funktion:
- serial wird durch printStream ersetzt
- der PLCStdIO Controller ist ein IOController der ein Ausgaben für die
Kommandozeile bereitstellt. Jede gebundene Variable kann mit
Variablenname=Wert zugewiesen werden und jede Werteänderung wird als
Variablennam=Wert ausgegeben. Der PlcStdIO Controller kann beliebige
Arduino Streams wie z.B. die Serials oder bei einem ESP32 über Wifi
einen Telnet Dienst bedienen.
- die Maschine kann beliebig viele Prozesse mit unterschiedlichen
Prioritäten registrieren.
die loop Funktion:
zyklicher Aufruf der Bibliothek
viele Grüße
> Vielleicht habt Ihr ein paar Anregungen
Ja, man benutzt einen Controller der bereits auf
unterster Ebene dafuer passende Befehle bereitstellt.
Dann reichen dafuer Assemblermakros.
Der Restaufwand ist gerade noch ein Scheduler, der sich um
Tasks, Queues und Timerevents kuemmert.
> beliebig viele clock Aufrufe
Glaube ich nicht.
> wird nicht blockiert wie es z.B. bei delay Aufrufen> der Fall wäre
Das ist woanders der Normalfall.
> auf einem Arduino als auch auf dem PC
Wozu soll das denn gut sein?
> kann beliebige> Arduino Streams wie z.B. die Serials oder bei einem ESP32 über Wifi> einen Telnet Dienst bedienen
Die je nach Medium aber einige 100 bis einige 10000 ms fuer die
Ausfuehrung brauchen. :)
> stell es online und gut
Ja, da stoert es keinen.
Motopick schrieb:> Glaube ich nicht.
Da gebe ich Dir recht. Ich korrigiere: Beliebig viele so lange es die
Ressourcen der Hardware zulassen.
Natürlich wird für jeden Aufruf Speicherplatz auf dem Stack reserviert.
Klar wenn der Speicher voll ist können auch keine Merker im Hintergrund
reserviert werden.
Motopick schrieb:> Wozu soll das denn gut sein?
Debuggen auf einen PC in Visualstudio ist um einiges effektiver als auf
dem yC.
Motopick schrieb:> Die je nach Medium aber einige 100 bis einige 10000 ms fuer die> Ausfuehrung brauchen. :)
Lass es mal bei einem Stream für die Serial Ausgabe 10-50 ms sein .
100-10000 ms ist schon sehr übertrieben.
Für Hardware IO's werden in den IO Controllern natürlich KEINE Streams
benutzt. Da liegen die Latenzzeiten unter 1 ms.
Also ich gucke da rein, wie ein Schwein ins Uhrwerk. Da ist wohl der
Großteil der Funktionalität in den nicht gezeigten h-Files versteckt.
Ich mag es bei Steueraufgaben, wenn alle Abläufe einfach zu verfolgen
sind. Also wann wird was und in welcher Reihenfolge ausgeführt. Und
nicht über 1000 Instanzen hinweg von hinten durch die Brust ins Auge.
Z.B. wodurch die Anzeige von measuredTime getriggert wird, ist mir
vollkommen unklar.
Peter D. schrieb:> Also ich gucke da rein, wie ein Schwein ins Uhrwerk.> Da ist wohl der> Großteil der Funktionalität in den nicht gezeigten h-Files versteckt.
Bei einem solchem Framework ist es normal das Funktionalität abstrahiert
und hinter einer Fassade versteckt wird. Genau das ist Sinn und Zweck
der Sache. Letztendlich dient es dazu Dinge schneller und fehlerfreier
zu entwickeln.
Die ganze Bibliothek hat ca. 85 KB Quellcode. gPeter D. schrieb:> Z.B. wodurch die Anzeige von measuredTime getriggert wird, ist mir> vollkommen unklar.
Ein Zeiger auf die Variable measuredTime wird mit der Funktion add(int&
ref, const char* name) dem IO Controller gemeldet. Beim Aufruf der add
Funktion wird im Controller zusätzlich eine Kopie der Speicherzelle
erzeugt. Bei jedem Zyklus wird die referenzierte Variable mit der Kopie
verglichen. Wenn die beiden Werte unterschiedlich sind, wird eine
Triggerfunktion ausgelöst und die Kopie aktualisiert.
Wir koennen ja nicht kommentieren, weil wir nicht die Quellen und
Examples gesehen haben.
- Vergleich Performance/Umfang z.B. zu FreeRTOS oder Azure? IO
(Disk/Netzwerk)
- Unterstuetzte Prozessoren ("Arduino" ist ein Framework, keine
Hardware)
- Ist das ein Ersatz fuer ein RTOS?
Du solltest versuchen, Deine SW in einem anderen Forum vorzustellen
(hier wird es zerredet) oder ein Abo fuer "Asbestos Underwear" haben.
Thomas K. schrieb:> Bei jedem Zyklus wird die referenzierte Variable mit der Kopie> verglichen. Wenn die beiden Werte unterschiedlich sind, wird eine> Triggerfunktion ausgelöst und die Kopie aktualisiert.
Das wäre mir zu aufwendig, haufenweise Schattenspeicher einzurichten und
zu vergleichen.
Ich habe auch die Erfahrung gemacht, daß es sicherer ist, alle Ausgänge
zyklisch neu zu setzen, egal, ob sie sich geändert haben. Es können ja
Störungen auf SPI, I2C usw. einstrahlen, ein DAC kippt um, eine Anzeige
zeigt Müll an usw. Oder es passiert ein kurzer Spannungseinbruch, den
die CPU gar nicht mitkriegt.
Thomas K. schrieb:> clockTick = clockRun & clockResult;
Ich würde diese Variablen aber als bool definieren und mit &&
verknüpfen.
Oder ist das & überladen und was bewirkt es hier?
Peter D. schrieb:> Ich habe auch die Erfahrung gemacht, daß es sicherer ist, alle Ausgänge> zyklisch neu zu setzen, egal, ob sie sich geändert haben.
Bei direkten IO's ist das sicherlich die Standardvorgehensweise, die
Ports immer wieder zyklisch neu zu setzen. Aber nehmen wir einmal an, Du
hast einen Zyklus der 10 ms dauert und Du möchtest keinen direkten IO
setzen, sondern irgendetwas das sich außerhalb des Controllers befindet.
Zum Beispiel eine SCADA Anzeige über Modbus RS485. Dann würden in dieser
Situation (alle Ausgänge zyklisch neu setzen)alle 10 ms der gesamte
Status der Ausgänge über den Bus verschickt. Wenn 32 solcher Ausgänge
übermittelt werden sollen und jeder genau 1 byte an Informationen hat,
dann wären das 256 Bit's netto + Overhead. Mit diesen 256 Bit's alleine
wären bei einer Zyklusrate von 10 ms schon 25.600 Baud komplett
ausgelastet. Den Overhead noch gar nicht mit einberechnet.
Da erstens das menschliche Auge eine solche Anzeigerate auf der SCADA
GUI gar nicht benötigt und zweitens es auch durchaus Maximalbegrenzung
einer Bandbreite bei einem Datenbus gibt, finde ich das neu senden aller
Daten bei jedem Zyklus für keine so gute Lösung. Und wenn Du dann noch
Pesch hast, läuft Dir der Sendebuffer über oder der Prozess blockiert,
weil alles verstopft. Ja kann man so machen, natürlich. Hier finde ich
es besser nach jedem Zyklus die Änderungen zu erfassen und nur die
Änderungen zu übertragen. ggf. alle 100 Zyklen zusätzlich einmal eine
Komplettsynchronisation. Das ganze zusammen mit einem
Bandbreitenmanagement das im Hintergrund läuft und notfalls den
Datenstrom abbremst.
Peter D. schrieb:> Ich würde diese Variablen aber als bool definieren und mit &&> verknüpfen.> Oder ist das & überladen und was bewirkt es hier?
Fehler von mir, müßte eine boolsche Verknüpfung sein und keine
mathematische AND Operation auf den Integerwert. Obwohl, solange der
Integer nur 1 und 0 als Wert annehmen kann müsste sich das Programm
gleich verhalten.
Vax W. schrieb:> - Ist das ein Ersatz fuer ein RTOS?
Wenn ich die Frage an dieser Stelle mit ja beantworten würde, wäre das
formell falsch, weil es kein Multithreading gibt. Aber es gibt auf der
Anwendungsebene eine Art "gefakte Paralellität", weil vieles in möglich
kleine Slices aufgeteilt wird. Der Parser z.B. der die String Eingaben
parst, liest pro Zyklus eine gewisse feste Anzahl an Bytes aus dem
Stream und speichert den Status zwischen, bis der Ausdruck vollständig
gelesen und interpretiert ist. Das Gegenteil wäre z.B. alles zeilenweise
zu lesen und die Zeile dann komplett auf einmal auszuwerten. Aus meiner
Erfahrung führt das dann manchmal dazu, das der Zyklus zu lange
unterbrochen wird und über die Schwelle der maximal gewünschten
Zyklusdauer geht.
Vax W. schrieb:> - Unterstuetzte Prozessoren ("Arduino" ist ein Framework, keine> Hardware)
Das stimmt. Aber die Adrdunio Plattform hat klar definierte
Schnittstellen für die Bibliotheken und einen HAL Layer für die
darunterliegende Hardware. Solange es eine Arduino kompatible HAL
Implementation und einen C++ Compiler für den Prozessor gibt, sollte es
darauf laufen. Ich habe bis jetzt noch nicht alle getestet, aber Arduino
MEGA 2560, Arduino DUE, Arduino Zero, ESP32, ESP8266 laufen damit, weil
ich die selber zu Hause habe
Thomas K. schrieb:> ...
Da du klug bist, ist dir auch klar, dass ich das nicht testen/beurteilen
kann.
Und da du schlau bist, sorgst du absichtlich dafür.
(warum auch immer)
Als alter PLC/SPS Liebhaber/Nutzer verstehe ich dein Vorhaben, habe
allerdings gänzlich andere Wege genutzt um das für meine Belange zu
realisieren. Z.B. mich Für die Datenfluss Variante entschieden, wie sie
z.B. von den Koppelplänen bekannt ist.