mirror of
https://github.com/meshtastic/python.git
synced 2026-01-14 18:57:56 -05:00
begin support for multiple power meter types
This commit is contained in:
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -172,7 +172,7 @@
|
||||
"request": "launch",
|
||||
"module": "meshtastic",
|
||||
"justMyCode": false,
|
||||
"args": ["--power-mon", "/dev/ttyUSB1", "--port", "/dev/ttyUSB0", "--noproto", "--seriallog", "stdout"]
|
||||
"args": ["--power-riden", "/dev/ttyUSB0", "--port", "/dev/ttyACM0", "--noproto", "--seriallog", "stdout"]
|
||||
},
|
||||
{
|
||||
"name": "meshtastic test",
|
||||
|
||||
@@ -21,7 +21,8 @@ from meshtastic import channel_pb2, config_pb2, portnums_pb2, remote_hardware, B
|
||||
from meshtastic.version import get_active_version
|
||||
from meshtastic.ble_interface import BLEInterface
|
||||
from meshtastic.mesh_interface import MeshInterface
|
||||
from meshtastic.power_mon import PowerMonClient
|
||||
from meshtastic.powermon import RidenPowerSupply
|
||||
from meshtastic.slog.power_mon import PowerMonClient
|
||||
|
||||
def onReceive(packet, interface):
|
||||
"""Callback invoked when a packet arrives"""
|
||||
@@ -1090,8 +1091,9 @@ def common():
|
||||
# We assume client is fully connected now
|
||||
onConnected(client)
|
||||
|
||||
if args.power_mon:
|
||||
PowerMonClient(args.power_mon, client)
|
||||
if args.power_riden:
|
||||
meter = RidenPowerSupply(args.power_riden)
|
||||
PowerMonClient(meter, client)
|
||||
|
||||
|
||||
have_tunnel = platform.system() == "Linux"
|
||||
@@ -1506,8 +1508,14 @@ def initParser():
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--power-mon",
|
||||
help="Capture any power monitor records. You must use --power-mon /dev/ttyUSBxxx to specify which port the power supply is on",
|
||||
"--power-riden",
|
||||
help="Talk to a Riden power-supply. You must specify the device path, i.e. /dev/ttyUSBxxx",
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--power-ppk2",
|
||||
help="Talk to a Nordic Power Profiler Kit 2",
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
|
||||
1
meshtastic/powermon/__init__.py
Normal file
1
meshtastic/powermon/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .riden import *
|
||||
58
meshtastic/powermon/riden.py
Normal file
58
meshtastic/powermon/riden.py
Normal file
@@ -0,0 +1,58 @@
|
||||
"""code logging power consumption of meshtastic devices."""
|
||||
|
||||
import logging
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from riden import Riden
|
||||
|
||||
class PowerMeter:
|
||||
"""Abstract class for power meters."""
|
||||
|
||||
def getWattHour(self) -> float:
|
||||
"""Get the current watt-hour reading."""
|
||||
|
||||
|
||||
|
||||
class PowerSupply(PowerMeter):
|
||||
"""Abstract class for power supplies."""
|
||||
|
||||
def setMaxCurrent(self, i: float):
|
||||
"""Set the maximum current the supply will provide."""
|
||||
|
||||
def powerOn(self, v: float):
|
||||
"""Turn on the power supply."""
|
||||
|
||||
|
||||
|
||||
class RidenPowerSupply(PowerSupply):
|
||||
"""Interface for talking to programmable bench-top power supplies.
|
||||
Currently only the Riden supplies are supported (RD6006 tested)
|
||||
"""
|
||||
|
||||
def __init__(self, portName: str = "/dev/ttyUSB0"):
|
||||
"""Initialize the RidenPowerSupply object.
|
||||
|
||||
Args:
|
||||
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())
|
||||
|
||||
def setMaxCurrent(self, i: float):
|
||||
"""Set the maximum current the supply will provide."""
|
||||
self.r.set_i_set(i)
|
||||
|
||||
def powerOn(self, v: float):
|
||||
"""Power on the supply, with reasonable defaults for meshtastic devices."""
|
||||
self.r.set_v_set(v) # my WM1110 devboard header is directly connected to the 3.3V rail
|
||||
self.r.set_output(1)
|
||||
|
||||
def getWattHour(self) -> float:
|
||||
"""Get the current watt-hour reading."""
|
||||
self.r.update()
|
||||
return self.r.wh
|
||||
|
||||
0
meshtastic/slog/__init__.py
Normal file
0
meshtastic/slog/__init__.py
Normal file
@@ -6,77 +6,35 @@ import atexit
|
||||
from datetime import datetime
|
||||
|
||||
import pandas as pd
|
||||
from riden import Riden
|
||||
|
||||
from meshtastic.mesh_interface import MeshInterface
|
||||
from meshtastic.observable import Event
|
||||
from meshtastic.powermon import PowerSupply
|
||||
|
||||
|
||||
class PowerSupply:
|
||||
"""Interface for talking to programmable bench-top power supplies.
|
||||
Currently only the Riden supplies are supported (RD6006 tested)
|
||||
"""
|
||||
|
||||
def __init__(self, portName: str = "/dev/ttyUSB0"):
|
||||
"""Initialize the PowerSupply object."""
|
||||
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())
|
||||
|
||||
def powerOn(self):
|
||||
"""Power on the supply, with reasonable defaults for meshtastic devices."""
|
||||
self.r.set_i_set(
|
||||
0.300
|
||||
) # Set current limit to 300mA - hopefully enough to power any board but not break things if there is a short circuit
|
||||
|
||||
# self.r.set_v_set(3.7) # default to a nominal LiPo voltage
|
||||
self.r.set_v_set(3.3) # my WM1110 devboard header is directly connected to the 3.3V rail
|
||||
self.r.set_output(1)
|
||||
|
||||
"""Get current watts out.
|
||||
But for most applications you probably want getWattHour() instead (to prevent integration errors from accumulating).
|
||||
"""
|
||||
return self.r.get_p_out()
|
||||
|
||||
def getWattHour(self):
|
||||
"""Get current Wh out, since power was turned on."""
|
||||
# FIXME: Individual reads seem busted in the riden lib. So for now I just read everything.
|
||||
self.r.update()
|
||||
return self.r.wh
|
||||
# return self.r.get_wh()
|
||||
|
||||
def clearWattHour(self):
|
||||
"""Clear the watt-hour counter FIXME."""
|
||||
|
||||
|
||||
"""Used to match power mon log lines:
|
||||
INFO | ??:??:?? 7 [Blink] S:PM:0x00000080,reason
|
||||
"""
|
||||
logRegex = re.compile(".*S:PM:0x([0-9A-Fa-f]+),(.*)")
|
||||
|
||||
|
||||
class PowerMonClient:
|
||||
"""Client for monitoring power consumption of meshtastic devices."""
|
||||
|
||||
def __init__(self, portName: str, client: MeshInterface) -> None:
|
||||
def __init__(self, power: PowerSupply, client: MeshInterface) -> None:
|
||||
"""Initialize the PowerMonClient object.
|
||||
|
||||
Args:
|
||||
power (PowerSupply): The power supply object.
|
||||
client (MeshInterface): The MeshInterface object to monitor.
|
||||
|
||||
"""
|
||||
self.client = client
|
||||
self.state = 0 # The current power mon state bitfields
|
||||
self.columns = ["time", "power", "reason", "bitmask"]
|
||||
self.rawData = pd.DataFrame(columns=self.columns) # use time as the index
|
||||
self.rawData = pd.DataFrame(columns=self.columns) # use time as the index
|
||||
|
||||
# for efficiency reasons we keep new data in a list - only adding to rawData when needfed
|
||||
# for efficiency reasons we keep new data in a list - only adding to rawData when needed
|
||||
self.newData: list[dict] = []
|
||||
|
||||
self.power = power = PowerSupply(portName)
|
||||
power.powerOn()
|
||||
self.power = power
|
||||
power.setMaxCurrent(0.300) # Set current limit to 300mA - hopefully enough to power any board but not break things if there is a short circuit
|
||||
power.powerOn(3.3)
|
||||
|
||||
# Used to calculate watts over an interval
|
||||
self.prevPowerTime = datetime.now()
|
||||
@@ -89,7 +47,6 @@ class PowerMonClient:
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: The raw data.
|
||||
|
||||
"""
|
||||
df = pd.DataFrame(self.newData, columns=self.columns)
|
||||
self.rawData = pd.concat([self.rawData, df], ignore_index=True)
|
||||
@@ -107,8 +64,7 @@ class PowerMonClient:
|
||||
"""Callback function for handling log messages.
|
||||
|
||||
Args:
|
||||
message (str): The log message.
|
||||
|
||||
ev (Event): The log event.
|
||||
"""
|
||||
m = logRegex.match(ev.message)
|
||||
if m:
|
||||
@@ -124,9 +80,7 @@ class PowerMonClient:
|
||||
Args:
|
||||
mask (int): The power mon state bitfields.
|
||||
reason (str): The reason for the power mon state change.
|
||||
|
||||
"""
|
||||
|
||||
now = datetime.now()
|
||||
nowWattHour = self.power.getWattHour()
|
||||
watts = (
|
||||
@@ -139,5 +93,5 @@ class PowerMonClient:
|
||||
self.state = mask
|
||||
|
||||
self.newData.append(
|
||||
{"time": now, "power": watts, "reason": reason, "bitmask": mask})
|
||||
# self.getRawData()
|
||||
{"time": now, "power": watts, "reason": reason, "bitmask": mask}
|
||||
)
|
||||
Reference in New Issue
Block a user