{"id":422,"date":"2021-06-18T17:07:13","date_gmt":"2021-06-18T15:07:13","guid":{"rendered":"https:\/\/www.iot-embedded.de\/iot-2021\/?p=422"},"modified":"2021-06-18T17:11:34","modified_gmt":"2021-06-18T15:11:34","slug":"serielle-schnittstelle-im-balena-local-mode-nutzen","status":"publish","type":"post","link":"https:\/\/www.iot-embedded.de\/iot-2021\/wichtige-hinweise\/serielle-schnittstelle-im-balena-local-mode-nutzen\/","title":{"rendered":"Serielle Schnittstelle im Balena Local Mode nutzen"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Wo liegt das Problem?<\/h2>\n\n\n\n<p>Hier ein interessanter Stolperstein in Balena, der einem das Leben ganz sch\u00f6n schwer machen kann, wenn man die eingebauten UARTs des Raspberry Pi f\u00fcr die serielle Kommunikation nutzen will. Fallbeispiele k\u00f6nnten hier die Anbindung von Sensoren mit serieller Schnittstelle oder die Kommunikation mit anderen Microcontrollern wie einem Arduino sein. \u00dcbertr\u00e4gt man hierf\u00fcr einen unter Raspbian fehlerfrei laufenden Quellcode in die Balena-Welt, kann es sein, dass die serielle Kommunikation pl\u00f6tzlich nicht mehr funktioniert. Folgende zwei Ursachen konnte ich heute hierf\u00fcr herausfinden:<\/p>\n\n\n\n<ul><li>Die symbolischen Links \/dev\/serial0 und \/dev\/serial1, die unter Raspbian verwendet werden sollten, um den richtigen UART anzusprechen, existieren zwar im Balena Host OS, werden standardm\u00e4\u00dfig aber nicht an die Container durchgereicht.<\/li><li>Die Development-Version des Belana OS startet zu Debuggingzwecken eine serielle Konsole, welche die UARTs belegt.<\/li><\/ul>\n\n\n\n<p>Um diese zu verstehen muss man wissen, dass der Raspberry Pi zwei UARTs besitzt: Den vollwertigen UART-Baustein unter \/dev\/ttyAMA0 und einen &#8222;Mini UART&#8220; genannten, abgespeckten Baustein unter \/dev\/ttyS0. Ab dem Raspberry Pi 3 ist ersterer mit dem WiFi\/Bluetooth-Baustein verbunden (\u00e4ltere Pis hatten noch kein eingebautes WiFi oder Bluetooth), der zweite mit den GPIO-Pins:7<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/www.abelectronics.co.uk\/docs\/kb\/serialpi\/uart-port.svg\" alt=\"UART GPIO Pins\"\/><\/figure>\n\n\n\n<p>Der Mini UART hat allerdings den Nachteil, an den GPU-Takt gekoppelt zu sein, wodurch die Geschwindigkeit der seriellen Kommunikation den Ger\u00fcchten im Internet nach wohl ziemlich schwanken kann. (Zumindest wird dies immer wieder behauptet, gepr\u00fcft hat es wohl noch niemand :-)). Da ab dem Raspberry Pi 3 \/dev\/ttyS0 anstelle von \/dev\/ttyAMA0 mit den GPIO-Pins verbunden ist, wurde in Rasbpian der symbolische Link \/dev\/serial0 eingef\u00fchrt. Dieser zeigt immer auf den UART, der am GPIO-Header h\u00e4ngt. Der symbolische Link wurde in Balena OS \u00fcbernommen, allerdings vergessen an die Container durchzureichen. Innerhalb der Docker-Container gibt es in Balena daher nur \/dev\/ttyAMA0 und \/dev\/ttyS0, wobei anscheinend beide mit den GPIOs verbunden sind. Konkret hei\u00dft das: <strong><span class=\"has-inline-color has-vivid-red-color\">Um die serielle Schnittstelle des Pis zu nutzen, muss in Balena daher immer \/dev\/ttyAMA0 oder \/dev\/ttyS0 angesprochen werden.<\/span><\/strong><\/p>\n\n\n\n<p>Das zweite Problem ist leider etwas hartn\u00e4ckiger, betrifft daf\u00fcr aber nur die Development-Version von Balena OS. Dort wird in bester Embedded-Tradition eine Login-Konsole \u00fcber die seriellen Ports verf\u00fcgbar gemacht, was einerseits zwar ganz nett ist, andererseits aber dazu f\u00fchrt, dass die UARTs nicht mehr anderweitig genutzt werden k\u00f6nnen. Dummerweise l\u00e4sst sich das Verhalten in der Balena Cloud nirgends deaktivieren. Stattdessen empfehlen die Balena-Macher einfach, eine Produktivversion von Balena OS zu nutzen. Bl\u00f6derweise unterst\u00fctzt dieser aber den \u201eLocal Mode\u201d f\u00fcr die Entwicklung nicht. Insbesondere w\u00e4hrend der Entwicklung ist dies also auch keine Option. <strong><span class=\"has-inline-color has-vivid-red-color\">Stattdessen muss w\u00e4hrend der Entwicklung bei jedem Neustart des Pi der Service serial-getty@serial0 wie folgt deaktiviert werden:<\/span><\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Entwicklungsrechner: balena ssh 569e406.local\nRaspberry Pi: systemctl stop serial-getty@serial0\nRaspberry Pi: exit<\/pre>\n\n\n\n<p>Dadurch werden die serielle Konsole beendet und die UARTs wieder freigegeben. Leider gibt es in der Balena Cloud keine Option, diese \u00c4nderung dauerhaft vorzunehmen. Zwar kann man mit folgenden Befehlen das Root-Filesystem in den Lese\/Schreib-Modus versetzen und die Einstellung \u00e4ndern, beim n\u00e4chsten Upgrade von Balena OS geht sie aber wieder verloren:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Raspberry Pi: <code>mount -o remount,rw \/<\/code>\nRaspberry Pi: <code>systemctl mask serial-getty@serial0<\/code>\nRaspberry Pi: <code>systemctl stop serial-getty@serial0<\/code>\nRaspberry Pi: <code>mount -o remount,ro \/<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Wie kann die serielle Kommunikation getestet werden?<\/h2>\n\n\n\n<p>Zum Test kann man einfach ein die GPIO-Pins 14 und 15 mit einem Kabel verbinden und folgendes Testprogramm auf dem Pi laufen lassen. Wenn alles funktioniert, sollte in der Konsole folgende Meldungen protokolliert werden:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">[Logs]    [6\/18\/2021, 4:19:19 PM] [main] 1 Hallo, Serial Port!\n[Logs]    [6\/18\/2021, 4:19:19 PM] [main] 1 Hallo, Serial Port!\n[Logs]    [6\/18\/2021, 4:19:20 PM] [main] 2 Hallo, Serial Port!\n[Logs]    [6\/18\/2021, 4:19:21 PM] [main] 3 Hallo, Serial Port!\n...<\/pre>\n\n\n\n<p>Erh\u00e4lt man stattdessen folgende Exception, sind die UARTS noch durch die serielle Konsole belegt:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">serial.serialutil.SerialException: device reports readiness to read but returned no data (device disconnected or multiple access on port?)<\/pre>\n\n\n\n<p>Hier der Quellcode des <strong>Dockerfile.template<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">FROM balenalib\/%%BALENA_MACHINE_NAME%%-debian-python:latest-run\n\nWORKDIR \/usr\/src\/app\nCOPY requirements.txt .\nRUN pip install --no-cache-dir -r requirements.txt\n\nCOPY .\/src .\nCMD [\"python\", \"app.py\"]<\/pre>\n\n\n\n<p>Hier die <strong>requiremeints.txt<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">pyserial<\/pre>\n\n\n\n<p>Und hier die <strong>src\/app.py<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">#! \/bin\/env\/python3\nimport serial, time\n\nif __name__ == \"__main__\":\n    seqnr = 0\n\n    with serial.Serial(\"\/dev\/ttyS0\", timeout=0.1) as sp:\n        while True:\n<code>            seqnr += 1<\/code>\n    <code>        msg = \"%s Hallo, Serial Port!\" % seqnr<\/code>\n<code>            sp.write(msg.encode())<\/code>\n<code>            sp.flush()<\/code>\n\n    <code>        read_buffer = sp.read(50)<\/code>\n<code>            print(read_buffer.decode())<\/code>\n\n<code>            time.sleep(1)<\/code><\/pre>\n\n\n\n<p>Die Dateien m\u00fcssen auf dem Entwicklungsrechner in einem leeren Verzeichnis angelegt werden. Alle Dateien befinden sich im selben Verzeichnis, mit ausnahme der <strong>app.py<\/strong>, die im Unterverzeichnis <strong>src<\/strong> liegen muss. Mit folgendem Befehl kann die Anwendung dann auf ein Device im Local Mode gepusht werden:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">balena push 569e406.local<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Wo liegt das Problem? Hier ein interessanter Stolperstein in Balena, der einem das Leben ganz sch\u00f6n schwer machen kann, wenn man die eingebauten UARTs des Raspberry Pi f\u00fcr die serielle Kommunikation nutzen will. Fallbeispiele k\u00f6nnten hier die Anbindung von Sensoren<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/posts\/422"}],"collection":[{"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/comments?post=422"}],"version-history":[{"count":2,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/posts\/422\/revisions"}],"predecessor-version":[{"id":424,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/posts\/422\/revisions\/424"}],"wp:attachment":[{"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/media?parent=422"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/categories?post=422"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/tags?post=422"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}