Example #1
0
    def periodic_event(self, arg):
        if self.data_type == "gps":
            # Get position from gps to be sent over LoRA
            _thread.start_new_thread(GpsSIM28.get_position,
                                     (self.logger, self.lora))

        elif self.data_type == "sensors":
            # Put new averages in lora buffer and also print to terminal.
            get_sensor_averages(logger=self.logger,
                                sensor_loggers=self.sensor_loggers,
                                lora=self.lora)

            if self.lora is not False:  # Schedule LoRa messages if LoRa is enabled
                # if device was last transmitting a day or more ago, reset message_count for the day
                today = time.gmtime()
                date = str(today[0]) + str(today[1]) + str(today[2])
                if self.lora.transmission_date != date:
                    self.lora.message_count = 0
                    self.lora.transmission_date = date
                    config.save_config({
                        "message_count": self.lora.message_count,
                        "transmission_date": date
                    })

                # send 2, 3 or at most 4 messages per interval based on length of interval
                lora_slot = int(
                    float(config.get_config("interval")) *
                    60) // 30  # lora_rate changes for each 30 seconds
                max_lora_slot = self.lora.message_limit // 96  # max number of msg per interval optimized around 15 min
                if max_lora_slot < 2:
                    max_lora_slot = 2
                if lora_slot < 2:
                    raise Exception("Interval has to be at least a minute")
                else:
                    lora_rate = lora_slot
                    if lora_slot > max_lora_slot:
                        lora_rate = max_lora_slot

                waiting = self.lora.lora_buffer.size(
                    lora_rate
                )  # check number of messages in stack (up to max to send)
                remaining = self.lora.message_limit - self.lora.message_count  # check how many more we can send today
                if remaining <= 0:
                    self.logger.info("LoRa message limit reached for the day")
                if remaining - waiting >= 0:
                    count = waiting  # if we have more than we want to send, send them all
                else:
                    count = remaining  # if we have less than we want to send, send up to the limit
                for val in range(
                        count
                ):  # Schedule up to 4 randomly timed messages within interval
                    self.random_alarm = Timer.Alarm(self.random_event,
                                                    s=get_random_time(),
                                                    periodic=False)

        else:
            raise Exception("Non existent data type")
Example #2
0
def process_data(received_data, logger):
    """
    Processes form sent by the user as a json string and saves new configurations. Also updates time on the RTC module.
    :param received_data: json string received from the web socket
    :type received_data: str
    :param logger: status logger
    :type logger: LoggerFactory
    :return: True or False
    :rtype: bool
    """
    #  find json string in received message
    first_index = received_data.rfind("time_begin")
    last_index = received_data.rfind("time_end")

    if first_index != -1 and last_index != -1:
        config_time_str = received_data[(first_index +
                                         len("time_begin")):last_index]
        config_time_lst = config_time_str.split(":")
        config_time_lst[0] = config_time_lst[0][2:]
        h_yr, h_mnth, h_day, h_hr, h_min, h_sec = (
            int(config_time_lst[0], 16),
            int(config_time_lst[1], 16),
            int(config_time_lst[2], 16),
            int(config_time_lst[3], 16),
            int(config_time_lst[4], 16),
            int(config_time_lst[5], 16),
        )
        try:
            clock.set_time(h_yr, h_mnth, h_day, h_hr, h_min, h_sec)
            logger.info("RTC module calibrated via WiFi")
        except Exception:
            logger.warning(
                "RTC module is not available for calibration via WiFi")

    #  find json string in received message
    first_index = received_data.rfind("json_str_begin")
    last_index = received_data.rfind("json_str_end")

    if first_index != -1 and last_index != -1:
        config_json_str = received_data[(first_index +
                                         len("json_str_begin")):last_index]
        new_config_dict = {
            "LORA": "OFF"
        }  # checkbox default value is false - gets overwritten if its true
        new_config_dict.update(ujson.loads(config_json_str))

        if len(config_json_str) >= 1000:
            logger.error("Received configurations are too long")
            logger.info("Enter configurations with valid length")
            return False  # keep looping - wait for new message from client

        logger.info("Configuration data received from user")
        config.save_config(new_config_dict)
        return True

    return False  # keep looping - wait for new message from client
Example #3
0
    def lora_send(self):
        """Lora send method to run as a thread. Checks if messages are up to date in the lora buffer, pops the one on
        top of the stack, encodes it to a message and sends it to the right port.
        Takes two dummy arguments required by the threading library"""

        if lora_lock.locked():
            self.logger.debug("Waiting for other lora thread to finish")
        with lora_lock:
            self.logger.debug("LoRa thread started")

            try:
                self.check_date()  # remove messages that are over a month old

                if self.lora.has_joined():
                    self.logger.debug("LoRa connected")
                else:
                    raise Exception("LoRa is not connected")

                if s.lora_file_name not in os.listdir(s.root_path +
                                                      s.processing):
                    raise Exception('LoRa - File: {} does not exist'.format(
                        s.lora_file_name))
                else:
                    port, payload = self.get_sending_details()

                    self.lora_socket.bind(
                        port)  # bind to port to decode at backend
                    self.lora_socket.send(
                        payload)  # send payload to the connected socket
                    self.logger.debug("LoRa - sent payload")

                    self.message_count += 1  # increment number of files sent over LoRa today

                    config.save_config({"message_count": self.message_count
                                        })  # save number of messages today

                    # remove message sent
                    self.lora_buffer.remove_head()

            except Exception:
                self.logger.exception("Sending payload over LoRaWAN failed")
                blink_led((0x550000, 0.4, True))
Example #4
0
    def lora_recv(self, arg):
        """
        Callback for receiving packets through LoRaWAN. Decodes messages to commands for updating over WiFi.
        Requires a dummy argument.
        """

        payload = self.lora_socket.recv(600)  # receive bytes message
        self.logger.info("Lora message received")
        msg = payload.decode()  # convert to string

        try:
            if msg == "0":  # reboot device
                self.logger.info("Reset triggered over LoRa")
                self.logger.info("Rebooting...")
                machine.reset()
            elif msg == "1":  # start software update
                self.logger.info("Software update triggered over LoRa")
                config.save_config({"update": True})
                machine.reset()
            else:
                split_msg = msg.split(":")
                if split_msg[0] == "2":  # update wifi credentials
                    self.logger.info("WiFi credentials updated over LoRa")
                    config.save_config({
                        "SSID": split_msg[1],
                        "wifi_password": split_msg[2]
                    })
                elif split_msg[
                        0] == "3":  # update wifi credentials and start software update
                    self.logger.info("WiFi credentials updated over LoRa")
                    config.save_config({
                        "SSID": split_msg[1],
                        "wifi_password": split_msg[2]
                    })
                    self.logger.info("Software update triggered over LoRa")
                    config.save_config({"update": True})
                    machine.reset()
                else:
                    self.logger.error("Unknown command received over LoRa")
        except Exception as e:
            self.logger.exception(
                "Failed to interpret message received over LoRa")
def software_update(logger):
    """
    Connects to the wlan and fetches updates from a server. After having applied the patches successfully, it reboots
    the device.
    :param logger: status logger
    :type logger: LoggerFactory object
    """

    with wifi_lock:
        try:
            logger.info("Over the Air update started")

            led_lock.acquire(1)  # disable all other indicator LEDs
            pycom.rgbled(0x555500)  # Yellow LED

            # Get credentials from configuration
            ssid = config.get_config("SSID")
            password = config.get_config("wifi_password")
            server_ip = config.get_config("server")
            port = int(config.get_config("port"))

            logger.info("SSID: " + str(ssid))
            logger.info("server_ip: " + str(server_ip))
            logger.info("port: " + str(port))

            version = config.get_config("code_version")

            # Perform OTA update
            ota = WiFiOTA(logger, ssid, password, server_ip, port, version)

            # Turn off WiFi to save power
            w = WLAN()
            w.deinit()

            logger.info("connecting...")
            ota.connect()
            if ota.update():
                new_version = str(
                    ota.get_current_version())  # get updated version
                config.save_config({"code_version": str(new_version)
                                    })  # save new version to config on SD
                logger.info(
                    "Successfully updated the device from version {} to version {}"
                    .format(version, new_version))

        except Exception as e:
            logger.exception("Failed to update the device")
            pycom.rgbled(0x550000)  # turn LED to RED
            time.sleep(3)

        finally:
            # Turn off update mode
            config.save_config({"update": False})

            # Turn off indicator LED
            pycom.rgbled(0x000000)
            led_lock.release()

            # Reboot the device to apply patches
            logger.info("rebooting...")
            machine.reset()
Example #6
0
            reset()

try:
    from initialisation import initialise_time
    from ubinascii import hexlify
    from Configuration import config
    from setup import setup_new_config
    from software_update import software_update
    import ujson

    # Read configuration file to get preferences
    config.read_configuration()
    """SET CODE VERSION NUMBER - if new tag is added on git, update code version number accordingly"""
    # ToDo: Update OTA.py so if version is 0.0.0, it backs up all existing files, and adds all files as new.
    # ToDo: Set code_version to '0.0.0' in default config, and remove the line below
    config.save_config({"code_version": "0.2.6"})
    """SET FORMAT VERSION NUMBER - version number is used to indicate the data format used to decode LoRa messages in
    the back end. If the structure of the LoRa message is changed during update, increment the version number and
    add a corresponding decoder to the back-end."""
    config.save_config({"fmt_version": 1})

    # Override Preferences - DEVELOPER USE ONLY - keep all overwrites here
    if "debug_config.json" in os.listdir("/flash"):
        status_logger.warning(
            "Overriding configuration with the content of debug_config.json")
        with open("/flash/debug_config.json", "r") as f:
            config.set_config(ujson.loads(f.read()))
            status_logger.warning("Configuration changed to: " +
                                  str(config.get_config()))

    # Check if GPS is enabled in configurations
Example #7
0
    def __init__(self, logger):
        """
        Connects to LoRaWAN using OTAA and sets up a ring buffer for storing messages.
        :param logger: status logger
        :type logger: LoggerFactory object
        """

        self.logger = logger
        self.message_limit = int(
            float(config.get_config("fair_access")) /
            (float(config.get_config("air_time")) / 1000))
        self.transmission_date = config.get_config(
            "transmission_date")  # last date when lora was transmitting
        today = time.gmtime()
        date = str(today[0]) + str(today[1]) + str(today[2])
        if self.transmission_date == date:  # if device was last transmitting today
            self.message_count = config.get_config(
                "message_count")  # get number of messages sent today
        else:
            self.message_count = 0  # if device was last transmitting a day or more ago, reset message_count for the day
            self.transmission_date = date
            config.save_config({
                "message_count": self.message_count,
                "transmission_date": date
            })

        regions = {
            "Europe": LoRa.EU868,
            "Asia": LoRa.AS923,
            "Australia": LoRa.AU915,
            "United States": LoRa.US915
        }
        region = regions[config.get_config("region")]

        self.lora = LoRa(mode=LoRa.LORAWAN, region=region, adr=True)

        # create an OTAA authentication parameters
        app_eui = ubinascii.unhexlify(config.get_config("application_eui"))
        app_key = ubinascii.unhexlify(config.get_config("app_key"))

        # join a network using OTAA (Over the Air Activation)
        self.lora.join(activation=LoRa.OTAA,
                       auth=(app_eui, app_key),
                       timeout=0)

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

        # request acknowledgment of data sent
        # self.lora_socket.setsockopt(socket.SOL_LORA, socket.SO_CONFIRMED, True)

        # do not request acknowledgment of data sent
        self.lora_socket.setsockopt(socket.SOL_LORA, socket.SO_CONFIRMED,
                                    False)

        # sets timeout for sending data
        self.lora_socket.settimeout(
            int(config.get_config("lora_timeout")) * 1000)

        # set up callback for receiving downlink messages
        self.lora.callback(trigger=LoRa.RX_PACKET_EVENT,
                           handler=self.lora_recv)

        # initialises circular lora stack to back up data up to about 22.5 days depending on the length of the month
        self.lora_buffer = RingBuffer(self.logger, s.processing_path,
                                      s.lora_file_name,
                                      31 * self.message_limit, 100)

        try:  # this fails if the buffer is empty
            self.check_date()  # remove messages that are over a month old
        except Exception as e:
            pass