Blog der Heimetli Software AG

Decoder für das Prologue-Protokoll auf Raspberry Pico

Der Raspberry Pico ist kein Raspberry im üblichen Sinne, sondern nur ein Mikrokontroller. Das Board hat einige GPIOs, AD-Wandler, und einen USB-Anschluss.

Um ihn zu testen habe ich einen Decoder für einen Temperatursensor geschrieben. Der Sensor hängt schon fast fünf Jahre draussen und liefert zuverlässig die aktuelle Temperatur. Obwohl der Aufbau HF-mässig jeder Regel widerspricht, gelingt der Empfang des Signals ohne Probleme.

Raspberry Pico mit 433 Mhz Receiver

Python auf dem Raspberry Pico

Normalerweise werden Embedded-Projekte mit C oder C++ realisiert. Weil ich zum Test MicroPython auf den Pico geflasht hatte, wollte ich ausprobieren ob der Interpreter schnell genug ist um dieses Signal zu decodieren. Ergebnis: funktioniert tadellos.

Den Interpreter zu installieren könnte nicht einfacher sein: das Image auf den PC laden, einen Taster auf dem Pico drücken, und das Image auf den Pico kopieren.

Nach einem Reboot meldet sich der Pico beim PC als serielle Schnittstelle an. Mit einem Terminalemulator kommt man direkt auf den Prompt des Python-Interpreters.

Sobald die ersten kleinen Testprogramme gelaufen sind braucht es aber doch einen vernünftigen Editor. Mit CTRL-E wird der Interpreter in einen Download-Mode geschaltet, so dass der Code unverändert im Pico landet. Mit CTRL-D kommt man zurück in den REPL-Modus wo das Programm läuft.

Der 433 Mhz Empfänger

Das Vellemann-Modul vom Arduino Projekt habe ich nirgends mehr gefunden. Anscheinend wird er nicht mehr produziert. Als Ersatz habe ich einen AM-RX12E-433P von rfsolutions genommen der gute Resultate liefert.

Das Python-Programm

""" Decodes the prologue protocol used by wireless sensors

Setup:
    GPIO 16 is connected to tho output pin of an AM-RX12E-433P

    The output is sent to the serial port on USB.
"""

import time
from machine import Pin

p = Pin( 16, Pin.IN )

def check_pulse():
    """ Verifies that the pin is high for 450 to 650us """

    ts = time.ticks_us()

    while p.value() == 1:
        pass

    te = time.ticks_us()

    return 450 < te - ts < 650


def measure():
    """ Determines how long the pin is low """
    ts = time.ticks_us()

    while p.value() == 0:
        pass

    te = time.ticks_us()

    return te - ts


def prologue():
    """ Decodes the signal and writes the value to the USB port """
    while True:
        # Look for the start of a transmission
        duration = measure()
        if duration < 8500 or duration > 9500:
            continue

        # Check the pulse length
        if not check_pulse():
            continue

        signal  = 0
        counter = 0
        while counter < 32:
            # Read next bit of signal
            duration = measure()

            if 1500 < duration < 2500:
                signal = signal << 1
            elif 3500 < duration < 4500:
                signal = (signal << 1) | 1
            else:
                break

            if not check_pulse():
                break

            counter += 1

        # Print if we have all the bits
        if counter == 32:
            print( hex(signal) )


# Start the decoder
prologue()

Auf 433Mhz gibt es sehr viele Störsignale, und deshalb wird jedes Bit genau geprüft um sicherzustellen dass der empfangene Wert auch tatsächlich vom Sensor stammt.

Das Signal beginnt mit einem Low-Pegel von ca. 900ms, gefolgt von einem High-Pegel von ca. 500ms. Dahinter kommen 36 Bit, codiert mit langen und kurzen Low-Pegeln. Der obige Code ignoriert die letzten 4 Bits weil die keine nützlichen Informationen enthalten.

Wenn eine Sequenz von 32 gültigen Bits empfangen wurde, schickt der Code sie als Hex-Zahl über die Schnittstelle zum PC. Eine Checksumme oder etwas ähnliches gibt es nicht, die Uebertragung kann also nicht verifiziert werden. Bei mir war das kein Problem, aber wenn nötig kann man weitere Check für den Wert des ersten Nibbles und die ID des Sensors einbauen.

Auspacken des Messwerts auf dem PC

Das Programm auf dem Pico schickt die empfangene Sequenz als Hex-Zahl an den PC. Um den Messwert auszulesen läuft dort ebenfalls ein Python-Script:

from datetime import datetime, timedelta

seconds = datetime.now()
ds      = timedelta( seconds=20 )

with open( "/dev/ttyACM0" ) as input:
    for line in input:
        now = datetime.now()

        # Suppress repeated signals
        if now > seconds + ds:
            seconds = now

            value   = int( line, 16 )
            temp    = (value >> 4) & 0xFFF

            if temp > 2047:
                temp = temp - 4096

            temperature = temp / 10

            with open("temperature.csv","a") as logfile:
                logfile.write( "{},{:.1f}\n".format(now.strftime("%d.%m.%Y %H:%M:%S"),temperature) )

Das Script liest die Hex-Zahl von der Schnittstelle, extrahiert den Messwert, und schreibt ihn zusammen mit einem Timestamp in ein CSV-File.

Weil der Sensor die Meldung gleich mehrfach schickt, wird alle 20 Sekunden eine neue Messung akzeptiert.

Autostart auf dem Pico

Für den Autostart wird das Script als main.py auf dem Pico abgelegt. Das habe ich mit rshell für Python erledigt.

Auf meinem Linux wollte rshell zuerst nicht starteni, weil eine Library fehlt. Erst als ich die rshell mit USE_AUTOCONNECT = False gepatcht hatte lief sie ohne zu meckern.

Mit genau der gleichen Hardware kann auch der Auriol AFT 77 B2 decodiert werden.