mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-01 05:33:49 +08:00
hardware: add 30s sustained low voltage debounce and sentry power off notifications
This commit is contained in:
@@ -82,6 +82,32 @@ def notify_sentry_power_off(reason: str, power_monitor: PowerMonitoring) -> bool
|
||||
return False
|
||||
|
||||
|
||||
def notify_sentry_low_voltage(power_monitor: PowerMonitoring) -> bool:
|
||||
port = os.environ.get("SP_GALAXY_PORT", "8083" if PC else "8082")
|
||||
v = round(power_monitor.car_voltage_mV / 1000, 2)
|
||||
event = {
|
||||
"eventId": f"low-voltage-{time.time_ns()}",
|
||||
"kind": "warning",
|
||||
"detectedAt": datetime.now(timezone.utc).isoformat(),
|
||||
"reason": "low_voltage",
|
||||
"message": f"Low vehicle battery warning: {v:.2f}V (at or below 11.8V).",
|
||||
"voltage": v,
|
||||
"instantVoltage": round(power_monitor.car_voltage_instant_mV / 1000, 2),
|
||||
"batteryCapacityUwh": power_monitor.get_car_battery_capacity(),
|
||||
}
|
||||
try:
|
||||
response = requests.post(
|
||||
f"http://127.0.0.1:{port}/api/sentry/events",
|
||||
json=event,
|
||||
timeout=4,
|
||||
)
|
||||
response.raise_for_status()
|
||||
return True
|
||||
except requests.RequestException as error:
|
||||
cloudlog.warning(f"Sentry low-voltage notification unavailable: {error}")
|
||||
return False
|
||||
|
||||
|
||||
class Chestnut:
|
||||
"""Keep the ASM2464PD dock on the firmware expected by the GPU runtime."""
|
||||
MAX_ATTEMPTS = 3
|
||||
@@ -305,6 +331,8 @@ def hardware_thread(end_event, hw_queue) -> None:
|
||||
pwrsave = False
|
||||
offroad_cycle_count = 0
|
||||
sentry_power_off_notified = False
|
||||
sentry_low_voltage_notified = False
|
||||
last_low_voltage_notify_ts = 0.0
|
||||
|
||||
params = Params()
|
||||
power_monitor = PowerMonitoring()
|
||||
@@ -523,6 +551,10 @@ def hardware_thread(end_event, hw_queue) -> None:
|
||||
statlog.sample("som_power_draw", som_power_draw)
|
||||
msg.deviceState.somPowerDrawW = som_power_draw
|
||||
|
||||
if not onroad_conditions["ignition"] and (count % int(30. / DT_HW) == 0):
|
||||
low_v_str = f" [LOW VOLTAGE SUSTAINED: {time.monotonic() - power_monitor.low_voltage_start_time:.1f}s / 30.0s]" if power_monitor.low_voltage_start_time else ""
|
||||
print(f"[hardwared] Offroad Power: {power_monitor.car_voltage_mV / 1000.0:.2f}V (instant: {power_monitor.car_voltage_instant_mV / 1000.0:.2f}V), draw: {current_power_draw:.1f}W{low_v_str}", flush=True)
|
||||
|
||||
# Check if we need to shut down
|
||||
shutdown_reason = power_monitor.shutdown_reason(
|
||||
onroad_conditions["ignition"], in_car, off_ts, started_seen, starpilot_toggles,
|
||||
@@ -536,6 +568,21 @@ def hardware_thread(end_event, hw_queue) -> None:
|
||||
else:
|
||||
sentry_power_off_notified = False
|
||||
|
||||
# Low voltage warning notification (without device shutdown)
|
||||
if in_car and not onroad_conditions["ignition"] and off_ts is not None:
|
||||
voltage_v = power_monitor.car_voltage_mV / 1000.0
|
||||
if voltage_v <= 11.8:
|
||||
now_mono = time.monotonic()
|
||||
if not sentry_low_voltage_notified or (now_mono - last_low_voltage_notify_ts > 1800):
|
||||
sentry_low_voltage_notified = True
|
||||
last_low_voltage_notify_ts = now_mono
|
||||
if params.get_bool("SentryModeEnabled"):
|
||||
notify_sentry_low_voltage(power_monitor)
|
||||
elif voltage_v > 12.2:
|
||||
sentry_low_voltage_notified = False
|
||||
else:
|
||||
sentry_low_voltage_notified = False
|
||||
|
||||
msg.deviceState.started = started_ts is not None
|
||||
msg.deviceState.startedMonoTime = int(1e9*(started_ts or 0))
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ MAX_TIME_OFFROAD_S = 30*3600
|
||||
MIN_ON_TIME_S = 3600
|
||||
DELAY_SHUTDOWN_TIME_S = 300 # Wait at least DELAY_SHUTDOWN_TIME_S seconds after offroad_time to shutdown.
|
||||
VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S = 60
|
||||
VOLTAGE_SHUTDOWN_SUSTAINED_TIME_S = 30.0
|
||||
|
||||
class PowerMonitoring:
|
||||
def __init__(self):
|
||||
@@ -29,12 +30,13 @@ class PowerMonitoring:
|
||||
self.next_pulsed_measurement_time = None
|
||||
self.car_voltage_mV = 12e3 # Low-passed version of peripheralState voltage
|
||||
self.car_voltage_instant_mV = 12e3 # Last value of peripheralState voltage
|
||||
self.low_voltage_start_time = None # Monotonic timestamp when low voltage was first observed
|
||||
self.integration_lock = threading.Lock()
|
||||
|
||||
car_battery_capacity_uWh = self.params.get("CarBatteryCapacity") or 0
|
||||
car_battery_capacity_uWh = self.params.get("CarBatteryCapacity") or CAR_BATTERY_CAPACITY_uWh
|
||||
|
||||
# Reset capacity if it's low
|
||||
self.car_battery_capacity_uWh = max((CAR_BATTERY_CAPACITY_uWh / 10), car_battery_capacity_uWh)
|
||||
self.car_battery_capacity_uWh = max((CAR_BATTERY_CAPACITY_uWh / 2), car_battery_capacity_uWh)
|
||||
|
||||
# Calculation tick
|
||||
def calculate(self, voltage: int | None, ignition: bool):
|
||||
@@ -110,19 +112,29 @@ class PowerMonitoring:
|
||||
def shutdown_reason(self, ignition: bool, in_car: bool, offroad_timestamp: float | None,
|
||||
started_seen: bool, starpilot_toggles: SimpleNamespace) -> str | None:
|
||||
if offroad_timestamp is None:
|
||||
self.low_voltage_start_time = None
|
||||
return None
|
||||
|
||||
now = time.monotonic()
|
||||
offroad_time = (now - offroad_timestamp)
|
||||
low_voltage_shutdown = (self.car_voltage_mV < (starpilot_toggles.low_voltage_shutdown * 1e3) and
|
||||
offroad_time > VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S)
|
||||
|
||||
cutoff_voltage = starpilot_toggles.low_voltage_shutdown if getattr(starpilot_toggles, "low_voltage_shutdown", 0) > 0 else 11.8
|
||||
is_below_voltage = self.car_voltage_mV < (cutoff_voltage * 1e3)
|
||||
|
||||
if is_below_voltage and offroad_time > VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S:
|
||||
if self.low_voltage_start_time is None:
|
||||
self.low_voltage_start_time = now
|
||||
low_voltage_sustained_time = now - self.low_voltage_start_time
|
||||
low_voltage_shutdown = low_voltage_sustained_time >= VOLTAGE_SHUTDOWN_SUSTAINED_TIME_S
|
||||
else:
|
||||
self.low_voltage_start_time = None
|
||||
low_voltage_shutdown = False
|
||||
|
||||
reason = None
|
||||
if offroad_time > starpilot_toggles.device_shutdown_time:
|
||||
if starpilot_toggles.device_shutdown_time > 0 and offroad_time > starpilot_toggles.device_shutdown_time:
|
||||
reason = "offroad_timeout"
|
||||
elif low_voltage_shutdown:
|
||||
reason = "low_voltage"
|
||||
elif self.car_battery_capacity_uWh <= 0:
|
||||
reason = "battery_capacity_exhausted"
|
||||
|
||||
should_shutdown = reason is not None
|
||||
should_shutdown &= not ignition
|
||||
|
||||
Reference in New Issue
Block a user