Forum: Projekte & Code Statusanzeige für die Hausautomation mit Neopixel und MQTT


von Michael S. (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Harry L. (mysth)


Lesenswert?

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.

von Michael S. (Gast)


Lesenswert?

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.

von Johannes S. (Gast)


Lesenswert?

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?

von Harry L. (mysth)


Lesenswert?

MQTT auf einem 8bit-AVR ist ohnehin eine Illusion.

von Wolfgang (Gast)


Lesenswert?

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.

von Harry L. (mysth)


Lesenswert?

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.

: Bearbeitet durch User
von Kohlblatt (Gast)


Lesenswert?

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.

von Michael S. (Gast)


Lesenswert?

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.

von Marco H. (damarco)


Lesenswert?

Warum sollte das nicht gehen? Vorausgesetzt man lagert das TCP/IP auf 
Hardware aus. Wo ist das Problem bei einem String?

von Michael S. (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Marco H. (damarco)


Lesenswert?

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 ;)

: Bearbeitet durch User
von Michael U. (amiga)


Lesenswert?

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

von Marco H. (damarco)


Lesenswert?

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.

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.