header.png
Pico PI RC-5 Receiver

Decoder für RC-5 Infrarot Fernbedienungen
Free Cross Platform Development

Downloads

Pico-PI / XIAO-RP2040 IR Fernbedienung Receiver

Für die Boards PICO-PI oder XIAO-RP2040 lässt sich mit wenigen zusätzlichen Bauteilen ein Empfänger für Infrarot Fernbedienungen realisieren. Diese Fernbedienungen verwenden überwiegend den RC-5 Code zur Signalübertragung. Dieser RC-5 Code wird vom Board empfangen und per USB Schnittstelle an den PC übertragen.
Am PC benötigen wir dann noch eine zusätzliche Software z.B. lircwebcontrol die diesen Code entgegen nimmt und die entsprechenden Tastencodes generiert. lircwebcontrol ist dabei kompatibel zu LIRC (das Paket lirc muss nicht installiert werden). Programme wir z.B. KODI lassen sich damit steuern.

Hardware

Hier beschrieben mit dem Board PICO-PI, genau so gut lässt sich das auch mit kompatiblen Boards wie z.B. dem XIAO-RP2040 realisieren.
IR Receiver Stückliste
Menge Beschreibung
1 Pico PI (die einfachste Ausführung reicht)
1 TSOP 31236 (kommt auch mit 3,3V klar)
1 Kondensator z.B. 1µF / 20V
1 Widerstand ca 100 Ohm

Die Auswahl des IR-Receiver hier z.B. TSOP 31236 hängt auch von der Sendefrequenz verwendeten Fernbedienung ab, üblich sind aber 36 kHz. Außerdem sollte der TSOP Baustein auch mit den 3,3V klar kommen, ältere Modelle sind oft nur für 5V ausgelegt. Als Signal Eingang verwende ich hier den PIN 34 (GPIO28) wegen der Nähe zu dem +3,3V Pin.
Anschlussbelegung
Beschreibung PIN-Nr. (Pico PI Platine) PIN-Nr. (XIAO Platine)
GND 38 13
+3,3V 36 12
Signal 34 (GPIO 28) 11 (GPIO 3)

/Software/LircTools/imgPicoPi/IMG_Detail.jpg /Software/LircTools/imgPicoPi/IMG-Komplett.jpg

RC-5 Code

Eine ausführliche Beschreibung des RC-5 Codes findet ihr auf der Web Seite von sprut.de
Wie dort beschrieben ist, besteht ein einzelner RC-5 Code aus genau 14 Datenbits wobei jedes Bit aus einer Kombination von "10" (entsprechend einer "1") oder "01" (entsprechend einer "0") besteht. Ein Infrarotempfänger wir z.B. die TSOP... Serie liefert also am Ausgang eine Sequenz mit 28 Bits.
Von der Seite sprut.de stammen auch diese Grafiken, die das Timing des RC-5 Protokolls sehr anschaulich darstellen.
/Software/LircTools/imgPicoPi/rc5t.gif
/Software/LircTools/imgPicoPi/ir3.gif
/Software/LircTools/imgPicoPi/ir2.gif
RC-5 Code Timing
Beschreibung Zeit in ms
1 von 28 Bits 0,889 ms
1 von 14 Bits 1,778 ms
1 kompletter RC-5 Code 24,889 ms
Codewiederholung wenn die Taste länger gedruckt bleibt 113,778 ms


Software


Pico PI

Das Programm für den IR-Receiver besteht aus wenigen Zeilen und ist in Micropython geschrieben. Für die Übertragung des Programms in den Pico PI könnt ihr ganz einfach die Thonny IDE verwenden. Die ist auf allen Betriebssystemen schnell installiert und reicht für unsere Zwecke völlig aus.
Beschreibungen für die Arbeit mit Thonny und Pico PI gibt es genügend, das muss ich hier nicht noch einmal wiederholen.
Wichtig für Neueinsteiger (wie mich)
Der Pico PI muss zuerst mit der Firmware für MicroPython geladen werden. Dazu ist aber schon Alles in der Thonny IDE enthalten, ihr müsst nichts zusätzlich suchen und herunterladen.
Damit euer Programm auch automatisch startet, wenn das Board angeschlossen wird, muss der Programmname main.py lauten.

MicroPython RC-5 Receiver

Das MicroPython Programm decodiert nicht den RC-5 Code sondern erfasst nur die Datenbits die vom TSOP IR Receiver geliefert werden.
Bei einer fallenden oder steigenden Flanke des Dateneingangssignals (Pin.IRQ_FALLING|Pin.IRQ_RISING ) wird ein Interrupt ausgelöst und die Zeit in µs gemessen (time.ticks_us() ), die seit dem letzten Ereignis vergangen ist. Wir müssen dabei 4 unterschiedliche Zeitspannen berücksichtigen und auch genau in der Reihenfolge abarbeiten.

Ist die vergangene Zeit:
> 2000 µs (länger als 1778 µs)
dann muss eine neue Datensequenz beginnen.
< 800 µs ( genau 889 µs )
das sollte lt. Protokoll nicht vorkommen, allerdings könnten Störungen vorkommen mit unerwarteten Peaks. Dann ist die ganze Übertragungssequenz ungültig und wir verwerfen die bisherigen Bits.
> 1600µs ( genau 1778 µs )
seit dem letzten Ereignis sind 2 Bits mit '00' oder '11' gesendet worden.
> 800 µs
seit dem letzten Ereignis ist 1 Bit gesendet worden.

Hier der MicroPython Code für das Board PICO-PI. Für das Board XIAO muss die Zeile board = PICO_PI geändert werden in board = XIAO
Wer andere GPIO als Eingang des IR-Signals benutzen will muss die Zeile ir_pin = Pin(.... in dem zum Board passenden Abschnitt anpassen.

main.py
# Alle Variablen mit passenden Startwerten initialisieren. 
# Mit time.ticks_us() speichern wir die aktuelle Zeit. 

from machine import Pin import time
# Unterschiedliche Boards benötigen andere Initialwerte # deshalb hier das entsprechende Board definieren PICO_PI = 0 XIAO = 1 board = PICO_PI
# verwendete Variablen count = 0 interrupt_flag=0 state = 1 time_start = time.ticks_us() time_stop = 0 v_string = ""

# Für das Board PICO PI gilt: # An GPIO-28 liegt unser Eingangssignal, # wer einen anderen GPIO bevorzugt muss diesen Wert hier ändern. # An GPIO-25 ist die interne LED des Pico PI Boards angeschlossen. # Darüber realisieren wir das optische Feedback solange # eine Taste der Fernbedienung gedrückt wird. if board == PICO_PI: led_off = 0 led_on = 1 ir_pin = Pin(28,Pin.IN) led = Pin(25, Pin.OUT)
# Für das Board XIAO RP2040 gilt: # An GPIO-3 liegt unser Eingangssignal, # wer einen anderen GPIO bevorzugt muss diesen Wert hier ändern. # An GPIO-16 ist die interne (grüne) LED des Pico PI Boards angeschlossen. # Darüber realisieren wir das optische Feedback solange # eine Taste der Fernbedienung gedrückt wird. elif board == XIAO: led_off = 1 led_on = 0 led_green = Pin(16, Pin.OUT) led_green.value(led_off) led_red = Pin(17, Pin.OUT) led_red.value(led_off) led_blue = Pin(25, Pin.OUT) led_blue.value(led_off)
ir_pin = Pin(3,Pin.IN) led = led_green
# default use PICO PI else: led_off = 0 led_on = 1 ir_pin = Pin(28,Pin.IN) led = Pin(25, Pin.OUT)

led.value(led_off)
# Der ir_handler wird ausgelöst bei einer fallenden oder steigenden Flanke. # Wir messen sofort die aktuelle Zeit um sie anschließend zu vergleichen.
def ir_handler(pin): global interrupt_flag global time_stop time_stop = time.ticks_us() interrupt_flag=1

#Definition des Interrupts
ir_pin.irq(trigger=Pin.IRQ_FALLING|Pin.IRQ_RISING , handler=ir_handler)
# Endlosschleife
while True: if interrupt_flag == 1: # interrupt wurde ausgelöst
interrupt_flag = 0 state = ir_pin.value() if state == 1: led.value(led_on) else: led.value(led_off)
time_diff = time_stop - time_start
# nach mehr als 2000µs und weniger als 800µs # verwerfen wir die bisherigen Bits und starten neu.
if time_diff > 2000: v_string = "" count = 0 elif time_diff < 800: v_string = "" count = 0
# wenn mehr als 1600µs seit dem letzten Ereignis vergangen sind # müssen 2 Bits gesendet worden sein. # Deshalb müssen wir hier noch das 2. Bit registrieren und das # mit negierter Polarität
if time_diff > 1600: count = count + 1 if state == 1: v_string = v_string + "0" else: v_string = v_string + "1"
# wenn mehr als 800µs seit dem letzten Ereignis vergangen sind # müssen wir hier dises Bit registrieren.
if time_diff > 800: count = count + 1 if state == 1: v_string = v_string + "1" else: v_string = v_string + "0"
time_start = time_stop
# nach genau 28 empfangenen Bits senden wir das Ergebnis zum PC
if count == 28: # print("IR =",v_string,count) led.value(led_off); print(v_string) v_string = ""
# Wenn das letzte Bit eine '0' ist, # dann kommt anschließend noch ein Wechsel zur '1', # weil '1' die "Ruhestellung" des IR-Empfängers ist. # Das wäre dann (für dieses Programm) das 29. Bit
if count > 27: led.value(led_off) #\n