In der c't 18/2018 wurde als Bastelobjekt zum Thema Hausautomation eine
über MQTT adressierte Statusanzeige mit Neopixeln (für Arduino +
ESP8266) vorgestellt.
Das ist eine gute Idee, wie man mit Hilfe eines einzelnen freien Pins
den Status diverser Sensoren optisch signaliseren kann.
Da ich alle notwendigen Bauteile in der Bastellkiste hatte, habe das
Projekt nachgebaut.
Was mir nicht wirklich gefallen wollte, das war die Implementierung.
Zum einen fügte sie sich nicht in mein eigenes Konzept der MQTT-Clients,
zum anderen werden die Neopixel mit schwerem Geschütz zum Blinken
genötigt: Scheduler, JsonObjekt und reichlich Stringobjekte und
Stringmanipulationen werden resourcenverschwendend bemüht.
Darum habe ich das Programm komplett umgekrempelt, mich dabei auf die
klassischen Char-Arrays beschränkt, Scheduler und Json werden
überflüssig.
Die wesentlichen Funktionen sind in "neopixel.ino" zusammengefasst:
- neopixel() wertet die eingehende MQTT-Message aus, schaltet die
Neopixel auf die übergebenen Farbwerte und speichert ggf. die
Blinkwerte.
- check_blink() muss in regelmäßigen Abständen aufgerufen werden und
kümmert sich um das selbstständige Blinken.
"mqtt-neopixel.ino" führt lediglich die Initialisierung sowie setup()
und loop() aus.
"mqtt_client.ino" reicht eingehende MQTT-Messages an die zuständigen
Behandlungsroutinen durch.
Die Parameterübergabe:
Per MQTT-Message/Payload kann man entweder eine Komma-separierte-Liste
im ASCII-Format - oder die Daten in hexadezimaler Form kompakt
übermitteln.
Die Reihenfolge der 6 Felder ist in beiden Fällen gleich:
Erste-LED, Rot, Grün, Blau, letzte-LED, Blinkmodus (alle Werte zwingend
numerisch !)
Die Bedeutung des Teils "Erste-LED, Rot, Grün, Blau" ist
selbsterklärend.
Wenn "letzte-LED" > "erste-LED", dann wird der gesamte Bereich von
"erste-LED" bis "letzte-LED" geschaltet.
Die Blinkmodi sind:
0 -> Dauerlicht [default]
1 -> 1Hz,
2 -> 2Hz,
3 -> 4Hz,
4 -> 1Hz unsymmetrische 1:7 (125ms an, 875ms aus))
Beispiele: "0,255,0,0,2,0"
-> Erste-LED = 0, Rot = 255, Grün = 0, Blau = 0, Letzte-LED = 2,
Blinken = 0
Die LED's 0, 1, 2 werden auf Rot geschaltet bei Dauerlicht.
Bei der Übertragung im ASCII-Format können alle "0" entfallen, fehlende
Parameter werden intern auf "0" gesetzt.
Die Alternative Eingabe ist also: ",255,,,2"
Die kürzestmögliche Message bestehend aus einem Zeichen - etwa die
Payload "6" wird erweitert zu "6,0,0,0,0,0", die LED 6 wird
ausgeschaltet.
Und "," schaltet die LED 0 aus.
Eine hexadezimale Payload muss genau 6 Byte lang sein und im letzten
Byte muss das Bit.7 gesetzt sein.
0500FF000781 -> LED 5 bis 7 werden auf Grün gesetzt und Blinken auf 1
Hz.
Viel Spaß beim Blinken,
Michael S.
Michael S. schrieb:> Darum habe ich das Programm komplett umgekrempelt, mich dabei auf die> klassischen Char-Arrays beschränkt, Scheduler und Json werden> überflüssig.
Damit führst du die normale Funktionsweise von MQTT ad absurdum.
Sowas kann man sich nur erlauben, wenn man sicher sein kann, daß alle
MQTT-Nachrichten tatsäclich ohne JSON auskommen, und das ist in der
realen Welt nicht der Fall.
In meinem Umfeld (mit einer Menge MQTT-Publisher/Subscriber) wäre sowas
komplett unbrauchbar
Wieso man unbedingt Resourcen sparen muß, die anderweitig nicht benötigt
werden, erschließt sich mir sowieso nicht.
Harry L. schrieb:> Damit führst du die normale Funktionsweise von MQTT ad absurdum.> Sowas kann man sich nur erlauben, wenn man sicher sein kann, daß alle> MQTT-Nachrichten tatsäclich ohne JSON auskommen, und das ist in der> realen Welt nicht der Fall.
ich glaube, wir reden über verschiedene Dinge.
Wenn ich Nachrichten im JSON-Format empfange oder sende, dann kann ich
auf die entsprechenden Bibiotheken nicht verzichten.
Aber das JSON-Format wird im konkreten Fall nicht zur Datenübertragung
benutzt.
JSON wird benutzt, um die RGB-Werte für blinkende LED's in JSON-Objekten
zu speichern.
Ein Array mit uint8_t kann diesen Zweck mit deutlich weniger Overhead
erfüllen.
Aber selbstverständlich - viele Wege führen nach Rom.
> Wieso man unbedingt Resourcen sparen muß, die anderweitig nicht benötigt> werden, erschließt sich mir sowieso nicht.
Zum Thema "ressourcenschonen".
Wenn man mit ATtinys und ATmegas arbeitet, dann ist man schon gezwungen,
möglichst kompakten Code zu schreiben.
In den Beispielen zu ArduinoJson finde ich folgenden Hinweis:
// Use String objects sparingly, because ArduinoJson duplicates them in
the
// JsonBuffer. Prefer plain old char[], as they are more efficient in
term of
// code size, speed, and memory usage.
Auf dem ESP8266 kann man entspannter vorgehen, da kann man sich dann
schon mal ein "sprintf()" erlauben.
Übrigens habe ich auf die Verwendung von C++String / C-Strings komplett
verzichten können (Ausnahme zum Debugggen), indem ich die Payload direkt
als uint8_t ablege.
Eine sprachliche Korrektur zu meinem ersten Beitrag:
Wenn ich von "Hexadezimaler Payload" schreibe, dann ist eine Payload im
binären / Byte-Format gemeint (bytearray[] in Python) .
mfg
Michael S.
Ich sehe auch einen grossen Vorteil darin das man Menschenlesbare
Nachriten per MQTT verschickt. Es gibt viele Clients die sich an den
Broker anhängen können, selbst auf dem Smartphone kann ich die Meldungen
mitlesen. Da machen kryptische Hexcodes keinen Spass. Auch zur
Weiterverarbeitung in NodeRed z.B. ist das mit JSON Objekten viel
angenehmer.
Das man sich den Anachronismus mit Tinys für solche Aufgaben noch
antut... In gleicher Größe und für nahezu gleiches Geld bekomme ich 32
Bitter mit ausreichend Flash und RAM um auch da schon mit lesbaren Daten
arbeiten zu können.
Wie heisst der blöde Spruch nochmal? Für nicht benutzten Flash gibts
kein Geld zurück?
Harry L. schrieb:> MQTT auf einem 8bit-AVR ist ohnehin eine Illusion.
Es ist immer noch ein Unterschied, ob es sich um einen Client oder
Server handelt.
Wolfgang schrieb:> Es ist immer noch ein Unterschied, ob es sich um einen Client oder> Server handelt.
Hä?
Kann es sein, daß du MQTT nicht verstanden hast?
Da gibts nur Broker(Server) und Publisher/Subscriber(Client)
Ein 8bit-AVR ist für jede dieser Komponenten mindestens 3 Nummern zu
klein.
Harry L. schrieb:> MQTT auf einem 8bit-AVR ist ohnehin eine Illusion.
Eine Menge Bla-Bla gemischt mit heißer Luft, die du in diesem Thread
produzierst. Wäre es nicht besser du nimmst dein Eimerchen und dein
Schauffelchen und gehst spielen?
Das Prinzip und die Idee hinter MQTT wirst du verstehen, wenn du
erwachsen bist.
Zur allgemeinen Beruhigung der Gemüter:
Wenn man einige Zeit mit den ATtinys und ATmegas gearbeitet hat, dann
geht einem schlankes Programmieren (zumindest der Versuch dazu) in
Fleisch und Blut über.
Selbstverständlich läuft der MQTT-Client auf einem größeren Controller,
nämlich dem ESP8266.
Michael S.
Ein kleiner Nachtrag:
Damit der MQTT-Client angeschlossenen Neopixels auch ohne den Umweg über
den MQTT-Server direkt ansteuern kann, ist eine zusätzliche
Schnittstelle in der "neopixel.ino" eingefügt:
void set_mypixel(uint8_t* pixel);
ein Array mit 6 Byte wird zur Übergabe der Parameter benötigt:
uint8_t pixel[] = {0,0,0,0,0,0};
die Bedeutung der Bytes:
pixel[0] = Start-LED
pixel[1] = rot
pixel[2] = grün
pixel[3] = blau
pixel[4] = End-LED
pixel[5] = Blinkmode [0...4]
Die Farbwerte einzelner Neopixel können natürlich auch mit einer Methode
des neopixel-Objektes geändert werden:
pixels.setPixelColor(pixelnr, pixels.Color(rot, grün, blau));
Allerdings kann man auf diesem Wege keine Reihe von LED's setzen und
auch den Blinkmodus nicht nutzen.
Viel Spaß beim Blinken,
Michael S.
Was soll daran Ressourcen sparend sein ? Also die Möglichkeiten des RTOS
nutzt du nicht. Warum ? Dein Code beschäftigt die CPU zu 100% .. Das hat
folgen.. früher ka... die WIFI Verbindung ab....
Unter RTOS kann man das besser lösen das eine Thread nach x wieder
aufwacht ;)
Hallo,
interessante Diskussion...
Muß ich jetzt meinen AVR Mega328 mit ENC28J60 und BME280 und MQTT-Client
wegwerfen?
Dabei schickt der doch artig die Daten zum MQTT-Broker.
Harry L. schrieb:> Damit führst du die normale Funktionsweise von MQTT ad absurdum.> Sowas kann man sich nur erlauben, wenn man sicher sein kann, daß alle> MQTT-Nachrichten tatsäclich ohne JSON auskommen, und das ist in der> realen Welt nicht der Fall.
Ein MQTT-Client bekommt nur die Daten, die er auch abboniert hat.
Ich bin mir auch sicher, daß ich keine JSON-Messages bekomme, da ich
meinem Broker keine schicke.
Michael S. schrieb:> Auf dem ESP8266 kann man entspannter vorgehen, da kann man sich dann> schon mal ein "sprintf()" erlauben.>> Übrigens habe ich auf die Verwendung von C++String / C-Strings komplett> verzichten können (Ausnahme zum Debugggen), indem ich die Payload direkt> als uint8_t ablege.
Warum soll ich Flash sparen wenn alle gewünschten Funktionen auch mit
der String-Klasse bequem reinpassen? Der Ram-Verbrauch kann bei großen
Strings und bearbeiten mit den String-Funktionen temporär knapp werden.
Allerdings habe ich selten Strings mit knapp 6kB Größe, auf die ich z.B.
String.replace() loslasse.
Gruß aus Berlin
Michael
Gruß aus Berlin
Michael
Nunja mit Strings muss der MQTT Stack ja sowie so umgehen können und ich
meine damit nicht die payload. Alles andere im Protokoll sind strings..
Da kommt es auf die Payload auch nicht mehr an und jeder kann lesen was
gemeint ist.