Welcome to the Pycom forum Forum
    • \[\[global:header.categories\]\]
    • \[\[global:header.recent\]\]
    • \[\[global:header.tags\]\]
    • \[\[global:header.popular\]\]
    • Login

    Explore Pybytes | Official Documentation | Report a Firmware Bug/Issue | GitHub

    TTN ABP from LoPy4 loses many packets despite low RSSI

    Scheduled Pinned Locked Moved LoPy
    ttn lora
    6 Posts 3 Posters 678 Views 3 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • D Offline
      dnear1
      last edited by

      I have several LoPy4 and a Laird TTN gateway in the same room. I've noticed that out of hundreds of uplinked packets listed on TTN frame counter, only a handful actually show. I have simple 4 byte payloads being sent every 5 minutes. The data changes a small amount each time as it's measuring dust particulate in Parts per Billion. Is that still too frequent to trigger airtime limits? It says Estimated Airtime 30.976ms
      Doing a similar project with a Heltec ESP32 Lora board written in C, we were getting very high success rates. My friend who suggested I try Pycom is having similar issues.
      Here's one of the received packets shown on the TTN site
      {
      "time": "2019-01-20T02:09:37.924262461Z",
      "frequency": 905.1,
      "modulation": "LORA",
      "data_rate": "SF7BW125",
      "coding_rate": "4/5",
      "gateways": [
      {
      "gtw_id": "eui-c0ee40ffff29477e",
      "gtw_trusted": true,
      "timestamp": 1571763780,
      "time": "",
      "channel": 6,
      "rssi": -33,
      "snr": 10.75,
      "rf_chain": 1,
      "latitude": 33.539913,
      "longitude": -86.7531
      }
      ]
      }
      And my current code

      # main.py -- put your code here!
      from network import LoRa
      import socket
      import ubinascii
      import struct
      import time
      import os
      import math
      import abpkeys #local py file with the abp keys stored for easy code reuse across devices
      from machine import UART
      
      uart = UART(1, 9600)                         # init with given baudrate
      uart.init(9600, bits=8, parity=None, stop=1) # init with given parameters
      
      # Initialise 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.US915)
      
      # create an ABP authentication params
      dev_addr=abpkeys.dev_addr
      nwk_swkey=abpkeys.nwk_swkey
      app_swkey=abpkeys.app_swkey
      #dev_addr = struct.unpack(">l", ubinascii.unhexlify('xxxxxxxx'))[0]
      #nwk_swkey = ubinascii.unhexlify('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
      #app_swkey = ubinascii.unhexlify('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
      print('restoring LoRa NVRAM')
      lora.nvram_restore()
      print('Joining LoRa via ABP')
      # join a network using ABP (Activation By Personalization)
      lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey))
      
      # 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, 3)
      
      while (1):
          readytosend=False
          print('polling Dust Sensor')
          data = uart.readall()# get everythitng from the serial buffer
          if data is not None:#make sure it'st not an empty buffer
              bytestream=data.split(b'BM')[1][0:31]# HPMA sends 32 byte packets that start with \x42\x4D ('BM')
              if len(bytestream)==30:#check tthat we actually got 30 bytes (excluding the data we split out)
                  if bytestream[0:2]==b'\x00\x1c':#HPMA packet is always 28 data bytes(excluding start header and checksum)
                      checksum=143#since split command removed the \x42\x4D, add them back to get correct checksum
                      validated=0
                      for i in range(0,28):
                          checksum+=bytestream[i]#cycle thru the data values and add them up
                      validated=int(bytestream[28])*256+int(bytestream[29])#read checksum bytes from packet and make them a 2-byte integer
                      if checksum==validated:# both values should match, else we have a corrupt packet
                          pm25=int(bytestream[4])*256+int(bytestream[5])#these two bytes represent the particulate detected that's 2.5um large
                          pm10=int(bytestream[6])*256+int(bytestream[7])#these two bytes represent the particulate detected that's 10um large
                          print('pm2.5 = ',pm25)
                          print('pm10 = ',pm10)
                          readytosend=True
                      else:
                          print('checksum failed')
                  else:
                      print('invalid packet length')
              else:
                  print('incomplete packet')
          if readytosend==True:
              # make the socket blocking
              # (waits for the data to be sent and for the 2 receive windows to expire)
              s.setblocking(True)
              print("Sending data!")
              # send some data
              # s.send(bytes([0x01, 0x02, 0x03]))
              s.send(bytes([bytestream[4], bytestream[5], bytestream[6],bytestream[7]]))
              pybytes.send_virtual_pin_value(False, 0, (bytestream[4]<<8)+bytestream[5])
              time.sleep(1)
              pybytes.send_virtual_pin_value(False, 1, (bytestream[6]<<8)+bytestream[7])
              time.sleep(1)
              #pybytes.send_virtual_pin_value(False,2,100)
              #time.sleep(10)
      
          print('saving LoRa NVRAM')
          lora.nvram_save()
          # make the socket non-blocking
          # (because if there's no data received it will block forever...)
          s.setblocking(False)
          print("receiving data!")
          # get any data received (if any...)
          data = s.recv(64)
          print(data)
          print("Go to sleep 5 minutes!")
          time.sleep(300)```
      1 Reply Last reply Reply Quote 0
      • M Offline
        misterlisty
        last edited by

        I'm find a similar issue with Multitech Gateway and pycom but was assuming that Lorawan operates this way.

        I have a Multitech lora gateway in the same room as pycom and i get about 60% success rate!

        D 2 Replies Last reply Reply Quote 0
        • D Offline
          dnear1 @misterlisty
          last edited by dnear1

          @misterlisty
          It's early to tell, what with 5 minute delays between packets, but after sniffing packets on TTN all day to see what channels were received, I was able to figure out which 8 channels to add and get 4 packets in succession thru.

          from network import LoRa
          import socket
          import ubinascii
          import struct
          import time
          import os
          import math
          import abpkeys
          from machine import UART
          
          uart = UART(1, 9600)                         # init with given baudrate
          uart.init(9600, bits=8, parity=None, stop=1) # init with given parameters
          
          # Initialise 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.US915)
          
          # create an ABP authentication params
          #my keys are stored in abpkeys.py then linked here so I have the same source running 
          #on multiple devices
          dev_addr=abpkeys.dev_addr
          nwk_swkey=abpkeys.nwk_swkey
          app_swkey=abpkeys.app_swkey
          #dev_addr = struct.unpack(">l", ubinascii.unhexlify('xxxxxxxx'))[0]
          #nwk_swkey = ubinascii.unhexlify('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
          #app_swkey = ubinascii.unhexlify('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
          for i in range(0, 72):
              lora.remove_channel(i)
          print('Removed default channels')
          time.sleep(1)
              
              # Set US ISM 915 channel plan for TTN US
          lora.add_channel(0, frequency=903900000, dr_min=0, dr_max=3)
          lora.add_channel(1, frequency=904100000, dr_min=0, dr_max=3)
          lora.add_channel(2, frequency=904300000, dr_min=0, dr_max=3)
          lora.add_channel(3, frequency=904500000, dr_min=0, dr_max=3)
          lora.add_channel(4, frequency=904700000, dr_min=0, dr_max=3)
          lora.add_channel(5, frequency=904900000, dr_min=0, dr_max=3)
          Lora.add_channel(6, frequency=905100000, dr_min=0, dr_max=3)
          lora.add_channel(7, frequency=905300000, dr_min=0, dr_max=3)
          #channel	8:	903900000	hz	min_dr	0	max_dr	3				
          #channel	9:	904100000	hz	min_dr	0	max_dr	3				
          #channel	10:	904300000	hz	min_dr	0	max_dr	3				
          #channel	11:	904500000	hz	min_dr	0	max_dr	3				
          #channel	12:	904700000	hz	min_dr	0	max_dr	3				
          #channel	13:	904900000	hz	min_dr	0	max_dr	3				
          #channel	14:	905100000	hz	min_dr	0	max_dr	3				
          #channel	15:	905300000	hz	min_dr	0	max_dr	3				
          
          print('US channels set')
          time.sleep(1)
          
          
          print('restoring LoRa NVRAM')
          lora.nvram_restore()
          print('Joining LoRa via ABP')
          # join a network using ABP (Activation By Personalization)
          lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey))
          
          # 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, 3)
          #The first 5 seconds of dust data always seems irregular, so let's delay and clear the buffer
          data = uart.readall()
          time.sleep(10)
          data= uart.readall()
          time.sleep(1)# then give it another second for the next good byte to come in.
          while (1):
              readytosend=False
              print('polling Dust Sensor')
              data = uart.readall()# get everythitng from the serial buffer
              if data is not None:#make sure it'st not an empty buffer
                  bytestream=data.split(b'BM')[1][0:31]# HPMA sends 32 byte packets that start with \x42\x4D ('BM')
                  if len(bytestream)==30:#check tthat we actually got 30 bytes (excluding the data we split out)
                      if bytestream[0:2]==b'\x00\x1c':#HPMA packet is always 28 data bytes(excluding start header and checksum)
                          checksum=143#since split command removed the \x42\x4D, add them back to get correct checksum
                          validated=0
                          for i in range(0,28):
                              checksum+=bytestream[i]#cycle thru the data values and add them up
                          validated=int(bytestream[28])*256+int(bytestream[29])#read checksum bytes from packet and make them a 2-byte integer
                          if checksum==validated:# both values should match, else we have a corrupt packet
                              pm25=int(bytestream[4])*256+int(bytestream[5])#these two bytes represent the particulate detected that's 2.5um large
                              pm10=int(bytestream[6])*256+int(bytestream[7])#these two bytes represent the particulate detected that's 10um large
                              print('pm2.5 = ',pm25)
                              print('pm10 = ',pm10)
                              readytosend=True
                          else:
                              print('checksum failed')
                      else:
                          print('invalid packet length')
                  else:
                      print('incomplete packet')
              if readytosend==True:
                  # make the socket blocking
                  # (waits for the data to be sent and for the 2 receive windows to expire)
                  s.setblocking(True)
                  print("Sending data!")
                  # send some data
                  # s.send(bytes([0x01, 0x02, 0x03]))
                  s.send(bytes([bytestream[4], bytestream[5], bytestream[6],bytestream[7]]))
                  pybytes.send_virtual_pin_value(False, 0, (bytestream[4]<<8)+bytestream[5])
                  time.sleep(1)
                  pybytes.send_virtual_pin_value(False, 1, (bytestream[6]<<8)+bytestream[7])
                  time.sleep(1)
                  #pybytes.send_virtual_pin_value(False,2,100)
                  #time.sleep(10)
          
              print('saving LoRa NVRAM')
              lora.nvram_save()
              # make the socket non-blocking
              # (because if there's no data received it will block forever...)
              s.setblocking(False)
              print("receiving data!")
              # get any data received (if any...)
              data = s.recv(64)
              print(data)
              print("Go to sleep 5 minutes!")
              time.sleep(300)```
          J 1 Reply Last reply Reply Quote 0
          • J Offline
            jcaron @dnear1
            last edited by

            @dnear1 There are a lot of possible uplink channels in the US915 band (a total of 72). Most gateways (including the Multitech gateway) can only listen on a small subset of those channels at a given time.

            The Multitech gateway has a setting to select the "sub-band", which is a set of 8 consecutive channels. Apparently you're on the second sub-band (don't know if the sub-band numbering starts at 0 or 1, so it's either sub-band 1 or 2), which covers channels 8 to 15.

            Note: the range operator in python does not include the upper bound, so you want range(0,72), not range(0,71).

            D 1 Reply Last reply Reply Quote 0
            • D Offline
              dnear1 @misterlisty
              last edited by

              @misterlisty
              I still need to build a TTN utility to visualize the data captured, but based on when I have my TTN page open looking at data received, it looks like I'm getting greater than 95% packets at my close range. The web page doesn't seem to like being open for more than 45 minutes or so before I get some page error, but during that period, I seem to see packets at the 5 minute intervals set in my software. So it's definitely worth the effort to capture back any info you can about what channels are used and delete all the unused ones for a particular location..
              Now, if only I can find an automated way to do this... (not have to hard-code the channels but let the code figure them out..)

              1 Reply Last reply Reply Quote 0
              • D Offline
                dnear1 @jcaron
                last edited by

                @jcaron Thanks for recommending the range adjustment. I've added that to my code.

                1 Reply Last reply Reply Quote 0

                Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                With your input, this post could be even better 💗

                Register Login
                • 1 / 1
                • First post
                  Last post

                Pycom on Twitter

                Pycom