Forum: Mikrocontroller und Digitale Elektronik Raspberry Pi 3 433MHz ELRO


von Joe (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich habe einen Raspberry Pi 3 und ein Funksteckdosen Set von ELRO.
Mit einem 433MHz Set wollte ich den Code zum Ansteuern der 
Funksteckdosen auslesen und dann später in einer GUI in Python anwenden. 
Dafür würde ich Buttons definieren, jeweils für "AN" und "AUS".

Mithilfe des RFSniffers von https://github.com/ninjablocks/433Utils 
konnte ich den Code meiner Funkfernbedienung erfolgreich auslesen.

Kanal B ON →  1118545
Kanal B OFF → 1118548

Das sind, soweit ich verstanden habe, die Codes als Dezimalzahl.
In der Library RCSwitch.cpp von https://github.com/ninjablocks/433Utils 
ist die Rede von 0-bit, 1-bit und Sync-bit. Im Datenblatt des ICs 
PT2262/PT2272 ist allerdings auch von einem Floating-bit die Rede.

Die Informationen von Beitrag "Funksteckdose Elro schalten mit RFM12" 
Beitrag am 17.02.2017 12:06 besagen, dass für "Ein" bzw. "Aus" die Bits 
"FF" bzw. "00" gesendet werden müssen.

Braucht man nun die "Floating-bits" oder nicht, denn in der Library von 
github ist von Floating-bits niemals die Rede.

Was ich bisher auch verstanden habe, ist, dass die Länge für einen Puls 
350µsec sein muss.

Ich habe jetzt aber keinerlei Plan, wie ich den oben ausgelesen 
Dezimalcode nun in meinem eigen Python-GUI-Programm anwenden kann.
Kann mir bitte jemand helfen?

Danke!

von Joachim S. (oyo)


Lesenswert?

Joe schrieb:
> Mithilfe des RFSniffers von https://github.com/ninjablocks/433Utils
> konnte ich den Code meiner Funkfernbedienung erfolgreich auslesen.
>
> Kanal B ON →  1118545
> Kanal B OFF → 1118548
>
> Das sind, soweit ich verstanden habe, die Codes als Dezimalzahl.
> In der Library RCSwitch.cpp von https://github.com/ninjablocks/433Utils
> ist die Rede von 0-bit, 1-bit und Sync-bit. Im Datenblatt des ICs
> PT2262/PT2272 ist allerdings auch von einem Floating-bit die Rede.

Korrekt, die bei diesen Funksteckdosen verwendeten Encoder/Decoder 
benutzen Datenworte/Adressen, bei denen die einzelnen "Bits" drei 
Zustände haben können: 0, 1 und "f"(loating). Und eine komplette 
Nachricht bzw. ein gesendeter Befehl besteht immer aus exakt 12 dieser 
"Bits". Und obwohl es theoretisch auch anders genutzt werden kann, 
benutzen Funksteckdosen fast immer folgendes Format:
10 "Bits" Adresse + 2 "Bits" für den eigentlichen Befehl (also: an- oder 
ausschalten)
Die obige Darstellung als 7-stellige Dezimalzahl ist da schon 
ungewöhnlich, und meiner Meinung nach: Zumindest wenn es um 
Funksteckdosen mit diesem Protokoll geht eher verwirrend als sonstwas.

> Die Informationen von Beitrag "Funksteckdose Elro schalten mit RFM12"
> Beitrag am 17.02.2017 12:06 besagen, dass für "Ein" bzw. "Aus" die Bits
> "FF" bzw. "00" gesendet werden müssen.

Wenn ich mich richtig erinnere, ist es zumindest bei den von Dir 
benutzten ELRO 440 streng genommen so:
wenn das letzte "Bit" "0" ist, wird die Steckdose ausgeschaltet; bei 
allen anderen Kombination wird die Steckdose eingeschaltet. Also...
Einschalten: 01/0f/11/1f/f1/ff
Ausschalten: 00/10/f0

> Braucht man nun die "Floating-bits" oder nicht, denn in der Library von
> github ist von Floating-bits niemals die Rede.

Ein klares "Jain". ;-)

"Ja", weil: Wenn Du Dich an das im Datenblatt des PT22x2 dokumentierte 
Protokoll hältst, mit 12 "dreiwertigen Bits", dann benötigst Du zwingend 
diese "floating-Bits".
Im Falle der von Dir als Foto hochgeladenen Steckdose wäre das übrigens 
so:
Adresse = 0f0f0-f0fff (-> DIP-Schalter von links nach rechts lesen; 
"oben" bzw. "on" = "0"-Bit, "unten" bzw. "off" = "f"-Bit)

Kommando zum Einschalten: 0f0f0-f0fff-(01|0f|11|1f|f1|ff)
Kommando zum Ausschalten: 0f0f0-f0fff-(00|10|f0)

"Nein", weil: Man kann die von den PT22x2-Encoder/Decodern verwendeten 
Adressen auch anders darstellen als als 12-stellige Datenworte mit 
"dreiwertigen Bits", nämlich als 24-stellige Datenworte mit normalen, 
"zweiwertigen Bits"; dazu einfach jedes "dreiwertige Bit" 
folgendermassen ersetzen:
"0" -> "00"
"1" -> "11"
"f" -> "01"
Obige Funksteckdose hätte in der 24-stelligen-Darstellung also folgende 
Adresse:
"0001000100-0100010101"

> Was ich bisher auch verstanden habe, ist, dass die Länge für einen Puls
> 350µsec sein muss.

Ungefähr, ja. Ich persönlich benutze 360µs, aber mit 400µs funktioniert 
es bei mir genauso gut. Absolut kritisch sind die Werte nicht, 350-400µs 
funktionieren meiner Erfahrung nach bei fast jeder Funksteckdose.

> Ich habe jetzt aber keinerlei Plan, wie ich den oben ausgelesen
> Dezimalcode nun in meinem eigen Python-GUI-Programm anwenden kann.
> Kann mir bitte jemand helfen?

Vergiss den Dezimalcode oder das umständliche Auslesen der Codes per 
433MHz-Sniffer; mit der obigen Anleitung kannst Du die Adresse und die 
Codes zum Ansteuern ganz einfach von den DIP-Schaltern ablesen.

Derartige Codes mit "dreiwertigen Bits" kannst Du dann z.B. mit 
folgenden Funktionen verwenden:
1
def xx1527_code_to_timings(xx1527_code, number_of_repetitions = None, cycle_time = None):
2
  number_of_repetitions = number_of_repetitions or 6
3
  cycle_time = cycle_time or 360
4
  sync_bit_cycles = [1, 31]
5
  data_bit_cycles = {'0':[1, 3], '1':[3, 1]}
6
  code_word_cycles = [number_of_cycles for xx1527_code_bit in xx1527_code if xx1527_code_bit in  ['0', '1'] for number_of_cycles in data_bit_cycles[xx1527_code_bit]]
7
  frame_cycles = code_word_cycles + sync_bit_cycles
8
  frame_timings = [(number_of_cycles * cycle_time) for number_of_cycles in frame_cycles]
9
  repeated_frame_timings = frame_timings * number_of_repetitions
10
  return repeated_frame_timings
11
12
def xxx2x2_code_to_xx1527_code(xxx2x2_code):
13
  return ''.join({'0':'00', '1':'11', 'f':'01'}[xxx2x2_bit] for xxx2x2_bit in xxx2x2_code.lower() if xxx2x2_bit in ['0', '1', 'f'])
14
15
def xxx2x2_code_to_timings(xxx2x2_code, number_of_repetitions = None, cycle_time = None):
16
  return xx1527_code_to_timings(xxx2x2_code_to_xx1527_code(xxx2x2_code), number_of_repetitions, cycle_time)
17
18
def xxx2x2_turn_on_code(xxx2x2_address):
19
  return xxx2x2_address + '11'
20
21
def xxx2x2_turn_off_code(xxx2x2_address):
22
  return xxx2x2_address + '00'
23
24
def xxx2x2_turn_on_timings(xxx2x2_address, number_of_repetitions = None, cycle_time = None):
25
  return xxx2x2_code_to_timings(xxx2x2_turn_on_code(xxx2x2_address), number_of_repetitions, cycle_time)
26
27
def xxx2x2_turn_off_timings(xxx2x2_address, number_of_repetitions = None, cycle_time = None):
28
  return xxx2x2_code_to_timings(xxx2x2_turn_off_code(xxx2x2_address), number_of_repetitions, cycle_time)

xxx2x2_turn_on_timings('0f0f0-f0fff') würde Dir dann z.B. die Timings 
zurückgeben, um Dir von Dir abgebildete Funksteckdose einzuschalten:
1
print(xxx2x2_turn_on_timings('0f0f0-f0fff', number_of_repetitions=1))
2
[360, 1080, 360, 1080, 360, 1080, 1080, 360, 360, 1080, 360, 1080, 360, 1080, 1080, 360, 360, 1080, 360, 1080, 360, 1080, 1080, 360, 360, 1080, 360, 1080, 360, 1080, 1080, 360, 360, 1080, 1080, 360, 360, 1080, 1080, 360, 360, 1080, 1080, 360, 360, 1080, 1080, 360, 360, 11160]
Die Zahlen in diesem Array bedeuten: Zuerst den 433MHz-ASK-Sender für 
360µs einschalten (bzw. den entsprechenden GPIO-Pin auf high setzen), 
dann den 433MHz-Sender für 1080µs ausschalten (bzw. den entsprechenden 
GPIO-Pin auf low setzen) usw. usw.
Zu beachten ist, dass ich in obigem Beispiel die number_of_repetitions 
auf 1 gesetzt habe; damit eine Funksteckdose tatsächlich schaltet, 
müssen die Daten aber mehrfach hintereinander gesendet werden; ich mache 
das standardmässig 6-mal.

: Bearbeitet durch User
von Joe (Gast)


Lesenswert?

Nabend Joachim S.n

vielen Dank für Deine ausführliche Antwort. Ich habe es jetzt mit Hilfe 
eines Arduino-Tutorials hinbekommen die STeckdose mit dem Arduino 
anzusteuern.
Das geht bei mir mit dem Code "101010010010" (ON) und "101010010001" 
(OFF).Also, wenn ich Dich richtig verstanden habe sind die Bits "F" 
trotzdem noch quasi mit drin in meinem zweiwertigen Code?

Finde es echt beeindruckend, wie Du Python beherrschst. Ich kann grad 
mal Funktionsdefinitionen, bisschen "import" und bisschen GUI mit 
Tkinter. Aber die elementaren Dinge wie Schleifen, Funktionsaufrüfe etc. 
kann ich noch nicht.
Bin auch vorhin daran kläglich gescheitert den einfachen Arduino-Code 
für den Pi nach Python zu portieren.

Mein Arduino-Code:
1
void setup() 
2
{
3
  // put your setup code here, to run once:
4
  pinMode(2, OUTPUT);
5
6
}
7
8
void wait(int tTime)
9
{
10
  delayMicroseconds(tTime*350);
11
}
12
13
14
void sendByte(char i)
15
{
16
  switch(i)
17
  {
18
    case '0':
19
      digitalWrite(2, HIGH);
20
      wait(1);
21
      digitalWrite(2, LOW);
22
      wait(3);
23
      digitalWrite(2, HIGH);
24
      wait(3);
25
      digitalWrite(2, LOW);
26
      wait(1);
27
      break;
28
    case '1':
29
      digitalWrite(2, HIGH);
30
      wait(1);
31
      digitalWrite(2, LOW);
32
      wait(3);
33
      digitalWrite(2, HIGH);
34
      wait(1);
35
      digitalWrite(2, LOW);
36
      wait(3);
37
      break;
38
   case 'x':
39
      digitalWrite(2, HIGH);
40
      wait(1);
41
      digitalWrite(2, LOW);
42
      wait(31);
43
      break;   
44
  }
45
  
46
}
47
48
49
boolean sendCode(char code[])
50
{
51
  for(short z = 0; z <5; z++)
52
  {
53
    for(short i = 0; i <12; i++)
54
    {
55
      sendByte(code[i]);
56
    }
57
58
    sendByte('x');
59
  }
60
  return true;
61
}
62
63
64
65
void loop() 
66
{
67
  // put your main code here, to run repeatedly:
68
  sendCode("101010010010");
69
  delay(5000);
70
  sendCode("101010010001");
71
  delay(5000);
72
73
}

Werde es jetzt vielleicht erstmal so machen, dass ich den Arduino für 
einen einfachen ATmega protiere und dann über I2C diesen mit dem Pi 
verbinde und dann über die GUI Befehle per I2C an den ATmega raushaue 
und der dann über Funk mit den Steckdosen kommuniziert.
Falls DU allerdings meinen Code direkt in Python übersetzen kannst, wäre 
natürlich super.
Habe es statt mit einem String als Code auch mit der Binärdarstellung 
0b01010... probiert. Aber das funktioniert auch nicht.


Besten Dank vorab für jede Hilfe!

Joe

von Joachim S. (oyo)


Lesenswert?

Joe schrieb:
> Nabend Joachim S.n
>
> vielen Dank für Deine ausführliche Antwort. Ich habe es jetzt mit Hilfe
> eines Arduino-Tutorials hinbekommen die STeckdose mit dem Arduino
> anzusteuern.
> Das geht bei mir mit dem Code "101010010010" (ON) und "101010010001"
> (OFF).Also, wenn ich Dich richtig verstanden habe sind die Bits "F"
> trotzdem noch quasi mit drin in meinem zweiwertigen Code?

Ja, die "F"-Bits sind da tatsächlich noch mit drin, aus einem ganz 
einfachen Grund:
Was bei Deinem Arduino-Code ein "1"-Bit ist, ist in Wahrheit ein 
"0"-Bit, und was bei Deinem Code ein "0"-Bit ist, ist in Wahrheit ein 
"F"-Bit.

Sprich: Wenn Du in Deinem Arduino-Code
1
sendCode("101010010010");
aufrufst, um die Steckdose einzuschalten, dann sendest Du in Wahrheit 
das PT22x2-Codeword "0f0f0-ff0ff-0f", und wenn Du
1
sendCode("101010010010");
aufrufst, um die Steckdose auszuschalten, dann sendest Du in Wahrheit 
das PT22x2-Codeword "0f0f0-ff0ff-f0".

Somit ist das, was Du da tust, tatsächlich völlig in Linie mit dem, was 
ich oben geschrieben habe, mit einer kleinen Ausnahme:
Mit Deinem Beispiel-Code steuerst Du nicht die im von Dir 
hochgeladenen Foto abgebildete Steckdose (die laut Foto auf "Kanal B" 
eingestellt ist) - sondern eine andere Steckdose, die offensichtlich auf 
"Kanal C" eingestellt ist.

Ich weiss nicht, wo Du obigen Arduino-Code her hast, aber ich finde ihn 
nicht sonderlich sinnvoll. Von den in Wahrheit 3^12=531441 möglichen 
"Codewords" bzw. Kombinationen, die eigentlich von den PT22x2-ICs 
gesendet/empfangen werden können, kann dieser Code nur 2^12=4096 
Kombinationen senden. Und dass da "0"-Bits in Wahrheit "f"-Bits, und 
"1"-Bits in Wahrheit "0"-Bits sind, finde ich persönlich jetzt auch eher 
verwirrend - auch wenn ich den Sinn dahinter vermute zu erahnen:
Auf diese Weise kann man die Adresse noch leichter von den DIP-Schaltern 
der ELRO 440-Steckdosen ablesen, weil dann "on" = "1" und "off" = "0" 
ist, statt wie in Wahrheit "on" = "0" und "off" = "f".

> Falls DU allerdings meinen Code direkt in Python übersetzen kannst, wäre
> natürlich super.

Das kann ich schon deshalb nicht, weil ich nicht weiss, was für eine 
Python-Library oder was weiss ich Du benutzt, um
1. GPIO-Pins zu schalten
2. Mikrosekunden-Delays auszuführen

Aber einfach mal angenommen, Du hättest zwei Funktionen:
Eine digitalWrite-ähnliche Funktion
set_gpio_pin(gpio_pin_id, high)
die als Argumente die ID eines GPIO pins, sowie mit "high" einen 
Boolean-Wert erwartet (mit True = Pin auf high setzen), sowie eine 
Funktion
delay_microeconds(number_of_microseconds)
die halt eine bestimmte Mikrosekunden-Delay ausführt, dann sollte 
folgender Code in Verbindung mit den weiter oben von mir geposteten 
Funktionen funktionieren:
1
def send_433mhz_signal(timings):
2
  for index, number_of_microseconds in timings:
3
    set_gpio_pin(gpio_pin_433mhz_sender, (index % 2 == 0))
4
    delay_microseconds(number_of_microseconds)
5
  set_gpio_pin(gpio_pin_433mhz_sender, False)
6
7
def send xxx2x2_code(xxx2x2_code):
8
  send_433mhz_signal(xxx2x2_code_to_timings(xxx2x2_code))

Das wäre meiner Meinung nach der sinnvollste Weg:
Du hast eine universelle Funktion send_433mhz_signal(), die ein 
absolut beliebiges 433MHz-ASK/OOK-Signal senden kann, das ihm als 
Array von Mikrosekunden-Timings übergeben wird, und dann hast Du 
Funktionen wie xxx2x2_code_to_timings(), die ein gültiges 
PT22x2-Codeword wie "0f0f0ff0ff0f" in ein derartiges Array von Timings 
umwandeln.
Und der Bequemlichkeit halber hast Du noch eine 
send_xxx2x2_code()-Funktion, die beides auf einen Schlag erledigt.

Allerdings ist die grosse Frage hierbei, ob es überhaupt so eine 
ausreichend genaue Funktion a la "delay_microseconds()" gibt, die Du in 
Deinem Python-Code verwenden kannst. Falls nicht, kannst Du den 
Gedanken, das komplett in Python zu erledigen, eh vergessen. In C geht 
es auf dem Raspberry Pi, das weiss ich aus persönlicher Erfahrung, in 
Python habe ich es noch nicht selbst ausprobiert.

von Joe (Gast)


Lesenswert?

Hi,

danke für Deine Anmerkungen. Werde das auf jeden Fall im Hinterkopf 
behalten mit den "0"en und "F"s.
Die einzigen zwei Herausforderungen, den obigen Arduino-Code in 
Python-Code zu transkodieren, ist, einen String wie z.B. "101010010010" 
in Python an die zu senden Funktion zu übergeben. Ich habe beim Versuch, 
die Sende-Funktion mit einem String-Objekt als Parameter aufzurufen, 
immer den Fehler erhalten, dass das String-Objekt "not collable" ist 
oder so ähnlich.

Das zweite Problem wird das Timing von Mikrosekunden mit Python sein. In 
C würde da einfach die wirignPi-Library nehmen oder mir mal selbst ne 
Library schreiben.


Der Hintergrund, warum ich überhaupt erst Python nehmen will, ist, dass 
ich mit Python auch gleichzeitig sehr einfach grafische 
Bedienoberflächen erzeugen kann.

Hast Du schonmal in einem Projekt mit den Funktsteckdosen eine grafische 
Bedienoberfläche programmiert? Welche Sprache hast Du dabei eingesetzt?

von Joachim S. (oyo)


Lesenswert?

Joe schrieb:
> Die einzigen zwei Herausforderungen, den obigen Arduino-Code in
> Python-Code zu transkodieren, ist, einen String wie z.B. "101010010010"
> in Python an die zu senden Funktion zu übergeben. Ich habe beim Versuch,
> die Sende-Funktion mit einem String-Objekt als Parameter aufzurufen,
> immer den Fehler erhalten, dass das String-Objekt "not collable" ist
> oder so ähnlich.

Ohne Deinen entsprechenden Code zu sehen, kann ich dazu natürlich wenig 
sagen. Ausser vielleicht, dass "not callable" möglicherweise darauf 
hindeutet, dass in Deinem Code irgendetwas a la string_object(...) 
steht; die Fehlermeldung klingt so, als ob Du einen String wie eine 
Funktion bzw. als Funktionsaufruf behandeln willst.

> Das zweite Problem wird das Timing von Mikrosekunden mit Python sein. In
> C würde da einfach die wirignPi-Library nehmen oder mir mal selbst ne
> Library schreiben.

Das sehe ich wie gesagt auch das potentiell grösste Problem. In C mit 
wiringPi ist das kein Problem, das weiss ich aus eigener Erfahrung, aber 
ob es auf dem Raspberry Pi auch für Python ausreichend genaue Funktionen 
für Mikrosekunden-Delays gibt, ist mir derzeit schlicht unbekannt.

> Der Hintergrund, warum ich überhaupt erst Python nehmen will, ist, dass
> ich mit Python auch gleichzeitig sehr einfach grafische
> Bedienoberflächen erzeugen kann.
>
> Hast Du schonmal in einem Projekt mit den Funktsteckdosen eine grafische
> Bedienoberfläche programmiert? Welche Sprache hast Du dabei eingesetzt?

Jain; meine eigenen Funksteckdosen (und andere 433MHz-Geräte) steuere 
ich bislang über eine grafische Oberfläche auf Web-/HTML-Basis, also 
letztlich über den Webbrowser, damit ich von jedem internetfähigen Gerät 
aus auf die Benutzeroberfläche zugreifen bzw. die Geräte steuern kann. 
Als Sprache benutze ich eben falls Python, als Web-Framework benutze ich 
"Django".
Über diese Oberfläche steuere ich auch nicht nur Funksteckdosen, sondern 
auch andere 433MHz-Geräte mit anderen 433MHz-Protokollen (z.B. Rolladen- 
bzw. Jalousiemotor) sowie per Infrarot steuerbare Geräte.

Allerdings steuert mein Python-Code eh nicht direkt einen an irgendeinem 
GPIO-Pin hängenden 433MHz-Sender.
Stattdessen habe ich vor einigen Jahren einen eigenständigen, 
universellen 433MHz-Sender namens "ConnAir" gekauft, den ich immer noch 
benutze. Das ist ein kleines Kästchen mit LAN-Anschluss und eigener 
IP-Adresse; wenn der irgendein 433MHz-Signal senden soll (in der Regel 
halt, um eine Funksteckdose zu steuern), dann schickt man ein 
UDP-Datagram an dessen IP-Adresse, das die Beschreibung eines beliebigen 
433MHz-Signals als Array von Mikrosekunden-Timings enthält - ziemlich 
exakt so, wie es die von mir im ersten Posting geposteten Funktionen 
liefern.

Anyway: Wenn Du es in Python mangels passender Libraries oder was auch 
immer nicht gescheit hinbekommst, dann lagere die eigentliche 
Funktionalität zum Senden der 433MHz-Signale halt wirklich einfach aus - 
entweder in eigenständige Hardware auf Arduino/Mikrocontroller-Basis, 
mit der Du dann halt per I2C, USB oder wie auch immer kommunizierst, so, 
wie Du das ja bereits überlegst.
Oder aber, Du schreibst halt z.B. mit wiringPi in C ein kleines 
Kommandozeilentool, dem man als Parameter die zu sendenden Daten 
übergibt, und das dann ein entsprechendes 433MHz-Signal sendet; und in 
Deiner Python-App mit GUI rufst Du einfach dieses in C geschriebene 
Kommandozeilen-Tool auf, wann immer Du etwas per 433MHz senden möchtest, 
statt die 433MHz-Sender-Hardware direkt in Python anzusprechen.

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.