(F)OTA-update für FiPy

Für den Betrieb im Feld möchten wir installierte Software updaten, das ist

  • die pycom-Firmware aber auch
  • unsere MicroPython-Software
  • ggf. auch nur mal die Konfigurationsdatei als Spezialfall

Das geht unter bestimmten Voraussetzungen händisch per FTP. Hier soll es um das vollautomatisch update gehen.

Unter OTA update gibt es software, die das realisiert. Sie besteht aus einem Server-Teil (in Python), bei dem strukturiert die unterschiedlicher Software-Versionen hinterlegt sein müssen, so weit ich sehe als MicroPython-Code und nicht vorcompiliert. Optional kann auch eine Firmware-Version zum code angegeben werden, die dann auch upgedatet wird.

Dazu gibt es eine lib für den node (“client library”) die die aktuell installierte Software mit der Version auf den Server vergleicht und ggf. updatet.

1 Like

Bisher konnte ich den Server mit Beispielcode zum laufen bringen und auf dem node die lib installieren und laufen lassen. Das update scheitert gerade aber noch. Warum muss ich noch genauer eruieren. Lokal kann ich über die IP auf den Server zugreifen (läuft auf dem gleichen Rechner), ob das Ausliefern der updates nicht funktioniert, die Kontaktaufnahme mit dem Updateserver oder der Updatprozess an sich muss ich noch herausfinden.

Ein update der software unter /flash/ hat jetzt zwei, drei mal geklappt, bei ca. 20 Versuchen. Nun bekomme ich auf dem client:

Initializing filesystem as LittleFS!
Starting loop
Performing OTA
Requesting: manifest.json?current_ver=1.0.0
Traceback (most recent call last):
  File "main.py", line 45, in <module>
  File "/flash/lib/OTA.py", line 52, in update
  File "/flash/lib/OTA.py", line 46, in get_update_manifest
  File "/flash/lib/OTA.py", line 194, in get_data
OSError: [Errno 113] ECONNABORTED

http://192.168.178.21:8000/manifest.json?current_ver=1.0.0

lieferte heute manifest.json aus, nun gerade nicht mehr. Ich spiele gerade an den firewall-Einstellungen, ggf. hängt es daran. Auch die WLAN-Stabilität habe ich hier im Verdacht, heute ging es mal besser, mal schlechter je nach Position des FiPys.

warum das denn? Es ging doch bereits mit den ‘alten’, vorherigen Einstellungen am firewall. Das ist ja noch eine Variable, die Du änderst, so wird es schwerer, den tatsächlichen Grund für die Verbindungsabbrüche ([Errno 113] ECONNABORTED) zu erkennen.

Du logst ja schon RSSI (auch bei diesem Gerät? - weiß ich gerade nicht) und ggf. noise der WLAN-Strecke. Derartige durch wechselnde WLAN-Verbindungsqualität entstehende Fehler erwähnst Du ja schon: forsche mal dort weiter.

Es ging nicht reliabel. Der Python-Server läuft in einer Linux WSL-Umgebung unter Win 10. Und ich weiß gerade nicht, wo es klemmt, der Win10-Rechner kann auf den Python-Server zugreifen, das Handy im gleichen WLAN mal ja, mal nein. Und ähnlich wird es mit dem FiPy gerade sein.

Der Server streikt ab und an und wirft nach dem händischen Abbrechen dann so was raus:

  File "OTA_server.py", line 168, in do_GET
    self.wfile.write(j.encode())
  File "/usr/lib/python3.5/socket.py", line 593, in write
    return self._sock.send(b)
BrokenPipeError: [Errno 32] Broken pipe
----------------------------------------
Got query for: /manifest.json?current_ver=1.0.0
192.168.178.21 - - [26/Sep/2019 14:13:17] "GET /manifest.json?current_ver=1.0.0 HTTP/1.1" 200 -
Generating a manifest from version: 1.0.0

Auch hier hängt es gerne mal

Got query for: /manifest.json?current_ver=1.0.0
192.168.178.21 - - [26/Sep/2019 14:20:50] "GET /manifest.json?current_ver=1.0.0 HTTP/1.1" 200 -
Generating a manifest from version: 1.0.0

Der Browser wartet dann, aber das Dokument wird nicht ausgeliefert.

Hört sich bekannt an. I’m with you.

Nun läuft es reliabel und halbwegs stabil! Habe den port 8000 in der firewall freigegeben, nutze Rechner und node näher am WLAN-Router und verwende als OTA-lib jetzt pycom-libraries/OTA.py at master · pycom/pycom-libraries · GitHub – da ist eine andere, ältere Version im Verzeichnis /1.0.1/flash/lib

Wenn es funktioniert schaut das so aus: Der node übermittelt seine aktuelle Versionsnummer als URL-Parameter an den Server:

http://192.168.178.21:8000/manifest.json?current_ver=1.0.0

Der Server spuckt nun das aus:

{
    "delete": [],
    "new": [],
    "update": [
        {
            "URL": "http://192.168.178.21:8000/1.0.2/flash/main.py",
            "dst_path": "/flash/main.py",
            "hash": "9f372699c1ade7a182459367f2e84c2918677cd9"
        }
    ],
    "version": "1.0.2"
}

dann holt sich der node die neuen Dateien, in unserem Fall nur die geänderte main.py, dann wird auf dem node eine neue Datei OTA_VERSION.py, angelegt, die als Variable

VERSION = '1.0.2'

enthält. Der node macht einen restart und checkt – jetzt mit seiner neuen Versions-Nummer 1.0.2 nochmal die Lage. Keine neuere Software da, dann passt es!

auf dem node

Initializing filesystem as LittleFS!
Starting loop
Performing OTA
Requesting: manifest.json?current_ver=1.0.0
Requesting: 1.0.2/flash/main.py
ets Jun  8 2016 00:22:57

rst:0x7 (TG0WDT_SYS_RESET),boot:0x12 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff8028,len:8
load:0x3fff8030,len:2156
ho 0 tail 12 room 4
load:0x4009fa00,len:19208
entry 0x400a05f4
Initializing filesystem as LittleFS!
Starting loop
Performing OTA
Requesting: manifest.json?current_ver=1.0.2
Already on the latest version

auf dem Server

root@XPS13-CGruber:~/ota-server# python3 OTA_server.py
Got query for: /manifest.json?current_ver=1.0.0
192.168.178.28 - - [27/Sep/2019 16:59:06] "GET /manifest.json?current_ver=1.0.0 HTTP/1.0" 200 -
Generating a manifest from version: 1.0.0
Got query for: /1.0.2/flash/main.py
192.168.178.28 - - [27/Sep/2019 16:59:07] "GET /1.0.2/flash/main.py HTTP/1.0" 200 -
Got query for: /manifest.json?current_ver=1.0.2
192.168.178.28 - - [27/Sep/2019 17:01:02] "GET /manifest.json?current_ver=1.0.2 HTTP/1.0" 200 -
Generating a manifest from version: 1.0.2
3 Likes

Der Python-OTA-Server ist ganz clever und vergleicht die in seinem Dateisystem abgelegten Dateien für verschiedene Versionen, dann gibt er fürs update nur die Dateien an, die tatsächlich Änderungen habe.

Weiter kann man auch duch Depublikation auf dem Server wieder zu einer früheren Version downgraden, wenn ich auf dem Server Version 1.0.1 und 1.0.2 habe und auf dem Gerät auch die 1.0.2 holt sich der node, wenn ich die 1.0.2 auf dem Server (!) lösche wieder die 1.0.1 als “neueste” bzw. latest available version.

Exzellent!

Fein!

Es wäre (für die nächsten Schritte) praktisch, wenn man dieses Detail (“latest available”) feiner konfigurieren könnte – nicht?

Was meinst du da genau? Nur stable, oder auch beta oder alpha?

Wahrscheinlich irgendwas in dieser Richtung, ja. Ich denke es wird sich im weiteren Verlauf etwas ergeben.

Gut, war ein bischen fies, habe als neueste Version das Hiveeyes-Release 0.6.0 auf den Server gepackt, da mag die OTA-Routine was nicht:

Requesting: manifest.json?current_ver=1.0.2
Requesting: 1.1.0/dist-packages/cayennelpp/lpp_type.py
[Errno 19] ENODEV
Error downloading `http://192.168.178.21:8000/1.1.0/dist-packages/cayennelpp/lpp_type.py` retrying...
Requesting: 1.1.0/dist-packages/cayennelpp/lpp_type.py
[Errno 19] ENODEV
Error downloading `http://192.168.178.21:8000/1.1.0/dist-packages/cayennelpp/lpp_type.py` retrying...
Requesting: 1.1.0/dist-packages/cayennelpp/lpp_type.py
[Errno 19] ENODEV
Error downloading `http://192.168.178.21:8000/1.1.0/dist-packages/cayennelpp/lpp_type.py` retrying...
Requesting: 1.1.0/dist-packages/cayennelpp/lpp_type.py
[Errno 113] ECONNABORTED
Error downloading `http://192.168.178.21:8000/1.1.0/dist-packages/cayennelpp/lpp_type.py` retrying...
Requesting: 1.1.0/dist-packages/cayennelpp/lpp_type.py
[Errno 113] ECONNABORTED
Error downloading `http://192.168.178.21:8000/1.1.0/dist-packages/cayennelpp/lpp_type.py` retrying...
Traceback (most recent call last):
  File "main.py", line 45, in <module>
  File "/flash/lib/OTA.py", line 69, in update
Exception: Failed to download `http://192.168.178.21:8000/1.1.0/dist-packages/cayennelpp/lpp_type.py`

[edit] die Dateien waren im root-Verzeichnis, da gehört aber noch eine Ebene /flash/ dazwischen, daher ging hier einiges nicht!

Das Release-Archiv mit vorkompilierten Quellen [1] ist kompakter und lässt sich damit u.U. robuster auf das Gerät übertragen.

[1] https://github.com/hiveeyes/hiveeyes-micropython-firmware/releases/download/0.6.0/hiveeyes-micropython-firmware-0.6.0-pycom-mpy.tar.gz

afaik kann das OTA-Tool kein vorcompilierten Quellen, zumindest hab ich dazu nix gefunden.

Mir ist nicht klar, was ENODEV bei uns konkret bedeutet

https://www-numi.fnal.gov/offline_software/srt_public_context/WebDocs/Errors/unix_system_errors.html

ENODEV 19 /* No such device */

Also das device ist ja da! Datei zu groß? Kann auf 3. Verzeichnisebene nix ablegen? Nicht zwischenspeichern? …

… unter Umständen out-of-memory o.ä., ja.

Vom Server aus gesehen war es die erste Datei, die angefordert wurde, wenn der Fipy nicht ganz schräge Dinge macht, sollte noch reichlich Platz sein.

Sobald die Übertragung robust genug ist, könnte man einen Blick auf Tankful / update-server · GitLab werfen. Siehe auch New OTA Update Server | Pycom user forum.

Hier ist gerade gar nichts robust, was vor ein paar Tagen noch funktionierte geht heute wieder nicht, der FiPy kam zuerst nicht ins Netz, jetzt funktioniert es, der Python-Server kann vom gleichen PC gelesen werden, aber vom FiPy aus geht es nicht, er kannmanifest.json nicht laden. Dann geht es wieder nach einem kompletten Win10-Systemneustart, agrrr!

Um der Frage nachzugehen, ob es an den Verzeichnisebenen liegen kann habe ich mal ein paar Dateien ins Hauptverzeichnis verlegt und den update-Prozess angestoßen. Aber auch hier gleiches Ergebnis:

Starting loop
Performing OTA
Requesting: manifest.json?current_ver=1.0.2
Requesting: 1.1.0/sensor_bme280.py
[Errno 19] ENODEV
Error downloading `http://192.168.178.21:8000/1.1.0/sensor_bme280.py` retrying...
Requesting: 1.1.0/sensor_bme280.py
[Errno 19] ENODEV
Error downloading `http://192.168.178.21:8000/1.1.0/sensor_bme280.py` retrying...
Requesting: 1.1.0/sensor_bme280.py
[Errno 19] ENODEV
Error downloading `http://192.168.178.21:8000/1.1.0/sensor_bme280.py` retrying...
Requesting: 1.1.0/sensor_bme280.py
[Errno 113] ECONNABORTED
Error downloading `http://192.168.178.21:8000/1.1.0/sensor_bme280.py` retrying...
Requesting: 1.1.0/sensor_bme280.py
[Errno 113] ECONNABORTED
Error downloading `http://192.168.178.21:8000/1.1.0/sensor_bme280.py` retrying...
Traceback (most recent call last):
  File "main.py", line 45, in <module>
  File "/flash/lib/OTA.py", line 69, in update
Exception: Failed to download `http://192.168.178.21:8000/1.1.0/sensor_bme280.py`

Beim Server kommen die Anfragen aber an und werden auch ausgeliefert:

Got query for: /manifest.json?current_ver=1.0.2
192.168.178.28 - - [30/Sep/2019 12:17:30] "GET /manifest.json?current_ver=1.0.2 HTTP/1.0" 200 -
Generating a manifest from version: 1.0.2
Got query for: /1.1.0/sensor_bme280.py
192.168.178.28 - - [30/Sep/2019 12:17:30] "GET /1.1.0/sensor_bme280.py HTTP/1.0" 200 -
Got query for: /1.1.0/sensor_bme280.py
192.168.178.28 - - [30/Sep/2019 12:17:30] "GET /1.1.0/sensor_bme280.py HTTP/1.0" 200 -
Got query for: /1.1.0/sensor_bme280.py
192.168.178.28 - - [30/Sep/2019 12:17:30] "GET /1.1.0/sensor_bme280.py HTTP/1.0" 200 -

Vielleicht finden wir Unterstützung im Pycom Forum. Es gibt dort bereits einige Einträge zu OTA.