Issues restoring LoRa state after join() TimeOut



  • I am having difficulties trying to restore the LoRa state (by calling lora.nvram_restore()) after an attempt to Join times out.

    I've been trying several things, and in my non-LoPy Expert mind, this is would be what I would like to achieve:

    1. Device was previously joined successfully, sent/received data ok, lora.nvram_save() called, shut down.
    2. After that, each time the device tuns on, attempt to Join.
      a. If successful, proceed to create socket and send/recieve.
      b. If TimeOut, call lora.nvram_restore() and proceed to create socket, send/receive.
    3. Call lora.nvram_save() each time data is sent and before shutdown.

    My LoPy is connected to an external power source, and it's initialized as CLASS_C.

    However, after getting a Timeout on the join(), and then calling nvram_restore() I do get True when calling lora.has_joined(). But when I create a socket and then attempt to send data everything freezes, and I have to manually shutdown as Ctrl+C won't work.

    Now, if I skip the whole join attempt part, and immediately call nvram_restore(), I get True on has_joined(), and I also am able to successfully send/receive data after creating the socket. This is what baffles me, as it would seem that calling join() somehow affects what was stored with nvram_save().

    Perhaps I am doing some steps wrong, or missing some detail on the LoRa module Pycom provides? Any guidance or suggestions to be able to achieve what I want ("attempt join else restore on timeout") will be greatly appreciated :)

    Here is the micropython code that implements the pseudo-code above mentioned:

    lora = LoRa(mode=LoRa.LORAWAN, adr=False, device_class=DEVICE_CLASS, region=REGION_US, tx_retries=3)
    #setup the channels specified in my lorasever config
    [lora.remove_channel(channel) for channel in range(0,72)]
    
    [lora.add_channel(channel, frequency=thisFreq, dr_min=0, dr_max=3) for channel, thisFreq in zip(CH_RANGE, FREQ_RANGE)]
    
    try:
        lora.join(activation=LoRa.OTAA, auth=(app_eui, app_key), timeout=some_timeout)
    except Exception as e:
        pass     #timeout 
    
    if lora.has_joined():
        s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)        
        s.setsockopt(socket.SOL_LORA, socket.SO_DR, my_dr)
        #proceed to send/recieve etc. no issues sending here
    else:
        #join timeout
        lora.nvram_restore()
        #lora.has_joined() gives True 
        s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)        
        s.setsockopt(socket.SOL_LORA, socket.SO_DR, my_dr)
        #proceed to send/recieve data. Here is where it FREEZES
    

    Again, if I delete the whole join part and go straight for the restore it works ok.


    Some background:

    This "attempt join else restore state" approach is what I need (?) as I want the device to try join first but have the chance to restore it's state in case it is unable to join promptly for any reason.

    As far as I know, I "need" to do be able to join again to the network eventually, as to obtain new keys and reset the FrameCounter. Otherwise, as my device is on for long amounts of time, and sends data in periods as short as one minute, I did some numbers and I will run out of FrameCounters (65535) in about 20 days.

    Any other info I can provide do tell and I'll gladly include it.



  • In general, rejoining for every payload is bad practice. Instead you should always try to restore the LoRa status in favor of a rejoin. I'm also guessing that you heavily violate TTNs Fair Access Policy [1] especially when join requests are added to the air time allowed. Forget this comment in case you are operating on a different LoRaWAN backend provider where other policies apply.

    Secondly, you should give lora.join() some time before testing on has_joined, e.g. in a while loop as proposed here [2]. If your concern is running out of FrameCounters I'd go along with the tx_counter from lora.stats() and rejoin only if you get close to the limit.

    Here's some untested example code:

    lora = LoRa(mode=LoRa.LORAWAN, adr=False, device_class=DEVICE_CLASS, region=REGION_US, tx_retries=3)
    #setup the channels specified in my lorasever config
    [lora.remove_channel(channel) for channel in range(0,72)]
    
    [lora.add_channel(channel, frequency=thisFreq, dr_min=0, dr_max=3) for channel, thisFreq in zip(CH_RANGE, FREQ_RANGE)]
    
    lora.nvram_restore()
    stats = lora.stats()
    
    if not lora.has_joined() or stats['tx_counter'] > 65000:
        lora.join(activation=LoRa.OTAA, auth=(app_eui, app_key), timeout=some_timeout)
        while not lora.has_joined():
            time.sleep(2.5)
            print('Not yet joined...')
    
    s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)        
    s.setsockopt(socket.SOL_LORA, socket.SO_DR, my_dr)
    
    

    [1] https://www.thethingsnetwork.org/forum/t/limitations-data-rate-packet-size-30-seconds-uplink-and-10-messages-downlink-per-day-fair-access-policy-guidelines/1300
    [2] https://docs.pycom.io/tutorials/lora/lorawan-otaa/



  • @jcaron Thanks for your response. Seem you are right. Doing some further test it seems that calling join() erases the nvram.

    I was planning on using lora.stats() to obtain the tx_counter and attempt to rejoin when close to the limit.



  • @mnovella As far as I know, this just won’t work. The saved state is reset when you try to join.

    You could maintain a counter and re-join after a given number of uplinks instead.


Log in to reply
 

Pycom on Twitter