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:
- Device was previously joined successfully, sent/received data ok,
lora.nvram_save()
called, shut down. - After that, each time the device tuns on, attempt to Join.
a. If successful, proceed to create socket and send/recieve.
b. If TimeOut, calllora.nvram_restore()
and proceed to create socket, send/receive. - 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 getTrue
when callinglora.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 onhas_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 callingjoin()
somehow affects what was stored withnvram_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.
- Device was previously joined successfully, sent/received data ok,
-
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 onhas_joined
, e.g. in awhile
loop as proposed here [2]. If your concern is running out of FrameCounters I'd go along with thetx_counter
fromlora.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.