Blog der Heimetli Software AG

Decoder für das Nexus-Protokoll auf Raspberry Pico

Gleiche Firma, gleiche Marke, anderes Protokoll des Funksenders...

Bei Lidl gab es wieder eine Wetterstation mit Funksender. Diesmal eine die die Luftfeuchtigkeit misst. Als Marke stand wiederum Auriol auf der Packung, und der Sensor sieht fast genau gleich aus.

Sensor HG07890A-DCF-TX sold by Lidl

Ein Versuch mit dem Auriol-Decoder war erfolglos, er konnte das Signal nicht empfangen. rtl_433 erkannte ein Nexus-Protokoll. Das ist im Sourcefile im Repository recht genau beschrieben, was es einfach machte einen Decoder in MicroPython zu schreiben.

Der Decoder läuft auf einem Raspberry Pico mit einem Empfängermodul für 433MHz. Der Aufbau ist hier beschrieben.

Das Script

""" Decodes the nexus 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( ts ):
    """ Verifies that the pin is high for 450 to 650us """

    while p.value() == 1:
        pass

    te = time.ticks_us()

    return 450 < te - ts < 650, te


def measure( ts ):
    """ Determines how long the pin is low """

    while p.value() == 0:
        pass

    te = time.ticks_us()

    return te - ts, te


def nexus():
    """ Decodes the signal and writes the value to the USB port """
    while True:
        # Look for the start of a transmission
        duration, te = measure( time.ticks_us() )
        if duration < 3500 or duration > 4500:
            continue

        # Check the pulse length
        valid, te = check_pulse( te )

        if not valid:
            continue

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

            if 700 < duration < 1300:
                signal = signal << 1
            elif 1700 < duration < 2300:
                signal = (signal << 1) | 1
            else:
                break

            valid, te = check_pulse( te )

            if not valid:
                break

            counter += 1

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


# Start the decoder
nexus()

Auch hier war es nötig die Zeit der letzten Flanke durch das ganze Script zu ziehen weil das Timing sonst aus dem Ruder lief.

Die Erkennung des Signals ist erstaunlich zuverlässig, auch wenn es keinerlei Verifikation des gesendeten Codes enthält. Ein Nibble enthält immer 0x0F, das könnte einen Anhaltspunkt geben ob das Signal tatsächlich von diesem Sensor stammt.

Die Messwerte auspacken und Loggen

Das obige Script schreibt eine Hex-Zahl auf die Konsole. Die Konsole ist in diesem Fall eine serielle Verbindung über USB.

Auf der PC-Seite läuft ein zweites Python-Script das die Werte auspackt und loggt.

Weil der Sensor die Messwerte mehrfach hintereinander sendet, werden nur Signale mit mindestens 20 Sekunden Abstand geloggt.

from datetime import datetime, timedelta

def log_weather( line ):
    value    = int( line, 16 )
    humidity = value & 0xFF
    raw      = (value >> 12) & 0xFFF

    if raw > 2047:
        raw = -raw

    temperature = float(raw) / 10

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

last     = datetime.now()
delta    = timedelta( seconds=20 )
previous = ""

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

        if (line != previous) or (now > last + delta):
            log_weather( line )
            previous = line
            last     = now