Shelly 1 – WiFi Relay Switch

Ein Wifi Schalter also.

Mal etwas grundsätzliches am Anfang

Ich bin generell kein Freund davon, alles nur auf eine (Technologie) Basis zu stellen. Ich denke, auch wenn es am Ende durchaus komplexer sein kann, bei jeder Technologieentscheidung unter anderem über die folgenden Punkte nach:

  • Welchen Stellenwert wird die Technologie in Zukunft generell und für mich einnehmen?
    • Wird es eine Notlösung oder eine (permanente) Übergangslösung sein?
    • Ist die Technologie nur für dieses eine Szenario gedacht oder kann ich mehrere use- cases darüber abbilden?
    • Kann ich eine vorhandene Notlösung oder ggf. andere Technologien durch die neue Lösung ablösen?
  • Wie ist der Betriebsaufwand?
    • Muss ich hier häufig patchen?
    • Gibt es sonst noch Wartungsarbeiten zu verrichten.
  • Wie sicher ist die Lösung?
    • Schaffe ich hier eine zusätzliche Angriffsfläche durch ein neues Medium (Funkprotokoll, etc.)?
    • Ist die Lösung durch übergeordnete Schutzmaßnahmen bereits sicher genug?
    • Ist die Software quelloffen?
    • Wie ist das Update Konzept?
    • Gibt es einen Cloud Zwang?
    • Wird die hinter der Lösung stehende Company bzw. die Entwickler die Lösung noch länger supporten und mit Updates versehen?
  • Wie nahtlos ist die Integration in die bestehende Infrastruktur?
    • Läuft es direkt mit Loxone, NodeRed zusammen bzw. gibt es gut und vollständig implementierte Kommunikationsstandards? UDP, MQTT, etc.
    • Können die gesammelten Daten auch ohne großen Aufwand weiter genutzt werden bzw. gibt es eine Export Funktion?
  • Wie intensiv muss ich das Wissen dokumentieren?
    • Kann ich mich in einem Jahr noch gut daran erinnert, wie ich die Technologie konfiguriert habe oder muss ich alles auf „Klick Ebene“ aufschreiben?
  • Wie stabil ist die Lösung?
    • Ist es eine selbstverdrahtete Bastelversion mit einem ESP8266 auf einem PCB mit irgendwelchen Sensoren oder ein gemäß bestehenden regulatorischen Anforderungen entwickeltes und getestetes Gerät?
  • Welche Abhängigkeiten bzw. Verschränkungen hat die Lösung?
    • Welche Seiteneffekte wirken auf die bestehende Infrastruktur bzw. gibt es starke, nicht standardisierte, Abhängigkeiten?

Manchmal ist es mir aber auch egal und ich sehe einfach etwas was ich dann ausprobieren möchte. So war es mit den Shelly Schaltern auch. Generell finde ich das WLAN als Kommunikationsmedium für Smart Home / IoT nicht sonderlich geeignet, weil…

  • man in gewisser Weise mit Kanonen auf Spatzen schießt.
    • Man braucht keinen Full TCP/IP Stack mit WLAN um ein Relais über Funk zu schalten.
    • Es wird ein Kleincomputer mit eigenem OS und Applikationssoftware für eine sehr einfache Aufgabe „mißbraucht“.
  • das Protokoll an sich bereits sehr gesprächig ist.
    • Alles was TCP/IP hat, neigt meist auch dazu etwas mehr Daten zu übermitteln als es für die eigentliche Funktion notwendig wäre.
  • über einen Web-Server die Angriffsoberfläche signifikant erhöht wird.
    • Der ESP8266 Stack hat in der Vergangenheit bereits einige Schwachstellen gezeigt.

… aber ich wollte mal ausprobieren wie nahtlos (auch mit Sicht auf die oben aufgeführten Fragen) sich diese Dinger integrieren lassen.

Also mal auf die Schnelle einen Zweierpack Shelly 1 besorgt und losgelegt. Die Shelly Family (Ähem!) ist mittlerweile recht groß und wächst ständig. Die Produktdiversifikation hat hier deutliche Spuren hinterlassen und neben UP Relais und Dimmern gibt es auch Sensoren und anderen Krams mit einem recht guten WAF. Das ganze natürlich noch garniert mit einem Anschluss an das Werk des Teufels aka Sprachassistenten wie Alexa & Co.

Ich fand es jetzt auch spannend zu sehen, wie sich die ESP 8266/32 CPUs in den Consumer Markt bewegen. Und wie immer hat Gott hier die Hardware vor die Software gesetzt. Also aufmachen das Ding. Don’t try this at home, kidz!

Die Hardware


Ob das jetzt etwas bringt oder nicht aber:
Bitte, bitte die Dinger nur anschließen, wenn das Gehäuse korrekt wieder verschlossen und nicht beschädigt ist.

Es sind ggf. 230 Volt angeschlossen und das kann sehr weh tun.

Ich übernehme hier keinerlei Haftung.




Das Öffnen war nicht so schwierig, obwohl die Dinger verklebt sind. Ich habe einen dünnen Schraubendreher in die Öffnung der 5 Pin Header gesteckt und das Gehäuse von innen dann etwas nach außen gedrückt. Dann mit einem Spudger / Spatel weiter vorarbeiten. Dann geht das Gehäuse ohne Beschädigungen auf.

Geöffneter Shelly 1
Unterseite Shelly1

Auf den ersten Blick erkennt der Fachmann unseren kleinen Freund in der Mitte, einen ESP8266 EX, den Winbond 25Q16JV für den Speicher von 2MB (16Mbit Chip), den LNK304 als Buck Converter und für AC/DC.

Auf der Website von Shelly wird immer von Open Source gesprochen, ich kann aber weder Schaltpläne noch den Source-Code irgendwo entdecken. Ich denke mit Open Source ist gemeint, dass die Pins für das „Betanken“ mit anderer Software zugänglich gemacht wurden.

Und es gibt tatsächlich Alternativen wie die Tasmota Firmware, aber dazu später mehr.

Bei dieser Gelegenheit noch ein Literaturtip zu diesem Post von Peter Scargill, den ich, das muss ich mal sagen, schon sehr genial finde.

Hardwareeinstellungen

Über einen Jumper können zwei verschiedene Eingangsspannungen eingestellt werden. 110-240 Volt AC (Wechselstrom) und 30-50 Volt DC (Gleichstrom) -> (Pin 1 und 2 gebrückt) und 12 Volt DC (Pin 2 und 3) gebrückt.

Die Besonderheit am Shelly ist, dass die notwendigen PINs für einen Flash-Vorgang nach außen geführt (Naja, nicht ganz) sind. So ist das Zugang zur ESP8266 Firmware leicht zu realisieren.

Nun halte mich hier aber nicht weiter mit Leistungsdaten, Anschlussmöglichkeiten etc. auf, sondern gehe direkt auf mein Kernthema der Systemintegration. Der Shelly One kann MQTT und das ist in Verbindung mit NodeRed mein favorisiertes Protokoll.

Erster Start

Nach der Shelly angeschlossen ist, startet er einen Access Point mit seinem Namen mit dem man sich dann mit jedem WiFi Gerät verbinden kann. Erste Amtshandlung ist dann die WLAN Konfiguration, so dass sich er sich nach dem Neustart im heimischen WLAN anmeldet.

Nach dem ersten Einschalten startet der Shelly als Access Point mit dem Namen shelly1-<ID>, wobei bei mir die ID B8D8C5 ist. Mit diesem Access Point kann man sich dann ohne Passwort verbinden und unter der Adresse 192.168.33.1 ist die Administrationsoberfläche dann zu erreichen.

Also wird das Ding zuersteinmal in das heimische WLAN integriert. Hierzu unter Internet & Security auf WIFI MODE – Client gehen und die entsprechende SSID des WLAN, das Kennwort und dann noch (optional) eine feste IP Adresse mit Default Gateway und DNS Server eingeben. Ist das alles erledigt, einfach auf Connect klicken.



Danach ist es die erste Bürgerpflicht zu schauen, ob es irgendwelche Firmware Updates gibt. Und natürlich auch woher die Updates kommen und wer sie veröffentlicht hat. Da habe ich ausnahmsweise mal nicht reflexartig auf den „Update Firmware“ Button der Weboberfläche geklickt, sondern erstmal geschaut wie das denn so läuft mit dem Update. Dazu aber weiter unten bei Support Strategie und Firmware Updates mehr.

Software & Einrichtung

Die Web-Oberfläche ist recht aufgeräumt und einfach. Für die Funktionen gibt es drei Kernbereiche.

  • Timer: Hier kann eingestellt werden, nach wie vielen Sekunden das Relais in den anderen Status geschaltet werden soll. Also „Wenn Relay an, schalte nach x Sekunden aus“ bzw. „Wenn Relay aus, schalten nach x Sekunden wieder an.“
  • Weekly Schedule: Hier kann ein Timer aktiviert werden, mit dessen Hilfe das Relay zu bestimmen Zeiten an und aus geschaltet werden kann.
  • Sunrise / Sunset: Hier kann nach Sonnenaufgang und -untergang das Relais geschaltet werden. (Woher kommen denn die astronomischenn Infos her?)

Unter Internet & Security kann man, wie oben bereits geschehen, den WiFi Modus einstellen (Client oder Access Point). Zudem kann der Zugriff mit Hilfe von Username und Password „gesichert“ werden. Und dann gibt es noch zwei sehr interessante Funktionen unter Advanced Developer Settings.

  • Enable actions execution via CoAP (CoIOT) protocol.
    • Habe ich bislang nicht genutzt, weil ich mit MQTT alles umsetzten konnte. Ja, ist RESTful und kann auch Kommandos an den Server senden, doch wurde bislang nicht gebraucht.
  • Enable actions execution via MQTT
    • Dazu weiter unten mehr….

Ja, und da darf die böse, böse Cloud Integration natürlich nicht fehlen. Den Warnhinweis weiter unten verstehe ich als Empfehlung.

Ich habe die Cloud Integration erstmal nicht eingeschaltet, da ich es sowieso nicht verwenden werde. Evtl. schalte ich das mal ein und schaue genauer nach, wie das denn so laufen soll mit der Cloud usw.

Einstellungen unter Internet & Security

MQTT Integration

Nachdem das Feld aktiviert wurde, klappt die Parameter-Eingabe auf und die MQTT Parameter können eingegeben werden.

Ich habe die Adresse meines Mosquitto angegeben und mal geschaut, was so ankommt.

1592052094: New connection from 192.168.0.92 on port 1883.
1592052094: New client connected from 192.168.0.92 as shelly1-B8D8C5 (c1, k60).

OK, wunderbar. Der connect zum MQTT Broker hat funktioniert. So dann wollen wir den Shelly mal zu einem MQTT Topic subscriben. Hmm, hier ist aber kein Feld für Subscribe oder Publish. OK, dann schauen wir mal mit dem MQTT Explorer ob evtl. schon was geht. Und in der Tat sendet der Shelly bereits fleißig MQTT Nachrichten. Hier den Status des Schalters. Und wenn ich über die Web-Oberfläche den Schalter drücke, kommt ein Update.

Die erste „unkonfigurierte“ Shelly MQTT Nachricht.

Da ist ja schon sehr nett, dass es alles „out of the box“ schon funktioniert, doch was ist wenn ich hier mein eigenes MQTT Namens-Schema konfigurieren möchte? Wo geht denn das? Kurze Antwort: Das geht nicht. Ich habe in den Foren etwas geschaut, doch scheint die MQTT Topic Konfig nicht veränderbar zu sein. Das finde ich etwas … unglücklich. Auch verstehe ich die Felder Will Topic und Will Message nicht.

Gut das mit dem Publish hat sich sozusagen schon erledigt, doch was ist mit Subscribe. Auf der Shelly Homepage gibt es dazu eine Liste mit den den entsprechenden Topics.

Um den Aktor also zu schalten, muss shellies/shelly1-B8D8C5/relay/0/command mit Text „on“ bzw. „off“ geschickt werden.

Das Thema der fest definierten MQTT Topics wird heiß in der Community diskutiert. Da gibt es Pro und Contra, doch für mich ist klar das eine solche Einschränkung nicht akzeptabel ist. Als Alternative wird immer die Tasmota Firmware genannt, die ich dann wohl auch dem Shelly installieren werden.

Oder gibt es mittlerweile ein Firmware Update, was hier helfen könnte. Schauen wir mal nach … und tatsächlich gibt es eine sehr viel neuere Version als mein 1.3.0 vom 29.10.2018. Es ist die Version 1.7.0 vom 01.06.2020. Das Change Log dazu gibt es hier.

The current Firmware version of your Shelly device is 20181029-145244/v1.3.0@7da7a3c1 New version 20200601-122823/v1.7.0@d7961837 is available.

Und in der Tat ist hier einiges neu. Jaja, hätte ich gleich am Anfang der Update Button gedrückt, dann wäre es anders. Aber das Beste hebe ich mit zum Schluß halt immer gern auf.

Neues MQTT Interface nach Update

Da ist dann jetzt auch das Feld „Use custom MQTT prefix“, welches in dem oben genannten Forum Post bereits thematisiert wurde. Leider verändert diese Einstellung nicht den Topic ab der ersten Stelle, sondern er ersetzt nur die Shelly ID. Das erste „Shellies/“ bleibt also unveränderbar. Das finde ich noch immer etwas … unglücklich.

Support Strategie / Firmware Updates und Vertrauenswürdigkeit

Als primärer Supportkanal wird Facebook eingesetzt was ich nicht so premium finde, da für eine umfassende Nutzung ein Facebook Konto benötigt wird, welches ich nicht habe. (-1).

Es gibt noch ein offizielles Supportforum, welches m.E. nach von einem Menschen aus Duisburg betrieben wird. Dort kann mal aber auch .bin firmware Dateien laden. Ob diese Quelle vertrauenswürdig ist kann ich nicht sagen, weil es hier keinen Hinweis auf den Hersteller gibt, keine Release-Notes zu sehen sind und die Dateien auch nicht mit einer Signatur versehen sind.

Warum reite ich so auf dem Thema rum? Ich würde gerne vermeiden, dass so ein Ding durch ein komisches Update Bestandteil der IoT Hölle wird und ich mir einen Trojaner ins Netz hole.

Details

Nun habe ich das Ding erstmal an den Rechner angeschlossen, um einen Detailblick auf die Software zu bekommen. Wie das Ding angeschlossen wird, ist hier gut auf den Seiten der Alternativen Firmware Tasmota zu sehen.

ACHTUNG, ACHTUNG. Wichtige Durchsage!

Achtung: Der Shelly darf nicht gleichzeitig über die Pin-Header am Rechner angeschlossen sein und über die Schraubklemmen mit Strom versorgt sein. Da, nach Aussagen von hier, der GND Pin auf Phase (230 Volt Wechselstrom) liegt, bzw. ja nachdem, was man gerade angeschlossen hat. (-1)

Wenn man jetzt an den Pins zieht,
bleibt die Pin-Header Halterung im Shelly Gehäuse.

Hier ein kleiner Tipp: Die Female Pin-Header sind sehr tief, so dass sich hier lange Male-Header für den Anschluss anbieten. Ich habe also Male-Male Pin Header genommen, 5 abgebrochen und dann die Pin mit der Zange einfach verschoben. Darauf habe ich dann female Header zum USB FTDI gelegt.

Problem ist aber, dass die Gehäuseöffnung sehr klein ist und mein Custom Male/Male Header beim rausziehen im Gehäuse geblieben ist und ich nur die blanken Kontakte in der Hand hatte. Daher empfehle ich direkt mit Kabeln in den Female-Header des Shelly zu gehen, die man wieder rausziehen kann. Nachdem ich den Shelly geöffnet hatte und den leeren Pin-Header entfernt hatte habe ich die Öffnung etwas vergrößert.

Wichtig ist, dass TX und RX gekreuzt werden, sonst geht nix. Beim Einstecken des FTDI Adapters in den Rechner sollte GPIO 0 auf GND gesetzt werden. Das habe ich gemacht in der Hoffnung, dass am Terminal was kommt…. Kam aber nix. Das Ding spricht nicht über die Konsole. Egal, dann schauen wir mal was so an TCP/IP Kommunikation stattfindet.

Kommunikationsverhalten

Nun habe ich den Shelly also erstmal allein in ein eigenes WLAN gesetzt und am virtuellen Access Point geschaut, was er dann so anstellt. Die erste Auswertung mit PiHole ergab, dass erstmal nach der Zeit gefragt wird. Danach geht es dann direkt an die api von shelly. Ansonsten ist das Gerät sehr nah am Puls der Zeit und fragt regelmäßig, also ca. alle 2 Stunden, nach der Zeit im Internet. Ich finde den Zeitintervall doch recht unregelmäßig. Ich kann mir nicht vorstellen, dass (Gerade weil dem Shelly die Zeit so wichtig ist) der Trigger hier so ungenau läuft. Aber egal.

Auflösungserscheinungen über DNS

Als nächste ist dann also die Kommunikation zu api.shelly.cloud in der Reihe. Also mal eben den Drahthai am Gateway auf die IP gestellt und den Shelly durchgeholt.

Conversations View vom Wireshark

OK, dass ist jetzt nicht so spannend die 23.251.142.183 ist der api.shelly.cloud. Also dann weiter in die Pakete.

  • 12-15: DNS Auflösung für time.google.com und der NTP Verkehr
  • 16-26: DNS Auflösung für api.shelly.cloud für die Feststellung der Zeitzone. Warum neben der der Unix Epoch time (time=1578945763) auch noch die Version der aktuellen Firmware mitgeliefert wird verstehe ich nicht so genau.
  • 29-46: DNS Auflösung für api.shelly.cloud für die Abfrage nach aktueller Firmware
    • Interessant ist hier Zeile 35, also der HTTP Get Statement, in dem auch gleich die Device ID=Shelly1-B8D8C5 und (nochmals) die aktuelle Firmware Version mitgeschickt wird.

Da die Shelly ID (B8D8C5) einmalig ist, frage ich mich warum diese übertragen werden muss. Ich denke, dass bei Shelly eine Statistik darüber gefahren wird, welche Geräte mit welchen Firmware Release-Stand noch draußen sind und wie „Update-Faul“ die User sind. Eine Statistik, die mich übrigens auch interessieren würde. Etwas blöd finde ich dabei, dann damit auch im Prinzip ausgewertet werden kann wieviele Shelly Geräte ich im Einsatz habe.

Wenn also alle meine drölfzig Geräte jeden Tag mal nach Hause funken, dann kann aufgrund der übermittelten IP Adresse schon eine Statistik erstellt werden. Ups, habe ich eigentlich irgendwo meine Zustimmung zur Übermittelung der ID und der Firmware Version gegeben? Hmmm, war da was mit GDPR.

OK, aber schauen wir mal nach, was denn da an für JSON Daten (Zeile 40) übertragen werden. Also URL rauskopieren, in den Browser, Ergebnis in den JSON Beautifier und siehe da eine komplette Liste der Firmware Versionen für die verschiedenen Geräte. Browsen ist im Verzeichnis api.shelly.cloud/firmware aber nicht möglich.

Zusammenfassung: Also das Kommunikationsverhalten ist jetzt mit der Zeitsynchronisation und dem Firmware Update nicht sehr intensiv. Ich habe allerdings die Analyse nur einige Stunden laufen lassen. Eine längere Messung steht also noch aus. Insbesondere bei aktiver Nutzung. Der Firmware update Prozess ist aber m.E. doch recht wackelig. Das Fehlen einer kryptografischen Signatur der Firmware und der unverschlüsselte HTML Verkehr sind m.E Dinge, die geändert werden sollten. (-1)

….

Firmware

Wo wir uns nun den Weg an die Firmware freigeräumt haben, schaue wir doch nochmal drüber. Ich nehme dazu aber nicht die Original-Firmware, sondern ich schaue mir die Firmware an die ich nach eine Grundkonfiguration vom Shelly runtergeladen habe.

Da ich den Shelly wohl etwas häufiger über die serielle Schnittstelle ansprechen werde, habe ich mir den USB2UART auf das Gehäuse geklebt und mir kleinen Breadboard Kabeln den Anschluss gelegt. Ich nutze an meinem MacBook Pro den SparkFun FTDI Basic Connector und der gibt mir als Interface /dev/cu.usbserial… über das dann alles läuft. Der hat dann zwar noch einen Mini-USB, aber meine Kabelschublade ist immer gut gefüllt.

Die Verdrahtung ist schnell erledigt. Auf der Shelly Seite müssen die 5 Pins belegt werden. Doku ist hier und auf der FTDI Seite müssen nur RX/TX gedreht werden. GND und 3.3 Volt werden normal angeschlossen.

Damit der ESP8266 in den Boot-Modus geht, muss der GPIO 0 Pin (der zweite von links) vor dem einstecken in den USB Port mit GND verbunden werden. Dann USB Kabel in den REchner stecken und den GPIO 0 PIN wieder loslassen

Mit dem ESP Tool kann man dann die Firmware über die serielle Schnittstelle runterladen.

Nun müssen wir erstmal schauen, wie groß der Flash Speicher ist.

./esptool.py  --p /dev/cu.usbserial-A505WX8P --baud 115200 flash_id                                                  

esptool.py v2.8
Serial port /dev/cu.usbserial-A505WX8P
Connecting....
Detecting chip type... ESP8266
Chip is ESP8266EX
Features: WiFi
Crystal is 26MHz
MAC: 98:f4:ab:b8:d8:c5
Uploading stub...
Running stub...
Stub running...
Manufacturer: ef
Device: 4015
Detected flash size: 2MB
Hard resetting via RTS pin...

Wir haben es hier also mit einem ESP8266 mit 2 MB Flash zu tun, was wir beim Auslesen mit dem Esptool auch angeben müssen.

./esptool.py  --p /dev/cu.usbserial-A505WX8P --baud 115200 read_flash 0x00000 0x200000 /users/Keule/shellyfirmware.bin

esptool.py v2.8
Serial port /dev/cu.usbserial-A505WX8P
Connecting....
Detecting chip type... ESP8266
Chip is ESP8266EX
Features: WiFi
Crystal is 26MHz
MAC: 98:f4:ab:b8:d8:c5
Uploading stub...
Running stub...
Stub running...
2097152 (100 %)
2097152 (100 %)
Read 2097152 bytes at 0x0 in 197.9 seconds (84.8 kbit/s)...
Hard resetting via RTS pin...

Wenn die folgende Meldung kommt, dann ist das ESP wahrscheinlich nicht im Boot Modus. Also Stecker raus, GPIO0 Pin an GND halten, Stecker rein und nochmal probieren.

% ./esptool.py  --p /dev/cu.usbserial-A505WX8P --baud 115200 read_flash 0x00000 0x200000 /users/Keule/shellyfirmware.bin
esptool.py v2.8
Serial port /dev/cu.usbserial-A505WX8P
Connecting........_____....._____....._____....._____.....^C

Der Esptool kann mit dem Befehl image_info auch gleich prüfen, wie die Speichersegmentierung ist und ob das Image soweit in Ordnung ist. Das sieht soweit gut aus.

./esptool.py --chip esp8266 image_info /users/Keule/shellyfirmware.bin 
esptool.py v3.0-dev
Image version: 1
Entry point: 40100740
2 segments

Segment 1: len 0x00758 load 0x40100000 file_offs 0x00000008 [IRAM]
Segment 2: len 0x00338 load 0x3ffe8000 file_offs 0x00000768 [DRAM]
Checksum: 99 (valid)

Kurze Firmware Analyse

Nun bin ich mit meinem persönlichen Paranoia-Faktor bis hier gekommen und wenn ich schonmal da bin, schaue ich mich doch noch ein bisschen weiter um.

Strings suchen: Die erste Aktion ist ja immer zu schauen, ob man irgendwelche Springs im Hex Editor sehen kann. Einfach mal direkt einladen und ein wenig umschauen. Da habe ich die folgenden Sachen auf den ersten Blick entdecken können.

  • rBoot v1.2.1 – richardaburton@gmail.com
    • Aha, der Standard Espressif bootloader war wohl nicht so schön, da hat man glatt mal einen anderen genommen.
  • Die AccessPoint Informationen sind auch alle im JSON Format vorhanden. Inklusive cleartext password versteht sich.
{
  "device": {
    "id": "shelly1-B8D8C5"
  },
  "wifi": {
    "sta": {
      "enable": true,
      "ssid": "raspion",
      "pass": "shiePie4y"
    },
    "ap": {
      "enable": false,
      "ssid": "shelly1-B8D8C5",
      "channel": 3,
   }, 
"host": {
"|name": "shelly1-B8D8C5"
    }
  },
  "dns_sd": {
    "host_name": "shelly1-B8D8C5"
  },
  "mqtt": {
    "will_topic": "shellies/shelly1-B8D8C5/online"
  },
  "first_boot": false,
  "restart_counter": 1,
  "wrong_sta_protect": {
    "sta_enable": true,|
    "sta_ssid": "raspion",
    "sta_pass": "shiePie4y"
  },
  "timezone": "Europe/Berlin"
}

Aber man kann das natürlich auch mit dem xtensa-lx106-elf-strings tool mal in die binaries schauen, doch dazu muss man erstmal ein ELF File erstellen.

ELF File erstellen: Um einen etwas genaueren Blick auf die Software zu werfen, kann man die Firmware in einen Decompiler laden. Da das Image aber nur das Abbild des Flash Speichers ist und dieser beim Start des ESP an verschiedene Adressen auf den SOC geladen wird, müssen die verschiedenen Speichersegmente erstmal aus dem Image extrahiert und in das ELF Format (https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) gebracht werden.

Wenn man bei GitHub ein wenig nachschaut, bekommt man einiges an Tools dazu. Ich habe mal ein paar ausprobiert und war mit https://github.com/zayfod/esp-bin2elf recht erfolgreich. Es gibt da noch die Vorgängerversion, die ist aber buggy (https://github.com/jsandin/esp-bin2elf). Wichtig ist, dass elffile2 als library genutzt wird, da die alte Elffile nicht mehr läuft.

Hier ein Python Scipt mit dem ein iso in eine .elf Datei konvertiert wird.

import esp_bin2elf
import flash_layout

# Load image
flash_layout = flash_layout.layout_without_ota_updates
rom = esp_bin2elf.parse_rom('shellyfirmware.bin', '/root/shellyfirmware.bin', flash_layout)

print(rom)
for section in rom.sections:
    print(section)

# Generate ELF
section_names = esp_bin2elf.name_sections(rom)
elf = esp_bin2elf.convert_rom_to_elf(rom, section_names, 'shellyfirmware.bin.elf')

Dann die Datei ausführen und die .elf Datei wird erstellt.

python3 converttoelf.py 

EspRom(name: shellyfirmware.bin, header: EspRomE9Header(magic: 0xe9, sect_count: 2, flags1: 0x02, flags2: 0x30, entry_addr: 0x40100740), len(sections): 3, len(contents): 2097152)
EspRomSection(address: 0x40200000, length: 245760)
EspRomSection(address: 0x40100000, length: 1880)
EspRomSection(address: 0x3ffe8000, length: 824)
Select a unique name for each section in the rom.
Sensible defaults are available for the following common names:
 .data .rodata .bss .text .irom0.text .bootrom.text .shstrtab .symtab
If defaults are unavailable for a name, generic values will be used.
Enter a name for 0x40200000> 
Enter a name for 0x40100000> 
Enter a name for 0x3ffe8000> 

Um zu prüfen, ob das extrahiere File auch wirklich vollständig ist kann man es mit readelf testen. Die Option -a haut alles an Infos aus, was das File hergibt.

root@debian:~/esp/xtensa-lx106-elf/bin# ./xtensa-lx106-elf-readelf -a /root/shellyfirmware.bin.elf 

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Tensilica Xtensa Processor
  Version:                           0x1
  Entry point address:               0x40100740
  Start of program headers:          316079 (bytes into file)
  Start of section headers:          315799 (bytes into file)
  Flags:                             0x300
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         5
  Size of section headers:           40 (bytes)
  Number of section headers:         7
  Section header string table index: 1

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000034 000000 00      0   0  1
  [ 1] .shstrtab         STRTAB          00000000 000034 000313 00      0   0  1
  [ 2] .symtab           SYMTAB          00000000 000347 0003c0 10      1   0  1
  [ 3] .bootrom.text     PROGBITS        40000000 000707 010000 00  AX  0   0  1
  [ 4]                   NULL            40200000 010707 03c000 00      0   0  1
  [ 5]                   NULL            40100000 04c707 000758 00      0   0  1
  [ 6]                   NULL            3ffe8000 04ce5f 000338 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

There are no section groups in this file.

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000034 0x00000000 0x00000000 0x00000 0x00000     0x1
  LOAD           0x000707 0x40000000 0x40000000 0x10000 0x10000 R E 0x1
  LOAD           0x010707 0x40200000 0x40200000 0x3c000 0x3c000     0x1
  LOAD           0x04c707 0x40100000 0x40100000 0x00758 0x00758 R E 0x1
  LOAD           0x04ce5f 0x3ffe8000 0x3ffe8000 0x00338 0x00338 RW  0x1

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .bootrom.text 
   02     
   03     
   04     

There is no dynamic section in this file.

There are no relocations in this file.

The decoding of unwind sections for machine type Tensilica Xtensa Processor is not currently supported.

Symbol table '.symtab' contains 60 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 400040c0     0 FUNC    GLOBAL DEFAULT    3 SPI_sector_erase
     2: 40004174     0 FUNC    GLOBAL DEFAULT    3 SPI_page_program
     3: 400042ac     0 FUNC    GLOBAL DEFAULT    3 SPI_read_data
     4: 400043c8     0 FUNC    GLOBAL DEFAULT    3 SPI_read_status
     5: 40004400     0 FUNC    GLOBAL DEFAULT    3 SPI_write_status
     6: 4000443c     0 FUNC    GLOBAL DEFAULT    3 SPI_write_enable
     7: 4000448c     0 FUNC    GLOBAL DEFAULT    3 Wait_SPI_Idle
     8: 400044c0     0 FUNC    GLOBAL DEFAULT    3 Enable_QMode
     9: 40004508     0 FUNC    GLOBAL DEFAULT    3 Disable_QMode
    10: 40004678     0 FUNC    GLOBAL DEFAULT    3 Cache_Read_Enable
    11: 400047f0     0 FUNC    GLOBAL DEFAULT    3 Cache_Read_Disable
    12: 40004f40     0 FUNC    GLOBAL DEFAULT    3 lldesc_build_chain
    13: 40005050     0 FUNC    GLOBAL DEFAULT    3 lldesc_num2link
    14: 4000507c     0 FUNC    GLOBAL DEFAULT    3 lldesc_set_owner
    15: 4000c538     0 FUNC    GLOBAL DEFAULT    3 __adddf3
    16: 4000c180     0 FUNC    GLOBAL DEFAULT    3 __addsf3
    17: 4000cb94     0 FUNC    GLOBAL DEFAULT    3 __divdf3
    18: 4000ce60     0 FUNC    GLOBAL DEFAULT    3 __divdi3
    19: 4000dc88     0 FUNC    GLOBAL DEFAULT    3 __divsi3
    20: 4000cdfc     0 FUNC    GLOBAL DEFAULT    3 __extendsfdf2
    21: 4000ccb8     0 FUNC    GLOBAL DEFAULT    3 __fixdfsi
    22: 4000cd00     0 FUNC    GLOBAL DEFAULT    3 __fixunsdfsi
    23: 4000c4c4     0 FUNC    GLOBAL DEFAULT    3 __fixunssfsi
    24: 4000e2f0     0 FUNC    GLOBAL DEFAULT    3 __floatsidf
    25: 4000e2ac     0 FUNC    GLOBAL DEFAULT    3 __floatsisf
    26: 4000e2e8     0 FUNC    GLOBAL DEFAULT    3 __floatunsidf
    27: 4000e2a4     0 FUNC    GLOBAL DEFAULT    3 __floatunsisf
    28: 4000c8f0     0 FUNC    GLOBAL DEFAULT    3 __muldf3
    29: 40000650     0 FUNC    GLOBAL DEFAULT    3 __muldi3
    30: 4000c3dc     0 FUNC    GLOBAL DEFAULT    3 __mulsf3
    31: 4000c688     0 FUNC    GLOBAL DEFAULT    3 __subdf3
    32: 4000c268     0 FUNC    GLOBAL DEFAULT    3 __subsf3
    33: 4000cd5c     0 FUNC    GLOBAL DEFAULT    3 __truncdfsf2
    34: 4000d310     0 FUNC    GLOBAL DEFAULT    3 __udivdi3
    35: 4000e21c     0 FUNC    GLOBAL DEFAULT    3 __udivsi3
    36: 4000d770     0 FUNC    GLOBAL DEFAULT    3 __umoddi3
    37: 4000e268     0 FUNC    GLOBAL DEFAULT    3 __umodsi3
    38: 4000dcf0     0 FUNC    GLOBAL DEFAULT    3 __umulsidi3
    39: 40002ae8     0 FUNC    GLOBAL DEFAULT    3 bzero
    40: 400018d4     0 FUNC    GLOBAL DEFAULT    3 memcmp
    41: 400018b4     0 FUNC    GLOBAL DEFAULT    3 memcpy
    42: 400018c4     0 FUNC    GLOBAL DEFAULT    3 memmove
    43: 400018a4     0 FUNC    GLOBAL DEFAULT    3 memset
    44: 40002aa8     0 FUNC    GLOBAL DEFAULT    3 strcmp
    45: 40002a88     0 FUNC    GLOBAL DEFAULT    3 strcpy
    46: 40002ac8     0 FUNC    GLOBAL DEFAULT    3 strlen
    47: 40002ab8     0 FUNC    GLOBAL DEFAULT    3 strncmp
    48: 40002a98     0 FUNC    GLOBAL DEFAULT    3 strncpy
    49: 40002ad8     0 FUNC    GLOBAL DEFAULT    3 strstr
    50: 40004cf0     0 FUNC    GLOBAL DEFAULT    3 gpio_input_get
    51: 40004ed4     0 FUNC    GLOBAL DEFAULT    3 gpio_pin_wakeup_disable
    52: 40004e90     0 FUNC    GLOBAL DEFAULT    3 gpio_pin_wakeup_enable
    53: 40001f00     0 FUNC    GLOBAL DEFAULT    3 ets_io_vprintf
    54: 40003b8c     0 FUNC    GLOBAL DEFAULT    3 uart_rx_one_char
    55: 40007268     0 FUNC    GLOBAL DEFAULT    3 rom_i2c_readReg
    56: 4000729c     0 FUNC    GLOBAL DEFAULT    3 rom_i2c_readReg_Mask
    57: 400072d8     0 FUNC    GLOBAL DEFAULT    3 rom_i2c_writeReg
    58: 4000730c     0 FUNC    GLOBAL DEFAULT    3 rom_i2c_writeReg_Mask
    59: 40000080     0 FUNC    GLOBAL DEFAULT    3 rom_software_reboot

No version information found in this file.

Jetzt kann man das xtensa-lx106-elf-strings Tool nochmal auf die elf Datei loslassen und schauen, welche Strings so zu sehen sind. Aber spannende Sachen habe ich jetzt nicht gefunden.

./xtensa-lx106-elf-strings -a -t x /root/shellyfirmware.bin.elf | less

Firmware Analyse

Jetzt kann man es natürlich noch ein bisschen weiter treiben und sich den Assembler Code mit einem Decompiler anschauen. Da könnte man nochmal mit den oben aufgeführten Symbols schauen, was denn hier so eigentlich im Detail läuft.

Aber für jetzt lasse ich es mal gut sein. Meine letzten Decompiler Aktivitäten sind jetzt schon ein paar Jahre / Jahrzehnte her und außerdem nur mit x86 Opcodes. Der lx106 hat da ja sein ganz eigenes Instruction Set. Evtl. mache ich später noch einen Beitrag zum Thema ESP8266 Firmware reengineering.

Fazit

Hier mal in Summe meine Erkenntnisse aus meinem kleinen Projekt.

  • Positiv – Hardware
    • Die Bauform ist für eine Unterputz Montage schon sehr geeignet. Auch die Möglichkeit einen Schalter für manuelles Schalten einzuschließen ist sinnvoll.
    • Die Bandbreite an schaltbaren Spannungen finde ich auch sehr gut, da es ja nicht immer 230 Volt AC sein müssen.
  • Negativ – Hardware
    • Elektrische Sicherheit: Der Umstand, dass man das Gerät nicht gleichzeitig im Rechner und am Stromnetz angeschlossen haben sollte, ist m.E. nicht optimal.
    • Feste MQTT Prefix: Da ich immer MQTT als Kommunikationsprotokoll zwischen den Geräten im Haus verwende, ist mir hier eine gute Unterstützung wichtig. Leider unterstützt der Shelly hier nur fest eingestellte MQTT Prefix, so dass ich nicht vollständig frei wählen kann mit welchem Topic die Telegramme versehen werden. Hier habe ich den Tipp bekommen, es mit der Tasmota Firmware zu versuchen.
    • Auch finde ich
  • Positiv – Software
    • Die Einrichtung ist schnell erledigt und das Web-Interface ist übersichtlich.
    • Das Thema fest eingestellter MQTT Prefix „Shellies“ finde ich nicht gut. Aber mit einer Tasmota Firmware sollte das besser gehen.
  • Negativ – Software
    • Firmware Upgrade Prozess: Hier gibt es m.E. nach mehrere Dinge zu bemängeln. Zuerst einmal finde ich den Upgrade Prozess nicht gelungen.
      • Es fehlt eine verschlüsselte Kommunikation
      • Der Update Server sollte hinsichtlich der Echtheit geprüft werden.
      • Es sollten beim Update selbst weniger Daten übermittelt werden. Keiner dort braucht meine Shelly ID und den Release Stand des Gerätes.
      • Die Firmware sollte mit einem Hash signiert und im Upgrade Prozess geprüft werden.

Der Preis aber ist natürlich unschlagbar. Während bei anderen Protokollen Lizenzgebühren anfallen oder wie bei EnOcean noch ein Transceiver eingebaut werden muss, der von einem Hersteller kommt ist das hier nicht notwendig.

Ob aber WLAN das beste Protokoll ist, muss jeder für sich selbst beantworten.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert