Exemple #1
0
def get_new_config(sct, logger):
    """
    Sends an html form to a web socket, and waits for the user to connect
    :param sct: web socket
    :type sct: socket object
    :param logger: status logger
    :type logger: LoggerFactory object
    """
    try:
        while True:
            try:
                client, address = sct.accept()  # wait for new connection
            except Exception as e:
                raise Exception("Configuration timeout")
            client.send(get_html_form()
                        )  # send html page with form to submit by the user
            pycom.rgbled(0x005500)  # Green LED - Connection successful
            received_data = str(client.recv(3000))  # wait for client response
            # logger.debug(received_data)
            client.close()  # socket has to be closed because of the loop
            if process_data(received_data, logger):
                return
    except Exception as e:
        logger.exception("Failed to configure the device")
        led_lock.release()
        blink_led((0x550000, 3, True))  # Red LED - Error
        return
Exemple #2
0
def process_readings(args):
    """
    Method to be evoked by a timed alarm, which reads and processes data from the PM sensor, and logs it to the sd card
    :param args: sensor_type, sensor, sensor_logger, status_logger
    :type args: str, str, SensorLogger object, LoggerFactory object
    """

    sensor_type, sensor, sensor_logger, status_logger = args[0], args[1], args[
        2], args[3]

    try:
        recv = sensor.read()
        if recv:
            recv_lst = str(recv).split(',')
            curr_timestamp = recv_lst[0]
            sensor_reading_float = [float(i) for i in recv_lst[1:]]
            sensor_reading_round = [round(i) for i in sensor_reading_float]
            lst_to_log = [curr_timestamp
                          ] + [str(i) for i in sensor_reading_round]
            line_to_log = ','.join(lst_to_log)
            sensor_logger.log_row(line_to_log)
    except Exception as e:
        status_logger.error(
            "Failed to read from sensor {}".format(sensor_type))
        blink_led((0x550000, 0.4, True))
Exemple #3
0
def get_sensor_averages(logger, lora):
    """
    Gets averages for specific columns of defined sensor data, loads them into a line and appends it to the file which
    the LoRa thread sends data from.
    A sensor is defined if it is ticked in the configuration and has data to be sent (has a current.csv file).
    :type logger: LoggerFactory object (status_logger)
    :param is_def: Stores which sensors are defined in the form "sensor_name" : True/False
    :type is_def: dict
    """

    logger.debug("Calculating averages")

    # get a dictionary of sensors and their status
    sensors = get_sensors()
    fmt = get_format(sensors)
    version = str(config.get_config("version"))
    timestamp = s.csv_timestamp_template.format(
        *time.gmtime())  # get current time in desired format
    minutes = str(minutes_of_the_month())  # get minutes past last midnight

    try:
        sensor_averages = {}
        for sensor_name in [s.TEMP, s.PM1, s.PM2]:
            if sensors[sensor_name]:
                sensor_averages.update(calculate_average(sensor_name, logger))

        # Append averages to the line to be sent over LoRa according to which sensors are defined.
        line_to_log = '{}' + fmt + ',' + version + ',' + minutes
        for sensor_name in [s.TEMP, s.PM1, s.PM2]:
            if sensors[sensor_name]:
                line_to_log += ',' + str(
                    config.get_config(sensor_name + "_id")) + ',' + ','.join(
                        sensor_averages[sensor_name + "_avg"]) + ',' + str(
                            sensor_averages[sensor_name + "_count"])
        line_to_log += '\n'

        # Logs line_to_log to archive and places copies into relevant to_send folders
        log_averages(line_to_log.format(timestamp + ','))
        if config.get_config("LORA") == "ON":
            year_month = timestamp[2:4] + "," + timestamp[5:7] + ','
            lora.lora_buffer.write(line_to_log.format(year_month))

        # If raw data was processed, saved and dumped, processing files can be deleted
        path = s.processing_path
        for sensor_name in [s.TEMP, s.PM1, s.PM2]:
            if sensors[sensor_name]:
                filename = sensor_name + '.csv'
                try:
                    os.remove(path + filename)
                except Exception as e:
                    logger.exception("Failed to remove " + filename + " in " +
                                     path)

    except Exception as e:
        logger.exception("Failed to flash averages")
        blink_led((0x550000, 0.4, True))
def get_sensor_averages(logger, lora):
    """
    Takes the averages of sensor readings and constructs a line to log to the SD card, terminal and lora buffer
    :param logger: status logger
    :type logger: LoggerFactory object
    :param lora: LoRaWAN object, False if lora is not enabled
    :type lora: LoRaWAN object
    """

    logger.debug("Calculating averages")

    # get a dictionary of sensors and their status
    sensors = get_sensors()
    fmt = get_format(sensors)
    version = str(config.get_config("fmt_version"))
    timestamp = s.csv_timestamp_template.format(
        *time.gmtime())  # get current time in desired format
    minutes = str(minutes_of_the_month())  # get minutes past last midnight

    try:
        sensor_averages = {}
        for sensor_name in [s.TEMP, s.PM1, s.PM2]:
            if sensors[sensor_name]:
                sensor_averages.update(calculate_average(sensor_name, logger))

        # Append averages to the line to be sent over LoRa according to which sensors are defined.
        line_to_log = '{}' + fmt + ',' + version + ',' + minutes
        for sensor_name in [s.TEMP, s.PM1, s.PM2]:
            if sensors[sensor_name]:
                line_to_log += ',' + str(
                    config.get_config(sensor_name + "_id")) + ',' + ','.join(
                        sensor_averages[sensor_name + "_avg"]) + ',' + str(
                            sensor_averages[sensor_name + "_count"])
        line_to_log += '\n'

        # Logs line_to_log to archive and places copies into relevant to_send folders
        log_averages(line_to_log.format(timestamp + ','))
        if lora is not False:
            year_month = timestamp[2:4] + "," + timestamp[5:7] + ','
            lora.lora_buffer.write(line_to_log.format(year_month))

        # If raw data was processed, saved and dumped, processing files can be deleted
        path = s.processing_path
        for sensor_name in [s.TEMP, s.PM1, s.PM2]:
            if sensors[sensor_name]:
                filename = sensor_name + '.csv'
                try:
                    os.remove(path + filename)
                except Exception as e:
                    pass

    except Exception as e:
        logger.exception("Failed to flash averages")
        blink_led((0x550000, 0.4, True))
Exemple #5
0
 def process_readings(self, arg):
     # read and log pm sensor data
     try:
         timestamp = csv_timestamp_template.format(
             *time.gmtime())  # get current time in desired format
         read_lst = self.read(
         )  # read SHT35 sensor - [celsius, humidity] to ~5 significant figures
         round_lst = [
             int(round(x, 1) * 10) for x in read_lst
         ]  # round readings to 1 significant figure, shift left, cast to int
         str_round_lst = list(map(str, round_lst))  # cast int to string
         lst_to_log = [timestamp] + str_round_lst
         line_to_log = ','.join(lst_to_log)
         self.sensor_logger.log_row(line_to_log)
     except Exception as e:
         self.status_logger.exception(
             "Failed to read from temperature and humidity sensor")
         blink_led((0x550000, 0.4, True))
Exemple #6
0
def process_readings(args):
    sensor_type, sensor, sensor_logger, status_logger = args[0], args[1], args[
        2], args[3]

    try:
        recv = sensor.read()
        if recv:
            recv_lst = str(recv).split(',')
            curr_timestamp = recv_lst[0]
            sensor_reading_float = [float(i) for i in recv_lst[1:]]
            sensor_reading_round = [round(i) for i in sensor_reading_float]
            lst_to_log = [curr_timestamp
                          ] + [str(i) for i in sensor_reading_round]
            line_to_log = ','.join(lst_to_log)
            sensor_logger.log_row(line_to_log)
    except Exception as e:
        status_logger.error(
            "Failed to read from sensor {}".format(sensor_type))
        blink_led((0x550000, 0.4, True))
Exemple #7
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))
Exemple #8
0
    def lora_send(self, arg1, arg2):

        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_configuration(
                        {"message_count":
                         self.message_count})  # save number of messages today

                    # remove message sent
                    self.lora_buffer.remove_head()

            except Exception as e:
                self.logger.exception("Sending payload over LoRaWAN failed")
                blink_led((0x550000, 0.4, True))
Exemple #9
0
def pm_thread(sensor_name, status_logger, pins, serial_id):

    status_logger.debug("Thread {} started".format(sensor_name))

    sensor_logger = SensorLogger(sensor_name=sensor_name, terminal_out=True)

    sensor_type = config.get_config(sensor_name)
    init_time = config.get_config(sensor_name + "_init")

    init_count = 0

    if sensor_type == "PMS5003":

        # initialize sensor
        sensor = Plantower(pins=pins, id=serial_id)

        # warm up time  - readings are not logged
        while init_count < init_time:
            try:
                time.sleep(1)
                sensor.read()
                init_count += 1
            except PlantowerException as e:
                status_logger.exception("Failed to read from sensor PMS5003")
                blink_led((0x550000, 0.4, True))

    elif sensor_type == "SPS030":

        # initialize sensor
        while True:
            try:
                sensor = Sensirion(
                    retries=1, pins=pins,
                    id=serial_id)  # automatically starts measurement
                break
            except SensirionException as e:
                status_logger.exception("Failed to read from sensor SPS030")
                blink_led((0x550000, 0.4, True))
                time.sleep(1)

        # warm up time  - readings are not logged
        while init_count < init_time:
            try:
                time.sleep(1)
                sensor.read()
                init_count += 1
            except SensirionException as e:
                status_logger.exception("Failed to read from sensor SPS030")
                blink_led((0x550000, 0.4, True))

    # start a periodic timer interrupt to poll readings every second
    processing_alarm = Timer.Alarm(process_readings,
                                   arg=(sensor_type, sensor, sensor_logger,
                                        status_logger),
                                   s=1,
                                   periodic=True)
def calculate_average(sensor_name, logger):
    """
    Calculates averages for specific columns of sensor data to be sent over LoRa. Sets placeholders if it fails.
    :param sensor_name: PM1, PM2 or TEMP
    :type sensor_name: str
    :param logger: status logger
    :type logger: LoggerFactory object
    """

    filename = sensor_name + '.csv'
    sensor_type = config.get_config(sensor_name)
    sensor_id = str(config.get_config(sensor_name + "_id"))
    # headers in current file of the sensor according to its type
    headers = s.headers_dict_v4[sensor_type]

    # data to send if no readings are available
    avg_readings_str = list('0' * len(s.lora_sensor_headers[sensor_type]))
    count = 0

    try:
        with current_lock:
            # Move sensor_name.csv from current dir to processing dir
            os.rename(s.current_path + filename, s.processing_path + filename)

            with open(s.processing_path + filename, 'r') as f:
                # read all lines from processing
                lines = f.readlines()
                lines_lst = [
                ]  # list of lists that store average sensor readings from specific columns
                for line in lines:
                    stripped_line = line[:-1]  # strip \n
                    stripped_line_lst = str(stripped_line).split(
                        ',')  # split string to list at comas
                    named_line = dict(zip(
                        headers,
                        stripped_line_lst))  # assign each value to its header
                    sensor_reading = []
                    for header in s.lora_sensor_headers[sensor_type]:
                        sensor_reading.append(int(named_line[header]))
                    # Append extra lines here for more readings - update version number and back-end to interpret data
                    lines_lst.append(sensor_reading)
                    count += 1

                # Compute averages from sensor_name.csv.processing
                avg_readings_str = [
                    str(int(i)) for i in mean_across_arrays(lines_lst)
                ]

                # Append content of sensor_name.csv.processing into sensor_name.csv
                with open(
                        s.archive_path + sensor_name + '_' + sensor_id +
                        '.csv', 'a') as f:
                    for line in lines:
                        f.write(line)

    except Exception as e:
        logger.error("No readings from sensor {}".format(sensor_name))
        logger.warning("Setting 0 as a place holder")
        blink_led((0x550000, 0.4, True))
    finally:
        return {
            sensor_name + "_avg": avg_readings_str,
            sensor_name + "_count": count
        }
Exemple #11
0
    # Initialize button interrupt on pin 14 for user interaction
    user_button = UserButton(status_logger)
    pin_14 = Pin("P14", mode=Pin.IN, pull=Pin.PULL_DOWN)
    pin_14.callback(Pin.IRQ_RISING | Pin.IRQ_FALLING,
                    user_button.button_handler)

    # Mount SD card
    sd = SD()
    os.mount(sd, '/sd')

except Exception as e:
    print(str(e))
    reboot_counter = 0
    while True:
        blink_led((0x550000, 0.5, True))  # blink red LED
        reboot_counter += 1
        if reboot_counter >= 180:
            reset()

try:
    from machine import RTC, unique_id, Timer
    from initialisation import initialize_time
    from ubinascii import hexlify
    from Configuration import config
    from new_config import new_config
    import strings as s
    import ujson

    # Read configuration file to get preferences
    config.read_configuration()
Exemple #12
0
def pm_thread(sensor_name, sensor_logger, status_logger, pins, serial_id):
    """
    Method to run as a thread that reads, processes and logs readings form pm sensors according to their type
    :param sensor_name: PM1 or PM2
    :type sensor_name: str
    :param status_logger: status logger
    :type status_logger: LoggerFactory object
    :param pins: serial bus pins (TX, RX)
    :type pins: tuple(int, int)
    :param serial_id: serial bus id (0, 1 or 2)
    :type serial_id: int
    """

    status_logger.debug("Thread {} started".format(sensor_name))
    sensor_type = config.get_config(sensor_name)

    # sensor_logger = SensorLogger(sensor_name=sensor_name, terminal_out=True)

    init_time = int(config.get_config(sensor_name + "_init"))

    init_count = 0

    if sensor_type == "PMS5003":

        # initialise sensor
        sensor = Plantower(pins=pins, id=serial_id)

        time.sleep(1)

        # warm up time  - readings are not logged
        while init_count < init_time:
            try:
                time.sleep(1)
                sensor.read()
                init_count += 1
            except PlantowerException as e:
                status_logger.exception("Failed to read from sensor PMS5003")
                blink_led((0x550000, 0.4, True))

    elif sensor_type == "SPS030":

        # initialise sensor
        try:
            sensor = Sensirion(
                retries=1, pins=pins,
                id=serial_id)  # automatically starts measurement
        except SensirionException as e:
            status_logger.exception("Failed to read from sensor SPS030")
            blink_led((0x550000, 0.4, True))
            time.sleep(1)

        # warm up time - readings are not logged
        while init_count < init_time:
            try:
                time.sleep(1)
                sensor.read()
                init_count += 1
            except SensirionException as e:
                status_logger.exception("Failed to read from sensor SPS030")
                blink_led((0x550000, 0.4, True))

    # start a periodic timer interrupt to poll readings every second
    processing_alarm = Timer.Alarm(process_readings,
                                   arg=(sensor_type, sensor, sensor_logger,
                                        status_logger),
                                   s=1,
                                   periodic=True)