Forum: Mikrocontroller und Digitale Elektronik 433MHz Daten-protokoll


von Fabian F. (fabian_f55)


Lesenswert?

Moin moin,
kennt jemand vielleicht eine Lib für Atmel für Datenübertragung über ein 
433MHz Sender/Empfänger-Paar?

Nachdem man keine gemeinsame Clock hat, muss man ja wohl über Pulsbreite 
gehen?
Die Länge der Pulse wird allerdings ordentlich verschliffen durch die 
Sender/Empfänger.
Sender-µC sind bei mit mehrere Attiny85 und Empfänger ein AtMege64M1.

Die Übertragung ist nicht zeitkritisch, aber die Sender laufen auf 
Batterie, sollten also nicht ständig senden müssen.

Übertragen möchte ich 3 Bytes, wovon Byte 1 die Sender-ID & Checksum, 
Byte 2 die Art der Daten und Byte 3 die Nutzdaten sind.

Die Empfänger-Module scheinen eine art Auto-tune zu haben. Wenn keiner 
etwas sendet wird das Hintergrundrauschen verstärkt. Demnach erscheint 
mir Interrupt-basierte Abfertigung nicht geeignet, weil der ständig 
auslösen würde.

Hat jemand Erfahrung damit

von Jürgen S. (jurs)


Lesenswert?

Fabian F. schrieb:
> Die Empfänger-Module scheinen eine art Auto-tune zu haben. Wenn keiner
> etwas sendet wird das Hintergrundrauschen verstärkt.

Ja, die einfachste Ausführung sind "Regenerativempfänger", die nach Art 
eines Röhrenaudions arbeiten. Allerdings mit automatischer 
Transistorregelung: Die Verstärkung wird stets so weit aufgedreht, bis 
der Empfänger etwas empfängt. Also empfängt diese Art Empfänger immer 
etwas: Entweder ein starkes Nutzsignal. Oder schwaches weißes Rauschen 
als Datenmüll.

> Demnach erscheint mir Interrupt-basierte Abfertigung nicht geeignet,
> weil der ständig auslösen würde.
>
> Hat jemand Erfahrung damit

Doch, interruptbasierte Auswertung ist das einzig sinnvolle.
Du brauchst aber ein sinnvolles Sendeprotokoll und Du mußt beim Emfpang 
entsprechend filtern.

Hast Du Dir schon mal ein Protokoll angesehen, wie "drahtlose 
Temperatursensoren" oder "Funksteckdosen" mit solchen billigen 433-MHz 
Sedern und Empfängern umgehen?

Im Prinzip wird einfach mit Ein-Aus-Tastung gesendet und das wird am 
Empfänger in High-Low umgesetzt.

Das typische Sendeprotokoll sieht zum Beispiel so aus:
- Sende "Trainingbits"
- Sende ein "superlanges Start-Bit"
- Sende die Daten-Bits

Die "Trainingsbits" dienen dabei anfänglich nur dazu, dass der Empfänger 
seine Verstärkung so auf das Sendesignal einregelt, dass er dessen 
Sendung einwandfrei erkennen kann.

Das "superlange Startsignal" ist dann das Signal für den Empfänger, ab 
Erkennung des Signals mit der Decodierung von Nutz-Bits zu beginnen.

Die dann folgenden Datenbits werden einfach gezählt: Wenn z.B. 32 
Datenbits gesendet werden, wird beim Empfang nach 32 Bits die 
Plausibilität ausgewertet, also z.B. ob die CRC-Prüfsumme stimmt.

Die einzelnen Bits können entweder mit zwei Längen codiert werden, z.B.:
0-Bit: 600 µs HIGH und 1200 µs LOW
1-Bit: 1200 µs HIGH und 600 µs LOW
Startbit: 8000 µs HIGH und 600 µs LOW

Oder als Bits mit unterschiedlicher Länge und gleicher Pause, z.B.:
0-Bit: 600 µs HIGH und 400 µs LOW
1-Bit: 1200 µs HIGH und 400 µs LOW
Startbit: 8000 µs HIGH und 400 µs LOW

Die Empfangslogik kann dann als Finite State Machine realisiert werden, 
die drei Zustände hat:
- warte aufs Starbit
- warte aufs nächste Bit
- Datensatz vollständig
Die Programmlogik in der Change-ISR muß dann nur sinnvoll je nach 
empfangenen High/Low Impulsdauern zwischen den drei Zuständen umschalten 
bzw. Bits zählen, bis ein vollständiger Datensatz empfangen wurde, den 
das Hauptprogramm dann auswerten kann. Und der Datenmüll zwischen den 
Aussendungen wird einfach dadurch ausgefiltert, dass wenn im Status 
"warte aufs nächste Bit" eine falsche Bitlänge außerhalb einer 
vorgegebenen Toleranz empfangen wird, der Status stets auf "warte aufs 
Startbit" gesetzt wird. Wobei die Toleranzen durchaus recht großzügig 
bemessen sein dürfen.

von Guest (Gast)


Lesenswert?

Evtl. kommt auch eine Arduino-Lib in Frage. Zumindest kann man sich da 
auch einige Idee holen:

* http://www.airspayce.com/mikem/arduino/VirtualWire/
* http://www.airspayce.com/mikem/arduino/RadioHead/
* https://code.google.com/p/rc-switch/

von Fabian F. (fabian_f55)


Angehängte Dateien:

Lesenswert?

Ok, danke.

Ich weiß nicht ob hier irgendwas auf 433MHz sendet.

Ich hab mal was auf Sender-Seite Programmiert.
Wenn ich nichts sende sieht es aus wie auf Bild 23.
Die Daten sind auf bild 30.
Hab ich das so richtig Verstanden?

Wie würde man das dann auswerten? ISR-auf steigende Flanke->Timer 
starten->Stop auf nächster steigender Flank->Zeit in ein array schreiben 
und am ende evaluieren?

von Jürgen S. (jurs)


Lesenswert?

Fabian F. schrieb:
> Ich hab mal was auf Sender-Seite Programmiert.
> Wenn ich nichts sende sieht es aus wie auf Bild 23.
> Die Daten sind auf bild 30.
> Hab ich das so richtig Verstanden?

Das sieht soweit schon mal gut aus:
- 5 Trainingspulse (nur zum Einpegeln der Empfängerverstärkung)
- überlanger Startimpuls
- Anzahl n Bits als zu übertragende "Payload"

Innerhalb der Payload scheint es bei Dir drei verschiedene Bitlängen zu 
geben: HIGH-Bits in einfacher und doppelter Länge sowie LOW-Bits in 
einfacher, doppelter und vierfacher(?) Länge?

Kannst Du mal das Sende-Codeschema genau beschreiben?

Oder soll das eine Mehrfachaussendung werden?

Im Falle einer Dreifachaussendung einer identischen Payload wäre das 
Sendeschema:
- Trainingsbits
- Überlanges Startbit
- Payload zum ersten
- Überlanges Startbit
- Payload zum zweiten
- Überlanges Startbit
- Payload zum dritten

Also Trainingsbits nur vor der ersten Sendung, danach bei den 
Folgeaussendungen jeweils zwar das Überlange Startbit und dann die 
Payload.

> Wie würde man das dann auswerten? ISR-auf steigende Flanke->Timer
> starten->Stop auf nächster steigender Flank->Zeit in ein array schreiben
> und am ende evaluieren?

Ich mache das nur unter Arduino mit Change-Interrupt auf beide Flanken.

In der Interruptbehandlung wird der Pin-Status 1 oder 0 ausgelesen und 
die Zeitdifferenz gebildet aus aktuellem und letztem Timerstand, sowie 
der aktuelle Timerstand gespeichert. Und je nachdem weiß man dann
- wenn der Pegel jetzt HIGH ist, haben wir die Zeit für einen LOW-Impuls
- wenn der Pegel jetzt LOW ist, haben wir die Zeit für einen HIGH-Impuls

Bei LOW-Impuls: Wenn Dauer +/- Toleranz == Startimpulsdauer, dann setze 
"counting" auf true und "bitcount" auf 0.
Ansonsten:
Wenn Impulslänge einem 1-Bit entspricht: Bit merken, bitcount++
Wenn Impulslänge einem 0-Bit entspricht: Bit merken, bitcount++
Wenn danach bitcount== Anzahl Bits in der Payload ==> Ergebnis z.B. in 
einen FIFO-Puffer schieben
Beim Auftreten "falscher" Bitlängen ==> counting=false, bitcount=0

Mit welcher Taktfrequenz soll denn Dein Controller laufen?
Hast Du schon irgendwelche Timer für irgendwas verbraten oder sind noch 
alle Timer frei verfügbar?

von Fabian F. (fabian_f55)


Lesenswert?

Jürgen S. schrieb:

> Innerhalb der Payload scheint es bei Dir drei verschiedene Bitlängen zu
> geben: HIGH-Bits in einfacher und doppelter Länge sowie LOW-Bits in
> einfacher, doppelter und vierfacher(?) Länge?

Die unterschiedlichen Längen waren keine Absicht. Zwischen den Bytes hat 
er die richtigen Werte in einem großen Array im EEPROM gesucht, was 
offenbar länger gedauert hat, als eine Bitbreite. Jetzt such ich mir die 
Werte vorher zusammen. Jetzt hab ich nur noch 2 Längen
>
> Kannst Du mal das Sende-Codeschema genau beschreiben?

Ich hab festgestellt, dass ich den Reciever erst mit einem langen 
Startbit (5 ms) aufwecken muss. Also sende ich jetzt:

Startbit - 5ms high
Signal Marker - 10x200µs High-Low
Payload - Byte 1-3
Stuffing Byte



> Ich mache das nur unter Arduino mit Change-Interrupt auf beide Flanken.

Mein Sender Empfänger- Paar verschleift mir die Bit-längen ziemlich. Auf 
sender-Seite mache ich 200µs high, dann 200µs low (Für 0) oder 400µs 
high und 200µs low (für 1).
Auf der Empfängerseite kommt an:
0: 115µs high 285µs low
1: 205µs high 400µs low

Die zeitliche Position der steigenden Flanken stimmt also, die der 
fallenden ist grotesk verzerrt.

Ich trigger daher nur auf die steigende Flanke. Damit verpasse ich das 
letzte Bit, also hab ich noch ein Byte angehängt. Davon kann ich dann 
die ersten höheren 7 Bit als Checksumme nutzen.


> In der Interruptbehandlung wird der Pin-Status 1 oder 0 ausgelesen und
> die Zeitdifferenz gebildet aus aktuellem und letztem Timerstand, sowie
> der aktuelle Timerstand gespeichert. Und je nachdem weiß man dann
> - wenn der Pegel jetzt HIGH ist, haben wir die Zeit für einen LOW-Impuls
> - wenn der Pegel jetzt LOW ist, haben wir die Zeit für einen HIGH-Impuls
>
> Bei LOW-Impuls: Wenn Dauer +/- Toleranz == Startimpulsdauer, dann setze
> "counting" auf true und "bitcount" auf 0.
> Ansonsten:
> Wenn Impulslänge einem 1-Bit entspricht: Bit merken, bitcount++
> Wenn Impulslänge einem 0-Bit entspricht: Bit merken, bitcount++
> Wenn danach bitcount== Anzahl Bits in der Payload ==> Ergebnis z.B. in
> einen FIFO-Puffer schieben
> Beim Auftreten "falscher" Bitlängen ==> counting=false, bitcount=0

Danke für den Part. Hab ich genau so übernommen, nur, dass ich nicht 
zwischen High und Low differenziere.

> Mit welcher Taktfrequenz soll denn Dein Controller laufen?
Der Sender mit 1 MHz, der Empfänger mit 8MHz

> Hast Du schon irgendwelche Timer für irgendwas verbraten oder sind noch
> alle Timer frei verfügbar?

Alle timer noch Frei. Für die Messung der Länge der Pulse hab ich den 
16-Bit Timer mit 1 MHz hergenommen.

Für die Checksumme hatte ich mir einfach gedacht Byte1+byte2+byte3=CHK
Läuft ein uint8 einfach über(und fängt bei 0 an) wenn das größer als 255 
wird, oder ist der dann limitiert auf 255?

von Conny G. (conny_g)


Angehängte Dateien:

Lesenswert?

Mit der Prüfsumme zusammen hättest Du die Möglichkeit eine höhere 
Toleranz beim Abstand der Flanken zuzulassen, z.B. +/-10% akzeptierst Du 
als die gewünschte Länge.
Wenn am Ende der CRC stimmt, dann hast Du valide Daten empfangen.
Dann muss Du kein Byte anhängen wg. fehlender fallender Flanke.

Ich habe mir mal einen Empfänger für uralte ELV-Funksensoren einer 
Wetterstation selbst gebaut und dort festgestellt, dass die 
Flankenabstände durch schwächeres Signal sehr stark verzerrt werden - ab 
einem bestimmten Level hast du nur noch mit hoher Toleranz eine Chance.

Das Protokoll gibt's hier:
http://www.dc3yc.privat.t-online.de/protocol.htm

Aber das kombiniert mit einer Prüfsumme und ggf. einer State Machine 
(z.B. Präambel 11111111 > Nibble > Bit 1 > Nibble > Bit 1 > Nibble ... > 
CRC ) gibt Dir die Sicherheit, dass Du echte Daten erkannt hast und Du 
kannst an die Grenzen gehen.
Die State Machine resettet einfach immer, wenn das Empfangene nicht dem 
erwarteten entspricht.
Und durch die Präambel weisst sofort, ob da überhaupt der Anfang von 
Daten im Gange ist. D.h. bei "wirrem" Emfang geht die Statemachine nach 
wenigen Bits sofort wieder auf Anfang und Du verpasst nichts.
Es könnte auch nach jedem Nibble/Byte eine Parität mitkommen, dann 
weisst an der Stelle schon, ob das sein kann.

Ich hänge Dir die Library für den ELV-Empfang und Versand in C einfach 
mal an.

: Bearbeitet durch User
von Conny G. (conny_g)


Lesenswert?

Hat mich grad selber interessiert. Wie man sieht hab ich sehr hohe 
Toleranz eingestellt:

#define BIT_TOLERANCE  12

#define BIT0_TICKS  54     // 53
#define BIT1_TICKS  24     // 23
#define BIT_TICKS   80     // 76

Also bei Bit 1 sogar 50% der Soll-Flankenzeit, bei Bit 0 25%.

von Jürgen S. (jurs)


Lesenswert?

Fabian F. schrieb:
> Mein Sender Empfänger- Paar verschleift mir die Bit-längen ziemlich. Auf
> sender-Seite mache ich 200µs high, dann 200µs low (Für 0) oder 400µs
> high und 200µs low (für 1).
> Auf der Empfängerseite kommt an:
> 0: 115µs high 285µs low
> 1: 205µs high 400µs low

Wie nah beieinander befinden sich Sender und Empfänger dabei?

Falls sich Sender und Empfänger sehr nah beieinander befinden, kann es 
sein, dass der Empfänger aufgrund der hohen Feldstärke total übersteuert 
wird, und Regenerativempfänger verzerren in dem Fall extrem stark. 
Abhilfe wäre dann, den Abstand zwischen Sender und Empfänger zu 
vergrößern, dann sollte die Verzerrung kleiner werden.

> Für die Checksumme hatte ich mir einfach gedacht Byte1+byte2+byte3=CHK
> Läuft ein uint8 einfach über(und fängt bei 0 an) wenn das größer als 255
> wird, oder ist der dann limitiert auf 255?

Kann man machen, das Byte läuft einfach über.
Du bekommst als Prüfsumme dann bei einem uint8 quasi "Summe modulo 256".

Bei 433MHz-Temperatursensoren habe ich schon gesehen, dass für das 
Prüfsummenbyte einfach alle Datenbytes mit "bitweisem XOR" verknüpft 
werden. Ich habe aber keine Ahnung, ob das ggf. gegenüber einer 
Prüfsumme irgendwelche Vorteile bei der Fehlererkennung hat.

von Fabian F. (fabian_f55)


Angehängte Dateien:

Lesenswert?

Jürgen S. schrieb:
> Fabian F. schrieb:
>> Mein Sender Empfänger- Paar verschleift mir die Bit-längen ziemlich. Auf
>> sender-Seite mache ich 200µs high, dann 200µs low (Für 0) oder 400µs
>> high und 200µs low (für 1).
>> Auf der Empfängerseite kommt an:
>> 0: 115µs high 285µs low
>> 1: 205µs high 400µs low
>
> Wie nah beieinander befinden sich Sender und Empfänger dabei?
>
> Falls sich Sender und Empfänger sehr nah beieinander befinden, kann es
> sein, dass der Empfänger aufgrund der hohen Feldstärke total übersteuert
> wird, und Regenerativempfänger verzerren in dem Fall extrem stark.
> Abhilfe wäre dann, den Abstand zwischen Sender und Empfänger zu
> vergrößern, dann sollte die Verzerrung kleiner werden.

Sehr nah. Sind noch auf einem Panel. Ich hab das Funkmodul mal mit einem 
Kabel verbunden und weiter weg bewegt. jetzt sieht es besser aus (s. 
Bild)


> Kann man machen, das Byte läuft einfach über.
> Du bekommst als Prüfsumme dann bei einem uint8 quasi "Summe modulo 256".

Sollte für meine Zwecke reichen. Ob ein Wert plausibel ist erkenne ich 
auch so. Dass sich die Temperatur im Wohnzimmer auf einen Schlag um 30° 
ändert ist unwarscheinlich.

@  Conny G
Dankeschön. Für dieses Projekt ist das vermutlich etwas overkill. Könnte 
ich aber wohl brauchen wenn ich den Türöffner automatisere. Der soll nur 
öffnen wenn wirklich ich vor der Tür stehe :-)

von Jürgen S. (jurs)


Lesenswert?

Fabian F. schrieb:
> Sehr nah. Sind noch auf einem Panel. Ich hab das Funkmodul mal mit einem
> Kabel verbunden und weiter weg bewegt. jetzt sieht es besser aus (s.
> Bild)

Ich habe nochmal in alte Arduino-Codes von mir reingeschaut, in denen es 
um die Decodierung von gekauften Tchibo und anderen 
Funktemperatursensoren geht, und dabei ist mir beim Datenprotokoll im 
Zusammenspiel mit den handelsüblichen Sensoren folgendes aufgefallen:

Die relevanten Informationen werden nicht in den On-Phasen des 
Funksignals gesendet, sondern nur in den Off-Phasen (LOW). Es gibt also 
eine inverse Sendelogik.

D.h. das auszuwertende überlange Startbit nach den Trainingsbits wird 
nicht mit Sender-Ein gesendet, sondern mit umgekehrter Sendelogik als 
Sender-Aus, entsprechend Empfänger-LOW.

Die relevanten Bits der Payload genau so: Ausgewertet wird immer die 
Sendepause als Bitlänge für ein 0-Bit oder 1-Bit, und zwischendrin wird 
kurz gesendet, aber die Sendedauer ist irrelevant für die 
Datenauswertung.

Das dient vermutlich zur Optimierung der Laufzeit beim Batteriebetrieb: 
Wenn man möglichst wenig Sendezeit hat, hält die Batterie um so länger 
durch.

Falls Du also auch für Batteriebetrieb planst, ist vermutlich die 
"umgekehrte Sendelogik" mit Codierung von Startbit, 0-Bit und 1-Bit in 
den Sendepausen (LOW-Zeiten) günstiger als die Auswertung der 
Sendezeiten (HIGH-Zeiten).

Für eine optimale Reichweite benötigen diese billigen Sender/Empfänger 
übrigens eine Antenne. Im einfachsten Fall für gute Reichweite eine 
Lambda/4-Antenne in Form von 17,5 cm Draht, angelötet am "Ant" Lötauge 
des Senders bzw. Empfängers.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.