kopia lustrzana https://github.com/peterhinch/micropython-samples
225 wiersze
9.0 KiB
Python
225 wiersze
9.0 KiB
Python
# mt.py Display module for the power meter
|
|
|
|
# The MIT License (MIT)
|
|
#
|
|
# Copyright (c) 2017 Peter Hinch
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
# THE SOFTWARE.
|
|
|
|
import uasyncio as asyncio
|
|
from mains import Scaling
|
|
from constants import *
|
|
from lcd160_gui import Button, Label, Screen, Dropdown, Dial, LED, ButtonList
|
|
from lplot import CartesianGraph, Curve
|
|
import font10
|
|
from lcd_local import setup
|
|
from utime import ticks_ms, ticks_diff
|
|
from os import listdir
|
|
|
|
if 'mt.py' in listdir('/flash'):
|
|
mains_device = Scaling() # Real
|
|
# mains_device = Scaling(True, False) # Simulate
|
|
else: # Running on SD card - test setup
|
|
mains_device = Scaling(True, False) # Simulate
|
|
|
|
# STANDARD BUTTONS
|
|
def fwdbutton(y, screen, text, color):
|
|
def fwd(button, screen):
|
|
Screen.change(screen)
|
|
return Button((109, y), font = font10, fontcolor = BLACK, callback = fwd,
|
|
args = [screen], fgcolor = color, text = text)
|
|
|
|
def backbutton():
|
|
def back(button):
|
|
Screen.back()
|
|
return Button((139, 0), font = font10, fontcolor = BLACK, callback = back,
|
|
fgcolor = RED, text = 'X', height = 20, width = 20)
|
|
|
|
def plotbutton(y, screen, color):
|
|
def fwd(button, screen):
|
|
Screen.change(screen)
|
|
return Button((139, y), font = font10, fontcolor = BLACK, callback = fwd,
|
|
args = [screen], fgcolor = color, text = '~', height = 20, width = 20)
|
|
|
|
# **** BASE SCREEN ****
|
|
|
|
class BaseScreen(Screen):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.pwr_range = 3000
|
|
# Buttons
|
|
fwdbutton(57, IntegScreen, 'Integ', CYAN)
|
|
fwdbutton(82, PlotScreen, 'Plot', YELLOW)
|
|
# Labels
|
|
self.lbl_pf = Label((0, 31), font = font10, width = 75, border = 2, bgcolor = DARKGREEN, fgcolor = RED)
|
|
self.lbl_v = Label((0, 56), font = font10, width = 75, border = 2, bgcolor = DARKGREEN, fgcolor = RED)
|
|
self.lbl_i = Label((0, 81), font = font10, width = 75, border = 2, bgcolor = DARKGREEN, fgcolor = RED)
|
|
self.lbl_p = Label((0,106), font = font10, width = 75, border = 2, bgcolor = DARKGREEN, fgcolor = RED)
|
|
self.lbl_va = Label((80,106), font = font10, width = 79, border = 2, bgcolor = DARKGREEN, fgcolor = RED)
|
|
# Dial
|
|
self.dial = Dial((109, 0), fgcolor = YELLOW, border = 2)
|
|
# Dropdown
|
|
self.dropdown = Dropdown((0, 0), font = font10, width = 80, callback = self.cbdb,
|
|
elements = ('3000W', '600W', '150W', '60W', '30W'))
|
|
self.led = LED((84, 0), color = GREEN)
|
|
self.led.value(True)
|
|
# Dropdown callback: set range
|
|
def cbdb(self, dropdown):
|
|
self.pwr_range = int(dropdown.textvalue()[: -1]) # String of form 'nnnW'
|
|
mains_device.set_range(self.pwr_range)
|
|
# print('Range set to', self.pwr_range, dropdown.value())
|
|
|
|
def reading(self, phase, vrms, irms, pwr, nelems, ovr):
|
|
# print(phase, vrms, irms, pwr, nelems)
|
|
self.lbl_v.value('{:5.1f}V'.format(vrms))
|
|
if ovr:
|
|
self.lbl_i.value('----')
|
|
self.lbl_p.value('----')
|
|
self.lbl_pf.value('----')
|
|
self.lbl_va.value('----')
|
|
else:
|
|
self.lbl_i.value('{:6.3f}A'.format(irms))
|
|
self.lbl_p.value('{:5.1f}W'.format(pwr))
|
|
self.lbl_pf.value('PF:{:4.2f}'.format(pwr /(vrms * irms)))
|
|
self.lbl_va.value('{:5.1f}VA'.format(vrms * irms))
|
|
self.dial.value(phase + 1.5708) # Conventional phasor orientation.
|
|
if ovr:
|
|
self.led.color(RED) # Overrange
|
|
elif abs(pwr) < abs(self.pwr_range) / 5:
|
|
self.led.color(YELLOW) # Underrange
|
|
else:
|
|
self.led.color(GREEN) # OK
|
|
|
|
def on_hide(self):
|
|
mains_device.set_callback(None) # Stop readings
|
|
|
|
def after_open(self):
|
|
mains_device.set_callback(self.reading)
|
|
|
|
# **** PLOT SCREEN ****
|
|
|
|
class PlotScreen(Screen):
|
|
@staticmethod
|
|
def populate(curve, data):
|
|
xinc = 1 / len(data)
|
|
x = 0
|
|
for y in data:
|
|
curve.point(x, y)
|
|
x += xinc
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
backbutton()
|
|
Label((142, 45), font = font10, fontcolor = YELLOW, value = 'V')
|
|
Label((145, 70), font = font10, fontcolor = RED, value = 'I')
|
|
g = CartesianGraph((0, 0), height = 127, width = 135, xorigin = 0) # x >= 0
|
|
Curve(g, self.populate, args = (mains_device.vplot,))
|
|
Curve(g, self.populate, args = (mains_device.iplot,), color = RED)
|
|
|
|
# **** INTEGRATOR SCREEN ****
|
|
|
|
class IntegScreen(Screen):
|
|
def __init__(self):
|
|
super().__init__()
|
|
# Buttons
|
|
backbutton()
|
|
plotbutton(80, PlotScreen, YELLOW)
|
|
# Labels
|
|
self.lbl_p = Label((0, 0), font = font10, width = 78, border = 2, bgcolor = DARKGREEN, fgcolor = RED)
|
|
Label((90, 4), font = font10, value = 'Power')
|
|
self.lbl_pmax = Label((0, 30), font = font10, width = 78, border = 2, bgcolor = DARKGREEN, fgcolor = RED)
|
|
Label((90, 34), font = font10, value = 'Max')
|
|
self.lbl_pin = Label((0, 55), font = font10, width = 78, border = 2, bgcolor = DARKGREEN, fgcolor = RED)
|
|
self.lbl_pmin = Label((0, 80), font = font10, width = 78, border = 2, bgcolor = DARKGREEN, fgcolor = RED)
|
|
Label((90, 84), font = font10, value = 'Min')
|
|
self.lbl_w_hr = Label((0,105), font = font10, width = 78, border = 2, bgcolor = DARKGREEN, fgcolor = RED)
|
|
self.lbl_t = Label((88, 105), font = font10, width = 70, border = 2, bgcolor = DARKGREEN, fgcolor = RED)
|
|
|
|
table = [
|
|
{'fgcolor' : GREEN, 'text' : 'Max Gen', 'args' : (True,)},
|
|
{'fgcolor' : BLUE, 'text' : 'Mean', 'args' : (False,)},
|
|
]
|
|
bl = ButtonList(self.buttonlist_cb)
|
|
for t in table: # Buttons overlay each other at same location
|
|
bl.add_button((90, 56), width = 70, font = font10, fontcolor = BLACK, **t)
|
|
self.showmean = False
|
|
self.t_reading = None # Time of last reading
|
|
self.t_start = None # Time of 1st reading
|
|
self.joules = 0 # Cumulative energy
|
|
self.overrange = False
|
|
self.wmax = 0 # Max power out
|
|
self.wmin = 0 # Max power in
|
|
self.pwr_min = 10000 # Power corresponding to minimum absolute value
|
|
|
|
def reading(self, phase, vrms, irms, pwr, nelems, ovr):
|
|
self.wmax = max(self.wmax, pwr)
|
|
self.wmin = min(self.wmin, pwr)
|
|
if abs(pwr) < abs(self.pwr_min):
|
|
self.pwr_min = pwr
|
|
if ovr:
|
|
self.overrange = True
|
|
t_last = self.t_reading # Time of last reading (ms)
|
|
self.t_reading = ticks_ms()
|
|
if self.t_start is None: # 1st reading
|
|
self.t_start = self.t_reading # Time of 1st reading
|
|
else:
|
|
self.joules += pwr * ticks_diff(self.t_reading, t_last) / 1000
|
|
|
|
secs_since_start = ticks_diff(self.t_reading, self.t_start) / 1000 # Runtime
|
|
mins, secs = divmod(int(secs_since_start), 60)
|
|
hrs, mins = divmod(mins, 60)
|
|
self.lbl_t.value('{:02d}:{:02d}:{:02d}'.format(hrs, mins, secs))
|
|
if ovr:
|
|
self.lbl_p.value('----')
|
|
else:
|
|
self.lbl_p.value('{:5.1f}W'.format(pwr))
|
|
|
|
if self.showmean:
|
|
self.lbl_pin.value('{:5.1f}W'.format(self.joules / max(secs_since_start, 1)))
|
|
else:
|
|
self.lbl_pin.value('{:5.1f}W'.format(self.wmin))
|
|
|
|
self.lbl_pmin.value('{:5.1f}W'.format(self.pwr_min))
|
|
if self.overrange: # An overrange occurred during the measurement
|
|
self.lbl_w_hr.value('----')
|
|
self.lbl_pmax.value('----')
|
|
else:
|
|
self.lbl_pmax.value('{:5.1f}W'.format(self.wmax))
|
|
units = self.joules / 3600
|
|
if units < 1000:
|
|
self.lbl_w_hr.value('{:6.0f}Wh'.format(units))
|
|
else:
|
|
self.lbl_w_hr.value('{:6.2f}KWh'.format(units / 1000))
|
|
|
|
def buttonlist_cb(self, button, arg):
|
|
self.showmean = arg
|
|
|
|
def on_hide(self):
|
|
mains_device.set_callback(None) # Stop readings
|
|
|
|
def after_open(self):
|
|
mains_device.set_callback(self.reading)
|
|
|
|
def test():
|
|
print('Running...')
|
|
setup()
|
|
Screen.change(BaseScreen)
|
|
|
|
test()
|