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.

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.

Categories
Stuff

Hello Noteblog

Started this Blog to have a place to dump random notes that other people might find useful and so I can find it again later. ­čÖé