Funkklingel
Posted on So 03 April 2022 in Computer & Electronics
Hin und wieder wollen andere Menschen uns darauf aufmerksam machen, dass Sie sich gerade vor unserem Haus befinden. Manche davon kennen wir, andere nicht. Manche möchten gerne hereingelassen werden, andere würden nur gerne ein Paket loswerden, das an uns adressiert ist. Allen gemein ist, dass es sie nervt, dass unsere Klingel nicht funktioniert.
Eigentlich gibt es einen Klingelknopf am Gartenzaun, aber der ist uralt und wir wollen die Klingel nach der Sanierung des Hauses eh erneuern, aber aus verschiedenen Gründen ist das bisher nicht passiert. Wenn ich ganz ehrlich bin fand ich den klingellosen Zustand eigentlich auch ganz angenehm, denn man hat so wirklich seine Ruhe. Dennoch wurde die Behebung dieses Misstands im Familienrat auf die Tagesordnung gebracht. Und so hatte ich irgendwann halbherzig so eine Funkklingel angeschafft und den Sender neben dem Briefkasten montiert. Die funktioniert auch ganz OK. Aber es hat sich herausgestellt dass die meisten Leute, mangels Hoftor, einfach ungefragt bis zur Haustür vorrücken und dort vergeblich nach einer Klingelmöglichkeit suchen. Zudem halten die blöden Knopfzellen im Sender nicht ewig und was noch blöder ist – man bekommt nicht wirklich mit wenn sie leer sind – bis einem irgendwann auffällt, dass es in letzter Zeit so friedlich ist und niemand klingelt.
Also muss eine bessere Lösung her.
Wunschliste und Planung
Meine Vorstellung besteht nun darin, je einen Klingelknopf am nicht vorhanden Tor und einen zweiten direkt am Haus anzubringen. Beide sollen dann per Funk je einen Empfänger (je einen im EG und OG) zu verheißungsvollem "Gongen" ermuntern.
Außerdem will ich nicht diese blöden Plastik-Klingelsender einfach an Wand bzw. Zaun pappen, sondern altmodische Messing-Klingelknöpfe haben. Zuguterletzt fände ich es auch ganz nett, wenn auch mein Homeassistant mitbekäme, wenn es klingelt. Also ist ein wenig Basteln angesagt.
Die Einkaufsliste umfasst also
- 2x Funkklingel mit je einem Sender und Empfänger. Letzter bitte mit Stromversorgung aus der Steckdose
- 2x Messingklingelknopf
- ein bisschen Holz für das Gehäuse
- Kleines Netzteil für einen der beiden Sender (beim zweiten gibt's keinen Stromanschluss)
Nach etwas Recherche habe ich mich für das Modell AVANTEK D-3B entschieden. Zwar habe ich nur ein Set mit einem Sender und zwei Empfängern gefunden, aber dafür konnte ich den Sender einzeln kaufen und bin so zu meiner Wunschkonfiguration gekommen. Angeblich reicht das Funksignal bis zu 300m weit und die Empfänger können als Relais-Stationen fungieren, um die Reichweite zu einem Zweitempfänger nochmal zu erhöhen. Die Beschreibung prahlt mit 52 verschiedenen Melodien bei 115dB (niemals...). Laut ist das Teil wirklich (kann man zum Glück runterregeln) aber die 52 "Melodien" haben mir spontan ein DejaVue an die Zeiten des Jamba-Spar-Abos verschafft. Bis auf 2 halbwegs dezente Ding-Dong Töne ist der Rest wohl eher auf die Zielgruppe (prä-)pubertärer Nutzer optimiert. Aber die muss ich ja nicht verwenden.
Und so schaut eine Sender-Empfänger-Kombi aus:
Technik
Zuerst machen wir mal so einen Sender auf. Als erstes fällt auf, dass der Sender keine Dichtung hat. D.h. es empfiehlt sich nicht, ihn einfach so zu montieren. Von wegen IP55... Im Inneren befindet sich eine recht simple Platine, die auf der einen Seite nur den Taster und zwei LEDs trägt und auf der Rückseite finden wir neben einer Knopfzelle diverse Bauteile:
Bei genauerer Betrachtung haben wir da
- Knopfzellenhalter (CR2032)
- Quarz (26.2982 MHz)
- Eine Antenne
- Einen IC
- Einen Datenport (VCC, GND, CLK, DATA)
- Diverse passive SMD Bauteile sowie ein Transistor
Alles in allem recht unspektakulär. Spannend aber ist der IC. Ich musste die Augen ordentlich zusammenkneifen und eine gute Lupe zuhilfe nehmen, um dann bei optimaler Beleuchtung was lesen zu können: CMT2157B steht drauf. Eine Googlesuche später habe ich tatsächlich ein Datenblatt. Die Beschreibung sagt "240 MHz-960 MHz Single Chip OOK Transmitter with Encoder". Das Ding funkt bei der vorliegenden Konfiguration auf der Frequenz 433.92 MHz. Das klingt erfolgversprechend in Sachen HA-Anbindung. Laut Datenblatt ist der Chip programmierbar wenn man einen Jumper brückt. Dazu ist der Datenport da. Allerdings habe ich keine Angaben zum Protokoll. Nur den Verweis auf ein Programmiergerät das ich nicht habe.
Anschließen
Die beiden Empfänger kommen in je eine Steckdose im Erdgeschoss und 1. Stock. Bei Bedarf kann man dann einen davon rausziehen und in eine Außensteckdose auf der Terrasse verpflanzen. Das finde ich einen echten Vorteil gegenüber fest installierten Gongs/Türklingeln.
Ein Klingelknopf soll an den Gartenzaun – netterweise habe ich noch das ursprüngliche Klingelkabel und so habe ich beschlossen, das zu benutzen und den Sender dann im Haus an selbiges anzuschließen. So kann ich mir mittels Netzteil auch die Knopfzelle sparen und der Sender muss weder Wind noch Wetter ertragen.
Den zweiten Sender baue ich in ein kleines Holzkästchen ein, auf das der andere Messing-Klingelknopf kommt. Und anstelle der Knopfzelle nehme ich eine größere Batterie, damit das möglichst lange hält. D.h. ich stelle mir das in etwa so vor:
Funkprotokoll
Bleibt noch die Frage, ob und wie man das Klingelsignal auch am Heimserver auffangen kann. Wäre doch cool, wenn ich das irgendwie in Homeassistant einspeisen könnte und dann z.B. eine Email oder andere Nachricht versenden würde, wenn es an der Tür klingelt.
Wir hatten ja schon gelernt, dass wir hier einen 433MHz Sender vor uns haben und dass der On/Off-keying (OOK) verwendet. D.h. das sollte sich ganz gut reverse-engineeren lassen.
Also erstmal den RTL-SDR Stick rauskramen. Als erstes habe ich einfach mal
rtl_433
gestartet, den Klingelknopf gedrückt und gehofft, dass da vielleicht
schon was kommt. Leider nicht.
Aber das wäre ja auch zu langweilig gewesen. Also dickere Geschütze auffahren. In diesem Fall den Universal Radio Hacker (urh).
Den kann man ganz leicht via pip install
bekommen, allerdings hat er dann gemeckert,
dass das systemseitig installierte numpy offenbar nicht kompatibel ist
❯ urh
ValueError: numpy.ndarray size changed, may indicate binary incompatibility. Expected 96 from C header, got 80 from PyObject
Also alles in ein virtual environement packen und dann installieren:
python3 -m venv env
source env/bin/activate
python3 -m pip install urh
Nun können wir es verwenden:
urh
Als erstes mal den Spectrum-Analyzer starten, ein paarmal auf den Klinkelknopf drücken und schauen, ob wir das Signal finden:
Das schaut doch schon ganz gut aus. Als nächstes wollten wir mal das Signal aufzeichnen. Also in den recording mode, dreimal den Knopf drücken. Und schon haben wir dies:
Hübsch. Das speichern wir ab und wechseln in das Interpretation interface.
Zunächst zoomen wir mal ordentlich in das Signal hinein:
Das Signal ist sehr gleichförmig und wird über bestimmte Perioden ein bzw. aus geschaltet. On-Off-Keying (OOK) eben. OOK ist letztlich ein Spezialfall von ASK (amplitude shift keying = Amplitudenmodulation).
Als nächstes gilt es, das zu decodieren. Also setzen wir die Demodulation auf ASK und schauen, was wir bekommen:
Wunderbar. Das sieht schon richtig digital aus. Und nun müssen wir über etwas
reden, das mich anfänglich total verwirrt hat: Auf den ersten Blick hätte ich
gesagt, dass ich hier ein Folge von Bits sehe, die man als 0101010110010
interpretieren würde. Aber das ist so nicht ganz richtig, denn auf dieser Ebene
(Roh-Signal) spricht man noch nicht von Bits, sondern von Symbols. Bits
bekommen wir erst nach der Decodierung. Diese kann die Symbols eins zu eins
in Bits verwandeln, aber das muss nicht so sein. Z.B. gibt es Signale, die auf
Symbol-Ebene nicht binär sind, sondern z.B. vier verschiedene Signal-Level
kennen und so 2 Bits pro Symbol übertragen können. Und auf der anderen Seite
gibt es Methoden, die binär sind, aber mehr als ein Symbol pro Bit verwenden.
Und schließlich gibt es Codierungs-Verfahren die den Input Bit-Stream so
verändern, dass es möglichst viele Übergänge von 0 auf 1 im Rohsignal gibt,
damit sich der Empfänger leichter tut, den Takt (CLK
) sauber synchron zu
halten. Die Bits sind quasi die Nutzlast, die Symbols der Träger – so wie wir
unser analog Signal durch Demodulation aus dem Radiosignal extrahiert hatten
bekommen wir das digitale Signal in Bits durch Decodierung aus dem
Symbol-Stream.
Ich habe ein sehr schöne Übersicht der kompletten Kette in einer wissenschaftlichen Publikation (Abb. 5 in Tianqi Wang at al. Deep Learning for Wireless Physical Layer: Opportunities and Challenges, China Communications, 14:11, Nov. 2017) gefunden (Copyright bei den Autoren):
Bevor wir nun anfangen das demodulierte Signal zu decodieren werfen wir mal einen Blick auf das, was wir schon auf Symbol-Ebene sehen:
Die Message startet mit 16 Symbols, die abwechselnd 1 und 0 sind (gelb hinterlegt). Das nennt man die Preamble. Die braucht der Empfänger, um aufzuwachen und zu lernen wie hoch die Symbol Rate ist – also wie lang so ein Symbol dauert.
Als nächstes folgt eine Sequenz, die man Sync oder Sync Word nennt. Das ist eine vordefinierte Folge von Symbols. Der Empfänger prüft nun, ob das Sync Word so aussieht wie erwartet. Wenn ja kann es losgehen.
Lektüre
Jetzt ist ein guter Zeitpunkt, um mit dem reinen Rumraten aufzuhören und sich durch die Lektüre von Datenblättern zu bilden.
Ich habe ein bisschen im großen weiten Netz recherchiert und auf den Seiten
www.cmostek.com und hoperf.com
weitere Datenblätter und application notes gefunden. Teils bezogen diese sich
nicht genau auf diesen exakten Chip, aber ich habe den Eindruck, dass das
meiste übertragbar ist. V.a. die Dokumente
AN112_CMT2150A_Configuration_Guideline
und
AN1004-CMT2157A_CMT2219A_communication_example(1920 coding)
erscheinen sehr
hilfreich.
Und darin findet man, dass es mehrere verschiedene Formate gibt und diese konfigurierbar sind. Das erste im Handbuch ist das 1920 packet format und man kann da im Konfigurations-Tool verschiedenes einstellen:
Zwar ist die Länge bzw. das Vorhandensein der Preamble konfigurierbar, aber das
folgende Beispiel legt 16 Symbols nahe und passt somit perfekt zu unserem
Signal (0xaaaa
, wenn man die Symbols als Bits interpretiert):
Und als nächstes wird dort definiert, dass das sync word (dort head_n pattern
genannt) so aussehen muss (0xcaca5353
, wenn man die Symbols als Bits
interpretiert):
Und auch das finden wir genau so in unseren Signalen (grün hinterlegt). Damit ist das Vorgeplänkel abgeschlossen und es folgen die eigentlichen Daten. Und die können nun laut Data Sheet unterschiedlich codiert sein. Hier die Abbildung aus dem Datenblatt:
Schauen wir also mal auf die Datenpakete, die wir nach Preamble und Sync haben:
1: 11011011011011011011011011011011010011010010011011010011011011010010010011011...
2: 11011011011011011011011011011011010011010011010010010011011010010010010011010...
3: 11011011011011011011011011011011010011010010011011010011011011010010010011011...
4: 11011011011011011011011011011011010011010011010010010011011010010010010011010...
Für mich schaut das doch sehr nach 3 Symbols pro Bit aus:
1: 110 110 110 110 110 110 110 110 110 110 110 100 110 100 100 110 110 100 110 ...
2: 110 110 110 110 110 110 110 110 110 110 110 100 110 100 110 100 100 100 110 ...
3: 110 110 110 110 110 110 110 110 110 110 110 100 110 100 100 110 110 100 110 ...
4: 110 110 110 110 110 110 110 110 110 110 110 100 110 100 110 100 100 100 110 ...
Ich denke, das dürfte stimmen. Ich sehe nur die beiden Triplets 110 (0) und 100 (1). Und wenn man nun einfach im Texteditor die beiden Symbol-Triplets durch das entsprechende Bit ersetzt, bekommt man das hier:
1: 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 1 0 ...
2: 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 1 1 0 ...
3: 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 1 0 ...
4: 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 1 1 0 ...
Passt!
Gegenprobe – 4 Symbols pro Bit:
1: 1101 1011 0110 1101 1011 0110 1101 1011 0100 1101 0010 0110 1101 0011 ...
2: 1101 1011 0110 1101 1011 0110 1101 1011 0100 1101 0011 0100 1001 0011 ...
3: 1101 1011 0110 1101 1011 0110 1101 1011 0100 1101 0010 0110 1101 0011 ...
4: 1101 1011 0110 1101 1011 0110 1101 1011 0100 1101 0011 0100 1001 0011 ...
Nee – das ist es ganz klar nicht, denn es sollten nur 1110 und 1000 vorkommen.
Analog für 5 und 6 Symbols pro Bit...
Big picture
Bevor wir uns dem genauen Inhalt der Daten widmen, treten wir aber einen Schritt zurück und betrachten nochmal unsere demodulierten Daten:
Für mich schaut das so aus, als hätten wir bei einem Klingel-Event tatsächlich drei separate Datenpakete, die durch eine kurze Pause getrennt sind und so rein optisch ziemlich gleich aussehen.
Also spielen wir mal mit der Einstellung rum, damit er das als separate Messages erkennt. Und schon ...
... sind die Teile als separate Messages erkannt so dass aus den ursprünglich 4 Messages nun 3∙3=9 Messages geworden sind.
Daten Analyse
Nun stellt sich noch die Frage, was die ganzen Daten bedeuten. Vor allem ist zu
erwarten, dass jeder Klingelknopf eine andere ID sendet, weil es sonst auch bei
allen Nachbarn klingelt. Und in der Tat musste ich den separat erworbenen
Sender auch erstmal an den Empfängern anmelden, damit er erkannt wurde. Bei
einem so simplen Gerät wie einer Haustürklingel würde ich erwarten, dass hier
einfach jedes mal der Gleiche Code gesendet wird und keine Tricks wie Rolling
codes verwendet werden, denn die Sicherheitsimplikationen eines Klingel Hacks
bestehen im Wesentlichen in der Möglichkeit von Klingelstreichen und dazu braucht
man einfach nur den Knopf drücken und wegrennen, also lohnt sich der Aufwand
nicht. Oben hatten wir schon gesehen, dass es da so D0
, D1
etc. Bits
gibt – die sind für den Fall, dass mehr als ein Taster angeschlossen ist.
Um der Sache mit der Sender-ID auf den Grund zu gehen zeichnen wir ein neues Signal auf. Diesmal drücken wir die beiden unterschiedlichen Sender abwechselnd, um zu erkennen, ob es Unterschiede gibt. Hier zunächst das analoge Signal:
Wie man leicht sehen kann ist die Amplitude der beiden Sender sehr verschieden – einen habe ich direkt neben die Antenne meines Empfängers gehalten, den anderen weiter weg. Auf diese weise sehe ich leicht wer der Sender ist und auch urh kann so die Participants automatisch zuordnen. Und so schaut das dann in der Analyse aus:
Wie versprochen, hat urh erkannt, dass hier zwei verschiedene Sender (A: hellgrün und B: dunkelgrün) beteiligt waren und das in der Zeilenbeschriftung kenntlich gemacht). Je drei Messages in jeder Gruppe. Die Preamble hat das Programm korrekt erkannt. Bei der Länge des Sync words habe ich nachgeholfen, weil urh nicht wissen kann wo es endet.
Bisher haben wir also folgendes Bild vom Message Format (Koordinaten und Länge in Symbols):
Start | End | Length | Bedeutung |
---|---|---|---|
1 | 16 | 16 | Preamble |
17 | 48 | 32 | Sync |
49 | 134 | 86 | Address/ID |
135 | 146 | 12 | Buttons |
147 | 154 | 8 | CRC |
Das Ganze hat nur ein klitzekleines Problem: Es kann nicht stimmen, denn wie wir uns erinnern, brauchen wir 3 Symbols/Bit und 86 ist nicht durch 3 teilbar. Und auch der Start des Button Feldes kann nicht stimmen, denn dort sind wir bezüglich der Bit-Codierung aus dem Raster:
1: 011 010 011 011
2: 011 010 011 011
3: 011 010 011 011
4: 010 010 010 011
5: 010 010 010 011
6: 010 010 010 011
7: 011 010 011 011
8: 011 010 011 011
9: 011 010 011 011
10: 010 010 010 011
11: 010 010 010 011
12: 010 010 010 011
Das sind keine gültigen Codes für Bits. Was ist da los? Also nochmal das Datenblatt studieren. Und dort erfahren wir, dass es mehrere verschiedene Button-Modes gibt: Normal, Matrix, Toggle und PWM. Bei den letzten drei stimmt die Angabe, dass es 4 Bits sind. Im Normal-mode hingegen können 1-4 Button Bits eingestellt werden. Da der Sender eh nur einen Button hat kann es natürlich sein, dass nur ein Bit konfiguriert ist. Wenn wir das mal so annehmen könnte das also so aussehen:
Start | End | Length (Sym/Bits) | Bedeutung |
---|---|---|---|
1 | 16 | 16 | Preamble |
17 | 48 | 32 | Sync |
49 | 144 | 96/32 | Address/ID |
145 | 147 | 3/1 | Buttons |
148 | 154 | 7 ? | CRC |
32 Bit für Address würde auch insofern Sinn machen, als die Seriennummern auf den beiden Sender "1K26" bzw. "1L21" lauten – also 4 Byte = 32 Bit.
Prüfen wir doch mal diese Hypothese, indem wir versuchen, das Address-Wort zu decodieren. Eigentlich kann urh das und man kann eigene Decoder definieren, aber irgendwie habe ich es nicht hinbekommen, meine simple Symbol -> Bit Übersetztung in urh anzuwenden, ohne dass es über decoding errors gemeckert hat Ich glaube da muss ich mal das Handbuch gründlicher lesen.
Also machen wir das von Hand. Dazu habe ich die 96 Symbols aus dem Signal ausgeschnitten und ein eine Textdatei kopiert und mir schnell ein kleines Python-Skript geschrieben, um die Daten in Bits zu übersetzen und in Binär, Hex und ASCII auszugeben.
Bits Hex ASCII
00000000000101100100011100111010 0016473a G:
00000000000101100100011100111010 0016473a G:
00000000000101100100011100111010 0016473a G:
00000000000101011100111101101111 0015cf6f Ïo
00000000000101011100111101101111 0015cf6f Ïo
00000000000101011100111101101111 0015cf6f Ïo
00000000000101100100011100111010 0016473a G:
00000000000101100100011100111010 0016473a G:
00000000000101100100011100111010 0016473a G:
00000000000101011100111101101111 0015cf6f Ïo
00000000000101011100111101101111 0015cf6f Ïo
00000000000101011100111101101111 0015cf6f Ïo
Hm – nicht erhellend. Aber wir haben auch noch was übersehen: Aus dem Datenblatt (dort wird von Sync ID gesprochen, was wir Address nannten):
The Sync ID is sent and received starting from the LSB. For example, if the “Sync ID Length” is set to 16 and the “Sync ID Value” is set to 1, the binary value of the Sync ID is “0000 0000 0000 0001”. In this case, bit<0> = 1 is received first and bit<15> = 0 is received at the end of the Sync ID field.
Also versuchen wir das mal reversed, also LSB zuerst:
Bits Hex ASCII
01011100111000100110100000000000 5ce26800 \âh
01011100111000100110100000000000 5ce26800 \âh
01011100111000100110100000000000 5ce26800 \âh
11110110111100111010100000000000 f6f3a800 öó¨
11110110111100111010100000000000 f6f3a800 öó¨
11110110111100111010100000000000 f6f3a800 öó¨
01011100111000100110100000000000 5ce26800 \âh
01011100111000100110100000000000 5ce26800 \âh
01011100111000100110100000000000 5ce26800 \âh
11110110111100111010100000000000 f6f3a800 öó¨
11110110111100111010100000000000 f6f3a800 öó¨
11110110111100111010100000000000 f6f3a800 öó¨
Nun dürfte es korrekt decodiert sein. Dennoch sehe ich nichts, was der Seriennummer ähnelt. Grumpf!
An diesem Punkt fällt mir wirklich nicht mehr viel ein, das ich noch tun könnte, um der genauen Codierung auf den Grund zu gehen. Vielleicht, wenn ich noch einen Stapel weitere Sender hätte? Wie auch immer. Ich weiß nun, wie die Datenpakete meiner beiden Sender aussehen und kann sie somit erkennen und auf sie reagieren. Ich könnte sie auch klonen und Sender mit anderer ID bauen – nur nicht anhand der Seriennummer, weil mir dieses Puzzle-Teil fehlt. Das fuchst mich zwar gewaltig, aber das Leben ist halt hart.
Und dann ist da noch eine große Ungereimtheit. Schaut Euch mal das Ende der Messages an:
SYNC-ID B CRC?
___________ ___ _______
... 100 110 110 1101101
... 100 110 110 1101101
... 100 110 110 1101101
... 100 100 110 1101101
... 100 100 110 1101101
... 100 100 110 1101101
... 100 110 110 1101101
... 100 110 110 1101101
... 100 110 110 1101101
... 100 100 110 1101101
... 100 100 110 1101101
... 100 100 110 1101101
Alles in Symbols. Die ersten beiden Triplets gehören noch zur Sync-ID. Dann kommt unser Button Bit (B) und schließlich 7 Symbols die CRC sein sollen. Mal davon abgesehen, dass wir laut Datenblatt eigentlich 8 Symbols CRC erwarten und nicht nur 7 fällt nochwas auf: Die vermeintlichen CRC Symbols sind in allen 12 Messages identisch! Das kann nicht sein, denn wir haben ja zwei unterschiedliche Messages von zwei Sendern und es müsste mit dem Teufel zugehen, wenn beide den selben CRC Wert hätten!
Irgendwas ist da noch oberfaul...
Fazit
- Das hat Spaß gemacht, obwohl es mir erstmal nicht gelungen ist das Message-Format komplett zu verstehen. Und das trotz Datenblatt... Dafür habe ich unterwegs viel über digitale Funk-Kommunikation gelernt.
- urh ist ein wirklich praktisches Werkzeug – das werde ich in Zukunft öfter verwenden.
- Wenn ich irgendwann Lust habe bastle ich vielleicht mal eine Lösung, um das an den homeassistant anzubinden (wahrscheinlich würde es reichen, die Events einfach an den MQTT Server weiter zu geben), oder vielleicht auch eine Standalone Lösung um z.B. eine Email zu versenden, wenn es klingelt. Aber für heute soll es gut sein.
- Wenn mir noch eine geniale Eingebung kommt, wie genau die Codierung der Seriennummer funktioniert werde ich den Post ergänzen. Sollte der geneigte Leser eine gute Idee haben, wo genau ich auf dem Schlauch stehe – immer her damit.