Categories
Stuff

Making PCBs at Home

I like building stuff with electronics, but for projects expected to actually go into ‘production’, a breadboard just doesn’t cut it. I know I can order PCBs cheap on the internet, but last I checked they’re ether from China and take a while to arrive, or not inexpensive.

So here is the approach I’m currently using to do PCBs at home in a relatively quick and cheap way. Note that ‘beautiful’ is not a word in the previous sentence 🙂 This method is based on ideas and processes I picked up on various sites, particularly https://www.mikrocontroller.net/ has lots of interesting stuff.

I design the PCB in Fusion 360 – I know I can use KiCad but I learned Eagle a few years ago and have a Fusion 360 license for doing 3d printer designs anyway.

Once the PCB is done I select all the relevant bottom layers, and print them in b/w and NOT MIRRORED. The goal is to have an image that is the mirror inverse of what needs to be the etch mask later. This sometimes takes me a few times to get completely right, so I print on normal paper first. The printer must be a laser printer and you need good toner with solid black areas!

Once it looks right I put a piece of magazine paper over it, attached at one end. The paper needs to be the type of slightly glossy, completely smooth thin paper you find in magazines or catalogues. I’m using half a page from an ‘ELV’ catalog here. 🙂 It’s only attached at one end because it needs to go through the laser printer without getting torn or folded and I found this works best on my super cheap Samsung M2020. Be aware that whatever laser printer you use might not like this at all, do at your own risk.

Once I have the size I cut a piece of pure PCB, just copper layer, no photosensitive layer. I find a manual jigsaw works best. After cutting I file off the edges, do a first clean of the surface with steel wool, and then a second clean with acetone to remove all grease from it. (Use gloves!)

I attach the piece of PCB with the copper surface to the printed design, using heat-resistant tape. Then I run it through a laminating machine, any cheap one should do fine. It needs to go through a couple of times, probably based on temperature and size. I just let it run through in all directions until it’s too hot to touch and the paper solidly sticks to the PCB – don’t pull it off!

I cut off most of the paper and soak the whole thing in water for a few minutes. The paper gets ‘darker’ and finally starts showing the underlying PCB layout. At that point I start gently wiping the soaked paper off the board, it should come loose with almost no effort. The toner is left behind, stuck to the copper.

If the toner is not properly sticking to the PCB then the treatment in the laminator was too short or it has not properly soaked. If that happens I clean the PCB with acetone, and go back to the previous step.

In the meantime I’ve preheated the etching solution (just ferric chloride) – the heat bed of a 3d printer works quite well for this 🙂 This will make the etching process take only a few minutes.

Etching in progress. I carefully wobble the PCB around to ensure fresh acid gets to all surface area uniformly. The toner acts as a protective layer, letting the acid remove all of the other copper. Once there is no more shiny copper left I remove the PCB and wash it in water to remove the remaining acid. (Again, use gloves!)

Finally I wipe off the toner with acetone to get the finished PCB. (Did I mention using gloves?)

I occasionally use the same laminating process to also get a silkscreen layer on the other side of the PCB – that’s useful for more complex designs but often unnecessary. I think it should also be possible to do a double sided PCB this way, but most of my designs work well single sided with a couple of jumpers and I avoid the hassle of lining up front and back.

The PCB can be used with the pure copper surface, but I like to put a layer of tin on it to make soldering easier. I found the easiest way is using this “Fittingslötpaste” that’s otherwise used for soldering pipes together. I smear a layer of this stuff on the copper surface, and then heat the whole thing with a heat gun. The paste first looks like its melting, and then it gets solid again. After that I stop applying heat, let it cool, and then wash off the solidified paste with water. Underneath it a uniform layer of tin has covered the copper.

Once all layers are etched/laminated/tinned, it’s time to drill holes as necessary using a drill press. Again I don’t strive for perfection, I just drill everything with an 0.9mm drill and then check if something doesn’t fit.

This here is just a simple breakout board for an experiment, but the pinout of the RJ45 connector just doesn’t fit in a breadboard and I didn’t want to order and wait for an alternative.

Categories
Home Automation

Proxmox VE Installation

I’m using Proxmox VE to host all most important home automation servers. This is a log of how I installed it.

  1. Download ISO, write to USB flash drive, boot from it, let installation run through. Enter static IP. Reboot and remove flash drive.
  2. Using the web-ui at server:8006, disable the pve-enterprise repository (under Updates->Repositories) and add a pve-no-subscription one. (This is a tiny home server, not a real production system.) Refresh updates, upgrade all packages, reboot.
  3. Verify SMART is enabled (smartctl -a /dev/sda), smartd is running, and it emails root when it finds something (/etc/smartd.conf has enabled -m root flag).
  4. Setup email: dpkg-reconfigure postfix, “satellite system”, system mail name “localdomain”, relayhost “[192.168.x.x]”, give full internal root email address, leave other settings as default. Send a test email to root: mail root
  5. Add Backup disk (under Datacenter -> Storage), e.g. for NFS on Synology see the following config. Enable daily backups to this disk for all VMs, using ZSTD compression.
nfs: proxmox-backup
	export /volume1/proxmox-backup
	path /mnt/pve/proxmox-backup
	server 192.168.5.50
	content backup
	options vers=4.1
	prune-backups keep-daily=7,keep-last=3,keep-monthly=6,keep-weekly=3,keep-yearly=3
  1. Donwload ISO(s), e.g. https://ubuntu.com/download/server, directly in “local” storage -> ISO Images. Download CT templates, too.

TBD: Backup server instead of NFS, Influxdb integration, setting up VMs

Categories
Stuff

TBD: Nuki Lock and Bluetooth Dev in Python

$ sudo apt install python3-venv
$ python3 -m venv doorctl
$ cd doorctl/
$ . bin/activate
$ sudo apt install pkg-config python3-dev libglib2.0-dev libboost-thread-dev libbluetooth-dev libboost-python-dev
$ pip install gattlib
$ pip install pybluez

scan for BLE devices with: sudo hcitool lescan

pybluez with examples, gattlib

Nuki Bluetooth API, forum

Python lib/tool: nukiPyBridge (does not seem to work with Lock V2 and current BT protocol?)

Categories
Stuff

Rademacher Bridge zerlegt

Es musste ein alter Gurtwickler nach Jahren harter Arbeit ersetzt werden, der Motor röchelte schon nur noch 🙂 Nachdem ich kürzlich schon einen Rademacher “RolloTron Basis DuoFern 1200” angeschaut und für ok befunden habe, habe ich fix noch einen gekauft, zusammen mit der Bridge. Und was soll ich sagen, die Bridge ist für DIY tatsächlich ausgesprochen gut geeignet!

Hardware

Interessanterweise ist nämlich ein Raspberry Pi Zero verbaut worden, in der Version ohne Wifi – daher kann die Bridge auch nur per Ethernet angebunden werden. Wenn unbedingt notwendig könnte man Wifi nun vermutlich dranbasteln, entweder indem man einen USB Wifi Stick verwendet, oder gleich durch Austausch des Raspi. Vielleicht ein Projekt für später 🙂

Die Elektronik auf der Zusatzplatine enthält kaum Überraschungen, im wesentlichen werkelt hier nur ein KSZ 8851 SNL für den Ethernetport, und ein NRF 905 für den Funk. (Der große scheinbar leere Teil der Platine gegenüber vom Raspi ist die Antenne – sehr gut diese möglichst weit vom Raspi entfernt zu positionieren!)

Doch es gibt noch einen dritten Chip, dieser ist nur beschriftet mit “104GKA 742FZ00” (0en könnten auch Os sein, und 1er könnten auch große I oder kleine l sein, der Font ist nicht ganz klar). Dazu finde ich keine Informationen oder Datenblätter, ich vermute dies ist ein kleiner Custom Microcontroller, welcher die DuoFern Verschlüsselung handhabt. Er schein jedenfalls in der Schaltung zwischen Raspi und Funkchip zu sitzen, im Detail habe ich das aber nicht untersucht.

Software

Die 16GB SD Karte habe ich natürlich an einem anderen Rechner ein wenig durchgesehen. Interessanterweise werden nur etwa 6,7GB verwendet, der Rest ist nicht einmal partitioniert. Die vier Partitionen sind:

  1. /boot, verwendet den U-Boot Bootloader
  2. Root-FS / für normalen Betrieb
  3. Root-FS / für “Recovery” – einfach ein Minimalsystem das die SD-Karte updaten kann
  4. /data, Ablage von Konfigurations- und Nutzerdaten mittels OverlayFS

Das verwendete Linux ist ein radikal abgespecktes Debian-Derivat, im Endeffekt läuft einfach nur ein nginx, ein mosquitto, und eine Reihe Java-basierte Server für die Homepilot Funktionalität. Nicht viel anders als z.B. eine Hue Bridge.

Im Prinzip wird auch ein SSH Server (Dropbear) gestartet, doch ist dieser nicht im normalen Betrieb erreichbar, und der root Account hat eh kein Passwort. Etwas merkwürdig ist dies alles aber schon, dazu evtl. später mehr.

Betrieb

Die Bridge holt sich über DHCP eine IP und ist nach ein oder zwei Minuten im Netz (ausschließlich) per http zu erreichen. Das Web-UI zeigt zu Anfang einige Fehlermeldungen, nach der Registrierung eines Gurtwicklers verschwanden diese aber.

Zuerst dachte ich, man braucht unbedingt die Mobile App um die Bridge zu betreiben, da dies in der Schnellstart-Doku so dargestellt ist. Die Homepilot App verlangt aber eine Anmeldung mit lauter persönlichen Daten und unter Abzeichnung einer ewig langen AGB, das ist jetzt echt nicht cool von Rademacher, da mache ich nicht mit, ich will deren Cloud doch überhaupt nicht verwenden. Die Bridge funktioniert auch ohne die App problemlos.

Anmelden der Gurtwickler geht problemlos, der Zustand ist in der Web-UI zu sehen und die Geräte auch von dort zu steuern. Zu meiner Überraschung und Freude stellt sich das Protokoll der Web-UI zwischen Browser und Server als einfache REST-API ohne Verschlüsselung oder andere Komplikationen dar. Sehr gut! \o/

Abfrage des Zustands aller Geräte, die aktuelle Position des Rolladens ist in “statusesMap.Position”:

GET http://192.168.1.100/v4/devices?devtype=Actuator
->
{"response":"get_visible_devices","devices":[{"description":"Ihre Gerätebeschreibung","deviceGroup":2,"did":1,"hasErrors":0,"iconSetInverted":0,"iconSet":{"k":"iconset15"},"messages":[],"name":"Gurtwickler","properties":{"closingContact":3,"dawn":3,"dusk":3,"motion":3,"rain":3,"smartphone":3,"smoke":3,"sun":3,"temperature":3,"time":3,"trigger":3,"warning":3,"wind":3},"statusValid":true,"statusesMap":{"Manuellbetrieb":0,"Position":100},"visible":true,"deviceNumber":"12345","uid":"12345_1","voiceControlledBy":"Alexa,Google","origin":"HomePilot"}]}

Um einen Gurtwickler z.B. hoch zu fahren wird dieser Befehl verwendet:

{"name":"POS_UP_CMD"}
->
PUT http://192.168.1.100/devices/1

Das lässt sich alles gut in den Chrome Developer Tools nachvollziehen. Die Web-UI pollt dabei alle 5 Sekunden den Zustand aller Geräte.

Automatisierung mit Node-Red

Ich bin seit einiger Zeit dabei meine Heimautomatisierung auf Node-Red umzustellen. Aufgrund der einfachen API ist das Einbinden der DuoFern Bridge einfach, z.B.:

Der HTTP Request ist schlicht ein GET auf die o.g. URL, dann wird payload.devices extrahiert, und nach dem Split topic auf die JSONata Expression "node-red/state/shutter/" & payload.name und payload auf payload.statusesMap.Position gesetzt.

Kompletter Flow zum Import:

[{"id":"c8b52443.ebd668","type":"http request","z":"7c7fbbe3.c93334","name":"get Homepilot Actuators","method":"GET","ret":"obj","paytoqs":"ignore","url":"http://192.168.5.84/v4/devices?devtype=Actuator","tls":"","persist":true,"proxy":"","authType":"","x":330,"y":100,"wires":[["554cb107.e498b"]]},{"id":"197a0837.8f5378","type":"inject","z":"7c7fbbe3.c93334","name":"every 10s","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"10","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":130,"y":100,"wires":[["c8b52443.ebd668"]]},{"id":"fbfbe102.faed","type":"split","z":"7c7fbbe3.c93334","name":"","splt":"\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":690,"y":100,"wires":[["54561ee1.d4a4a"]]},{"id":"554cb107.e498b","type":"change","z":"7c7fbbe3.c93334","name":"extract devices","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.devices","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":100,"wires":[["fbfbe102.faed"]]},{"id":"54561ee1.d4a4a","type":"change","z":"7c7fbbe3.c93334","name":"extract name and position","rules":[{"t":"set","p":"topic","pt":"msg","to":"\"node-red/state/shutter/\" & payload.name","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"payload.statusesMap.Position","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":530,"y":180,"wires":[["2dadb18f.485a5e"]]},{"id":"2dadb18f.485a5e","type":"rbe","z":"7c7fbbe3.c93334","name":"changed?","func":"rbe","gap":"","start":"","inout":"out","property":"payload","x":720,"y":180,"wires":[["8e95cd87.c3616"]]},{"id":"b980972.e76f068","type":"mqtt out","z":"7c7fbbe3.c93334","name":"retain in mqtt","topic":"","qos":"0","retain":"true","broker":"15113c55.7e1ad4","x":990,"y":180,"wires":[]},{"id":"8e95cd87.c3616","type":"json","z":"7c7fbbe3.c93334","name":"","property":"payload","action":"str","pretty":false,"x":850,"y":180,"wires":[["b980972.e76f068"]]},{"id":"15113c55.7e1ad4","type":"mqtt-broker","name":"mqtt","broker":"192.168.5.55","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"node-red/status","birthQos":"0","birthRetain":"true","birthPayload":"online","closeTopic":"node-red/status","closeQos":"0","closeRetain":"true","closePayload":"offline","willTopic":"node-red/status","willQos":"0","willRetain":"true","willPayload":"offline"}]

Zum Steuern wird der entsprechende Befehl per HTTP PUT gesendet, das lässt sich leicht testen:

Beispiel-Flow:

[{"id":"9a664c2c.2850f","type":"inject","z":"7c7fbbe3.c93334","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":120,"y":360,"wires":[["73147993.9bb628"]]},{"id":"82bdbdfd.1611a","type":"http request","z":"7c7fbbe3.c93334","name":"-> Rolladen Kinderzimmer","method":"PUT","ret":"txt","paytoqs":"ignore","url":"http://192.168.5.84/devices/1","tls":"","persist":false,"proxy":"","authType":"","x":550,"y":400,"wires":[[]]},{"id":"73147993.9bb628","type":"change","z":"7c7fbbe3.c93334","name":"POS_DOWN_CMD","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"name\":\"POS_DOWN_CMD\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":360,"wires":[["82bdbdfd.1611a"]]},{"id":"90dea158.bf2be","type":"change","z":"7c7fbbe3.c93334","name":"POS_UP_CMD","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"name\":\"POS_UP_CMD\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":300,"y":400,"wires":[["82bdbdfd.1611a"]]},{"id":"9801853d.7dc9c8","type":"change","z":"7c7fbbe3.c93334","name":"STOP_CMD","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"name\":\"STOP_CMD\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":290,"y":440,"wires":[["82bdbdfd.1611a"]]},{"id":"7444ae38.d4d86","type":"inject","z":"7c7fbbe3.c93334","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":120,"y":400,"wires":[["90dea158.bf2be"]]},{"id":"54598784.398fa8","type":"inject","z":"7c7fbbe3.c93334","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":120,"y":440,"wires":[["9801853d.7dc9c8"]]}]

Das ganze habe ich in mein Dashboard eingebunden, dazu habe ich im Netz einen interessanten Trick gefunden den Rolladen-Zustand mit einem SVG dynamisch darzustellen, und dies mit einem Popup-Kontextmenu verbunden, hier z.B. ganz geschlossen und ich habe das Symbol angetippt um das Menu zu öffnen:

Der entsprechende Flow basiert auf dem oben generierten MQTT Status, und sehr viel HTML/CSS/JS/SVG:

Kompletter Flow zum Import in Node Red:

[{"id":"ecf7b07a.89c4d","type":"ui_template","z":"c84d2e4e.6fecf","group":"915ba942.2c5bf8","name":"Rolladen","order":2,"width":1,"height":1,"format":"<div class=\"rollade__container\" ng-click=\"send({x:$event.clientX,y:$event.clientY})\">\n    <svg class=\"rollade__fenster\" width=\"100%\" height=\"100%\" viewBox=\"0 0 170 188\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xml:space=\"preserve\" xmlns:serif=\"http://www.serif.com/\" style=\"fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;\">\n        <g transform=\"matrix(1,0,0,1,-15.909,-45.6212)\">\n            <g transform=\"matrix(0.989904,0,0,0.992491,4.03539,0.837446)\">\n                <path d=\"M180.329,56.389C180.329,51.839 176.626,48.145 172.064,48.145L23.291,48.145C18.729,48.145 15.025,51.839 15.025,56.389L15.025,222.454C15.025,227.004 18.729,230.698 23.291,230.698L172.064,230.698C176.626,230.698 180.329,227.004 180.329,222.454L180.329,56.389Z\" />\n            </g>\n            <g transform=\"matrix(1,0,0,1.38253,3.04599,-37.9811)\">\n                <path d=\"M97.589,64.432L97.589,191.286\" />\n            </g>\n            <g transform=\"matrix(1,0,0,1,2.84135,56.1777)\">\n                <path d=\"M18.74,83.189L177.463,84.189\" />\n            </g>\n        </g>\n    </svg>\n    <svg class=\"rollade__rollade\" style=\"fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;top: calc((-1) * {{msg.payload}}%);\" width=\"100%\" height=\"100%\" viewBox=\"0 0 181 190\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xml:space=\"preserve\" xmlns:serif=\"http://www.serif.com/\">\n        <g transform=\"matrix(1.35281,0,0,1.35281,-45.3041,-102.109)\">\n            <path class=\"bg\" d=\"M167.134,82.161C167.134,78.473 164.14,75.479 160.452,75.479L40.171,75.479C36.483,75.479 33.489,78.473 33.489,82.161L33.489,208.93C33.489,212.618 36.483,215.612 40.171,215.612L160.452,215.612C164.14,215.612 167.134,212.618 167.134,208.93L167.134,82.161Z\"/>\n            <g transform=\"matrix(1.69574,0,0,1,18.3547,-9)\">\n                <path class=\"line\" d=\"M13.383,95.652L84.46,95.652\" />\n            </g>\n            <g transform=\"matrix(1.69574,0,0,1,18.3547,1)\">\n                <path class=\"line\" d=\"M13.383,95.652L84.46,95.652\" />\n            </g>\n            <g transform=\"matrix(1.69574,0,0,1,18.3547,11)\">\n                <path class=\"line\" d=\"M13.383,95.652L84.46,95.652\" />\n            </g>\n            <g transform=\"matrix(1.69574,0,0,1,18.3547,21)\">\n                <path class=\"line\" d=\"M13.383,95.652L84.46,95.652\" />\n            </g>\n            <g transform=\"matrix(1.69574,0,0,1,18.3547,31)\">\n                <path class=\"line\" d=\"M13.383,95.652L84.46,95.652\" />\n            </g>\n            <g transform=\"matrix(1.69574,0,0,1,18.3547,41)\">\n                <path class=\"line\" d=\"M13.383,95.652L84.46,95.652\" />\n            </g>\n            <g transform=\"matrix(1.69574,0,0,1,18.3547,51)\">\n                <path class=\"line\" d=\"M13.383,95.652L84.46,95.652\" />\n            </g>\n            <g transform=\"matrix(1.69574,0,0,1,18.3547,61)\">\n                <path class=\"line\" d=\"M13.383,95.652L84.46,95.652\" />\n            </g>\n            <g transform=\"matrix(1.69574,0,0,1,18.3547,71)\">\n                <path class=\"line\" d=\"M13.383,95.652L84.46,95.652\" />\n            </g>\n            <g transform=\"matrix(1.69574,0,0,1,18.3547,81)\">\n                <path class=\"line\" d=\"M13.383,95.652L84.46,95.652\" />\n            </g>\n            <g transform=\"matrix(1.69574,0,0,1,18.3547,91)\">\n                <path class=\"line\" d=\"M13.383,95.652L84.46,95.652\" />\n            </g>\n            <g transform=\"matrix(1.69574,0,0,1,18.3547,101)\">\n                <path class=\"line\" d=\"M13.383,95.652L84.46,95.652\" />\n            </g>\n            <g transform=\"matrix(1.69574,0,0,1,18.3547,111)\">\n                <path class=\"line\" d=\"M13.383,95.652L84.46,95.652\" />\n            </g>\n        </g>\n    </svg>\n</div>","storeOutMessages":false,"fwdInMessages":false,"resendOnRefresh":false,"templateScope":"local","x":820,"y":2480,"wires":[["1d646b43.9bcf25"]]},{"id":"31af164f.9f07ca","type":"mqtt in","z":"c84d2e4e.6fecf","name":"","topic":"node-red/state/shutter/Kinderzimmer Seitenfenster","qos":"2","datatype":"auto","broker":"15113c55.7e1ad4","x":250,"y":2480,"wires":[["6ce5e2fb.d46b6c"]]},{"id":"4ddbec36.18dfa4","type":"change","z":"c84d2e4e.6fecf","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"100 - payload","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":660,"y":2480,"wires":[["ecf7b07a.89c4d"]]},{"id":"6ce5e2fb.d46b6c","type":"json","z":"c84d2e4e.6fecf","name":"","property":"payload","action":"obj","pretty":false,"x":510,"y":2480,"wires":[["4ddbec36.18dfa4"]]},{"id":"a643ae90.71294","type":"ui_template","z":"c84d2e4e.6fecf","group":"c0e1c6ff.af3868","name":"Rolladen Style","order":3,"width":0,"height":0,"format":"<style>\n        :root {\n            --rollade-color: #097479;\n            --rollade-width: 32px;\n        }\n        .rollade__container {\n            width: var(--rollade-width);\n            overflow: hidden;\n            position: relative;\n        }\n        .rollade__fenster {\n            position: relative;\n            overflow: hidden;\n            z-index: 1;\n        }\n        .rollade__fenster path {\n            stroke: var(--rollade-color);\n            fill:none!important;\n            stroke-width:6px;\n        }\n        .rollade__rollade {\n            position: absolute;\n            width: 100%;\n            height: 100%;\n            left: 0px;\n            z-index: 10;\n            transition: all 1s;\n        }\n        .rollade__rollade path.line {\n            stroke: white;\n            fill: none;\n            stroke-width: 1.5px;\n        }\n        .rollade__rollade path.bg {\n            fill: var(--rollade-color);\n        }\n        \n</style>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"global","x":140,"y":2440,"wires":[[]]},{"id":"1d646b43.9bcf25","type":"ui_context_menu","z":"c84d2e4e.6fecf","group":"915ba942.2c5bf8","order":5,"width":0,"height":-1,"fontSize":"20","inputPositionXField":"x","inputPositionXType":"msg","inputPositionYField":"y","inputPositionYType":"msg","outputField":"payload","inputMenuField":"menu","inputMenuType":"fixed","menuItems":[{"id":"","icon":"","label":"auf","topic":"","payload":"POS_UP_CMD","payloadType":"str","visible":true,"enabled":true},{"id":"","icon":"","label":"zu","topic":"","payload":"POS_DOWN_CMD","payloadType":"str","visible":true,"enabled":true},{"id":"","icon":"","label":"Stop","topic":"","payload":"STOP_CMD","payloadType":"str","visible":true,"enabled":true}],"colors":"theme","textColor":"#000000","backgroundColor":"#ffffff","borderColor":"#626262","intervalLength":"10","intervalUnit":"secs","startTimerAtOpen":true,"startTimerAtLeave":false,"stopTimerAtEnter":false,"name":"","x":440,"y":2540,"wires":[["d727a854.e7e268"]]},{"id":"d727a854.e7e268","type":"change","z":"c84d2e4e.6fecf","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"name\":payload}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":640,"y":2540,"wires":[["77911e99.f0cf9"]]},{"id":"77911e99.f0cf9","type":"http request","z":"c84d2e4e.6fecf","name":"-> Rolladen Kinderzimmer","method":"PUT","ret":"txt","paytoqs":"ignore","url":"http://192.168.5.84/devices/1","tls":"","persist":false,"proxy":"","authType":"","x":870,"y":2540,"wires":[[]]},{"id":"915ba942.2c5bf8","type":"ui_group","name":"OG: Kinder","tab":"f36ba877.ed9c98","order":5,"disp":true,"width":6,"collapse":false},{"id":"15113c55.7e1ad4","type":"mqtt-broker","name":"mqtt","broker":"192.168.5.55","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"node-red/status","birthQos":"0","birthRetain":"true","birthPayload":"online","closeTopic":"node-red/status","closeQos":"0","closeRetain":"true","closePayload":"offline","willTopic":"node-red/status","willQos":"0","willRetain":"true","willPayload":"offline"},{"id":"c0e1c6ff.af3868","type":"ui_group","name":"Verschiedenes","tab":"a517c876.a6dfc8","order":1,"disp":true,"width":"6","collapse":false},{"id":"f36ba877.ed9c98","type":"ui_tab","name":"Fenster","icon":"mi-business","order":3,"disabled":false,"hidden":false},{"id":"a517c876.a6dfc8","type":"ui_tab","name":"Home","icon":"mi-home","order":1,"disabled":false,"hidden":false}]
Categories
Stuff

Kleine Analyse eines Gurtwicklers

Ich würde gerne ein (oder vielleicht mehrere) manuelle (Gurt-)Jalousien mit der Heimautomatisierung verbinden. Leider scheint es nichts zu geben das direkt den DIY-Markt anspricht. Ich finde nur irgendwelche geschlossenen “Lösungen” wie Rademacher’s DuoFern, oder Leute die einfach Kabel an die Knöpfe eines Gurtwicklers löten. Ich will aber eigentlich nicht noch irgendeine Bridge mit zweifelhafter Zukunft und mehr oder (mehr wahrscheinlich) weniger Funktionen haben.

Also habe ich mir mal ein RolloTron 1200 bestellt, dieser hat DuoFern als Funklösung eingebaut. Meine Hoffnung war entweder einen “alternativen Zugang” zur Elektronik zu finden, oder zumindest mehr über DuoFern zu lernen. Vielleicht ist ja einfach das Gefunke als diskrete kleine Schaltung “drangeflascht” und austauschbar?

Das erhaltene Gerät natürlich gleich auseinandergebaut 🙂 Der innere Aufbau ist recht einfach und die Haupt-Steuerplatine leicht zu entnehmen, hier perspektivkorrigierte Fotos von beiden Seiten (Rückseite spiegelverkehrt!) und übereinandergelegt, um Durchkontaktierungen besser zu erkennen:

Es fällt direkt auf das mehrere Bauelemente nicht bestückt sind – vermutlich wird dieselbe Platine für mehrere verschiedene Geräte benutzt und nur dann entsprechend bestückt. Ich habe im Netz ein Foto der Platine vom Rollotron 1100 gefunden (selbes Gerät wie meins, nur ohne DuoFern) und dort sind die beiden Chips ganz oben nicht bestückt, dafür statt dessen der untere Chip in der Mitte. Der obere unbestückte Chip dürfte für eine Version mit Display sein, dafür dann auch die horizontale Reihe Kontakte und die Einbuchtungen an den Seiten.

Schaltungsbeschreibung

  • Der unterste “Chip” mit den 2×4 Löchern durch die Platine hindurch ist die Kontaktierung für die Netzteil- und Motorplatine.
    • Jeweils ein Pin für 10V und Masse vom Netzteil.
    • Ein Pin führ ein 50Hz Signal bei etwa 4V vom Netzteil, ohne Zweifel aus dem 230V Netz abgeleitet. Ich vermute dies wird verwendet um über lange Zeit die innere Uhr des Mikrocontrollers zu stabilisieren, damit bei einer Zeitsteuerung jeden Tag zur gleichen Zeit die Jalousie verfährt, und dieser Zeitpunkt nicht “wegwandert”.
    • Zwei Pins für eine DC-Motor Steuerung: Ein Pin für die Richtung, einer mit PWM Signal zur Geschwindigkeit.
    • Drei Pins mit irgendeiner Art Steuer- oder Datensignal – ich bin mir nicht sicher was genau. Alle drei Signale zeigen regelmäßige Ausschläge mit 62.5Hz, wobei der Ausgangspegel von der Motor-Drehrichtung abhängt. An der Motorachse sitzt ein Magnet über irgendeinem “Chip” (nicht zu erkennen), vielleicht sind diese drei Signale ein Feedback um die Drehrichtung zu überprüfen und ggf. ein Blockieren des Motors zu erkennen?
  • Links neben dem unteren Schalter ist ein 78L33A Spannungsregler für 3.3V.
  • Der große Chip unterhalb des oberen Schalters ist ein R5F 100, das ist ein 16bit Microcontroller von Renesas aus der RL78 Serie, Datenblatt hier. (Nicht das ich Lust hätte einen großen Haufen Maschinencode zu entziffern, aber soweit eine kurze Recherche im Netz ergab erlaubt dieser Microcontroller sowieso kein Auslesen des Programm-Flash.)
  • Der kleinere Chip schräg daneben ist ein NRF 905: 433/868/915MHz Transceiver. (Datenblatt) Der Kleinkram links daneben ist die Antennenabstimmung, die Antenne selber ist die einzelne einsame Leiterbahn ganz am linken Rand. Gut zu erkennen der große Abstand zur restlichen Elektronik, “weil Funk” 🙂 Interessanterweise ist die Antenne nur etwa 11-12 cm lang – bei den verwendeten 434.5 MHz wäre etwa 17.2 cm (Lambda/4) eigentlich besser, aber passt vermutlich einfach nicht ins Gerät.
  • Microcontroller und Funkchip sind per SPI-Bus und ein paar weiterer Steuerleitungen verbunden. Soweit ich das sehe kann hat der Transceiver keine “höheren” Features, das Funkprotokoll und evtl. Verschlüsselung muss also im Mikrocontroller erfolgen. Man könnte überlegen den SPI-Bus abzuhöhren, aber das würde im wesentlichen auch nur dieselben Daten zeigen wie das was über den Funk geht.
  • Am oberen Platinenrand ist ein Reed-Kontakt, dieser wird durch einen Magneten in der Gurtrolle getriggert. Ich vermute dies ist Teil der Blockiererkennung – wenn die Jalousie sich nicht weiter abwärts bewegt wird der Gurt locker und die Rolle dreht sich nicht mehr.
  • Am rechten Rand sind drei nummerierte und zum Teil bestückte Wiederstände, diese sind elektrisch nicht mit der restlichen Platine verbunden und nur über die Kontaktflächen auf der Rückseite erreichbar. Überhaupt hat die Rückseite eine erstaunlich große Anzahl Kontaktflächen. Die drei “Code-Wiederstände” dürften es erlauben in Testständen die jeweils bestückte Platinenversion abzufragen und automatisch z.B. die Programmierung an die jeweils bestückten Chips anzupassen.

Fazit

So wie ich mich das vorgestellt habe wird das nichts – bei diesem Gerät müsste man den Microcontroller komplett umprogrammieren oder die Elektronik austauschen um ein anderes Funkprotokoll zu bekommen. Und einfach nur Drähte an die auf/ab-Knöpfe anzulöten mag ich nicht, dann gibt es keine Rückmeldung über den aktuellen Zustand der Jalousie.

Categories
Stuff

Raspberry Pi Kiosk Mode

For home automation or such. Directly boot into a graphical screen and allow user interaction through a touchscreen.

Setup 1: Living Room Control Panel

Intention is that this is “off” by default, turns on when the screen is touched, then allows interaction with a browser, and after some time of no use turns off again.

The method I’m using follows what’s lined out here, or on various other pages. Basically configure the raspi to autologin with graphical UI, then configure the user’s X session in ~/.config/lxsession/LXDE/autostart like this:

@xscreensaver
@/usr/bin/chromium-browser --check-for-update-interval=31536000 --noerrdialogs --disable-features=TranslateUI --disable-infobars --incognito --kiosk http://192.168.5.73:1880/ui/

Using xscreensaver because here it’s trivial to include in the LXDE autostart. There’s also an .xscreensaver file for a simple blank screensaver:

timeout: 0:01:00
lock: False
splash: False
dpmsEnabled: True
dpmsQuickOff: True
mode: blank

To make the boot slightly faster and avoiding extra graphics the /boot/cmdline.txt includes logo.nologo and /boot/config.txt includes disable_splash=1

I should consider a regular reboot, but this is running stable enough not to require that. If I remember correctly I also struggled a lot with getting Chromium to properly output sound to a USB-connected soundcard/speaker, but I don’t remember what I did to get it to work – I think it was about running pavucontrol while Chromium was playing some sound, and then fixing devices/volumes in there. Side note: Chromium does not include Google’s Text-to-Speech system, and I could not get some system-side thing (e.g. espeak) to work.

Setup 2: Device Controller

I’m working on a little something that I’ll share more about later. This “needs” (*ahem*) a Raspberry Pi with a small touchscreen as a local interactive controller. I have an (old) Watterott 2.8″ touch display I want to use on a Raspberry 3a.

The setup needs to be a little different than the one described above. This will run a fullscreen Kivy app and the screensaver should be steered by this app and not happen automatically. The system might even be shut down between it’s use, I’ll see about that later.

The basic Kivy installation is described here.

I’m using chilipie-kiosk as the concept for the basic setup, steps:

  • raspi-config: System -> Boot -> Console Autologin for user kapet (that should be automatically the case if sudo from that user).
  • Couple of small tweak/optimizations to make boot faster/cleaner:
    • Empty out /etc/motd, so it’s not shown when user is logged in.
    • Add disable_splash=1 to /boot/config.txt (not 100% sure this helps but it doesn’t hurt either…)
    • Make sure splash screen at boot is disabled, raspi-config: System -> Splash at boot
    • Add After=getty.target in the [Unit] section of /etc/systemd/system/raspi2fb@.service to show fewer of the boot msgs on the TFT (and maybe boot a tiny bit quicker).
  • And remember to enable the GL (not Legacy!) driver in raspi-config or Kivy will be nasty slow. “Fake KMS” GL driver works well on my Raspberry Pi 3a, but unfortunately X11 gets confused of the screen resolution and needs a manual hint by running xrandr in the .xsession file.

Add this at the top of ~/.profile to start X when auto-logged’in:

if [ "$(tty)" == "/dev/tty1" ]; then
    exec startx >/dev/null 2>&1
fi

Create an ~/.xsession file for X setup and starting the app:

#!/bin/bash

export DISPLAY=:0.0

# X11 gets the resolution wrong with the GL driver
xrandr --output HDMI-1 --mode 320x240

# disable automatic screensaver, app will handle this
xset s off
xset -dpms
xset s noblank

cd /home/kapet/ergo
bin/python3 ergo.py >stdout.log 2>stderr.log &

# a window manager is required of VNC doesn't work properly
exec matchbox-window-manager -use_titlebar no
Categories
Stuff

Kivy on Raspberry Pi with a Touchscreen

This took way too long to get right 😢 Really all I want is writing a simple graphical fullscreen UI in Python that runs on the small touchscreen attached to it.

Fundamental Problems:

  • The touchscreen is connected through the GPIO port and not through the GPU. This means access is through the kernel FB driver only and there is no hardware acceleration of any kind.
  • SDL on Raspbian has not been compiled with proper touch-device support, specifically tslib is not enabled. This breaks pygame in interesting ways (touch input is all over the place wrong) and requires a custom built SDL.
  • SDL also has not been compiled with the kmsdrm backend, which apparently breaks Kivy when run on the console.
  • As far as I can tell Kivy requires GL support of some kind, which is only provided by the GPU and not by the FB device anyway – in other words – Kivy can not output directly to the touchscreen.
  • I’ve played with Kivy before and found it somewhat frustrating – there’s so much magic going on in the background that I found very difficult to grok. Also when things go wrong, error messages are not helpful. (For example, during console testing I had set some SDL environment vars. They were still set when I tried running Kivy under x11 and caused fascinating but completely misleading errors that had me look in all sorts of wrong places for hours… 😒 ) Still, Kivy seems to be the best of breed if I don’t want to hack basic fundamental UI things by hand.
  • My first thought was to run the UI straight on the console because it’s “simpler”, but I eventually realized that because of all the SDL and framebuffer snafu it would require recompiling a bunch. (Which is something I don’t like – I prefer my automation hosts to have rather pristine system setups with as much customization limited to the apps as possible.) And there’s also another issue – I can’t reasonably test console apps on my other systems. So running the apps under X11 would have the advantage of allowing to VNC into the machine for remote testing at least.

Approach Taken:

  • Basic enablement of touchscreen through /boot/config.txt:
    dtoverlay=rpi-display,speed=32000000,rotate=270
  • Use the builtin GPU to get full hardware acceleration support (whatever little the Raspberry Pi really has…) and render video output without a HDMI device connected. For this force HDMI resolution to the same as the touchscreen by adding this to /boot/config.txt:
    hdmi_force_hotplug=1
    hdmi_group=2
    hdmi_mode=87
    hdmi_cvt=320 240 60 1 0 0 0
  • Run raspi2fb as a service under systemd to constantly copy the output of the GPU onto the touchscreen FB device.
    • I also experimented with rpi-fbcp. The code is much simpler but by default runs at ~40hz or so update rate, which eats a lot of CPU time. It has fewer options and no systemd file either.
    • I looked at fbcp-ili9341 too, which is incredibly impressive with a lot of clever tricks to speed up the copy process, achieving incredible FPS and apparently allowing fluent games even. But the speed comes at the cost of missing touch support – that’s a no-go for me.
  • Configure X input for touchscreen, in (new) directory /etc/X11/xorg.conf.d/ create file 99-ads7846-cal.conf. (This is the “270° rotation” setup from here.)
Section "InputClass"
    Identifier "calibration"
    MatchProduct "ADS7846 Touchscreen"
    Option "EmulateThirdButton" "1"
    Option "EmulateThirdButtonButton" "3"
    Option "EmulateThirdButtonTimeout" "1500"
    Option "EmulateThirdButtonMoveThreshold" "30"
    Option "InvertX" "0"
    Option "InvertY" "1"
    Option "SwapAxes" "1"
    Option "TransformationMatrix" "0 1 0 -1 0 1 0 0 1"
EndSection
  • Configure system using raspi-config:
    • System -> Boot -> Desktop autologin
    • Interface -> VNC -> enable
    • Performance -> GPU Memory -> 64M (or something, just not the smallest amount or X won’t start)
    • Advanced -> GL Driver -> GL with Fake KMS

Now after rebooting the touchscreen shows a (tiny) X11 desktop and it should be possible to connect through VNC and see the same. Next step is installing Kivy.

  • Confusingly some Kivy dependencies that need to be installed are only listed after the rest of the process is described, making them easy to miss. 😒 Look them up first, for Kivy2 they’re here right now. Install big list for Buster first, then small list for “Desktop environment”.
  • Now back to the normal Kivy installation. No need to install and use virtualenv, the Python3 venv works fine.
  • Testing with e.g. share/kivy-examples/demo/touchtracer/main.py should work now, again both on the actual touchscreen as well as over VNC, here is the essential bits of the Kivy log:
[INFO ] [Image ] Providers: img_tex, img_dds, img_sdl2, img_pil (img_ffpyplayer ignored)
[INFO ] [Text ] Provider: sdl2(['text_pango'] ignored)
[INFO ] [Window ] Provider: sdl2(['window_egl_rpi'] ignored)
[INFO ] [GL ] Using the "OpenGL" graphics system
[INFO ] [GL ] Backend used
[INFO ] [GL ] OpenGL version
[INFO ] [GL ] OpenGL vendor
[INFO ] [GL ] OpenGL renderer
[INFO ] [GL ] OpenGL parsed version: 3, 1
[INFO ] [GL ] Shading version
[INFO ] [GL ] Texture max size <8192>
[INFO ] [GL ] Texture max units <32>
[INFO ] [Window ] auto add sdl2 input provider
[INFO ] [Window ] virtual keyboard not allowed, single mode, not docked
[INFO ] [GL ] NPOT texture support is available
[INFO ] [ProbeSysfs ] device match: /dev/input/event0
[INFO ] [HIDInput ] Read event from
[INFO ] [Base ] Start application main loop
[INFO ] [HIDMotionEvent] using
[INFO ] [HIDMotionEvent] range ABS X position is 0 - 4095
[INFO ] [HIDMotionEvent] range ABS Y position is 0 - 4095
[INFO ] [HIDMotionEvent] range ABS pressure is 0 - 255

Important to check that it is using sdl2 and the touch input is properly detected.

Followup: setting up kiosk mode

Categories
Stuff

Kleine Spielzeugreparatur

Meine Kinder haben eine “Exost Loop” Autorennbahn, doch leider funktionierte jetzt eins der Autos nicht mehr – am Controller konnte man drücken wie man wollte, das Auto bewegte sich nicht. 🙁

Stellt sich heraus, bei einer der zwei IR-Leuchtdioden ist ein Lötpunkt samt Leiterbahn von der Platine abgerissen. Im Mikroskop gut zu erkennen, mit bloßem Auge kaum. Ich vermute, da ist mal jemand auf die Fernbedienung drauf getreten…

Etwas Lötlack abgekratzt, und die defekte Stelle mit einem Stück Draht überbrückt.

Es funktioniert wieder! (Aufnahme mit der Kamera des Handys; der Sensor der meisten Digitalkameras kann das IR-Licht der üblichen Fernbedienungen darstellen.)

Categories
Stuff

Raspberry Pi Setup

A few notes on how I set up new raspi systems, of which I am using quite a few for random home automation or toy things.

This is for the old version based on Debian Buster. For Debian Bullseye based OS just use the ‘advanced’ menu in the imager tool.

Prepping the SD card

  • Install “Lite” version of Raspberry Pi OS
  • This should have created one FAT formatted partition that Windows/Mac can access, this is the boot partition
  • Create file “ssh.txt” on boot partition to enable ssh right away, otherwise need to attach a screen for first login
  • If newer raspi and wifi should be used, create file “wpa_supplicant.conf” on boot partition (docs)
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=<Insert 2 letter ISO 3166-1 country code here>

network={
 ssid="<Name of your wireless LAN>"
 psk="<Password for your wireless LAN>"
}

Initial Setup

  • Startup raspi, find IP in DHCP server or try direct ssh to host “raspberrypi” and cross fingers that something somewhere worked right (I think Unifi DHCP server automatically using DHCP provided hostname for DNS? Or is this some MDNS magic? No idea, never looked into it.)
  • Log in with username “pi”, default password “raspberry”
  • sudo raspi-config
    • set system-> hostname
    • set localization -> timezone
    • exit without reboot, it’s not necessary now
  • sudo su -
    • adduser kapet
    • adduser kapet adm
    • adduser kapet sudo
      • and more groups as necessary: dialout, cdrom, audio, video, plugdev, games, input, netdev, spi, i2c, gpio
    • Copy /etc/sudoers.d/010_pi-nopasswd for kapet and update it accordingly
    • reboot
  • Log in as “kapet”
  • sudo su -
    • disable login to the pi account: usermod -e1 -L pi
    • enable longrunning and at-boot started scripts for kapet: loginctl enable-linger kapet
    • apt update
    • apt dist-upgrade
    • apt install lsof tcpdump vim-nox
    • set the NTP server in /etc/systemd/timesyncd.conf
    • reboot

Other Potential Setup

  • set up basic X11: apt install raspberrypi-ui-mods
  • disable bluetooth on pi3: add dtoverlay=pi3-disable-bt to /boot/config.txt
  • when using any GPU to framebuffer magic like raspi2fb for a local LCD display remember to give the GPU enough memory through raspi-config, the minimum amount is not sufficient
  • setup Watterott display: docs

Categories
Stuff

A Surprisingly Useful Tool

20x magnifying stereo microscope, with a common ruler in its fangs.

Based on a recommendation from a coworker I bought a simple stereo microscope for around 30 Euro a while ago. I believe the manufacturers idea is that kids look at insects or little rocks with this (it actually comes with a few tiny splinters of different rocks), but this thing turned out to be extremely useful for several of the electronics stuff I’ve done lately.

For example I’ve used it to check my work after etching a PCB or soldering it. The magnification of 20x turned out to be perfect – it’s significantly better than my eyes but still “real” enough that I can interact with the object using small tools or a soldering bit.

I found having two occulars is not magically improving what I can see, but it certainly makes it less stressful to look through it for a long time. The magnified image shows about 8mm of object width.


In a recent case I was struggling with an Arduino Nano, which was connected to a Raspberry Pi, and just did not want to work if the host rebooted while it was plugged in the USB port. All I got were errors like this in the logfile:

May 23 20:32:28 raspberrypi kernel: [    3.707998] usb 1-1.3: new full-speed USB device number 4 using dwc_otg
May 23 20:32:28 raspberrypi kernel: [    3.807985] usb 1-1.3: device descriptor read/64, error -32
May 23 20:32:28 raspberrypi kernel: [    4.028021] usb 1-1.3: device descriptor read/64, error -32
May 23 20:32:28 raspberrypi kernel: [    4.257989] usb 1-1.3: new full-speed USB device number 5 using dwc_otg         
May 23 20:32:28 raspberrypi kernel: [    4.357989] usb 1-1.3: device descriptor read/64, error -32
May 23 20:32:28 raspberrypi kernel: [    4.577988] usb 1-1.3: device descriptor read/64, error -32
May 23 20:32:28 raspberrypi kernel: [    4.698153] usb 1-1-port3: attempt power cycle
May 23 20:32:28 raspberrypi kernel: [    5.358060] usb 1-1.3: new full-speed USB device number 6 using dwc_otg
May 23 20:32:28 raspberrypi kernel: [    5.818026] usb 1-1.3: device not accepting address 6, error -32
May 23 20:32:28 raspberrypi kernel: [    5.918003] usb 1-1.3: new full-speed USB device number 7 using dwc_otg
May 23 20:32:28 raspberrypi kernel: [    6.357982] usb 1-1.3: device not accepting address 7, error -32
May 23 20:32:28 raspberrypi kernel: [    6.358192] usb 1-1-port3: unable to enumerate USB device

Turns out the problem is not with the USB driver in the Linux kernel, but instead it’s a hardware bug in the Nano: The FTDI serial chip’s “TEST” pin was left unconnected. This way it “floats” after a power cycle and reliably prevented prober USB device enumerating. Ugh. I realized this is the problem after a lengthy search and finally stumbling over this issue in the FHEM wiki.

So here it goes, on the bottom side of the Arduino Nano I need to connect two freaking small SMD pins with each other, so TEST is reliably pulled to ground. I can hardly see those pins properly!

Perfect situation to use the microscope: Put a tiny blob of solder on the very tip of the soldering iron, veeeery slowly direct it to the right pins, and done!

With this the Nano gets connected properly whenever then Raspberry Pi reboots. 🙂

Microscope set up to allow viewing the bottom side of the Arduino Nano.
Solder blob connecting TEST and AGND pins.