Guest User

beaconTracking Lopy4

a guest
Sep 3rd, 2020
49
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import ubinascii
  2. import time
  3. import sys
  4. from machine import Timer
  5. import socket
  6. from network import Bluetooth
  7. from network import LoRa
  8. from machine import SD
  9. import os
  10. import math
  11. import gc
  12. import pycom
  13. from network import WLAN
  14.  
  15.  
  16.  
  17. ### Parameter ###
  18. SCAN_DURATION_SEC = 30 # WARNING: affects lora send interval; Default at 300 seconds
  19. ENABLE_LORA = True
  20. EXT_ANTENNA = True # Set True when using an external antenna (Bluetooth)
  21. ENABLE_SCANNING = True # Enable Bluetooth Scanning for the duration of SCAN_DURATION, then sends lora messae to Backend
  22. sd_enabled = True # SD Card has to be formatted to either fat16 or fat32
  23.  
  24.  
  25. dev_eui = ubinascii.unhexlify('xxxxxxxxxxxxx')
  26. app_eui = ubinascii.unhexlify('xxxxxxxxxxxxx')
  27. app_key = ubinascii.unhexlify('xxxxxxxxxxxxxxxxxxxxxxxxxx')
  28.  
  29. CHAR_SIZE = 8
  30. LORA_PAYLOAD_MAX_SIZE = 52 # limitation of lora radion transmission under given lora parameters
  31. PAYLOAD_MAX_SIZE = 49 # multiple of 7 that is closest to lora payload max size
  32. UUID_PAYLOAD_LOWER_BOUND = 20
  33. UUID_PAYLOAD_UPPER_BOUND = 52
  34. MAJOR_LOWER_BOUND = 52
  35. MAJOR_UPPER_BOUND = 56
  36. MINOR_LOWER_BOUND = 56
  37. MINOR_UPPER_BOUND = 60
  38.  
  39. # Hilfsfunktionen
  40. def pad_hex_string(s):
  41.     if(len(s)%2 != 0):
  42.         s = b'0' + s
  43.     return s
  44.  
  45. def standard_variance(values):
  46.     sum = 0
  47.     average = avg(values)
  48.     for x in values:
  49.         expression = math.pow(x - average, 2)
  50.         sum += expression
  51.     return math.sqrt(sum / (len(values)-1))
  52.  
  53. def avg(values):
  54.     sum = 0
  55.     for e in values:
  56.         sum += e
  57.     return sum / len(values)
  58.  
  59. def s16(value):
  60.     return -(value & 0x80) | (value & 0x7f)
  61.  
  62. def tohex(val, nbits):
  63.   return hex((val + (1 << nbits)) % (1 << nbits))[2:]
  64.  
  65. def isInTransmitBuffer(mac):
  66.     x = 0
  67.     for i in transmit_buffer:
  68.         if i["mac"] == mac:
  69.             return x
  70.         x = x+1
  71.  
  72.     return -1
  73.  
  74. def rssi_for_mac_existing(mac):
  75.     for i in transmit_buffer:
  76.         if i.rssi_values:
  77.             return True
  78.     return False
  79.  
  80. def blink(color_hex1, color_hex2, ntimes, sleep_between_sec):
  81.     """
  82.    Makes the Lopy4 blink as desired, used for visual representation of internal states in case no console output can be accessed
  83.    """
  84.     for i in range(ntimes):
  85.         pycom.rgbled(color_hex1)
  86.         time.sleep(sleep_between_sec)
  87.         pycom.rgbled(color_hex2)
  88.         time.sleep(sleep_between_sec)
  89.  
  90. def segment_payload(payload, packet_max_size=PAYLOAD_MAX_SIZE):
  91.     """
  92.    Takes a payload and divides it into smaller payloads as to not exceed the maxmimum payload size limit of lorawan
  93.    """
  94.     fitted_payloads = []
  95.     offset = 0
  96.     length = len(payload)
  97.     while offset*packet_max_size < length:
  98.         fitted_payloads.append(payload[offset*packet_max_size : offset*packet_max_size+packet_max_size])
  99.         offset += 1
  100.     return fitted_payloads
  101.  
  102. def scanToBuffer(dur_sec): # alarm als param rausgenommen!
  103.     pycom.rgbled(0x0000CC)
  104.     print("Scanning to buffer...")
  105.     #bluetooth.init(antenna=Bluetooth.EXT_ANT)
  106.     bluetooth.start_scan(dur_sec)
  107.     if sd_enabled:
  108.         f = open('/sd/log.txt', 'a')
  109.         f.write("\n" + str(time.time()) + "   Free Memory: " + str(gc.mem_free() / 1024) + " Kilobyte --- Starting BLE Scanning...")
  110.         f.close()
  111.  
  112.     while bluetooth.isscanning():
  113.         adv = bluetooth.get_adv()
  114.         if adv:
  115.             adata = str(ubinascii.hexlify(adv.data))
  116.             uuid = adata[UUID_PAYLOAD_LOWER_BOUND:UUID_PAYLOAD_UPPER_BOUND]
  117.             major = adata[MAJOR_LOWER_BOUND:MAJOR_UPPER_BOUND]
  118.             minor = adata[MINOR_LOWER_BOUND:MINOR_UPPER_BOUND]
  119.             ble_mac = ubinascii.hexlify(adv.mac)
  120.  
  121.             #if ubinascii.hexlify(adv.mac) == b'cc78ab1f7e94' or adv.mac == "cc78ab1f7e94" or adv.mac == b'cc/78/ab/1f/7e/94':
  122.             #               for Axaet PC062 Beacons                    for Minew i3 & Minew e9 Beacons (small black, and rectangular white)
  123.             #if uuid == 'fda50693a4e24fb1afcfc6eb07647825' or uuid == 'e2c56db5dffb48d2b060d0f5a71096e0':
  124.             if uuid == 'fda50693a4e24fb1afcfc6eb0764affe' or uuid == 'e2c56db5dffb48d2b060d0f5a710affe':
  125.                 # Check if beacons already exists in transmit buffer
  126.                 isExisting = False
  127.                 for obj in transmit_buffer:
  128.                     if ble_mac == obj["mac"]:
  129.                         # beacon exists, just add new rssi datapoint
  130.                         print(".", end="")
  131.                         obj["rssi_list"].append(adv.rssi)
  132.                         obj["num"] += 1
  133.                         isExisting = True
  134.                         break
  135.  
  136.                 # beacon does not exist, create new data object
  137.                 if isExisting is False:
  138.                     rssi_list = []
  139.                     rssi_list.append(adv.rssi)
  140.                     data_obj = {"mac": ble_mac, "rssi_list": rssi_list, "major": major, "minor": minor, "num": 0}
  141.                     transmit_buffer.append(data_obj)
  142.                     print("New Beacon found, added to transmit buffer")
  143.                 blink(0x000000, 0x0000FF, 1, 0.03)
  144.     pycom.rgbled(0x000000)
  145.     if sd_enabled:
  146.         f = open('/sd/log.txt', 'a')
  147.         f.write("\n" + str(time.time()) + "   Finished Scanning! --- Free Memory:" + str(gc.mem_free() / 1024) + " Kilobyte")
  148.         f.close()
  149.  
  150. def print_rssi_list_and_send():
  151.     print("\n*********** RESULTS OF CYCLE **************")
  152.     lora_send_buffer = ""
  153.     for index in range(len(transmit_buffer)):
  154.         print("\n")
  155.         try:
  156.             obj = transmit_buffer[index]
  157.         except:
  158.             print(" error popping transmit buffer")
  159.  
  160.         print(" ",obj["mac"], "Number of measurements in total:", obj["num"])
  161.        
  162.         # Sortieren
  163.         rssi_list = obj["rssi_list"]
  164.         rssi_list.sort()
  165.         print(" after sorting:", rssi_list)
  166.  
  167.         # Min, Max und Median
  168.         min_value = rssi_list[0]
  169.         median = rssi_list[round(len(rssi_list)/2) -1]
  170.         max_value = rssi_list[len(rssi_list) -1]
  171.  
  172.         # 1. Quantil
  173.         pos = round(len(rssi_list) * 0.75) -1
  174.         if pos < 0: pos = 0 # validate index to not index at -1 if len is 1
  175.         q1 = rssi_list[pos]
  176.  
  177.         # 3. Quantil
  178.         pos = round(len(rssi_list) * 0.25) -1
  179.         if pos < 0: pos = 0
  180.         q3 = rssi_list[pos]
  181.  
  182.         # Interquantile range
  183.         iqr = q1 - q3
  184.  
  185.         # Standardvarianz & Durchschnitt
  186.         std = standard_variance(rssi_list)
  187.         average = avg(rssi_list)
  188.  
  189.         print(" min:", min_value, "q3:", q3, "median:", median, "q1:", q1, "max:", max_value)
  190.        
  191.         lora_send_buffer += obj["mac"].decode("utf-8")
  192.         lora_send_buffer += str(tohex(max_value, CHAR_SIZE))
  193.  
  194.         transmit_buffer[index]["rssi_list"].clear()
  195.         transmit_buffer[index]["num"] = 0
  196.    
  197.     length = len(lora_send_buffer)
  198.     if length == 0: # Send 00 as payload, because empty payloads are not forwarded by lorIOT
  199.         lora_send_buffer += "00"
  200.         length += 2
  201.     #print("last 3 values in hex string:", lora_send_buffer[(length-7):(length-1)])
  202.  
  203.     print("\n Lora Send Buffer:", lora_send_buffer, "Length:", length)
  204.     lora_send_bytes = ubinascii.unhexlify(lora_send_buffer)
  205.  
  206.     if ENABLE_LORA:
  207.         fitted_payloads = segment_payload(lora_send_bytes)
  208.         for payload in fitted_payloads:
  209.             print("Sending:", payload, end=" ")
  210.             print("of Length:", len(payload), "Bytes")
  211.             while(True):    # while True and break emulates a do-while loop
  212.                 try:
  213.                     if sd_enabled:
  214.                         f = open('/sd/log.txt', 'a')
  215.                         f.write("\n" + str(time.time()) + "   About to send :" + str(ubinascii.hexlify(payload)))
  216.                         f.close()
  217.                     print("lorawan has joined status:", lora.has_joined())
  218.                     s.send(payload)
  219.                     print("Success!")
  220.                     if sd_enabled:
  221.                         f = open('/sd/log.txt', 'a')
  222.                         f.write("\n" + str(time.time()) + "   Sent lorawan packet:" + str(ubinascii.hexlify(payload)))
  223.                         f.close()
  224.                     blink(0x26734d, 0x000000, 5, 0.1) # pinewood green for success
  225.                     break
  226.                 except OSError as err:
  227.                     print("OS error: {0}".format(err))
  228.                     blink(0xFF0000, 0x000000, 30, 0.1)
  229.                     if sd_enabled:
  230.                         f = open('/sd/log.txt', 'a')
  231.                         f.write("\n" + str(time.time()) + "   Sending failed: {0}".format(err))
  232.                         f.close()
  233.                     break
  234.     else:
  235.         print("Not sending, lora function is disabled")
  236.  
  237. def lora_tx_cb(lora):
  238.     events = lora.events()
  239.     print("Lora Tx callback invoked")
  240.     if events & LoRa.TX_FAILED_EVENT:
  241.         print('--- Tx-Callback: Transmit Failed Mask has been set')
  242.     else:
  243.         print(" --- Tx-Callback: Transmit Failed Mask has NOT been set!")
  244.  
  245. # Disable Heartbeat to enable custom LED usage
  246. pycom.heartbeat(False)
  247.  
  248. # Initialize SD Card for logging
  249. sd = None # foward declaration to enable global use while also defining in the following sub-scope
  250. f = None
  251. if sd_enabled:
  252.     try: # try mounting
  253.         sd = SD()
  254.         os.mount(sd, '/sd')
  255.  
  256.         f = open('/sd/log.txt', 'a')
  257.         f.write("\n" + str(time.time()) + '   --- Device Booted ---')
  258.         f.close()
  259.         print("written initial sequence to SD card")
  260.         blink
  261.     except OSError as err:
  262.         print('Error mounting SD Card: ', err)
  263.         sd_enabled = False
  264.  
  265. # Disable Wifi, it is enabled by default
  266. wlan = WLAN(mode=WLAN.STA)
  267. wlan.deinit()
  268.  
  269. # LoRa Initialization:
  270. lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868, adr=True)
  271. lora.init(mode=LoRa.LORAWAN, power_mode=LoRa.ALWAYS_ON)
  272. lora.callback(trigger=LoRa.TX_PACKET_EVENT, handler=lora_tx_cb)
  273. s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
  274. s.setsockopt(socket.SOL_LORA, socket.SO_DR, 0)
  275. s.setsockopt(socket.SOL_LORA, socket.SO_CONFIRMED, False)
  276. s.setblocking(True) # only important when receiving and for waiting the 2 received windows after sending
  277.  
  278. # Joining the Network
  279. if ENABLE_LORA is True:
  280.     if not lora.has_joined():
  281.         lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_eui, app_key), timeout=0, dr=0)
  282.     trying_counter = 0
  283.     while not lora.has_joined():
  284.         time.sleep(3)
  285.         trying_counter += 3
  286.         print('Not yet joined, have been trying for', trying_counter, 'seconds...')
  287.         blink(0x0000FF, 0x000000, 1, 0.2)
  288.     print("Connected to LoRaWAN Network!")
  289.     if sd_enabled:
  290.         f = open('/sd/log.txt', 'a')
  291.         f.write("\n" + str(time.time()) + '   joined lorawan network')
  292.         f.close
  293.     blink(0x00FF00, 0x000000, 10, 0.1)
  294.  
  295.  
  296.  
  297.  
  298. # Bluetooth Initialization
  299. print("Initializing Bluetooth...", end="")
  300. transmit_buffer = []
  301. bluetooth = Bluetooth()
  302. if EXT_ANTENNA is True:
  303.     bluetooth = Bluetooth(antenna=Bluetooth.EXT_ANT)
  304.     bluetooth.init(antenna=Bluetooth.EXT_ANT)
  305. else:
  306.     bluetooth.init()
  307. print("Done!")
  308.  
  309. # Printing available RAM, just for debug purposes
  310. print("--- Free Mem before main loop:", gc.mem_free() / 1024, " Kilobytes ---")
  311.  
  312. # Main loop
  313. running = True
  314. while running:
  315.     if ENABLE_SCANNING:
  316.         scanToBuffer(SCAN_DURATION_SEC)
  317.         print_rssi_list_and_send()
  318.         print("Remaining Memory: ", gc.mem_free() / 1024, "Kilobytes of RAM")
  319.  
RAW Paste Data