OXOBUTTON LoRaWAN

Mit IoT Push Buttons ist es möglich, ereignisbezogene Mitteilungen zu erzeugen und an einen Server resp. eine abgesetzte Anwendung zu senden. Es sind zahlreiche Varianten am Markt verfügbar, die unterschiedliche Technologien für die drahtlose Kommunikation verwenden.

In meinem Beitrag zu batteriebetriebenen IoT-Knoten habe ich den Strombedarf solcher Knoten mit WiFi- und LoRaWAN-Konnektivität untersucht und es zeigt sich erwartungsgemäss, dass LoRaWAN für den Batteriebetrieb zu bevorzugen ist. Der Einsatz von eInk-Displays ermöglicht Zustandsanzeigen, die für den Betrieb eines abgesetzten IoT-Knoten wichtig und hilfreich sind. Die Leistungsbilanz wird praktisch nicht verschlechtert .

Der OXOBUTTON für LoRaWAN der Schweizer Oxon AG bietet einen sehr guten Featuremix in ein ansprechendes Gehäuse verpackt. Das Datenblatt enthält alle erforderlichen Angaben, weshalb ich hier nur auf die für mich interessanten Features eingehen möchte:

  • LoRaWAN Interface zum Upload von Ereignisdaten (Button pressed, Timer Event etc.)
    und Device Configuration im Download
  • BLE Interface zu Device Configuration, Picture & Text Transfer, Firmware Over the Air (FOTA)
  • Mikrocontroller STM32L082 (32 MHz Clock), Strombedarf im Standby < 3 uA
  • Spannungsversorgung 3 V CR2032 Batterie (220 mAh)

Bei Batteriebetrieb ist der Strombedarf des IoT-Knotens immer ein Thema. Die beeinflussenden Grössen sind im Blogpost beschrieben und können durch entsprechende Konfiguration des Knotens berücksichtigt werden.

Knopfzellen, wie die hier verwendete CR2032 sind wegen ihrer kompakten Bauform in elektronischen Geräten stark verbreitet, weisen aber herstellerabhängig unterschiedliche Innenwiderstände auf, die für die beim Senden auftretenden Stromspitzen nicht immer unproblematisch sind. Der Batterieentwicklung ist es gelungen, den Innenwiderstand zu senken und die Kapazität zu erhöhen. CR2032 und CR2450 werden in diesem Blogpost auf ihre Eignung zur Versorgung von LoRaWAN-Knoten hin untersucht und kommt zu dem folgenden Ergebnis:

Gibt es CR2032- und CR2450-Knopfzellenbatterien, die den Strombedarf eines IoT-Geräts für LoRaWAN decken können? Nun, das hängt von der gewählten Batterie ab und davon, ob Sie den aktiven Spitzenstrom bei ungefähr 40 mA halten können. Einige Batterien werden damit fertig und andere arbeiten sehr schlecht. Unsere Ergebnisse zeigen, dass die Leistung von Knopfzellen zwischen den Herstellern sehr unterschiedlich ist, insbesondere bei höheren Strömen.

qoitech.com/blog/will-my-lorawan-iot-device-work-with-a-coin-cell-battery/

Vom Hersteller des OXOBUTTONs wurden Batterien unterschiedlicher Hersteller getestet und das zitierte Ergebnis bestätigt. Empfohlen wird der Einsatz der CR2032 von Energizer und Murata, wenn entsprechende Batterielaufzeiten gesichert werden sollen.

Laufzeittests

Aus den oben geschilderten Gründen habe ich den OXOBUTTON in meine Laufzeittest mit aufgenommen. Unter http://www.ckuehnel.ch/OxoButton LoRaWAN.html können die übermittelten Daten für den Batterielevel und die Temperatur erfolgt werden. Die Laufzeitmessung ist am 4.03.2021 18:00 begonnen worden.

Inbetriebnahme und Konfiguration

Inbetriebnahme und Konfiguration sind auf den Hersteller-Seite detailliert beschrieben.

Folgen Sie am Einfachsten den beiden Links:

Neben der Konfiguration über den Oxon Device Manager kann der OxoButton auch über Download Messages konfiguriert werden. Hierzu stellt Oxon ein Python Script zur Verfügung, welches die erforderlichen Messages zusammenstellt.

Im Beispiel hier werden die LoRa- und die Peripherie-Parameter eingestellt.

# Configurations
lora_params = True
lora_adr = True                 # ADR enable/disable
lora_dr = 2                     # 0... 5
lora_send_trials = 3            # 1... 10
lora_join_trials = 1            # 1... 3
lora_port = 1                   # 1... 223
lora_cnf = True                 # confirmed messages enable/disable
lora_heartbeat = 2             # 0... 65535‬
lora_interval = 7               # 5... 65535‬

periphery_params = True
periphery_piezo_mode = 0        # 0 (Off), 1 (1 tone), 2 (2 tones), 3 (2*dur1), 4 (4*dur1), 5 (2*dur2), 6 (4*dur2), 7 (2*dur1 & 2*dur2), 8 (4*dur1 & 4*dur2)
periphery_piezo_freq1 = 1       # 0... 3 -> f1: 0 (500Hz), 1 (1kHz), 2 (2kHz), 3 (4kHz)
periphery_piezo_freq2 = 2       # 0... 3 -> f2: 0 (500Hz), 1 (1kHz), 2 (2kHz), 3 (4kHz)
periphery_leds_enable = 0x0F    # LED EN bits: 0b0000'1111
periphery_accel_mode = 0        # 0... 6
periphery_show_hourglass = True

image_params = False
image_epd_mode = 1              # 0 = show all in the memory, 1 = show selected, 2 = toggle selected
image_codes = [0x001D, 0x001E, 0x001F]

user_text_params = False
user_text_x_pos = 8             # 0... 199
user_text_y_pos = 8             # 0... 199
user_text_font_size = 24        # 8, 12, 16, 20, 24
user_text_state = 0             # 0 (0°), 1 (0° inverted), 2 (90°), 3 (90° inverted), 4 (180°), 5 (180° inverted), 6 (270°), 7 (270° inverted)
user_text_chars = "123\nABC"

#########################################################################
print("--> OxobuttonQT LoRa Downlink generator <--\n")
downlink_message = ""
if lora_params:
    print("generating the lora_params...")
    downlink_message += "B0"
    downlink_message += "01" if lora_adr else "00"
    if lora_dr < 0 or lora_dr > 5: print("-> invalid LoRa DR!")
    downlink_message += ''.join(format(lora_dr, '02X'))
    if lora_send_trials < 1 or lora_send_trials > 10: print("-> invalid LoRa send trials!")
    downlink_message += ''.join(format(lora_send_trials, '02X'))
    if lora_join_trials < 1 or lora_join_trials > 3: print("-> invalid LoRa join trials!")
    downlink_message += ''.join(format(lora_join_trials, '02X'))
    if lora_port < 1 or lora_port > 223: print("-> invalid LoRa port!")
    downlink_message += ''.join(format(lora_port, '02X'))
    downlink_message += "01" if lora_cnf else "00"
    if lora_heartbeat < 0 or lora_heartbeat > 65535: print("-> invalid LoRa heartbeat!")
    downlink_message += ''.join(format(lora_heartbeat, '04X'))
    if lora_interval < 5 or lora_interval > 65535: print("-> invalid LoRa interval!")
    downlink_message += ''.join(format(lora_interval, '04X'))

if periphery_params:
    print("generating the periphery_params...")
    downlink_message += "B1"
    if periphery_piezo_mode < 0 or periphery_piezo_mode > 15:  print("-> invalid piezo mode!")
    downlink_message += ''.join(format(periphery_piezo_mode, '01X'))
    if periphery_piezo_freq1 < 0 or periphery_piezo_freq1 > 3:  print("-> invalid piezo frequency1!")
    if periphery_piezo_freq2 < 0 or periphery_piezo_freq2 > 3:  print("-> invalid piezo frequency2!")
    periphery_piezo_freq = (periphery_piezo_freq2 << 2) | periphery_piezo_freq1
    downlink_message += ''.join(format(periphery_piezo_freq, '01X'))
    downlink_message += "00"    # reserved
    if periphery_leds_enable < 0 or periphery_leds_enable > 0x0F:  print("-> invalid LEDs enable!")
    downlink_message += ''.join(format(periphery_leds_enable, '02X'))
    if periphery_accel_mode < 0 or periphery_accel_mode > 6:  print("-> invalid accel mode!")
    downlink_message += ''.join(format(periphery_accel_mode, '02X'))
    downlink_message += "01" if periphery_show_hourglass else "00"

if image_params:
    print("generating the image_params...")
    downlink_message += "B2"
    if image_epd_mode < 0 or image_epd_mode > 2:  print("-> invalid epd mode!")
    downlink_message += ''.join(format(image_epd_mode, '02X'))
    if image_epd_mode != 0:
        downlink_message += ''.join(format(len(image_codes), '02X'))
        for image_code in image_codes:
            downlink_message += ''.join(format(image_code, '04X'))

if user_text_params:
    print("generating the user_text_params...")
    downlink_message += "B3"
    if user_text_x_pos < 0 or user_text_x_pos > 199: print("-> invalid text position x!")
    downlink_message += ''.join(format(user_text_x_pos, '02X'))
    if user_text_y_pos < 0 or user_text_y_pos > 199: print("-> invalid text position y!")
    downlink_message += ''.join(format(user_text_y_pos, '02X'))
    if user_text_font_size < 8 or user_text_font_size > 24: print("-> invalid text font size!")
    downlink_message += ''.join(format(user_text_font_size, '02X'))
    if user_text_state < 0 or user_text_state > 7: print("-> invalid text state!")
    downlink_message += ''.join(format(user_text_state, '02X'))
    downlink_message += ''.join(format(len(user_text_chars), '02X'))
    for char in user_text_chars:
        downlink_message += ''.join(format(ord(char), '02X'))

print("\nFinal LoRa downlink: ==> " + downlink_message + " <== (" + str(len(downlink_message)//2) + "/50 bytes)")
if len(downlink_message)//2 > 50: print("-> Error: Too many bytes!")
print("")

Die Ausgabe des Scripts gestaltet sich dann folgendermassen:

--> OxobuttonQT LoRa Downlink generator <--

generating the lora_params...
generating the periphery_params...

Final LoRa downlink: ==> B001020301010100020007B109000F0001 <== (17/50 bytes)

>>>

Die Downlink-Payload B001020301010100020007B109000F0001 setzt sich aus den beiden Messages B001020301010100020007 und B109000F0001 zusammen und kann in die Consolen-App kopiert werden.

Downlink Payload

Zum Zeitpunkt 19:44:29 zeigt sich in den Application Data, dass die Message aufgenommen wurde und nach dem nächsten Uplink dann auch an den OxoButton gesendet wird. Den nächsten Uplink habe ich durch das Betätigen einer Taste auf dem OxoButton erzwungen.

19:46:37 erfolgt der erzwungene Uplink und anschliessend der Empfang des Downlinks, wodurch die gesetzten Parameter in den OxoButton übernommen werden.

Der Heartbeat wurde auf 2 x 15 min gesetzt, weshalb der nächste Uplink 20:16:39 erwartet werden kann und 20:16:59 auch tatsächlich erfolgt..

Payloads von Up- und Downlink

2021-05-03/ck

OXOBUTTON LoRaWAN

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google Foto

Du kommentierst mit Deinem Google-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.