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 - damit noch keine Erfahrungen, offenbar aber nicht als „W“-Version. Den RPi Pico 1 gibt es auch als „WH“ mit bereits angelöteten Pins - macht es nochmal einfacher.
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).
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/* .
Zuerst muss die „embedded config“ in src/vzlogger_pico.cpp angepasst werden, insb die VZ Server URL, WiFi Zugangsdaten, meters und channels - wie üblich, nur eben embedded.
$ cd ~/projects/vzlogger/pico $ mkdir build $ cd build $ cmake -DPICO_BOARD=pico_w .. ... $ 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.