Einige Eindrücke zu und Erfahrungen mit Tasmota32

  • Grundlegend beinhaltet es die gleichen Möglichkeiten wie Tasmota für die ESP8266 Familie. Es gibt die Rules, die Var<x>, die Mem<x> und die gleichen Kommandos.
  • Zusätzlich ist eine kleine, sehr nützliche Hybrid-Programmiersprache Berry eingebaut.
    Hybrid: Es gibt eine Zwischensprache, die ein kleiner Interpreter interpretiert. Die Kompilierung von Berry Sourcecode erzeugt also per Interpreter lauffähige Programme. Das Prinzip gab es früher bei UCSD Pascal und ist bspw. heute mit Java im Einsatz.
  • Es gibt zwei weitere Konsolen.
    • Die "Berry Scripting console" ermöglicht das einfache Experimentieren mit dieser Sprache. Auch kann man hier sehr einfach ein Debugging betreiben.
    • Mit "Verwalte Dateisystem" lassen sich Dateien hochladen, editieren, löschen. Die vollständigen Möglichkeiten stehen erst zur Verfügung, wenn man entweder die kompilierte Tasmota32.bin/Tasmota32-DE.bin verwendet oder bei Selbstkompilieren das Dateisystem für SD Karten (SD Karte/LittleFS) einbezieht.

Mit Berry wird ein ESP32 fast zu einer sehr kleinen "Wundermaschine". Ich bin begeistert. Die Möglichkeiten der zusätzlichen Programmierung auf einer über dem Tasmota32 Grundsystem liegenden Ebene sind sehr flexibel. Zum automatischen laden von Berry Funktionen ... nach einem Neustart legt man eine Datei "autoexec.be" an, in welcher per load(<Berry-Datei>) die gewünschten Funktionen, Variablen und Anweisungen kompiliert und geladen werden.

Die Sprache Berry mit Tasmota32 Erweiterungen kurz umrissen

Fehlendes - jedenfalls fand ich folgendes nicht

  • keine literalen Konstanten, bspw. per def x=12 - diese würden die Lesbarkeit und die Pflege des Quellcodes unterstützen
  • keine zur Laufzeit persistenten lokalen Variablen, also solche, die nicht auf dem Stack angelegt werden und die zwischen Aufrufen ihren Wert behalten - in C++ bspw. static int x=0;
    Hierzu entdeckte ich inzwischen die "closures", mit denen persistente lokale Variable prinzipiell möglich sind. Ich bin noch auf dem Weg, dies zu verstehen und auf Anwendbarkeiten zu prüfen - s.a. unten Experimentelles und evtl. Nützliches.

Als Workaround können globale Variable eingesetzt werden - etwas suboptimal. Nur für persistente lokale Variable eine Klasse zu erstellen, finde ich zu aufwändig, ist aber möglich. 

Nützliches

  • Es gibt genügend einfache Datentypen und Operatoren.
  • Es gibt nützliche Konvertierungsfunktionen/-Methoden.
  • Kommunikation zwischen den nativen Rules und Berry Code ist relativ einfach möglich.
  • Indexierte Kommandos können selbst kreiert werden - sehr flexible Kommunikationsschnittstellen.
  • Es gibt nützliche komplexere Datentypen.
    • Range, für Zähler gesteuerte Schleifen bestens geeignet (for i:1..16)
    • Listen, als Datenfelder mit schnellem Zugriff per Index/Offset implementiert
      Methoden push() und pop() für leichte Manipulationen, tostring() zur Serialisierung ...
    • Maps, als assoziative Speicher per Key Value Paaren
      Methoden tostring() zur Serialisierung, insert() zum einfügen eines Key Value Paares, remove() zum entfernen eines Paares ...
  • Eine json Klasse u.a. zur Konvertierung JSON String -> Map (Parser) steht zur Verfügung.
  • Klassen können erstellt werden.

Experimentelles und evtl. Nützliches

Interessant sind die sog. "closures". Nach Manual sollen sich damit zugriffsgeschützte Variable implementieren lassen. Ich weiß gegenwärtig nicht, wie das mit einfachen Mitteln gelingen kann. Mit relativ komplexen Mitteln (Listen an Funktionsreferenzen, Maps mit Funktionsreferenzen als Werte) dürfte es gelingen, der Overhead erscheint mir aber erheblich.

Ich habe testweise Funktionen mit persistenten lokalen Variablen geschrieben. Variable einfachen Typs lassen sich damit auf einfache Weise schreibgeschützt implementieren, weil diese nicht per Referenz geliefert werden. Auf Inhalte/Werte von Variablen muss zumindest lesend zugegriffen werden können, andernfalls sind sie sinnfrei. Variable komplexen Typs wie list und map werden als Funktionsresultat per Referenz geliefert und sind somit auch veränderbar. Um einen Schreibschutz zu implementieren, müsste von einer solchen Variablen auch eine persistente Kopie existieren. Wenn per return ... eine Referenz auf die Kopie geliefert würde, könnte von außen das Original nicht verändert werden. Dies erscheint mir jedoch viel zu aufwändig. Wenn das Ziel verfolgt wird, schlanken und performanten Code zu erstellen, fallen solche "Lösungen" aus.

Als nützliche closures sehe ich Funktionen zur effizienten Handhabung von spezifischen Listen. Hierfür kann man eine äußere Initialisierungsfunktion und eine innere Arbeitsfunktion zusammenstellen. Dies erinnert etwas an C++ Funktoren. In meinem Zisternenprojekt will ich solche Listenfunktionen einsetzen, weil sie die Pflegbarkeit und die Lesbarkeit des Quellcodes steigern können. So habe ich Funktionen wie unsortedList(), insertSortList() und listCopy() erstellt und ausgiebig getestet.

Beispiel:
var store = insertSortList() # Leere Liste wird angelegt, store erhält die Funktionsreferenz auf die Arbeitsfunktion, welche neue Werte in die Liste einsortiert.
store(12) # sortiert 12 in die Liste ein und liefert die Referenz auf die Liste -> [12]
store(9) # sortiert 9 in die Liste ein und liefert die Referenz auf die Liste -> [9, 12]
store('text') # ist möglich, hier aber sinnfrei - vermutlich wird eine Ausnahme geworfen
store() # liefert nur die Referenz auf die Liste.
Für spezialisierte Listen sind solche Funktionen mit einer einzigen Arbeitsfunktion sehr gut geeignet. In meinem Messprojekt lasse ich bspw. Rohwerte sammeln, sortieren und spezifisch zu Nutzwerten verarbeiten.
Mit der per store() Aufruf gelieferten Listenreferenz kann die Liste auch mit list-Methoden manipuliert werden, bspw. store().push(3) fügt die 3, unsortiert, an die Liste an. Dies wäre hier kontraproduktiv.

Damit sind imho alle Dinge möglich, die man sich für ein ESP32 Board auszudenken in der Lage ist. Allenfalls die Zugriffsschutzschwächen seitens Berry können bei umfangreichen Projekten stören.

Ich habe inzwischen damit eine Messeinrichtung für den Wasserstand meiner Zisterne realisiert. Mehr dazu ist unter Projekte/Zisternenmessungen zu finden.

2021-08-20