lora.nvram_save / restore() | frame counter updates



  • Hey guys,

    I'm running a node which is a pycom lopy4 + pysense and i'm joining a lora session and saving that sessions sending values and restoring the lora session what i want to ask is:

    • does the session expires? if yes when (at what time)?
    • how can i update the frame counter? because i'm always reading the frame 0 despite the information being correct...
    • i have the tag "retry" in the application traffic; what's the consequence of this?

    Thank you guys!

    that's my code there:

    ################################################# General color code #################################################
    # pycom.rgbled(0x007f00) -> green = <action> accepted
    # pycom.rgbled(0x7f7f00) -> yellow = <action> pending
    # pycom.rgbled(0x7f0000) -> red = <action> gone wrong
    ######################################################################################################################
    ##################################################### Libraries ######################################################
    from network import LoRa
    from network import WLAN
    import machine
    import socket
    import ubinascii
    import struct
    import time
    import config
    import pycom
    from pysense import Pysense
    from deepsleep import DeepSleep
    import deepsleep
    from LIS2HH12 import LIS2HH12
    from SI7006A20 import SI7006A20
    from LTR329ALS01 import LTR329ALS01
    from MPL3115A2 import MPL3115A2,ALTITUDE,PRESSURE
    from math import log
    from struct import *
    import base64
    import gc
    
    
    pycom.heartbeat(False)              # turn off the heartbeat to make way for control pulses in rgb
    print("Starting sequence initiated...")
    pycom.rgbled(0x7f0000)              #red = <action> not forwarded
    time.sleep(1)
    pycom.rgbled(0x007f00)              # green = <action> accepted
    print("Off you go!")
    
    #Sensor instances created
    py = Pysense()
    mpp = MPL3115A2(py,mode=PRESSURE)   # Returns height in meters. Mode may also be set to PRESSURE, returning a value in Pascals
    si = SI7006A20(py)                  # temp, humidity
    lt = LTR329ALS01(py)                # light
    li = LIS2HH12(py)                   # accelerometer
    ds=DeepSleep()
    #gc.enable()                         # enable garbage collection
    
    # initialize LoRa in LORAWAN mode.
    # Please pick the region that matches where you are using the device:
    # Asia = LoRa.AS923
    # Australia = LoRa.AU915
    # Europe = LoRa.EU868
    # United States = LoRa.US915
    # lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868)
    #
    # # create an OTA authentication params
    # dev_eui = ubinascii.unhexlify('70B3D54995F6E9F1') # these settings can be found from TTN
    # app_eui = ubinascii.unhexlify('70B3D57ED0019C87') # these settings can be found from TTN       'o que estava aantes -- 70B3D57ED0019813'
    # app_key = ubinascii.unhexlify('169C2EF739EAAF4051CD16B8CDA3D56A') # these settings can be found from TTN
    #
    # # set the 3 default channels to the same frequency (must be before sending the OTAA join request)
    # lora.add_channel(0, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5)
    # lora.add_channel(1, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5)
    # lora.add_channel(2, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5)
    #
    # # join a network using OTAA
    # lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_eui, app_key), timeout=0, dr=config.LORA_NODE_DR)
    #
    # # wait until the module has joined the network
    # while not lora.has_joined():
    #     time.sleep(2.5)
    #     pycom.rgbled(0x7f7f00)                                  # yellow = lora.join pending
    #     print('Not joined yet...')
    # print('LoRa joined!')
    # pycom.rgbled(0x007f00)                                      # green = lora.join complete
    #
    # # remove all the non-default channels
    # for i in range(3, 16):
    #     lora.remove_channel(i)
    #
    # # create a LoRa socket
    # s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
    #
    # # set the LoRaWAN data rate
    # s.setsockopt(socket.SOL_LORA, socket.SO_DR, config.LORA_NODE_DR)
    #
    # # make the socket non-blocking
    # s.setblocking(False)
    #
    # time.sleep(5.0)
    
    ################################################ Funcoes de configuracao #############################################
    
    # Input: void
    # Output: None - LoRaWAN join
    # Join to LoRaWAN session
    def join_LoRaWAN():
        lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868)
    
        # create an OTA authentication params
        dev_eui = ubinascii.unhexlify('70B3D54995F6E9F1') # these settings can be found from TTN
        app_eui = ubinascii.unhexlify('70B3D57ED0019C87') # these settings can be found from TTN       'o que estava aantes -- 70B3D57ED0019813'
        app_key = ubinascii.unhexlify('169C2EF739EAAF4051CD16B8CDA3D56A') # these settings can be found from TTN
    
        # set the 3 default channels to the same frequency (must be before sending the OTAA join request)
        lora.add_channel(0, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5)
        lora.add_channel(1, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5)
        lora.add_channel(2, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5)
    
        # join a network using OTAA
        lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_eui, app_key), timeout=0, dr=config.LORA_NODE_DR)
    
        # wait until the module has joined the network
        while not lora.has_joined():
            time.sleep(2.5)
            pycom.rgbled(0x7f7f00)                                  # yellow = lora.join pending
            print('Not joined yet...')
        print('LoRa joined!')
        pycom.rgbled(0x007f00)                                      # green = lora.join complete
    
        # remove all the non-default channels
        for i in range(3, 16):
            lora.remove_channel(i)
    
        # create a LoRa socket
        s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
    
        # set the LoRaWAN data rate
        s.setsockopt(socket.SOL_LORA, socket.SO_DR, config.LORA_NODE_DR)
    
        # make the socket non-blocking
        s.setblocking(False)
    
        time.sleep(5.0)
        return s
    
    # Input: void
    # Output: None - WLAN join
    # Join to a WLAN of choice
    def join_WLAN():
        wlan = WLAN(mode=WLAN.STA)
        nets = wlan.scan()
        pycom.rgbled(0x7f7f00)                                    # yellow = scanning networks
        time.sleep(1)
        print("Scanning networks...")
        time.sleep(1)
        for net in nets:
            if net.ssid == WIFI_SSID:
                print('Network found!')
                wlan.connect(net.ssid, auth=(net.sec, WIFI_PASS), timeout=5000)
                # while not wlan.isconnected():
                #     machine.idle()                              # save power while waiting
                print('WLAN connection succeeded!')
                pycom.rgbled(0x007f00)                            # green = wlan.connect accepted
                break
    
    # Input: void
    # Output: None - RTC synchronized
    # Sincronizacao com RTC
    def sync_RTC():
        rtc=machine.RTC()
        time.sleep(1)
        pycom.rgbled(0x7f7f00)                                    # yellow = rtc.ntp_sync pending
        print("Synchronizing RTC ...")
        time.sleep(1)
        rtc.ntp_sync('pool.ntp.org',3600)
    
        while not rtc.synced():                                   # beware: maybe add counter if unsynced ntp doesn't lead to infinite loop
            rtc.ntp_sync('pool.ntp.org',3600)
            pycom.rgbled(0x7f0000)                                #red = <action> not forwarded
            print("RTC still not synchronized...")
            time.sleep(2.5)
    
        print("RTC synchronized!")
        time.sleep(2.5)
        pycom.rgbled(0x2b14c6)
        time.sleep(2.5)
        pycom.rgbled(0x007f00)                                    # green = rtc.ntp_sync accepted ALTHOUGH rtc.synced() == False
        time.sleep(1)
        print("Prepare for TAKEOFF!")
        time.sleep(1)
        return
    
    ######################################################################################################################
    
    ################################################# Funcoes auxiliares #################################################
    
    # Input: valor n
    # Output: Número de bytes necessários para codificar o valor n
    # Funcao auxiliar de calculo ao nº de bytes necessarios para codificacao de um dado valor
    def bytes_needed(n):
        if n == 0:
            return 1
        return int(log(n, 256)) + 1                             # int(log(n, 2)) + 1  para calcular nº de bits necessarios
    
    ######################################################################################################################
    
    ################################################# Funcoes de sensor ##################################################
    
    # Input: void
    # Output: lista contendo valores lidos em posicoes fixas
    # Leitura de sensores num determinado instante
    def readSensor():
        list=[]
    
        acc_x = li.acceleration()[0]
        acc_y = li.acceleration()[1]
        acc_z = li.acceleration()[2]
        pitch = li.pitch()                                        # [-90, 90]; Unidades: graus
        roll = li.roll()                                          # [-180, 180]; Unidades: graus
        humid = int(round(si.humidity()*10))                      # arredondamento a uma casa decimal; Unidades: %
        temp = int(round((si.temperature()+273.15)*10))           # arredondamento a uma casa decimal; Unidades: K
        light = lt.light()[0]
        light1 = lt.light()[1]
        press=int(mpp.pressure()/100)                             # descodificação a /100; Unidades: kPa com 1 casa decimal
        batt = int(py.read_battery_voltage()*10)                  # Unidades: dV
    
        list.append(acc_x)
        list.append(acc_y)
        list.append(acc_z)
        list.append(pitch)
        list.append(roll)
        list.append(humid)
        list.append(temp)
        list.append(light)
        list.append(light1)
        list.append(press)
        list.append(batt)
        return list
    
    def encoder(v, f):
        bytes=bytearray()
        values=v
        formats=f
    
        for i in range(len(values)):
    	   bytes.extend(struct.pack(formats[i], values[i]))
    
        return bytes
    
    def decoder(bytearray):
        formatSize = {
    	'b': 1,
    	'B': 1,
    	'H': 2,
    	'h': 2,
    	'f': 4,
    	'I': 4,
    	'i': 4,
        }
        headers = ['acc_x','acc_y','acc_z','pitch','roll','humid','temp','light','light1','press','batt']
        NR_VALS = 11
        fin_i=0
        curr_i = 0
        decoded_val=[]
    
        for i in range(NR_VALS):
     	  fin_i = curr_i + formatSize[formats[i]]
     	  subPkt = bytes2send[curr_i:fin_i]
     	  decoded_val.append(struct.unpack(formats[i], subPkt)[0])
     	  curr_i = fin_i
    
        return  decoded_val
    
    ######################################################################################################################
    
    ################################################ Ciclo principal #####################################################
    while (True):
        pycom.rgbled(0x007f00)                                  # green color comencing sequence
        bytes2send = bytearray()                                # bytearray que ira conter os valores em bytes a enviar
        values=[]
        formats = ['f','f','f','f','f','H','H','f','f','H','B']  #Total of 35 bytes + 13 bytes of LoRa header
        decoded_val=[]
    
    # a testar
        lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868)
        lora.nvram_restore()
    
        if not lora.has_joined():
            print("Firstly join")
    
            dev_eui = ubinascii.unhexlify('70B3D54995F6E9F1') # these settings can be found from TTN
            app_eui = ubinascii.unhexlify('70B3D57ED0019C87') # these settings can be found from TTN       'o que estava aantes -- 70B3D57ED0019813'
            app_key = ubinascii.unhexlify('169C2EF739EAAF4051CD16B8CDA3D56A') # these settings can be found from TTN
    
            # set the 3 default channels to the same frequency (must be before sending the OTAA join request)
            lora.add_channel(0, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5)
            lora.add_channel(1, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5)
            lora.add_channel(2, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5)
    
            # join a network using OTAA
            lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_eui, app_key), timeout=0, dr=config.LORA_NODE_DR)
    
            # wait until the module has joined the network
            while not lora.has_joined():
                time.sleep(2.5)
                pycom.rgbled(0x7f7f00)                                  # yellow = lora.join pending
                print('Not joined yet...')
            print('LoRa joined!')
            pycom.rgbled(0x007f00)                                      # green = lora.join complete
    
            # remove all the non-default channels
            for i in range(3, 16):
                lora.remove_channel(i)
    
            # create a LoRa socket
            s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
    
            # set the LoRaWAN data rate
            s.setsockopt(socket.SOL_LORA, socket.SO_DR, config.LORA_NODE_DR)
    
            # make the socket non-blocking
            s.setblocking(False)
    
            time.sleep(5.0)
    
            lora.nvram_save()
            print("AHHHHHHHHHHHHh")
        else:
            print("Join already")
            s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
            s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
            s.setsockopt(socket.SOL_LORA, socket.SO_DR, config.LORA_NODE_DR)
            s.setblocking(False)
    
    # a testar
    
        #join_WLAN()
        #sync_RTC()
    
    
        values=readSensor()
        bytes2send=encoder(values, formats)
        print('bytes2send', bytes2send)
        #print('Array size:', len(bytes2send))
        #decoded_val=decoder(bytes2send)
    
        #print("Decoded values: ", decoded_val)
        #print(len(decoded_val))
        print("going to sleep")
        time.sleep(2)
        pycom.rgbled(0x0000ff)
        time.sleep(2)
        #mpp.i2c.writeto_mem(MPL3115A2.MPL3115_I2CADDR, MPL3115A2.MPL3115_CTRL_REG1, bytes([0x0]))
    
        s.send(bytes2send)
        lora.nvram_save()
    
        #time.sleep(10)
        py.setup_sleep(30)   # setup a 10 seconds sleep time
        py.go_to_sleep(False)
        #machine.pin_deepsleep_wakeup(['P13'], machine.WAKEUP_ANY_HIGH, False)
        #time.sleep(2)
        #pycom.rgbled(0xff0000)
        #time.sleep(2)
        #machine.deepsleep(30000)
    
        # time.sleep(4)
        # rx, port = s.recvfrom(256)
        # if rx:
        #     print('Received: {}, on port: {}'.format(rx, port))
        # time.sleep(6)
    
    ##################### Debugging Section #####################
        # a=base64.b64encode(bytes2send)
        # print(a)
    #############################################################
    
    ######################################################################################################################```


  • @morrison my personal guess would be that you are going to sleep a bit too quickly after sending/saving state.

    The Pysense-controlled Deep Sleep is quite brutal, it just cuts the power supply to the ESP32 without any warning.

    Try adding short delays between send and save and between save and sleep to see if that helps.

    Also, is your device rejoining each time, or just sending with the same counter over and over again?



  • @morrison Joining (via ABP or OTAA) just establishes the keys for data exchange between the nodes. These keys are established once and do not expire. If you like, you can renew the, but there is nothing enforcing it. And there is not session that expires.

    Frame counter have to be managed at the node side. There is the lora.nvram_save / restore() mechanism, which is a little bit tricky, in that lora.nvram_restore() sets the stored counter to 0. So if you call it more than once in a row, you get 0 as counter value. That is, so to say, sub-optimal. If you request confirmed Lora messages, the message will be re-sent by the LoRa stack until confirmed, at least a few times. But that accumulates to the downlink budget, and some public networks limit that.

    @CoachAllen These questions are partially pycom-specific and have been discussed several times in the forum.



  • @morrison What do you know? Another dead end from Pycom. You have a question and they ignore you. How does it feel to ALPHA a product?


Log in to reply
 

Pycom on Twitter