From 91066f6aed48d191e670556ca987bf1079f02e1d Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Tue, 25 Jun 2024 08:57:37 -0700 Subject: [PATCH] add powermon_sim support --- meshtastic/powermon/__init__.py | 3 ++- meshtastic/powermon/power_supply.py | 7 +++--- meshtastic/powermon/ppk2.py | 33 +++++++++++++++++++++-------- meshtastic/powermon/riden.py | 10 ++++++--- meshtastic/powermon/sim.py | 17 +++++++++++++++ pyproject.toml | 3 ++- 6 files changed, 56 insertions(+), 17 deletions(-) create mode 100644 meshtastic/powermon/sim.py diff --git a/meshtastic/powermon/__init__.py b/meshtastic/powermon/__init__.py index c4aa654..9db5544 100644 --- a/meshtastic/powermon/__init__.py +++ b/meshtastic/powermon/__init__.py @@ -2,4 +2,5 @@ from .power_supply import PowerMeter, PowerSupply, PowerError from .riden import RidenPowerSupply -from .ppk2 import PPK2PowerSupply \ No newline at end of file +from .ppk2 import PPK2PowerSupply +from .sim import SimPowerSupply \ No newline at end of file diff --git a/meshtastic/powermon/power_supply.py b/meshtastic/powermon/power_supply.py index f87a4b8..18463a7 100644 --- a/meshtastic/powermon/power_supply.py +++ b/meshtastic/powermon/power_supply.py @@ -3,8 +3,10 @@ import math from datetime import datetime + class PowerError(Exception): """An exception class for powermon errors""" + def __init__(self, message): self.message = message super().__init__(self.message) @@ -19,7 +21,7 @@ class PowerMeter: self.prevWattHour = self._getRawWattHour() def getWatts(self) -> float: - """Get the total amount of power that has been consumed since the previous call of this method""" + """Get the total amount of power that is currently being consumed.""" now = datetime.now() nowWattHour = self._getRawWattHour() watts = ( @@ -36,14 +38,13 @@ class PowerMeter: return math.nan - class PowerSupply(PowerMeter): """Abstract class for power supplies.""" def __init__(self): """Initialize the PowerSupply object.""" super().__init__() - self.v = 3.3 + self.v = 0.0 def powerOn(self): """Turn on the power supply (using the voltage set in self.v).""" diff --git a/meshtastic/powermon/ppk2.py b/meshtastic/powermon/ppk2.py index 4fe4a88..db90635 100644 --- a/meshtastic/powermon/ppk2.py +++ b/meshtastic/powermon/ppk2.py @@ -4,8 +4,8 @@ import logging from typing import * from ppk2_api import ppk2_api -from .power_supply import PowerSupply, PowerError +from .power_supply import PowerError, PowerSupply class PPK2PowerSupply(PowerSupply): @@ -16,32 +16,47 @@ class PPK2PowerSupply(PowerSupply): def __init__(self, portName: Optional[str] = None): """Initialize the PowerSupply object. - portName (str, optional): The port name of the power supply. Defaults to "/dev/ttyACM0". + portName (str, optional): The port name of the power supply. Defaults to "/dev/ttyACM0". """ if not portName: devs = ppk2_api.PPK2_API.list_devices() if not devs or len(devs) == 0: raise PowerError("No PPK2 devices found") elif len(devs) > 1: - raise PowerError("Multiple PPK2 devices found, please specify the portName") + raise PowerError( + "Multiple PPK2 devices found, please specify the portName" + ) else: portName = devs[0] self.r = r = ppk2_api.PPK2_MP(portName) # serial port will be different for you r.get_modifiers() + self.r.start_measuring() # start measuring logging.info("Connected to PPK2 power supply") - super().__init__() # we call this late so that the port is already open and _getRawWattHour callback works + super().__init__() # we call this late so that the port is already open and _getRawWattHour callback works + + def setIsSupply(self, s: bool): + """If in supply mode we will provide power ourself, otherwise we are just an amp meter.""" + if ( + not s + ): # min power outpuf of PPK2. If less than this assume we want just meter mode. + self.r.use_ampere_meter() + else: + self.r.set_source_voltage( + int(self.v * 1000) + ) # set source voltage in mV BEFORE setting source mode + self.r.use_source_meter() # set source meter mode def powerOn(self): - """Power on the supply, with reasonable defaults for meshtastic devices.""" - self.r.use_source_meter() # set source meter mode - self.r.set_source_voltage(self.v * 1000) # set source voltage in mV + """Power on the supply.""" self.r.toggle_DUT_power("ON") - self.r.start_measuring() # start measuring + def powerOff(self): + """Power off the supply.""" + self.r.toggle_DUT_power("OFF") def _getRawWattHour(self) -> float: """Get the current watt-hour reading.""" - return 4 # FIXME + return 4 # FIXME diff --git a/meshtastic/powermon/riden.py b/meshtastic/powermon/riden.py index 17d5c02..c425855 100644 --- a/meshtastic/powermon/riden.py +++ b/meshtastic/powermon/riden.py @@ -4,8 +4,10 @@ import logging from datetime import datetime from riden import Riden + from .power_supply import PowerSupply + class RidenPowerSupply(PowerSupply): """Interface for talking to Riden programmable bench-top power supplies. Only RD6006 tested but others should be similar. @@ -14,14 +16,14 @@ class RidenPowerSupply(PowerSupply): def __init__(self, portName: str = "/dev/ttyUSB0"): """Initialize the RidenPowerSupply object. - portName (str, optional): The port name of the power supply. Defaults to "/dev/ttyUSB0". + portName (str, optional): The port name of the power supply. Defaults to "/dev/ttyUSB0". """ self.r = r = Riden(port=portName, baudrate=115200, address=1) logging.info( f"Connected to Riden power supply: model {r.type}, sn {r.sn}, firmware {r.fw}. Date/time updated." ) r.set_date_time(datetime.now()) - super().__init__() # we call this late so that the port is already open and _getRawWattHour callback works + super().__init__() # we call this late so that the port is already open and _getRawWattHour callback works def setMaxCurrent(self, i: float): """Set the maximum current the supply will provide.""" @@ -29,7 +31,9 @@ class RidenPowerSupply(PowerSupply): def powerOn(self): """Power on the supply, with reasonable defaults for meshtastic devices.""" - self.r.set_v_set(self.v) # my WM1110 devboard header is directly connected to the 3.3V rail + self.r.set_v_set( + self.v + ) # my WM1110 devboard header is directly connected to the 3.3V rail self.r.set_output(1) def _getRawWattHour(self) -> float: diff --git a/meshtastic/powermon/sim.py b/meshtastic/powermon/sim.py new file mode 100644 index 0000000..9b6ea12 --- /dev/null +++ b/meshtastic/powermon/sim.py @@ -0,0 +1,17 @@ +"""code logging power consumption of meshtastic devices.""" + +import math +import time +from typing import * + +from .power_supply import PowerError, PowerSupply + + +class SimPowerSupply(PowerSupply): + """A simulated power supply for testing.""" + + def getWatts(self) -> float: + """Get the total amount of power that is currently being consumed.""" + + # Sim a 20mW load that varies sinusoidally + return (20 + 5 * math.sin(time.time())) / 1000 diff --git a/pyproject.toml b/pyproject.toml index 525bf97..3eee1fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,9 +23,10 @@ pypubsub = "^4.0.3" bleak = "^0.21.1" packaging = "^24.0" riden = { git = "https://github.com/geeksville/riden.git#1.2.1" } -pandas = "^2.2.2" parse = "^1.20.2" ppk2-api = "^0.9.2" +pyarrow = "^16.1.0" +pyarrow-stubs = "^10.0.1.7" [tool.poetry.group.dev.dependencies] hypothesis = "^6.103.2"