• Day 4

    morph02/16/2023 at 20:49 0 comments

    Life is a struggle, and I've been struggling for the past few days with this project and many other things.

    I've spent most of my free time with CNC path operations for a small boat plug that I'm building out of XPS foam layers at school, but thats another story.

    With this project I've mostly had problems with passing/converting the serial output of the e5 mini 'gateway' to UDP packets that Chirpstack would understand.

    Nevertheless, I've succeeded in forwarding messages with my own python script. I thought that I'm too cool for the legacy Semtech Packet Forwarder so I wrote my own. Well... it took me ages, I'm a bit rusty with my boat adventures and everything happening lately. First I wanted to test the packet forwarder functionality with netcat, but ran into encoding issues. Then I tried C and got lost. Finally python solved the problem, python is not my forte, but it just worked. Next I need to implement downlink functionality to my UDP socket python script.

    import serial
    import socket
    
    serial_port = serial.Serial(port="/dev/ttyUSB0", baudrate=115200,
                                bytesize=8, timeout=2, stopbits=serial.STOPBITS_ONE)
    
    remote = ("hush.super.secret", 1700)
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    while (1):
        if (serial_port.in_waiting > 0):
            serial_buffer = serial_port.readline()
            sock.sendto(serial_buffer, remote)
            # data, addr = sock.recvfrom(1024)  # buffer size is 1024 bytes
            # print("received message: %s" % data)

    Python packet forwarder

    #include "STM32WL_LoRaRadio.h"
    #include "events/EventQueue.h"
    #include "jems.h"
    #include "mbed.h"
    
    #define MAX_LEVEL 10 // how deeply nested the JSON structures can get
    static jems_level_t jems_levels[MAX_LEVEL];
    static jems_t jems;
    
    #define FREQUENCY 868000000
    #define TX_POWER 22 // max should be 22 in lib
    #define BANDWIDTH 0
    #define PREAMBLE_LENGTH 8
    #define DOWNLINK_MAX_LENGTH 51 // keep sf12 max until we settle on a better number
    #define FREQ_HOP_ON 0
    #define HOP_PERIOD 4
    
    BufferedSerial router(USBTX, USBRX);
    static STM32WL_LoRaRadio radio;
    static EventQueue ev_queue(10 * EVENTS_EVENT_SIZE);
    
    DigitalOut led1(LED1);
    
    static void write_char(char ch, uintptr_t arg)
    {
        // fputc(ch, (FILE *)arg);
        router.write(&ch, 1);
    }
    
    //  Bytes  | Function
    // :------:|---------------------------------------------------------------------
    //  0      | protocol version = 2
    //  1-2    | random token
    //  3      | PUSH_DATA identifier 0x00
    //  4-11   | Gateway unique identifier (MAC address)
    //  12-end | JSON object, starting with {, ending with }, see section 4
    
    static void forward_packet(const uint8_t *payload, uint16_t size, int16_t rssi,
                               int8_t snr)
    {
        // char string_payload[(size << 1) + 1];
        // for (size_t i = 0; i < size; i++)
        // {
        //     sprintf(&string_payload[i * 2], "%02X", payload[i]);
        // }
        char gw_id[] = {0x02, 0xCF, 0x9A, 0x00, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB};
        router.write(gw_id, 12);
        jems_init(&jems, jems_levels, MAX_LEVEL, write_char, (uintptr_t)stdout);
        jems_object_open(&jems);
        jems_string(&jems, "rxpk");
        jems_array_open(&jems);
        jems_object_open(&jems);
        // jems_string(&jems, "time");
        // jems_string(&jems, "2022-02-15T22:17:00.000000Z");
        // jems_string(&jems, "tmst");
        // jems_number(&jems, 3512348611);
        // jems_string(&jems, "chan");
        // jems_number(&jems, 2);
        // jems_string(&jems, "rfch");
        // jems_number(&jems, 0);
        jems_string(&jems, "freq");
        jems_number(&jems, FREQUENCY);
        jems_string(&jems, "stat");
        jems_number(&jems, 1);
        jems_string(&jems, "modu");
        jems_string(&jems, "LORA");
        jems_string(&jems, "datr");
        jems_string(&jems, "SF9BW125");
        jems_string(&jems, "codr");
        jems_string(&jems, "4/5");
        jems_string(&jems, "rssi");
        jems_number(&jems, rssi);
        jems_string(&jems, "lsnr");
        jems_number(&jems, snr);
        jems_string(&jems, "size");
        jems_number(&jems, size);
        jems_string(&jems, "data");
        jems_string(&jems, "APtfAPB+1bNwjr3JaVJHXQAFUIADzW0=");
        jems_object_close(&jems);
        jems_array_close(&jems);
        jems_object_close(&jems);
    }
    
    static void stat_message()
    {
        char gw_id[] = {0x02, 0xCF, 0x9A...
    Read more »

  • Day 3 - J'S ON

    morph02/07/2023 at 20:33 0 comments

    {
      "rxpk": [
        {
          "time": "2013-03-31T16:21:17.528002Z",
          "tmst": 3512348611,
          "chan": 2,
          "rfch": 0,
          "freq": 868000000,
          "stat": 1,
          "modu": "LORA",
          "datr": "SF9BW125",
          "codr": "4/5",
          "rssi": -39,
          "lsnr": 11,
          "size": 23,
          "data": "00FB5F00F07ED5B3708EBDC96952475D0004FAEC5DD086"
        }
      ]
    }

    I decided to follow the easy route for now with the packet formatting, its JSON. Forget about the "time" and "tmst" values above, the rest is real information :)

    JSON is not ideal, but I'm worried that I'll get tangled up in writing gateway bridge code for OpenWRT. I can just compile the Semtech Packet Forwarder. Furthermore I'd have to anyway convert most other formats to something that Chirpstack understands. Eventually, when this single Channel LoRaWAN gateway traffic pipeline is complete and has been running for some time I will change JSON for a better packed format.

    I found a fresh and pure C JSON serializer library for embedded systems: https://github.com/rdpoor/jems 

    The best part is that it can "emit characters as you go", and I like to go. It means that the E5 can stream out JSON over USB character by character when a packet arrives. Combine that with the Mbed OS Event Queue and you can stream like there's no tomorrow without any major memory issues, other than my own.

    Next I'll compile the packet forwarder on the router and sniff what it wants from what it thinks is a 'normal' gateway. We will give it all the JSON it needs, but not more.

  • ​Day 2 - Wired & Wireless

    morph02/02/2023 at 18:29 0 comments

    So I rolled up my sleeves and:

    • Picked up the Wio-E5 mini and wrote a simple Mbed OS LoRaWAN traffic sniffer using the LoRa radio driver api.
    • Reinstalled OpenWRT on my Spitz (GL-X750V2) along with everything needed for my family to get back online and the needed USB kernel modules.
    • Plugged the E5 to the only USB port on the router and to my pleasant surprise I could see some debug messages indicating that the 'gateway' is indeed receiving LoRaWAN join request packages.

    Next I will probably write a simple packet decoder for the stm32, I could also dump every hot potato raw payload to the router, but thinking ahead some message format/protocol would be in order. There will be downlink messages after all. Who knows, the LoRaWAN packet does carry most of the necessary information anyway. Traditionally the communication is JSON, but I've been meaning to play around with alternative options like MessagePack and Protobuf. One thing to consider is the target single channel gateway mcu families, we would value a low footprint in the memory department.

    Eventually I will measure power draw. The Wio-E5 STM32WLE5JC Module draws 6.7mA in receive with mcu (active?) as per the manufacturer. The STM32WLE5JC datasheet does show similar figures when all the peripherals are switched off. Lowering the clock speed will drop that value as will sleep modes. It would be interesting to see if the SoC transceiver can wake up the MCU from deep sleep with a receive interrupt. Much to discover, and to get tangled up in, been there done that - looking at you Mbed OS...

    One the router side I currently feel like I want to pass the potato to the cloud as fast as possible with minimal effort. I might start with the traditional Semtech UDP forwarder method, it works. The traffic will probably land in my Chirpstack instance. However, yet again there is a chance to experiment, it would be nice to try MQTT message queuing/buffering when the connection to the external LoRaWAN network server is down. Someday I would like to write my own LoRa protocol for mesh networks and at least some infrastructure could be built now.

    Link to repo coming soon.

  • Day 1

    morph01/27/2023 at 18:54 0 comments

    So here we go again... on our own? Today's goal was to get this project set up here, baby steps.

    I ran out of project description space, I admit it's a good idea to limit that, ain't nobody gonna read my story. I'm no author and I don't know where that was going anyway :)

    What I'm planning on making is often called a single channel LoRaWAN gateway. I've done it before, a long time ago, at least in technology terms. Somewhere around 2017 I had just started a new job and was tasked to fiddle around with LoRa and LoRaWAN. It was all new to me, and I couldn't believe that a transmission often covering multiple kilometers without line of sight aka LOS with minuscule power was possible. One thing led to another and I ended up being the LoRa guy at my company, carrying out LoRa certification testing among other things.

    But before I got deep into the LoRa adventures/madness, I made an esp8266 single channel LoRaWAN gateway. I found code on Github, and back then there wasn't much available on this topic (I need to find the source to give credit to). I might have paired the esp8266 with a RFM95 or some other module. It worked and that was that, moved on to full blown LoRaWAN gateways and Bob is actually not my uncles name.

    Enter 2021, I get myself a small OpenWRT 4G GL router (GL-X750v2) - nifty apparatus by the way, and eventually try to use it as a LoRaWAN gateway with a USB dongle concentrator. The poor router didn't have enough pixies to power the dongle from the USB port. And that brings me to the current scenario, I have a bunch of LoRa hardware and no gateway.

    The idea is to design a board/motherboard with either a mcu and a LoRa tranceiver or the stm32wle5jc. Anyway the radio listens, mcu sleeps, gets interrupted and pushes what it received through USB to the router or whatever capable is hooked up and back and out again if so wishes the network server. MQTT might be mixed into this. A low power LoRaWAN gateway that's the goal.