Add LVSolarBuck64 firmware and debug console with uv support

STM32G474RB firmware for solar buck converter with MPPT, CC control,
Vfly compensation, and adaptive deadtime. Includes Textual TUI debug
console for real-time telemetry, parameter tuning, and SQLite logging.

Added pyproject.toml for uv: `cd code64 && uv run debug-console`

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 16:38:23 +07:00
parent 3f65b5f2f2
commit e7a23a3c7e
151 changed files with 231098 additions and 0 deletions

View File

@@ -0,0 +1,102 @@
"""Editable parameter rows per controller group."""
from textual.app import ComposeResult
from textual.containers import Vertical, Horizontal
from textual.widgets import Static, Input, Button
from ..protocol import ParamDef, PARAMS, build_param_write
class ParamRow(Horizontal):
"""Single parameter row with label, current value, input, and send button."""
DEFAULT_CSS = """
ParamRow {
height: auto;
width: 100%;
padding: 0;
margin: 0;
}
ParamRow .param-label {
width: 16;
height: 1;
padding: 0 1;
}
ParamRow .param-value {
width: 14;
height: 1;
padding: 0 1;
color: $success;
}
ParamRow Input {
width: 1fr;
}
ParamRow Button {
width: 6;
min-width: 6;
}
"""
def __init__(self, param: ParamDef, send_callback):
super().__init__()
self.param = param
self._send_callback = send_callback
def compose(self) -> ComposeResult:
yield Static(self.param.name, classes="param-label")
yield Static("---", id=f"val_{self.param.id:02x}", classes="param-value")
yield Input(placeholder="new", id=f"input_{self.param.id:02x}")
yield Button("Go", id=f"btn_{self.param.id:02x}", variant="primary")
def set_current_value(self, value: float):
fmt = self.param.fmt
label = self.query_one(f"#val_{self.param.id:02x}", Static)
label.update(f"{value:{fmt}}")
def on_button_pressed(self, event: Button.Pressed) -> None:
inp = self.query_one(Input)
try:
value = float(inp.value)
except ValueError:
return
frame = build_param_write(self.param.id, self.param.ptype, value)
self._send_callback(frame)
inp.value = ""
def on_input_submitted(self, event: Input.Submitted) -> None:
try:
value = float(event.value)
except ValueError:
return
frame = build_param_write(self.param.id, self.param.ptype, value)
self._send_callback(frame)
event.input.value = ""
class ParamGroup(Vertical):
"""A group of parameter rows for one controller."""
DEFAULT_CSS = """
ParamGroup {
width: 100%;
height: auto;
padding: 0 1;
margin: 0 0 1 0;
}
"""
def __init__(self, group_name: str, send_callback):
super().__init__()
self.group_name = group_name
self._send_callback = send_callback
self._params = [p for p in PARAMS if p.group == group_name]
def compose(self) -> ComposeResult:
yield Static(f"[bold]{self.group_name} CONTROLLER[/bold]")
for p in self._params:
yield ParamRow(p, self._send_callback)
def update_param(self, param_id: int, value: float):
for row in self.query(ParamRow):
if row.param.id == param_id:
row.set_current_value(value)
return