power_monitor.py 4.74 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#!/usr/bin/env python
"""Script to read a PZEM-016 Electric Power sensor.  Readings are posted
to the readings/final/power_monitor MQTT topic.

Uses a number of settings from the Mini-Monitor settings file.  See
system_files/settings_template.py, "Power Monitor" section for those 
settings.
"""

import time
import sys
import logging
import numpy as np
import minimalmodbus
import mqtt_poster
import config_logging
17
from loglib import change_detect
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43

try:
    # Configure logging and log a restart of the app
    config_logging.configure_logging(logging, '/var/log/power_monitor.log')
    logging.warning('power_monitor has restarted')

    # The settings file is installed in the FAT boot partition of the Pi SD card,
    # so that it can be easily configured from the PC that creates the SD card.
    # Include that directory in the Path so the settings file can be found.
    sys.path.insert(0, '/boot/pi_logger')
    import settings

    # Start up the object that will post the final readings to the MQTT
    # broker.
    mqtt = mqtt_poster.MQTTposter()
    mqtt.start()

    # determine the time when the next summarized post will occur
    next_log_time = time.time() + settings.LOG_INTERVAL

    # get the base Logger ID from the settings file
    logger_id = settings.LOGGER_ID

    # Set up the MODBUS instrument used to read the sensor
    minimalmodbus.CLOSE_PORT_AFTER_EACH_CALL=True
    instr = minimalmodbus.Instrument(settings.PWR_PORT, 1)   # Slave Address 1 assumed for sensor
44
    instr.serial.timeout = 0.2
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
    instr.serial.baudrate = 9600

except:
    logging.exception('Error initializing the script.')
    sys.exit(1)

# Arrays to hold the timestamps and readings for the values of importance
# from the sensor
tstamps = []
powers = []
volts = []
amps = []
freqs = []
pfs = []

while True:
    time.sleep(0.91)

    ts = time.time()
    data = None
    for i in range(5):
        try:
            data = instr.read_registers(0, 10, 4)
            break
        except:
            logging.exception('Error reading PZEM-016.')
    if data:
        tstamps.append(ts)
        volts.append(data[0]*0.1)
        amps.append((data[1] + data[2]*65536) * 0.001 / settings.PWR_CT_WRAPS)
        powers.append((data[3] + data[4]*65536) * 0.1 / settings.PWR_CT_WRAPS)
        freqs.append(data[7]*0.1)
        pfs.append(data[8]*0.01)

    # see if it is time to post summarized readings
    if time.time() > next_log_time:
81 82 83
        # Deal with clock catch-up when the Pi has been off.
        next_log_time = max(next_log_time + settings.LOG_INTERVAL, time.time() + settings.LOG_INTERVAL - 2.0)
        
84 85 86 87

        # If there is any data, process into desired form and post it
        if len(tstamps):

88
            try:
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
                # Convert arrays into numpy arrays
                tstamps = np.array(tstamps)
                volts = np.array(volts)
                amps = np.array(amps)
                powers = np.array(powers)
                freqs = np.array(freqs)
                pfs = np.array(pfs)

                # gathers lines to post to MQTT
                lines_to_post = []

                # first include the average power if requested
                if settings.PWR_INCL_PWR_AVG:
                    ts_avg = tstamps.mean()
                    pwr_avg = powers.mean()
                    lines_to_post.append('%s\t%s\t%s' % (ts_avg, logger_id + '_pwr_avg', pwr_avg))

                # Now add detail points for each of the requested measurements
                measures = [
                    ('pwr', powers),
                    ('volt', volts),
                    ('amp', amps),
                    ('freq', freqs),
                    ('pf', pfs)
                ]
                for lbl, val_array in measures:
                    if getattr(settings, 'PWR_INCL_' + lbl.upper()):
                        lines_to_post += change_detect.make_post_lines(
                            '%s_%s' % (logger_id, lbl),
                            tstamps,
                            val_array,
                            getattr(settings, 'PWR_THRESHOLD_' + lbl.upper()),
                            settings.PWR_MAX_INTERVAL
122
                        )
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137

            except:
                logging.exception('Error summarizing readings.')

            # Post the summarized readings
            try:
                if len(lines_to_post):
                    mqtt.publish(
                        'readings/final/power_monitor',
                        '\n'.join(lines_to_post)
                    )
                    logging.info('%d readings posted.' % len(lines_to_post))
            except:
                logging.exception('Error posting: %s' % lines_to_post)

138 139 140 141 142 143
            tstamps = []
            powers = []
            volts = []
            amps = []
            freqs = []
            pfs = []
144