Die Portierung des vzloggers auf den Raspberry Pico W war/ist ein „Bastelprojekt“ insb mit der Motivation „das müsste doch gehen“ und der aktuelle Zustand ist „funktioniert mit ein paar kleinen, noch zu behebenden Schönheitsfehlern“ (siehe ganz unten TODO), wozu jede Hilfe willkommen ist. Weitere Motivation war die Verfügbarkeit von analogen GPIO Ports (on-board ADC), also analogen Sensoren (s.u.), sowie geringerer Stromverbrauch (batterietauglich?) und Anschaffungskosten.
Der RPi Pico W („W“ für on-board WiFi - alles andere macht es noch komplizierter und lohnt vermutlich nicht) ist im Gegensatz zum „normalen“ Raspberry Pi ein „Microcontroller“ mit einem RP2040 Chip. Dies bedeutet insb:
Es gibt inzwischen RPi Pico 2 - insb noch sparsamer (hat auch mehr RAM u Flash-Speicher, mehr Schnittstellen usw usf - alles bisher nicht benötigt). Die RPi Pico gibt es auch als „WH“ mit bereits angelöteten Pins - macht es nochmal einfacher, aber auch etwas teurer.
Versuche, libcurl zu portieren, hatten sich als sehr aufwändig herausgestellt und wurden abgebrochen. Statt dessen wird eine IP Implementierung lwIP genutzt (sowie bei Bedarf sogar „mbedtls“ als TLS Implementierung, bisher nicht genutzt).
In der Konsequenz heisst das (nur, wenn entspr compiliert):
Dies wiederum erforderte ein paar Umstrukturierungen - der resultierende Code ist aber sowohl auf dem RPi Pico als auch einem normalen RPi lauffähig (muss natürlich separat gebaut werden).
Mittlerweile läuft der u.g. EmonLib-basierte „Stromzähler“ ununterbrochen seit mehreren Monaten ohne jegliche Komplikationen.
Siehe Doc hier:
https://www.raspberrypi.com/documentation/microcontrollers/c_sdk.html https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf
$ mkdir ~/projects/pico $ cd ~/projects/pico $ git clone https://github.com/raspberrypi/pico-sdk.git --branch master $ cd pico-sdk $ git submodule update --init $ cd .. $ git clone https://github.com/raspberrypi/pico-examples.git --branch master
$ mkdir ~/projects/vzlogger/pico $ cd ~/projects/vzlogger/pico $ git clone https://github.com/tge12/vzlogger
Es gibt 2 CMakeLists.txt Varianten - linke die Passende (ja, das ist hässlich, noch TODO):
$ ln -sf CMakeLists-pico.txt CMakeLists.txt
Diese müssen selbst compiliert werden, da eine „fertige“ lib für den RPi Pico nicht zu finden war/ist.
$ cd ~/projects/vzlogger/pico/libs/json-c $ git clone https://github.com/json-c/json-c .
Auch hier sind ein paar Anpassungen nötig, um die lib json-c kompilierbar zu machen für den RPi Pico (TODO auch hässlich und zu verbessern, evtl sind Erweiterungen im Bauprozess in der lib json-c nötig):
$ cp ../json-c-vz/* .
Ähnliche Situation. EmonLib ist für den eigentlichen RPi Pico-Port nicht notwendig, wohl aber für das neue „protocol“ zum Lesen von Strommesswerten mittels SCT013 (s.u.). Es gibt auch hier einen git fork, welcher 2 Erweiterungen enthält, die z.T. unbedingt notwendig sind (s.u.).
$ cd ~/projects/vzlogger/pico/libs/EmonLib $ git clone https://github.com/tge12/EmonLib .
Auch hier sind ein paar Anpassungen nötig, um die lib kompilierbar zu machen für den RPi Pico (TODO auch hässlich und zu verbessern, evtl sind Erweiterungen im Bauprozess in der lib json-c nötig):
$ cp ../EmonLib-vz/* .
Neu:
Zuerst muss die „embedded config“ in einem eigenen Header-File (Beispiel vz_pico_inline_config.h) angepasst werden, insb die VZ Server URL, WiFi Zugangsdaten, meters und channels - wie üblich, nur eben embedded. Mit einem Pico2 W beim cmake Kommando als Board „pico2_w“ angeben.
$ cd ~/projects/vzlogger/pico $ mkdir build $ cd build $ cmake -DPICO_BOARD=pico_w -DVZ_BUILD_ON_PICO=1 .. ... $ make ...
Wenn alles klappt, kommt ein File namens …/build/src/vzlogger.uf2 dabei raus, welches auf den RPi Pico kopiert werden kann wie in der Pico SDK Doc beschrieben.
Es gibt allerlei andere Projekte, die mit dem nicht-invasiven (einfach um stromführenden Leiter drum herum clippen) Stromsensor SCT013 den heimischen Energieverbrauch messen. Idee war, dies in VZ zu integrieren. Die mit Abstand beste Seite hierzu mit viel Theorie usw usf:
https://docs.openenergymonitor.org/electricity-monitoring/index.html
Dazu wird benötigt:
Sieht dann so aus - die beiden SCT013:
Die Adapterschaltung nebst RPi Pico (die Stecker: 2x I (SCT013), 1x U (kl 9V AC Netzteil)):
Die Grundidee ist nun, dass vzlogger kontinuierlich beide Sensoren (U und I) via EmonLib abfragt und daraus sowohl Wirk- als auch Scheinleistung berechnet (bei PV Einspeisung sogar negativ). Sieht dann so aus (bereits mit 2 Phasen, braun L1 und schwarz L2):
Ein Messzyklus mit EmonLib calcVI() misst 20 volle Sinuswellen (dauert bei 50Hz immer reichlich 200ms), dabei werden vom RPi Pico bis zu ~6000 Messwerte gewonnen. D.h. pro einzelner Sinuswelle gibt es Hunderte von einzelnen Messwerten, womit sich die Kurve gut rekonstruieren (selbst wenn in der Realität verzerrt) und auch die „Richtung“ des Stroms (Bezug oder Einspeisung) ermitteln lässt. All dies ist sehr gut erklärt auf o.g. Seite, dort auch die Adapterschaltung: https://docs.openenergymonitor.org/electricity-monitoring/ct-sensors/interface-with-arduino.html
Die EmonLib Erweiterung hat sich als notwendig entpuppt, da der o.g. kl Trafo eine hohe Phasenverschiebung (eingebaute Komponenten?) zwischen U und I erzeugte, d.h. U passte nicht mehr zu I, selbst und inbs. mit rein ohmschen Lasten (Toaster), wo genau dies eigtl nicht passiert. Zum anderen ist in der Erweiterung die Möglichkeit enthalten, die tatsächlichen ADC Messwerte auszugeben, um diese dann in ein Spreadsheet zu laden und damit die echten Sinuskurven anzuzeigen. Beispiel (U und I verschoben, P aber korrigiert):
Um Messfehler, die durch die eigene Messinfrastruktur erzeugt wurden, zu kompensieren, gibt es verschiedene EmonLib-Parameter (die in der vzlogger Config enthalten sein müssen). Diese „Kalibrierung“ ist ebenfalls auf der openenergymonitor Seite beschrieben, außer o.g. neue Phasenverschiebungsfehler-Kompensation. Idee hierbei ist es, im Simulations-Spreadsheet die Stichproben-Reihen zu ermitteln die für U und I jeweils am nächsten bei Null sind (ohne Phasenverschiebung und mit rein ohmscher Last (!!) wäre dies exakt die gleiche Stichprobe) und quasi softwaremäßig wieder zurück zu verschieben, d.h. passend zu U Reihe 44 und für I Reihe 18, macht 44-18=26. Heisst, I ist U etwa 26 Stichproben voraus und ein korrekter Wert für P wird ermittelt, indem für jedes U[i] ein I[i - 26] verwendet wird. All dies passiert innerhalb EmonLib - es muss nur der „phaseCalibration“ Config-Wert eingestellt werden.
Die resultierende config sieht dann so aus (bereits mit 2 gemessenen Phasen s.u. - dies hatte übrigens den Effekt, dass die L1 Phasenverschiebung nochmal etwas anders war: 35 statt 26. Möglicherweise Interferenz der beiden SCT013?):
static const char * inlineConfig = "{ 'verbosity': 5, \ 'retry': 30, \ 'meters': \ [ \ { \ 'enabled': true, \ 'skip': false, \ 'interval': 10, \ 'protocol': 'emonlib', \ 'adcCurrent': 0, \ 'adcVoltage': 1, \ 'currentCalibration' : 30, \ 'voltageCalibration' : 247.0, \ 'phaseCalibration' : 35.0, \ 'delay': 100, \ 'numSamples': 20, \ 'channels': \ [ \ { \ 'uuid': 'f3ef9b70-de3b-11ee-83b5-73042e2a7e09', \ 'api': 'volkszaehler', \ 'middleware': '" VZ_SERVER_URL "', \ 'identifier': 'RealPower'\ } \ ,{ \ 'uuid': '560ff4e0-2d94-11ef-9a04-7f5c06e34262', \ 'api': 'volkszaehler', \ 'middleware': '" VZ_SERVER_URL "', \ 'identifier': 'Voltage'\ } \ ,{ \ 'uuid': '2e2a8c90-dd66-11ee-9621-0d0747854c29', \ 'api': 'volkszaehler', \ 'middleware': '" VZ_SERVER_URL "', \ 'identifier': 'Current' \ } \ ] \ } \ ,{ \ 'enabled': true, \ 'skip': false, \ 'interval': 10, \ 'protocol': 'emonlib', \ 'adcCurrent': 2, \ 'adcVoltage': 1, \ 'currentCalibration' : 30, \ 'voltageCalibration' : 247.0, \ 'phaseCalibration' : 132.0, \ 'delay': 100, \ 'numSamples': 20, \ 'channels': \ [ \ { \ 'uuid': 'a8d6bba0-6462-11ef-aea1-3b1a1ab3364e', \ 'api': 'volkszaehler', \ 'middleware': '" VZ_SERVER_URL "', \ 'identifier': 'RealPower'\ } \ ,{ \ 'uuid': 'd9c7cd20-67bf-11ef-938c-3b62ce66ce08', \ 'api': 'volkszaehler', \ 'middleware': '" VZ_SERVER_URL "', \ 'identifier': 'Current' \ } \ ] \ } \ ] }";
Die einzelnen Meter-Attribute:
Der „delay“ entspricht einem „sleep(100us)“, damit kommen „nur“ ~1500 Stichproben pro Messzyklus heraus, hat aber den Vorteil, dass der L2 Phasecal-Wert mit 132 bedeutet, dass in EmonLib nicht mehr als 256 Stichproben aufgehoben werden müssen - diese Anzahl Stichproben ist immer noch viel mehr als genug um die Sinuskurve rekonstruieren zu können).
Um mehrere Phasen zu messen, gibt es die folgenden Möglichkeiten:
Variante 2 scheint ausreichend genau zu sein, d.h. U scheint für die verschiedenen Phasen ausreichend gleich. Es muss aber die gleiche Phasenkorrektur zwischen U und I wie oben stattfinden - wenn U und I an verschiedenen Phasen gemessen werden (dabei dann aber mit einer erheblich grösseren Differenz, in der realen Beispiel-Config 132).
Wenn libs/EmonLib mit -DCALIBRATION compiliert wird, gibt der vzlogger nach jedem Messzyklus genau 1000 Stichproben als CSV aus (Zeitstempel in Microsecs;ADC I;ADC U) sowie die initialen OffsetI und Offset U. Diese Werte können in das Spreadsheet kopiert werden, damit sind dann im Idealfall schöne Sinuskurven sichtbar (*immer* für U, je nach Last für I → Tip: große, rein ohmsche Lasten verwenden z.B. Wasserkocher, Toaster, …). Damit wiederum den phaseCal Wert wie oben beschrieben ermitteln, in config eintragen, neu compilieren. Dies muss für alle Phasen separat gemacht werden.
Ein paar Erfahrungen, die man nicht alle nochmal selbst machen muss:
$ sudo screen -L /dev/ttyACM0
Das gibt alles nach stdout aus, sowie gleichzeitig in ein File screenlog.0, was man dann später analysieren kann (enthält auch den CALIBRATION output). Alternativ zum Laptop tut es auch ein „normaler RPi“. Vorteil: Im Dauerbetrieb kann der Laptop wieder ab. Das Logdatenvolumen kann per log-level in der embedded config gesteuert werden. Wenn alles klappt, als Stromversorgung ein einfaches USB Netzteil.
Die DS18B20 w1 thermal Sensoren haben IDs, die in der config mit den channels verknüpft werden müssen. Anfangs sind diese IDs aber nicht bekannt - dann:
[Jan 01 19:54:07][mtr0] Querying meter ... [Jan 01 19:54:07][] Reading oneWire object 20012498 ... [Jan 01 19:54:07][] Have 2 oneWire devices. [Jan 01 19:54:07][] Adding ID[0]: 10D0F2BC020800A0 [Jan 01 19:54:07][] Adding ID[1]: 280FB18600000068
Mit einem USB-Meter kann man messen, wieviel ein Gerät eigtl verbraucht. Der Pico W („eins“ übrigens, derweil gibt es V2 - noch nicht getestet) verbraucht ohne irgendwelche Anpassungen ~200mW (also ~40mA). Genauere Analysen zeigen: 40mA mit verbundenem WiFi, ohne WiFi ~20mA, während „connect WiFi“ sogar 60mA - da ursprünglich WiFi dauerhaft verbunden war, resultieren daraus die ~40mA. Für Netzbetrieb gar nicht schlecht gut, für Batteriebetrieb aber noch viel zuviel. In der neuesten Version gibt es daher ein paar Config-Schalter, die Folgendes ermöglichen:
Mit all dem (und einer Config mit 1x HC-SR04 und 2x DS18B20) verbraucht der Pico W nur noch ~40mW (~8mA), der Pico2 W sogar nur ~25mW (~5mA). Mit einer 10000mAh Powerbank müsste dies also mindestens 8d reichen … TODO: