LoPy4 change uplink channels by itself?

  • Hi everyone, the problem I am having now is, LoPy4 changes uplink channels by itself.
    Since my gateway only listens to 8 channels, I deleted all the CN470 channels and add 8 channels by doing this:
    for i in range(0, 96):
    lora.add_channel(index=84, frequency=487100000, dr_min=0, dr_max=5)
    lora.add_channel(index=85, frequency=487300000, dr_min=0, dr_max=5)
    lora.add_channel(index=86, frequency=487500000, dr_min=0, dr_max=5)
    lora.add_channel(index=87, frequency=487700000, dr_min=0, dr_max=5)
    lora.add_channel(index=80, frequency=486300000, dr_min=0, dr_max=5)
    lora.add_channel(index=81, frequency=486500000, dr_min=0, dr_max=5)
    lora.add_channel(index=82, frequency=486700000, dr_min=0, dr_max=5)
    lora.add_channel(index=83, frequency=486900000, dr_min=0, dr_max=5)
    It works fine at the beginning. In the figure below you can see its sending uplinks using the 8 channels I added.
    After LoPy4 wakes up from deep sleep, I use lora.nvram_save() and lora.nvram_restore() to restore LoRa's join status. However, LoPy4 will not send uplink using the 8 channels I added at the beginning. Instead, its sending uplinks on random channels in the CN470 frequency band. In the figure below, you can see it is using none of the channels I defined at the beginning.
    In the documentation, it says that the lora.nvram_save can store the LoRaWAN state, but it looks like uplink channels information will not be saved?
    So I disabled the lora.nvram_save and lora.nvram_restore, then I copied the "delete all the channels and add my own channels" code and pasted it to the place where LoPy4 just wakes up from deep sleep. But I had the same issues, which makes no sense.
    Has anyone run into a similar problem?? Really appreciate if someone can give me some suggestions. Thank you!

  • @robert-hh Hello, any update on this? I have the same issue, I want to change the uplink/downlink channel from AS923 to AS923-2 (912.4Mhz and 921.6MHz) but Lopy4 still keep default channel. I've modified code from https://github.com/pycom/pycom-libraries/blob/master/examples/lorawan-regional-examples/main_AS923.py , here is my code

    # Copyright (c) 2019, Pycom Limited.
    # This software is licensed under the GNU GPL version 3 or any
    # later version, with permitted additional terms. For more information
    # see the Pycom Licence v1.0 document supplied with this file, or
    # available at https://www.pycom.io/opensource/licensing
        OTAA Node example  as per LoRaWAN AS923 regional specification
        - compatible with the LoPy Nano Gateway and all other LoraWAN gateways
        - tested works with a LoRaServer, shall works on TTN servers
    from network import LoRa
    import socket
    import ubinascii
    import struct
    import time
    LORA_NODE_DR = 4
        utility function to setup the lora channels
    def prepare_channels(lora, channel, data_rate):
        # AS923_FREQUENCIES = [
        #     { "chan": 1, "fq": "923200000" },
        #     { "chan": 2, "fq": "923400000" },
        #     { "chan": 3, "fq": "922200000" },
        #     { "chan": 4, "fq": "922400000" },
        #     { "chan": 5, "fq": "922600000" },
        #     { "chan": 6, "fq": "922800000" },
        #     { "chan": 7, "fq": "923000000" },
        #     { "chan": 8, "fq": "922000000" },
        # ]
        AS923_FREQUENCIES = [
            { "chan": 1, "fq": "922600000" },
            { "chan": 2, "fq": "922400000" },
            { "chan": 3, "fq": "922200000" },
            { "chan": 4, "fq": "922000000" },
            { "chan": 5, "fq": "921800000" },
            { "chan": 6, "fq": "921600000" },
            { "chan": 7, "fq": "921400000" },
            { "chan": 8, "fq": "921200000" },
        if not channel in range(0, 9):
            raise RuntimeError("channels should be in 1-8 for AS923")
        if channel == 0:
            import  uos
            channel = (struct.unpack('B',uos.urandom(1))[0] % 7) + 1
        for i in range(0, 8):
        upstream = (item for item in AS923_FREQUENCIES if item["chan"] == channel).__next__()
        # set default channels frequency
        lora.add_channel(int(upstream.get('chan')), frequency=int(upstream.get('fq')), dr_min=0, dr_max=data_rate)
        return lora
        call back for handling RX packets
    def lora_cb(lora):
        events = lora.events()
        if events & LoRa.RX_PACKET_EVENT:
            if lora_socket is not None:
                frame, port = lora_socket.recvfrom(512) # longuest frame is +-220
                print(port, frame)
        if events & LoRa.TX_PACKET_EVENT:
            print("tx_time_on_air: {} ms @dr {}", lora.stats().tx_time_on_air, lora.stats().sftx)
        Main operations: this is sample code for LoRaWAN on AS923
    lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AS923, device_class=LoRa.CLASS_C, adr=False, tx_power=20)
    # create an OTA authentication params
    # dev_eui = binascii.unhexlify('0000000000000000')
    # app_key = binascii.unhexlify('a926e5bb85271f2d') # not used leave empty loraserver.io
    # nwk_key = binascii.unhexlify('a926e5bb85271f2da0440f2f4200afe3')
    app_eui = ubinascii.unhexlify('**************')
    app_key = ubinascii.unhexlify('*******************')
    # join a network using OTAA
    # lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_key, nwk_key), timeout=0, dr=2) # AS923 always joins at DR2
    lora.join(activation=LoRa.OTAA, auth=(app_eui, app_key), timeout=0, dr=2) # AS923 always joins at DR2
    prepare_channels(lora, LORA_CHANNEL,  LORA_NODE_DR)
    # wait until the module has joined the network
    print('Over the air network activation ... ', end='')
    while not lora.has_joined():
        print('.', end='')
    # create a LoRa socket
    lora_socket = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
    # set the LoRaWAN data rate
    lora_socket.setsockopt(socket.SOL_LORA, socket.SO_DR, LORA_NODE_DR)
    # msg are confirmed at the FMS level
    lora_socket.setsockopt(socket.SOL_LORA, socket.SO_CONFIRMED, 0)
    # make the socket non blocking y default
    lora.callback(trigger=( LoRa.RX_PACKET_EVENT |
                            LoRa.TX_PACKET_EVENT |
                            LoRa.TX_FAILED_EVENT  ), handler=lora_cb)
    time.sleep(4) # this timer is important and caused me some trouble ...
    for i in range(0, 1000):
        pkt = struct.pack('>H', i)
        print('Sending:', pkt)

  • @sheng That's fine. I will then make a Pull Request for the fix of lora.remove_channel(). Because that did not work before.

  • @robert-hh It Finally Works! But I didn't use the lora.add_channel function, instead, I just remove all the channels I dont need. So its something like this.
    for i in range(0, 79):
    for i in range(88,96):
    Again, thank you so much for your help and patience, I really appreciate it!

  • @sheng You could try to move the join before the channel reconfiguration. AFAIK, the channel udate is part of the join response,

  • @robert-hh I see what you mean, but I am using ChirpStack. I tried the method you provided, but I still have the same problem. After I upload the code to the board, it works fine, it only uses the eight channels I added. But after it went back from deep sleep, it started sending using random frequencies again, which is annoying.
    It seems the lora_add_channel() is not working.

  • @sheng no. You have to remove all channels, like you did initially, because the ttn server can add channels by service messages. And these are the ones you see.
    edit: My code change makes the call to lora.remove_channel() working, which it did not before.

  • @robert-hh Thank you for your help, but I am having the same problem even though I am using the firmware you provided. I didn't make any changes to the firmware. What I did is just flash my board with the firmware you provided, and then run the same code again(but this time, I didn't remove all the channels, I just add 8 channels). Am I doing it right? Or I understand it wrong. Do you mean I don't need to remove any channels? And I can just add the channels I am using with "lora.add_channel" if I am using the firmware you provided.
    Again, thank you so much for your patience and time. Really appreciate it!

  • @sheng Right

  • @robert-hh I know this is a silly question, but how to implement this? Do I just download the file and modify this function first, and then how can I upload this file(or library) to my LoPy4? Is it like I upload the whole lora library to lopy4 using Atom, and then change the beginning of my code from "from network import lora" to "import lora" ? Thank you for your time! Appreciate it!
    Ok, so I can just flash this to my lopy4 using Pycom firmware update tool right?

  • @sheng In case that you do not have the build package running, I have prepared a tar-ball for offline-install named LoPy4-CN470-1.20.2.rc11.tar.gz here: https://github.com/robert-hh/Shared-Stuff.git
    It is the Basic compile package without PyBytes

  • @sheng In case that you have the code set-up to compile yourself, I have a modified version of the function .RegionCN470ChannelsRemove in file ../lib/lora/mac/region/RegionCN470.c, which you could try.

    bool RegionCN470ChannelsRemove( ChannelRemoveParams_t* channelRemove  )
        uint8_t id = channelRemove->ChannelId;
        if( id >= CN470_MAX_NB_CHANNELS )
        // Remove the channel from the list of channels
        Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
        return RegionCommonChanDisable( ChannelsMask, id, CN470_MAX_NB_CHANNELS );

  • @sheng Looking at the code, lora.add_channel() calls in the end RegionCN470ChannelManualAdd(), and at a first glance overwrites the channel data table entry, such that remove seems not really required, if the same channel is overwritten. But in your case the TTN server may have programmed other channel entries, which then will not be deleted and used for further transmission.

  • @robert-hh Thank you so much for your reply, I checked the code for CN470, and tried the channel_manual_add and remove, but it didn't work.

  • @sheng Looking further into the code, there seems to be a kind of implementation shortcut, the in some case, if the function is not implemented by the target region, the code for EU433 applies. That is done for CHANNEL_MANUAL_ADD, CHANNEL_MANUAL_REMOVE and FORCE_JOIN_DATARATE.
    And that is strange, since in the respective functions it seems not to be supported in the code at all (Region.c, line 1026ff and line 1065ff). I wonder if the returned error code is taken care of.

    So that all looks messy!

  • @sheng Looking into the code like I did back in 2018 I see, that for the CN470 region the functions RegionCN470ChannelAdd() and RegionCN470ChannelsRemove() are empty. They do not perform anything, in contrast e.g. to the code for the region EU868. That may explain you behavior of not being able to control the channel table. A full power cycle should bring you to the initial state. That is something I observe with my LoPy4, in that they sometimes refuse to work es expected until I do a power cycle.

    Edit: If you want to dive in it: the code is in ...../pycom-micropython-sigfox/lib/lora/mac/region. There are .c and .h file for each region, and the file region.c, which contains the function tables for all regions.

    P.S.: There is no consistent implementation for the region codes about which function is supported an which not. The best supported code seems to be EU868 code, that's where Semtech is located. For all other regions the code seems to be only partially implemented by Semtech and Pycom. Since the actual sources have a (C) 2013 notice, upgrading to a newer version of these drivers may be an option for improvement.

  • @sheng I found that someone had the same problem back in 2018, I am not sure if the problem is solved, does the add_channels work for CN470 frequency band?

  • @jcaron Thank you for your reply, I am using ChirpStack as the network server. In the ChirpStack Network Server's configuration file, I only enabled these eight channels(as shown below).
    However, in ChipStack Gateway Bridge, there is no such an option to enable sub-channels. So I am using the max and min frequency to limit the channels I am using. I tried to leave it as default (the default minimum frequency is 470MHz, and the maximum frequency is 510MHz), it doesn't work. I changed it to my current settings(as shown below), still doesn't work.

  • @sheng the network can send channels it wants the node to send uplinks on. What gateway/network are you connected to?

  • This post is deleted!

Log in to reply

Pycom on Twitter