Esempio n. 1
0
class LoraNet:
    def __init__(self,
                 frequency,
                 dr,
                 region,
                 device_class=LoRa.CLASS_C,
                 activation=LoRa.OTAA,
                 auth=None):
        self.frequency = frequency
        self.dr = dr
        self.region = region
        self.device_class = device_class
        self.activation = activation
        self.auth = auth
        self.sock = None
        self._exit = False
        self.s_lock = _thread.allocate_lock()
        self.lora = LoRa(mode=LoRa.LORAWAN,
                         region=self.region,
                         device_class=self.device_class)

        self._msg_queue = []
        self.q_lock = _thread.allocate_lock()
        self._process_ota_msg = None

    def stop(self):
        self._exit = True

    def init(self, process_msg_callback):
        self._process_ota_msg = process_msg_callback

    def receive_callback(self, lora):
        events = lora.events()
        if events & LoRa.RX_PACKET_EVENT:
            rx, port = self.sock.recvfrom(256)
            if rx:
                if '$OTA' in rx:
                    print("OTA msg received: {}".format(rx))
                    self._process_ota_msg(rx.decode())
                else:
                    self.q_lock.acquire()
                    self._msg_queue.append(rx)
                    self.q_lock.release()

    def connect(self):
        if self.activation != LoRa.OTAA and self.activation != LoRa.ABP:
            raise ValueError("Invalid Lora activation method")
        if len(self.auth) < 3:
            raise ValueError("Invalid authentication parameters")

        self.lora.callback(trigger=LoRa.RX_PACKET_EVENT,
                           handler=self.receive_callback)

        # set the 3 default channels to the same frequency
        self.lora.add_channel(0, frequency=self.frequency, dr_min=0, dr_max=5)
        self.lora.add_channel(1, frequency=self.frequency, dr_min=0, dr_max=5)
        self.lora.add_channel(2, frequency=self.frequency, dr_min=0, dr_max=5)

        # remove all the non-default channels
        for i in range(3, 16):
            self.lora.remove_channel(i)

        # authenticate with abp or ota
        if self.activation == LoRa.OTAA:
            self._authenticate_otaa(self.auth)
        else:
            self._authenticate_abp(self.auth)

        # create socket to server
        self._create_socket()

    def _authenticate_otaa(self, auth_params):

        # create an OTAA authentication params
        self.dev_eui = binascii.unhexlify(auth_params[0])
        self.app_eui = binascii.unhexlify(auth_params[1])
        self.app_key = binascii.unhexlify(auth_params[2])

        self.lora.join(activation=LoRa.OTAA,
                       auth=(self.dev_eui, self.app_eui, self.app_key),
                       timeout=0,
                       dr=self.dr)

        while not self.lora.has_joined():
            time.sleep(2.5)
            print('Not joined yet...')

    def has_joined(self):
        return self.lora.has_joined()

    def _authenticate_abp(self, auth_params):
        # create an ABP authentication params
        self.dev_addr = struct.unpack(">l",
                                      binascii.unhexlify(auth_params[0]))[0]
        self.nwk_swkey = binascii.unhexlify(auth_params[1])
        self.app_swkey = binascii.unhexlify(auth_params[2])

        self.lora.join(activation=LoRa.ABP,
                       auth=(self.dev_addr, self.nwk_swkey, self.app_swkey))

    def _create_socket(self):

        # create a LoRa socket
        self.sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW)

        # set the LoRaWAN data rate
        self.sock.setsockopt(socket.SOL_LORA, socket.SO_DR, self.dr)

        # make the socket non blocking
        self.sock.setblocking(False)

        time.sleep(2)

    def send(self, packet):
        with self.s_lock:
            self.sock.send(packet)

    def receive(self, bufsize):
        with self.q_lock:
            if len(self._msg_queue) > 0:
                return self._msg_queue.pop(0)
        return ''

    def get_dev_eui(self):
        return binascii.hexlify(self.lora.mac()).decode('ascii')

    def change_to_multicast_mode(self, mcAuth):
        print('Start listening for firmware updates ...........')

        if self.device_class != LoRa.CLASS_C:
            self.lora = LoRa(mode=LoRa.LORAWAN,
                             region=self.region,
                             device_class=LoRa.CLASS_C)
            self.connect()

        mcAddr = struct.unpack(">l", binascii.unhexlify(mcAuth[0]))[0]
        mcNwkKey = binascii.unhexlify(mcAuth[1])
        mcAppKey = binascii.unhexlify(mcAuth[2])

        self.lora.join_multicast_group(mcAddr, mcNwkKey, mcAppKey)
def loraTask():
    global lora_wdt_lock, lora_stop_flag
    global QueueDelay

    logger = Logger(name='LORA ' + __version__,
                    level=logging.DEBUG,
                    filename=None)
    logger.debug('** LORA Task started **')

    lora = LoRa(mode=LoRa.LORAWAN,
                region=LoRa.EU868,
                adr=True,
                public=True,
                device_class=LoRa.CLASS_C)
    lorawan_socket = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
    lorawan_socket.setblocking(False)

    RadioOffset = getConfigurationVariable(NVS_RADIO_DELAY)
    # defautl is OTAA (old config file may not have this defined)
    JM = 0
    try:
        from deviceid import lora_join_method
        JM = lora_join_method
    except:
        pass

    #add the multicasts
    try:
        from deviceid import multicasts
        for multicast_address in multicasts:
            multicast_devAddr = multicast_address[0]
            multicast_NwkSKey = multicast_address[1]
            multicast_AppSKey = multicast_address[2]
            try:
                lora.join_multicast_group(multicast_devAddr, multicast_NwkSKey,
                                          multicast_AppSKey)
            except:
                pass

    except:
        pass

    # define the reserved Downlink minutes if it exists
    DRM = [8]
    try:
        from deviceid import downlinks_reserved_minutes
        DRM = downlinks_reserved_minutes
    except:
        pass

    if pycom.nvs_get(POWER_FAIL_STATE) == MODE_POWER_FAIL:
        # SPECIAL CASE FOR POWER FAIL AFTER DEEP SLEEP WAKE UP
        lora.nvram_restore()
        processMsgImmediate(
            LoraQueueImmediate, logger, lora,
            lorawan_socket)  # the PF message should already be in the queue
        if lora_queue_immediate_semaphore.locked():
            lora_queue_immediate_semaphore.release(
            )  # tell the main thread we are done

        time.sleep(10)

    if JM == OTAA_JOIN:
        logger.debug("Attempting OTAA Join Kotahi.net...using device EUI " +
                     str(binascii.hexlify(dev_eui)))
        lora.join(activation=LoRa.OTAA,
                  auth=(dev_eui, app_eui, app_key),
                  timeout=0)
        counter = 0
        while not lora.has_joined():
            time.sleep(1)
            counter = counter + 1
            if lora_wdt_lock.locked():
                lora_wdt_lock.release(
                )  # tell the WDT that LoRa task is busy waiting to join
        logger.debug("Join successful after " + str(counter) + "s")
    elif JM == ABP_JOIN:
        from deviceid import dev_addr, nwk_swkey, app_swkey
        logger.debug("Joined Kotahi.net using ABP with EUI " +
                     str(binascii.hexlify(dev_eui)))
        lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey))
        #time.sleep(3)
    # save the state in NVS
    lora.nvram_save()

    #lora.nvram_save()

    # Lora loop starts here, processing the QOS queues

    while not lora_stop_flag.locked():

        # wait 1s while checking for any message to be sent immediately (e.g PF Alarm)
        counter = 10
        while counter > 0:
            if processMsgImmediate(LoraQueueImmediate, logger, lora,
                                   lorawan_socket) == True:
                if lora_queue_immediate_semaphore.locked():
                    lora_queue_immediate_semaphore.release(
                    )  # signal that the immediate msg has been sent
                time.sleep(1)
            counter = counter - 1
            time.sleep_ms(100)

        # Process any downlinks
        lora_rx_data(lora, lorawan_socket, logger)
        if lora_wdt_lock.locked():
            lora_wdt_lock.release(
            )  # tell the WDT that LoRa task is doing just fine

        unixLocalTime = time.time() + time.timezone()

        timetuple = time.localtime()
        maskUplinks = False
        for minute in DRM:
            if (timetuple[4] % 10 == minute):
                maskUplinks = True
                break

        # process the uplinks using basic QOS Queues
        if maskUplinks == False:
            processQOSQueue(LoraQueueP5, 5, unixLocalTime, logger, lora,
                            lorawan_socket, RadioOffset)
            processQOSQueue(LoraQueueP4, 4, unixLocalTime, logger, lora,
                            lorawan_socket, RadioOffset)
            processQOSQueue(LoraQueueP3, 3, unixLocalTime, logger, lora,
                            lorawan_socket, RadioOffset)
            processQOSQueue(LoraQueueP2, 2, unixLocalTime, logger, lora,
                            lorawan_socket, RadioOffset)
            processQOSQueue(LoraQueueP1, 1, unixLocalTime, logger, lora,
                            lorawan_socket, RadioOffset)

    logger.error('** LORA Task ended **')