Ich probiere gerade mit der UIFlow-IDE ein kleines Programm zu erstellen, das mittels ESP-Now eine Verbindung herstellt und Daten austauscht.
Mein erster Versuch (Master):
Zuerst wird ESP-Now initialisiert.
Dann wird ein Name (SSID) für die Arbeitsgruppe festgelegt.
Und schließlich die eigene MAC-Adresse geholt und angezeigt.
In der folgenden Schleife wird nach einem Mitglied der Arbeitsgruppe gesucht und dessen MAC angezeigt.
Das Mitglied wird dann in eine Liste eingetragen.
Die MainLoop ist noch nicht getestet.
Das Programm auf dem Slave:
Auch hier wird zuerst ESP-Now initialisiert.
Dann wird der Arbeitsgruppennamen (SSID) festgelegt und bekanntgemacht.
Schließlich wird die eigene MAC-Adresse geholt und angezeigt.
Mehr macht dieses Programm nicht. Das genügt aber, um vom Master erkannt zu werden!
Eine zuverlässige Datenübertragung hat aber nicht funktioniert.
Ein zweiter Anlauf
Also das Beispiel von MStack hat nicht funktioniert. Ich habe heute einen neuen Anlauf gemacht. Es funktionierte wieder nichts. Also habe ich einen ESP32 genommen und die Micropython Demo als Grundlage genommen. Das funktionierte in Thonny:
# ESPNow_test_001.py
#
import network
import espnow
sta = network.WLAN(network.STA_IF) # Or network.AP_IF
sta.active(True)
e = espnow.ESPNow()
e.active(True)
peer = b'\x94\xb9\x7e\x8d\x41\xdc'
e.add_peer(peer)
Dieses kleine Programm hat einen ESP-Now Sender erzeugt. Mit
>>> e.send(peer, "Starting...")
True
habe ich senden können. Das Programm RX_001.m5f hat diese Aussendungen empfangen. Es waren noch kleine Fehler drinn, deshalb hier die endgültige Fassung:
Wenn eine Message empfangen wird, so wird die MAC des Senders und die Nachricht für 1 Sekunde angezeigt, dann erscheinen wieder —.
Dann habe ich am Senderprogramm gearbeitet. Das automatische Suchen von Peers habe ich rausgeschmissen und die MAC des Peers (Empfänger) ins Programm geschrieben. Damit funktionierte dann auch der Sender. Hier das Programm TX_003.m5f:
Damit ist die Grundlage für eine einseitige Verbindung mittels ESP-Now geschaffen.
Das automatische finden des Peer hat prinzipiell funktioniert. Allerdings wurde eine Slave-MAC angezeigt, die um 1 höher war, als die vom Slave selbst angezeigte.
Nachgedanken
Es ist möglich, dass der Erste Versuch unbefriedigend verlief, weil ich den Kanal auf 0 eingestellt hatte. Kanal 0 bedeutet, das der eingestellte Kanal verwendet wird. Wenn sich aus irgendwelchen Gründen bei Sender und Empfänger unterschiedliche Kanäle einstellen finden sie sich nicht.
Das werde ich vielleicht später mal testen. Für eine vorgesehenen Reichweitentest ist das jetzt ausreichend.
Namensräume bei mehreren hintereinander ausgeführten Scripts.
Version 1.11.8
Wenn 2 Scripte nacheinander ausgeführt werden ohne den ESP32 zwischenzeitlich neu zu booten, so sind im M5Stick C Plus die Namen der Objekte des zuerst ausgeführten Scripts im anschließend ansgeführten Script bekannt.
Beim M5STAMP Pico ist das nicht der Fall! offenbar wird vor dem Start des zweiten Scripts hier der Speicher geleert. Der freie Speicher war nach dem Ausführen von 2 Scripten genauso groß wie nach dem Ausführen nur des zweiten Scriptes. Das gilt nur für die REPL! Im Normalbetrieb sind in main.py die Objekte aus boot.py sichtbar.
libs.urequests beim M5ATOM Lite funktioniert nicht.
Beim M5Stick C Plus und beim M5STAMP Pico tut sie was sie soll.
Nachdem der Fernsteuersender funktioniert ist jetzt der Empfänger dran.
Anforderungen
Die Umsetzung in kleinen Schritten
Das MAC-Adress Problem
Das erste Problem war, dass der M5Stack C mir mit dem Block „get mac adress“ 24:a1:60:53:bc:15 anzeigt. Als Absenderadresse sendet er aber 24:a1:60:53:bc:14.
Der erste Versuch – Kontaktaufnahme
Als die MAC-Adresse angepasst war funktionierte das erste Programm:
Der zweite Versuch – Daten extrahieren – dauert etwas länger
Nun der nächste Versuch. Die Daten hatte ich im Senderprogramm in eine Liste getan. So werden sie auch auf dem vorherigen Bild im Empfänger dargestellt. Diese soll nun entpackt werden, damit ich die einzelnen Werte verwenden kann:
Das Ergebnis verwundert mich dann doch sehr:
Das sieht so aus, als wären die Daten nicht als Liste, sondern als String übertragen worden. Index 0 gibt das letzte Zeichen „]“ aus, Index 1 das erste „[“ und Index 2 das zweite „0“.
Der Versuch den empfangenen String in eine Liste umzuwandeln bleibt auch erfolglos.
Die list() Funktion aus Python habe ich dann versucht:
Erstaunlich ist jedoch, dass die Ausgabe von rx_list leer ist oder ein # enthält (erste Zeile im Display), wärend die daraus extrahierten Werte „richtig“ angezeigt werden:
Allerdings ist damit dem Problem nicht bei zu kommen. Hier macht die typlosigkeit von Python wohl Probleme.
Der Versuch rx-data zu addieren endet mit einer Fehlermeldung (rotes Kreuz auf dem Display) ohne weiter Informationen.
Wenn rx_data aber vorher in ein Integer umgewandelt wird funktioniert die Addition und es wird die 2 im Display angezeigt.
Damit ist klar, der Wert wird immer als String gesendet! Und die Konvertierung in eine Liste funktioniert nicht. Nun ist guter Rat teuer. Mit Stringverarbeitung möchte ich mich hier nur ungern abgeben. Dauert lange und braucht viel RAM.
Das hätte ich viel einfacher haben können:
espnow.send(id=1, data=str(send_data))
So steht es im Micropython Code.
Trotz Brille
Diesen Block habe ich glatt übersehen.
Ins Programm eingefügt:
Und …
Das sieht doch schon besser aus. Die einzelnen Werte sind nun richtig getrennt. Aber die Klammern stören noch und es sind Leerzeichen enthalten.
Wie erwartet führt dieser Versuch zu einer Fehlermeldung, weil nicht numerische Zeichen enthalten sind. Also bleibt nur noch die Klammern und Leerzeichen gezielt zu entfernen.
Die Zeile
espnow.send(id=1, data=str(send_data))
bringt mich auf eine andere Idee. Den Micropython mit dem Ausführen Block ohne str() einbauen. Mal sehen ob’s klappt.
Fehlermeldung: „Type should be bytes or string“. Es wäre auch zu schön gewesen.
Also musste ich mich doch mit der Stringmanipulation beschäfftigen. Allerdings war es dann doch nicht schlimm wie ich befürchtet hatte. Zuerst habe ich die eckigen Klammern aus dem String rx_data entfernt. Dann daraus eine Liste erstellt und bei der Gelegenheit gleich die überflüssigen Leerzeichen entfernt. Schließlich erfolgt noch die Umwandlung in Integer oder Float. Damit habe ich die Daten so wie ich sie haben möchte.
Das Programm dazu sieht so aus:
Und die Anzeige zeigt rx_list immer noch nicht an. Die anderen Ausgaben entsprechen den Erwartungen. X-Wert (int), Y-Wert (int) und Batteriespannung (float).
Der Grund für die fehlend Anzeige von rx_list ist vermutlich, dass der Block „LCD ausgeben“ keine Typumwandlung von list zu String vornimmt. Das baue jetzt mal ins Programm ein.
… das war’s:
Das Ziel dieses Programms ist aber nicht die Werte anzuzeigen, sondern Servos anzusteuern. Also los auf zur nächsten Runde!
Etwas mit den Daten machen.
Die empfangenen Daten sollen 2 M5Stack 360° Servos als Antrieb für ein kleines LEGO-Fahrzeug steuern. Deshalb habe ich zuerst ausprobiert wie die Servoansteuerung funktioniert. Dazu habe ich ein kleines Gestell mit LEGO Steinen gebaut an dem die 360° und 180° Servos befestigt sind.
Das ist mein erstes Programm zum Servotesten:
Zur Servoansteuerung wurden nur die 4 Blöcke „set servos rotate“ hinzugefügt. Wenn man vorher am Servos Hat den kleinen Schalter auf ON stellt klappt’s dann mit den Servos.
Vorher musste ich die empfangen Daten aber noch aufbereiten. Die Servoansteuerung erfolgt mit Ganzzahlwerten zwischen 0 und 180. Bei den Servos ist also 90 die Grundstellung bzw. der Motorstillstand. Das ist ein Anlass die Übertragung von Wertung zwischen -100 und +100, die ich im Moment im Sender implementiert habe zu überdenken. Das hat aber noch Zeit. Zuerst soll alles zuverlässig funktionieren. Dann kann ich an Modifikationen denken.
Der Wert muss dem Servo nur einmal übermittelt werden. Er bleibt dann solange eingestellt bis ein neuer Wert übermittelt wird.
Das erste Testprogramm wertet nur die X-Achse aus und schickt diesen Wert zu allen 4 Servos. Die 180° Servos bewegen sich dabei parallel, die 360° drehen hingegen entgegengesetzt.
XY zu 2X
Die Überschrift dieses Kapitels sieht nach Mathe aus und ist es auch. Vom Sender kommen Werte für die X- und Y-Achse. Wenn aber keine normale Steuerung wie sie im Kfz üblich ist eingesetzt wird, sondern 2 Motoren parallel laufen, dann müssen diese unterschiedlich schnell drehen um eine Kurve zu fahren. Diese Aufgabenstellung gibt es auch in der Fliegerei bei Flugzeugen mit V-Leitwerk.
Nach einigen Überlegungen und Versuchen bin ich zu folgender Lösung gelangt. Die X- und Y-Werte werden für den einen Motor addiert und für den anderen subtrahiert. Dann sind noch einige Anpassungen erforderlich und schon passt es.
Nun noch die detailierteren Erklärungen:
Hauptprogramm
Das Hauptprogramm enthält hier die wesentlichen Teile des Code.
Setup
Im Setupteil wird zuerst das Display eingerichtet. Anschließend mit dem „Add peer“ Block ESP Now eingerichtet. Im zweiten „Add Peer“ Block wird die MAC Adresse des Senders eingetragen, damit der Empfänger Daten zum Sender schicken kann, z.B. Batterie ist schwach. Diese Funktion ist noch nicht implementiert. Die Variable rx_flag signalisiert dem Hauptprogramm wenn neue Daten eingetroffen sind. Deshalb erstmal auf 0 – keine neuen Daten gesetzt. Schließlich wird die RGB-LED auf dem Servos Hat in grün eingeschaltet.
Loop
In der Loop wird zuerst nachgeschaut ob Post (neue Daten) eingetroffen sind. Wenn nicht hat sie nichts zu tun und langweilt sich.
Wenn aber neue Daten vorhanden sind, also rx-flag = 1 ist, wird zuerst die RGB-LED auf rot gesetzt, das signalisiert neue Daten werden bearbeitet.
Die weiteren Abschnitte werden gleich erklärt.
Am Ende des aktiven Abschnittes wird rx-flag wieder auf 0 gesetzt und die RGB-LED wird wieder grün.
Nun zu den übersprungenen Abschnitten.
Stringliste entpacken
In Blockly verschickt ESP Now die Daten immer als String. Die Daten wurden im Sender in eine Liste gepackt. Im String wird die Liste nun incl. „[“ und „]“, Leerzeichen und Kommas dargestellt. Daraus müssen nun die Daten herrausgepult werden.
Die ersten beiden Blöcke entfernen die beiden eckigen Klammern. Aus dem übrig gebliebenen String lässt sich nun eine Liste machen. Deren Elemente sind auch Strings und enthalten noch die Leerzeichen. Diese werden mit den nächsten drei Blöcken entfernt. Gleichzeitig werden die Daten noch in integer bzw. float bei der Batteriespannung umgewandelt. Nun liegen die Daten so vor, dass sie weiterverarbeitet werden können.
XY Mischen
Hier werden die Geschwindigkeitswerte für die beiden Motoren errechnet von -100 … +100 auf 0 … 100 angehoben.
Bereichsanpassung
Nun wird noch die Drehrichtung angepasst und auf den Bereich 0 – 180 skaliert.
Anzeige
Eine Anzeige ist im Empfänger nicht wirklich erforderlich. Ich habe sie für Programmentwicklung benötigt. Diesen Teil kann man entfernen, oder sie den eigenen Bedürfnissen anpassen.
Empfangsroutine
Der Empfang der ESP Now Daten geschieht im Hintergrund. Da braucht man nichts zu programmieren. Allerdings muss bekannt sein, wo die MAC-Adresse des Absenders und die Daten gespeichert werden sollen. Die entsprechenden Variablennamen müssen „Receive“ Block eingetragen werden. Wenn Daten empfangen wurden, wird das darunter eingetragene Programm abgearbeitet (Interrupt Service Routine oder callback genannt). Hier wird nur geprüft, ob die Daten vom zugeordneten Sender kommen und eine Flagge (rx_flag) gehisst, die dem Hauptprogramm mitteilt, das es nun Daten verarbeiten muß.
Bei der MAC-Adresse die zu prüfen ist muss beachtet werden, das diese im 1 kleiner ist als die mit dem entsprechenden Block abgefrage MAC-Adresse.
Notaus
Bei meinen Experimenten ist es häufiger vorgekommen, dass die Servos nicht mehr abgeschaltet werden konnten, weil die Skalierung nicht stimmte. Deshalb habe ich mit der Taste A einen Notaus eingebaut.
Das Programm ist so wie es jetzt ist einsetzbar. Ich werde aber später noch den Rückkanal implementieren, der Statusdaten des Mobils an den Sender schickt.
Drehimpulsgeber sind beliebte und vielseitige Eingabegeräte. Im M5-Universum gibt es leider nur einen für den M5Face. Deshalb habe ich mich drangesetzt und eine Routine für die anderen M5-Geräte entworfen. Diese ermöglicht den Einsatz eines Drehimpulsgebers mit der UIFlow-IDE und Blockly / Micropython.
Als Hardware kommt ein M5Stick C plus und ein Drehimpulsgeber von az-Delivery zum Einsatz. Der Drehimpulsgeber hat 3 Pullup Widerstände auf der Platine.
Der Drehimpulsgeber liefert 3 digitale Signale. Davon liefern zwei die Information über den den aktuellen Zustand des Drehgebers und eines den Zustand des Tasters. Im Ruhezustand liefern alle drei Signale eine 1.
Es gibt im Internet so einige Beschreibungen zum Thema Drehimpulsgeber. Einige davon basieren auf Interrupts, die zwar beim ESP32 vorhanden sind, aber in UIFlow und Blockly nicht zur Verfügung stehen. Deshalb habe ich mich für einen pragmatischen Ansatz entschieden. Ich habe die 2 Bits in eine Zahl umgewandelt und mir angeschaut was passiert. Dabei ist herausgekommen, dass im eingerasteten Zustand die Zahl 3 (0b11) ansteht. Beim Drehen bis zur nächsten Rastung entsteht entweder die Zahlenfolge 201 für vorwärts oder 102 für rückwärts. Dann steht wieder die 3 beim nächsten Rastpunkt an. Es ist also nur wichtig zu wissen welche Zahl nach der 3 kommt. Ein überschaubares Problem.
Nun gibt es aber auch noch einen Taster, der ebenfalls abgefragt werden soll. Dieser liefert im Ruhezustand 1, wenn er gedrückt wird eine 0. Deshalb habe ich eine 3-Bit-Zahl zu Grunde gelegt. Es wird dann fortlaufend der Zustand des Drehimpulgebers eingelesen. Wenn eine 7 (entspricht der 3 im obigen Absatz) eingelesen wird, wird ein Flag gesetzt. Wird eine 5 (1) eingelesen, wird geprüft, ob das Flag gesetzt ist. Wenn nicht wird der Wert ignoriert. Wenn ja, so wird das Flag zurückgesetzt und -1 ausgegeben. Bei einer 6 (2) erfolgt die selbe Prozedur, nur wird dann 1 zurückgegeben. Wenn die Zahl < 4 ist, ist der Taster gedrückt und es wird eine 0 zurückgegeben. Die anderen Werte interessieren dann nicht. Wird eine andere Zahl eingelesen, wird diese ignoriert.
Bei dieser Lösung wird das Programm blockiert, solange keine Eingabe erfolgt. Es können auch keine Tastendücke unterschiedlicher Länge identifiziert werden. Weiterhin findet keine Entprellen der Kontakte statt, so dass es gelegentlich zu falschen Werten kommt. Wenn die Impulse nicht gezählt, sondern als Zahl im Display angezeigt werden ist das zwar nicht schön, aber für Bastler m.E. aktzeptabel. Für den Anfang soll es so genügen. Spätere Verbesserungen sind ja immer möglich.
Zunächst müssen unter Setup die verwendeten Pins initialisiert werden:
Und nun das Programm:
Nach einigem hin und her funktionierte das Programm dann ordnungsgemäß. Anfangs gab das Programm zwar die Werte 1 und -1 korrekt zurück, bei der 0 wurden aber immer viele Nullen ausgegeben. Ich habe lange nach der Ursache gesucht. Schließlich stellte ich fest, dass der nächste und viele weitere Aufrufe von dig_auswerten erfolgten bevor ich den Taster wieder losließ. Deshalb habe ich die Warteschleife für die Tasterauswertung eingebaut. Nun läuft das Programm.
Einen eigenen Block erzeugen
Nun werde ich versuchen einen eigenen Block für diese Funktion zu erzeugen. Eine umfangreiche Beschreibung des Blockmaker werde ich an anderer Stelle machen. Hier nur soviel wie nötig.
Es werden drei Blöcke benötigt:
init_dig – zum Initialisieren der Ports
read_dig – den aktuellen 3-Bit-Wert ermitteln
get_dig – daraus den Rückgabewert erzeugen und zurückgeben
Ich habe mich hier für englische Bezeichnungen entschieden, weil es sich dabei um die internen Bezeichnungen handelt und Englisch in der Programmierung Standard ist. Wobei dig dann in ris (rotary impuls switch) geänder werden müsste. Der Name des Blocks kann in Deutsch erfolgen.
Nachdem ich nun einige Zeit mit dem Blockmaker oder besser mit meinen mangelnden Pythonkenntnissen und einigen Nachlässigkeiten beim Testen zu kämpfen hatte, ist es mir gelungen entsprechende Blöcke zu erstellen.
Da die UIFlow-IDE die Blöcke wie Macros in den Code einfügt, ist der Block read_dig überflüssig geworden. Dessen Code habe ich gleich bei get_dig eingefügt. Die beiden übriggebliebenen Blöcke heissen jetzt init_dig und hole nächsten Impuls. Wie versprochen in deutsch.
Anwendung der Blöcke
init_dig
Wie der Name schon verrät wird mit diesem Block die Verwendung eines Drehimpusgebers initialisiert. In den Feldern pinA und pinB sind die Nummern der Pins anzugeben an denen die Anschlüsse A und B oder wie immer sie heissen mögen des Drehimpulsgebers einzutragen. Die Pinnummer des Tasteranschlusses wird bei pinT eingetragen. Das ist schon alles was zur Vorbereitung des Drehimpulsgeber Einsatzes erforderlich ist.
Hier gibt es aber einige Einschränkungen zu beachten: Es kann nur ein Drehimpulsgeber angeschlossen werden. Der Drehimpulsgeber kann nur direkt an Pins des M5-Gerätes angeschlossen werden. Ein I/O-Expander kann nicht eingesetzt werden. Weiterhin werden die drei Pins intern als Eingang und float initialisiert. Es ist also jeweils ein externen Widerstand gegen +3,3V erforderlich, so wie es bei dem von mir verwendeten Modul der Fall ist. Letzters kann leicht mit dem Blockmaker im Code geändert werden.
Da habe ich wohl schon ein neues Projekt 😉
hole nächsten Impuls
Die Impulse des Drehimpulsgebers werden einzeln geholt. Der Rückgabewert dieses Blocks ist:
1 = ein Schritt vorwärts
-1 = ein Schritt rückwärts
0 = Taster gedrückt (erst wenn Taster losgelassen wurde)
Wobei vorwärts und rückwärts sich auf meinen Testaufbau beziehen und hängt davon ab, welche internen Schalter des Drehimpulsgebers jeweils an pinA und pinB liegen. Wenn die falsche Richtung erscheint kann man:
die Kabel an pinA und pinB vertauschen,
im Blockmaker die Ausgabewerte ändern oder
die Auswertung umstellen auf -1 vorwärts und 1 rückwärts.
Wichtiger Hinweiß
Wenn Du in einem Blockly-Programm eigene Blöcke verwendest und es erneut in die IDE laden willst, so musst Du vorher die eingen Blöcke (das *.m5b-File) in die IDE laden, sonst wird das Blockly-Programm nicht geladen!
Nun viel Spaß beim Basteln.
Download
Geht noch nicht, weil WordPress den Upload der Datei noch blockiert und „WP Extra File Types“ nicht funktioniert. Ich arbeite daran.
wenn kein Blockcode eingetragen ist speichert der Blockeditor nicht. Im Zweifelsfall hilft ein # (Kommentar) Zeichen im Codefeld.
Kontakt
Wenn Jemand sich zu diesem Beitra äußern möchte, mich auf Fehler hinweisen oder Tipps dazu geben möchte, so geht dass über die Emailadresse peter@peters-bastelkiste.de
Die Kommentar- und Diskussionsfunktion ist noch nicht freigeschaltet.
Ich würde gerne in einem ESPNow-Netzwerk auch mit dem PC mitmachen. Vor allem, um einem Programm auf dem PC den Zugriff auf Geräte im ESPNow-Netzwerk zu ermöglichen. Deshalb habe ich mit einem M5ATOM lite ein entsprechendes Programm entworfen.
Hier ist der erste funktionsfähige Entwurf:
An diesem Entwurf gibt es noch viel zu tun. Die grundlegende Brückenfunktion zwischen ESPNow und PC funktionieren aber schon. Bisher können auf der ESPNow Seite aber nur Broadcast-Nachrichten benutzt werden. Später wird es erforderlich sein auch mit einzelnen Geräten direkt zu kommunizieren.
Die Variable serialmode wurde eingebaut, um ggf. den Echobetrieb, wie er standardmässig von Putty verwendet wird zu unterstützen. Sonst sieht man nicht was man schreibt. Das ist aber nur für Terminalbetrieb sinnvoll. Man kann natürlich auch im Terminalprogramm den Echomodus abschalten. Wenn ein Programm auf das ESPNow-Netzwerk zugreift, weiß es was es tut und benötigt die Rückmeldung nicht, es wird dadurch eher verwirrt.
Die serielle Schnittstelle (UART) ist mit den Pins 1 und 3 verbunden. Das ist der Anschluß des Seriell zu USB-Wandlers. Deshalb kann der PC einfach mit einem USB-Kabel angeschlossen werden. Über die entsprechende COM-Schnittstelle kann dann das ESPNow-Netzwerk erreicht werden.
Fortsetzung folgt …
2. Anlauf
Beim ersten Versuch habe ich als Hardware einen M5ATOM lite genommen. Nun wollte ich aber mehr Kontrolle über das Geschehen haben und auf einen M5Sick C+ zurückgegriffen, der gerade auf dem Arbeitsplatz herum lag. Die empfangenen Daten werden jetzt auf dem Display angezeigt.
So sieht das ganze Programm aus:
Das Zweite Programm für den M5Stick C+ (ESPNowAdapter_004.m5f)
Da die Einzelheiten darauf nur schlecht zu erkennen sind folgen jetzt die 3 Blöcke einzeln:
Das Hauptprogramm
Hauptprogramm
Display darstellen.
Diese Funktion stellt die Informationen auf dem Display dar. Allerdings nur halbherzig. Es gibt nur die seriell empfangenen Daten aus. Da es mir in diesem Programm nicht auf ein perfektes Design, sondern auf den Funktionalität ankommt, habe ich keine weitere Zeit inverstiert. Wird vielleicht später nachgeholt.
Funktion: refresh_screen
Die ESP Now Empfangsroutine
ESP Now arbeitet interruptgesteuert. Wenn eine Nachricht eintrifft, wird ein Inerrupt ausgelöst, das Hauptprogramm unterbrochen und die Interrupt Service Routine abgearbeitet. Also das was im nächsten Bild zu sehen ist. Anschließend wird das Hauptprogramm dort fortgesetzt wo es unterbrochen wurde.
Die Interrupt Service Routine für den ESP Now Empfang
Auf der seriellen Seite sieht es in einem Terminal u.B. so aus:
Terminalfesnster
Die letzte, die drittletzte und die fünftletzte Zeile wurden vom Terminal zum ESP Now Netz geschickt.
Heute ist es soweit. Ich werde meine ersten Versuche mit ESP Now unternehmen.
Das Sendeprogramm
Zuerst habe ich einen Sender erstellt. Dazu habe ich einen M5ATOM lite genommen. Dieser soll auf Knopfdruck eine Nachricht (Hallo #lfd.Nr.) senden. Diese Nachricht wird zur Kontrolle auch über ein UART und die USB-Schnittstelle an einen PC gesendet. Hier zeigt Putty die Nachricht an. Die RGB-LED des M5ATOM lite leuchtet normal gelb. Wenn die Nachricht erfolgreich versendet wurde leuchtet sie kurz blau und wenn sie an den PC übermittelt wurde kurz grün auf.
Mein erstes Sendeprogramm (ESPNowTX_003.m5f)
Im Bild oben ist eine Broadcast Aussendung, also eine Nachricht an alle aktiviert. Der deaktivierte Block sendet nur an eine Adresse aus der Adresstabelle, die am Anfang definiert ist.
Dieses Programm funktioniert wie es soll.
Das Empfangsprogramm
Für den Test des Empfangsprogrammes habe ich einen M5Stick C genommen, damit ich die Nachricht anzeigen kann. Und genau das tut es auch. Es zeigt in der Titelzeile die eigenen MAC-Adresse an. Darunter die MAC-Adresse des Senders und die Nachricht.
Mein erstes Empfangsprogramm (ESPNowRX_003.m5f)
Auch dieses Programm tut was es soll. Allerdings wurden beim ersten Versuch keine Nachrichten Empfangen. Der Austausch gegen einen anderen M5Stick C brachte aber Erfolg. Offenbar ist der zuerst verwendete M5Stick C defekt, ob wohl er sich über WLAN brennen ließ. Nachdem ich die Firmware und das Programm erneut aufgespielt hatte funktionierte dieser M5Stick C auch.
Ein Sende- und Empfangsprogramm
Da bisher alles sehr gut lief habe ich gleich noch ein Programm geschrieben, dass sowohl Senden, als auch Empfangen kann. Ich brauche es für die Entwicklung eines Konverters, der serielle Signale vom PC in das ESPNow-Netzwerk überträgt und umgekehrt.
Sende- und Empfangsprogramm (ESPNowTRX_001.m5f)
In dem Block „Taste A was Pressed“ sollte der Text für jedes Gerät individuell angepasst werden. Nach dem Text wird eine laufende Nummer ausgegeben.
Die Displayausgabe habe ich in eine Funktion getan, weil ich diese an 2 verschiedenen Stellen im Programm benötige.
Ganz unten im Display und ganz klein wird die Versorgungsspannung über die USB-Buchse und der aufgenommene Strom angezeigt. Wenn die Spannung > 4V ist in grün und < 4V in rot. Dann ist es Zeit die Powerbank zu wechseln!
Die Stromversorgungsdaten werden alle 10 Sekunden gemessen und ausgegeben.
Ein kleines Problem gibt es bei dem Sende- und Empfangsprogramm allerdings. Gelegentlich wird alles im Display ganz klein dargestellt. Beim nächsten Displayrefresh wird aber alles wieder so wie es sein soll. Also kein wirkliches Problem.
Leider funktionierte der Code nicht mehr, wenn er auf das Gerät downgeloaded wurde 🙁 Der ESPNow-Teil arbeitete nicht.
Schließlich fiel einer von den drei M5Stick C die ich im Einsatz hatte ganz aus. Nach dem Löschen im Brenner ging nichts mehr. Er wurde nicht mehr vom Brenner gefunden.
Ich habe die Reset-Taste 6 Sekunden gedrückt damit das Gerät sicher ausgeschaltet ist und erstmal bei Seite gelegt. Am nächsten Morgen habe ich es erneut an den PC angeschlossen. Nun hat der Brenner es erkannt und ich konnte es löschen und die Firmware darauf spielen. Nun läuft es wieder, als wäre nie etwas gewesen. Also immer etwas Geduld mit dem M5Stick C haben, nach einer Weile kommt er meist wieder zur Vernunft.
Weiterhin musste ich feststellen, dass die Kommunikation nicht auf Kanal 1, sondern auf Kanal 6 erfolgte, obwohl Kanal 1 im Programm eingestellt war! Und genau das war das Problem ! Ich habe es nicht gleich richtig eingeordnet. Nach dem ich auf meinem Tablet einen Netzwerkscanner gestartet hatte, konnte ich sehen, dass das Gerät mit dem auf ihm gespeicherten Programm tatsächlich auf Kanal 1 arbeitet und die anderen auf Kanal 6.
Also beim Testen im RAM läuft immer Kanal 6, beim echten Einsatz wird der im Programm vorgegebene Kanal benutzt!
Beim Übertragen des Programms per RUN wird dieser Block nicht ausgewertet! Ich habe es bei M5Stack gemeldet. Mal sehen wie lange es dauert bis dieser Bug behoben ist.
Erweiteres Sende-Programm für ATOM lite
Bisher hat das Sendeprogramm nur auf Knopfdruck Daten gesendet. Nun würde ich gerne eine automatische Aussendung implementieren. Dazu habe ich einen M5ATOM lite genommen und ESPNowTX_003.m5f erweitert. Ich habe einen Timer eingefügt, der durch einen kurzen Doppeldruck auf die Taste gestartet und durch einen langen Tastendruck (> 1 Sekunde) wieder gestoppt wird. Ein normaler Tastendruck löst wie bisher das Senden eines Datenpaketes aus. Außerdem habe ich die Sendeblöcke in ein Funktion „sende“ ausgegliedert, da diese nun an mehreren Stellen im Programm benötigt wird.
Bisher vermisste ich einen Kommentarblock in Blockly sehr. Ich hatte doch gelernt, das Programmcode unbedingt mit Kommentaren versehen werden muss. Diesen Block gibt es tatsächlich nicht, was ich als grobe Nachlässigkeit der Entwickler betrachte, da insbesondere bei einer Anfängersprache Kommentare unverzichtbar sind.
Nun habe ich aber in einem Beispielprogramm eine Lösung gefunden. Blockly enthält einen Block zum Ausführen von Micropython Code. Dieser darf aber nicht nur ausführbaren Code, sondern auch Kommentare enthalten. Warum sagt uns das niemand von M5Stack?
Die M5-Geräte mit dem ESP32 haben sehr viele Möglichkeiten mit anderen Geräten zu kommunizieren. Ein direckter Weg in den PC führt über die USB-Schnittstelle. Dazu braucht man in Blockly nur eine neue UART initialisieren mit TX-Pin = 1 und RX-Pin = 3. Und schon erfolgt die serielle Ausgabe der UART über USB.
Mein Testprogramm. Rechts ist die Putty-Ausgabe zu sehen.
Ich liebe die Geräte des M5Stack-Universums. Sie haben aber einen entscheidenen Nachteil – die Eingabe von Werten ist sehr umständlich. Insbesondere beim M5Stick C steht, wenn er in ein Gehäuse eingebaut wird, nur eine Taste zur Verfügung. Die UIFlow-IDE stellt zwar Blöcke für einen doppelten und einen langen Tastendruck zur Verfügung, so dass man mit dem normalen Tastendruck quasi 3 Tasten zur Verfügung hat. Damit Werte eingeben geht – ist aber sehr umständlich und m.E. nur eine Notlösung.
Da man meistens nicht ständig Eingaben macht – kaum einer wird auf dem M5Stack Textverarbeitung betreiben wollen – muss nicht jedes Gerät eine eigene Eingabetastatur besitzen. Diese kann, wie heute bei vielen Dingen üblich, „geschared“ werden.
Im einfachsten Fall kann dazu ein M5Stick C mit einem Tastatur-Hat herhalten. Die Kommunikation erfolgt über ESP-Now. Dieses wird von der UIFlow-IDE mit Blöcken unterstützt und ist im Internet gut dokumentiert.
Das Gerät, dass eine Eingabe benötigt ruft per Broadcast noch einem geeigneten Gerät. Dieses meldet sich und ab nun werden die Pakete direkt von Gerät zu Gerät ausgetauscht. Sind alle erforderlichen Eingaben erfolgt. Verabschiedet sich das anfordernde Gerät und Das Eingabegerät steht wieder anderen M5Stack’s zur Verfügung.
Realisierung
Aber wie meistens liegt der Teufel im Details. Es können keine Variablen benutzt werden um eine Mac-Adresse in die Liste einzufügen.
Neueste Kommentare