Erstellt am 17.06.2024
Neulich beim Dumpser-Diving
Neulich beim Dumpser-Diving eine 4-Kanal Netzwerksteckdose für den Serverschrank aus dem Container gezogen. Sogar mit einem kleinen Bapperl dran für Zugangsdaten und IP-Adresse. Das Kästchen ist natürlich gleich an der heimischen Müllkontrolle vorbei in das Bastelzimmer geschmuggelt worden. Nach etwas rumprobieren lief is dann tatsächlich sogar mal. Über das Webinterface kann man die einzelnen Steckdosen schalten und auch ein paar Zeitschaltuhrfunktionen einrichten.
Leider hat das Ding aber nicht sehr zuverlässig funktioniert. Beim nächsten Einschalten ist es in einem Resetloop stecken geblieben. Spannungen sahen am Oszi normal aus, also ist höchstwahrscheinlich irgend ein Bauteil, wie etwa der RAM für den Motorola 68k Mikrocontroller auf dem Board kaputt. Das könnte man zwar auch mal alles probieren zu tauschen oder mit dem Logikanalyzer schauen, wo es denn hängt, aber ich hatte hier eine andere Idee:
Der Schaltplan auf der Platine sah relativ simpel aus (mal von dem CMOS-Friedhof um den M68k abgesehen): ein paar Leiterbahnen gehen via Vorwiderstand zu Transistoren, welche die Relais für die einzelnen Kanäle schalten. Wenn ich mich hier einklinke, dann kann ich die Steckdosen einfach mit meinem eigenen Mikrocontroller schalten. Bleibt nocht die Netzwerkfunktion. Auch hier ist alles noch sehr disket aufgebaut. Vom Mikrocontroller geht es in den PHY-Chip, von dort in den Netzwerkübertrager und in die Netzwerkdose.
Ich hatte hier noch einen MilkV Duo rumliegen, der einen idealen Kandidaten zur Reparatur darstellt: Der Duo hat jede Menge IOs und vor allem, er hat schon ein PHY mit auf der Platine. Als weiterer Vorteil sei noch die Betriebsspannung genannt, die bei dem Modul von 5 bis 14 V reicht. Damit konnte ich also einfach die bereits vorhandene 5 V Versorgung weiter verwenden. Der Duo stellt dabei intern die benötigten 3,3 V bereit. Alles was ich jetzt also tun muss um das Ding wieder zum Laufen zu bringen, ist die zwei Adernpaare vom PHY des Duo mit dem Übertrager auf der Platine verbinden (den vorhandenen PHY-Chip habe ich einfach ausgelötet) und für die Transistoren vier weitere Drähte jeweils mit den GPIOs verbinden.
Als kleines extra habe ich noch einen DS18B20 Temperaturfühler spendiert, sodass ich jetzt auch Ausgänge nach Temperatur schalten kann, z.B. für den Lüfter im Serverschrank
Netzwerk Side Quest
Nach getaner Lötarbeit also freudig die SD-Karte bespielt und das system gebootet. Leider kam zunächst kein Link zu stande. Nach etwas Rumprobiererei hab ich dann den entscheidenden Hinweis im Datenblatt des Netzwerkübertragers gefunden: specifically designed to implement the functionality of analog interface for 10 Base-T Ethernet application. Nun ja, 10 Base-T ist dann doch 10x geringere Frequenz als 100 Mbit. Kein Wunder dass der Übertrager das signal derart bedämpft, dass am Ende nix sinnvolles mehr heraus kommt.
Abhilfe schaffte also ethtool mit in die Firmware zu übersetzen und dann das Netzwerkinterface auf 10 Mbit festzunageln. Leider ist das Buildtarget nicht in der menuconfig auswählbar, also müsst ihr es händisch zur default-config hinzufügen. Hier könnt ihr auch gleich noch ein paar andere Pakete einbinden, wie z.B. ein Webserver, PHP oder ein MQTT broker. Am einfachsten ist es, wenn ihr die duo-buildroot-sdk/buildroot-2021.05/configs/milkv-duo_musl_riscv64_defconfig editiert und dort die folgenden Zeilen ans Ende schreibt:
BR2_PACKAGE_ETHTOOL=y
BR2_PACKAGE_NGINX=y
BR2_PACKAGE_PHP=y
BR2_PACKAGE_PHP_EXT_JSON=y
BR2_PACKAGE_FCGIWRAP=y
BR2_PACKAGE_NANO=y
BR2_PACKAGE_MOSQUITTO=y
Welche weiteren Pakete es gibt könnt ihr nach dem kompilieren in der erstellten config finden: duo-buildroot-sdk/buildroot-2021.05/output/milkv-duo_musl_riscv64/.config
Um ethtool aufzurufen bevor dhcpd übernimmt ediert ihr jetzt noch die /etc/init.d/S41dhcpcd und fügt die folgende Zeile nach der start-stop-daemon hinzu:
case "$1" in
start)
echo "Starting dhcpcd..."
start-stop-daemon -S -x "$DAEMON" -p "$PIDFILE" -- -f "$CONFIG"
/usr/sbin/ethtool -s eth0 speed 10 duplex full autoneg off
Webserver mit FCGIWrap
Um nginx mit CGI scripten zu betreiben verwende ich FCGIWrap. Das benötigt ebenfalls ein startscript welches ihr einfach in /etc/init.d/S50fcgiwrap einrichten könnt.
#!/bin/sh
start() {
printf "Starting fcgiwrap: "
export PHP_CGI=/usr/bin/php-cgi
start-stop-daemon -S -m -b -p /var/run/fcgiwrap.pid \
--exec /usr/sbin/fcgiwrap \
-- -s unix:/var/run/fcgiwrap.socket -f
chgrp www-data /var/run/fcgiwrap.socket
chmod g+w /var/run/fcgiwrap.socket
}
stop() {
printf "Stopping fcgiwrap: "
start-stop-daemon -K -q -p /var/run/fcgiwrap.pid
[ $? = 0 ] && echo "OK" || echo "FAIL"
}
restart() {
stop
start
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart|reload)
restart
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit $?
Schliesslich noch nginx auf den wrapper verweisen: /etc/nginx/nginx.conf
location ~ \.php$ {
root /var/www/html;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
Mit dem CGI Wrapper ist der Aufruf eurer PHP scripte ein klein bisschen anders als gewohnt. Eure Scripte müssen ausführbar sein (chmod +x) und in die erste Zeile müsst ihr den Shebang für euren PHP-Interpreter schreiben, z.B. so:
#!/usr/bin/php-cgi
<?php
echo "Hallo vom MilkV Duo";
Bei mir gab es dann aber immer noch ein Problem mit PHP und zwar, dass beim cross-compilieren aus Gründen eine andere Linker-Bibliothek in den ELF header geschrieben wurde. Also anstatt des üblichen ld-musl-riscv64v0p7_xthead.so.1 auf der Duo Platform verlangt das php-cgi binary nach ld-musl-riscv64.so.1. Die Dateien sind identisch, nur der Name nicht. Also noch flux einen Link in /lib erstellen:
cd /lib
ln -s ld-musl-riscv64v0p7_xthead.so.1 ld-musl-riscv64.so.1
und schon bekommt man statt der kryptischen Fehlermeldung -sh: /usr/bin/php-cgi: not found die korrekte Ausgabe des PHP scripts.
Temperatursensor DS18B20 auslesen
Das letzte Abenteuer ist jetzt noch das Auslesen des Temperaturfühlers. Eigentlich heißt es, dass die Pinpong Python Bibliothek schon Beispiele zum Auslesen des Sensors mitbringt: /usr/lib/python3.9/site-packages/pinpong/examples/milkv-Duo/ds18b20.py
Wenn man sich das Beispiel aber dann anschaut, wird dort ein ganz anderes Board angesprochen (Board("UNIHIKER").begin()) und selbst wenn man das auf duo ändert wird man immer noch enttäuscht, denn die DS18B20 Funktionen sind schlicht nicht für den Duo implementiert. Leider gibt es auch keinen direkten GPIO 1Wire Kernelsupport, sodass owserver ebenfalls keine Option ist. Schließlich habe ich mich in den Sensor-Demos im Wiki umgesehen und ein kleines Ausleseprogramm auf Basis der wiringX Bibliothek implementiert. Das Programm gibt einen JSON String aus, den man dann einfach in weiteren Programmen einlesen kann.
Angeschlossen wird der Sensor an 3,3 V, GND und Pin GPIOA15. Zwischen 3,3V und GPIOA15 müsst ihr noch einen 4,7 kOhm Widerstand einlöten. Das ist wie bei jedem anderen 1Wire Projekt.
Um die Temperatur im Webinterface auszugeben habe ich einen asynchronen Ansatz gewählt, bei dem ein kleines Shellscript in der Dauerschleife läuft und die Ausgabe einfach in das tmpfs in /tmp schreibt. Von hier aus können es dann andere Programme auslesen und auswerten.
Den Quellcode verlinke ich euch hier: ds18b20-milkv-duo.tar.bz2
Gestartet wird das Script in der /mnt/system/auto.sh
while [ 1 ]; do
# Update temperature file every 30 seconds
/mnt/system/ds18b20 > /tmp/ds18b20.json
# publish to mqtt on success
TMP="$(fgrep true /tmp/ds18b20.json | cut -d":" -f3 | cut -d'}' -f1)"
if [ "$TMP" != "" ]; then
/usr/bin/mosquitto_pub -h 192.168.x.y -t 'homie/RackTemp/sensor/temperature' -m $TMP
fi
sleep 30
done
Wie ihr sehen könnt schickt das Script den Wert auch gleich noch auf den MQTT bus. In seltenen Fällen schlägt das Auslesen fehl (was man an einer falschen CRC Checksum erkennt). In dem Fall erhält das JSON Object einfach ein "success":false. Ich greppe hier also einfach nach true und sende nur dann den Wert auf den Bus.
Das Webinterface
Mein Webinterface ist noch sehr rudimentär daher hier nur zwei Screenshots. Bisher kann man die Steckdosen per Click ein- und ausschalten. Die anderen Funktionen in den Settings sind noch nicht implementiert. Falls ich das irgendwann mal zu ende bringe kann ich euch aber hier den Code noch nachliefern.
Wie immer hoffe ich dass hier ein paar nützliche Informationen dabei waren und wenn ihr etwas ähnliches bastelt würde ich mich über ein paar Fotos sehr freuen.