feat: receive ChargeLimit_pct from VCU on CAN 0x212 (SKUDAK-516) #4

Merged
bastian merged 1 commits from feat/skudak-516-receive-vcu-chargelimit into main 2026-05-10 10:50:21 -04:00
Owner

Summary

Pairs with stm32-hal-vcu PR #42. Adds a runtime Param::vcuchglim that the VCU broadcasts every 200 ms on CAN ID 0x212; on receipt, the param's Change() callback translates 20-100% to the charger's udcspnt (DC voltage setpoint).

Without this, the teslacharger had no path for the user's app-set charge limit to actually cap charging — udcspnt was effectively a compile-time setpoint that lived only in the OI web UI.

What's added

include/param_prj.h

  • New saveable param: PARAM_ENTRY(CAT_CHARGER, vcuchglim, "%", 20, 100, 80, 23)
    • Range 20-100 (matches VCU validation)
    • Default 80% so a flashed charger paired with a silent VCU still behaves predictably
    • ID 23 (next-free per file's tracker, comment bumped to 24)
  • VER bump 1.19.R → 1.20.R. Combined with the existing -S1 suffix, the OI web UI shows 4=1.20.R-S1. Verified in the built binary.

src/chargercan.cpp

  • New can->AddRecv(Param::vcuchglim, 0x212, 0, 8, 1) in ChargerCAN::MapMessages(). libopeninv's CanMap auto-dispatches to Param::Change() on each frame; no custom RX dispatcher needed.

src/main.cpp Param::Change()

  • New case Param::vcuchglim block. Linear cell-voltage ramp:

    • cell_target = 3.30 + (4.15 - 3.30) * (pct - 20) / 80
    • udcspnt = cell_target * 96

    Endpoints (Tesla LDU / VW MEB / Volt2 compatible):

    pct cell V pack V
    20 3.30 316.8 V (deep-cycle storage floor)
    80 3.94 378.0 V (recommended daily ceiling)
    100 4.15 398.4 V (matches existing udclim default)

    Tuneable via the CELL_LOW_V / CELL_HIGH_V / CELL_COUNT constants if a specific pack chemistry needs a tighter range.

Build

text data bss dec hex filename
25288 2040 324 27652 6c04 stm32_charger

Links clean. Pre-existing pedantic warning on extern "C" int main is unchanged. Version string in the binary verified as 4=1.20.R-S1.

Test plan

After both PRs flashed (this + VCU PR #42):

  • OI web UI shows version 4=1.20.R-S1 and vcuchglim parameter present
  • Set ChargeLimit in app, observe vcuchglim updating in OI web UI within 200 ms
  • Observe udcspnt updating in lockstep with vcuchglim (verifies the Change() handler fires and the math is right)
  • Power-cycle teslacharger only — within 200 ms of boot, vcuchglim re-set by VCU's next periodic frame (steady-broadcast verification)
  • Power-cycle VCU only — after VCU EEPROM read, next 0x212 frame carries the persisted user value

🤖 Generated with Claude Code

## Summary Pairs with [stm32-hal-vcu PR #42](https://git.skudak.com/Skudak/stm32-hal-vcu/pulls/42). Adds a runtime `Param::vcuchglim` that the VCU broadcasts every 200 ms on CAN ID 0x212; on receipt, the param's `Change()` callback translates 20-100% to the charger's `udcspnt` (DC voltage setpoint). Without this, the teslacharger had no path for the user's app-set charge limit to actually cap charging — `udcspnt` was effectively a compile-time setpoint that lived only in the OI web UI. ## What's added ### `include/param_prj.h` - New saveable param: `PARAM_ENTRY(CAT_CHARGER, vcuchglim, "%", 20, 100, 80, 23)` - Range 20-100 (matches VCU validation) - Default 80% so a flashed charger paired with a silent VCU still behaves predictably - ID 23 (next-free per file's tracker, comment bumped to 24) - VER bump `1.19.R → 1.20.R`. Combined with the existing `-S1` suffix, the OI web UI shows `4=1.20.R-S1`. Verified in the built binary. ### `src/chargercan.cpp` - New `can->AddRecv(Param::vcuchglim, 0x212, 0, 8, 1)` in `ChargerCAN::MapMessages()`. libopeninv's CanMap auto-dispatches to `Param::Change()` on each frame; no custom RX dispatcher needed. ### `src/main.cpp` `Param::Change()` - New `case Param::vcuchglim` block. Linear cell-voltage ramp: - `cell_target = 3.30 + (4.15 - 3.30) * (pct - 20) / 80` - `udcspnt = cell_target * 96` Endpoints (Tesla LDU / VW MEB / Volt2 compatible): | pct | cell V | pack V | |---|---|---| | 20 | 3.30 | 316.8 V (deep-cycle storage floor) | | 80 | 3.94 | 378.0 V (recommended daily ceiling) | | 100 | 4.15 | 398.4 V (matches existing `udclim` default) | Tuneable via the `CELL_LOW_V` / `CELL_HIGH_V` / `CELL_COUNT` constants if a specific pack chemistry needs a tighter range. ## Build ``` text data bss dec hex filename 25288 2040 324 27652 6c04 stm32_charger ``` Links clean. Pre-existing pedantic warning on `extern "C" int main` is unchanged. Version string in the binary verified as `4=1.20.R-S1`. ## Test plan After both PRs flashed (this + VCU PR #42): - [ ] OI web UI shows version `4=1.20.R-S1` and `vcuchglim` parameter present - [ ] Set ChargeLimit in app, observe `vcuchglim` updating in OI web UI within 200 ms - [ ] Observe `udcspnt` updating in lockstep with `vcuchglim` (verifies the Change() handler fires and the math is right) - [ ] Power-cycle teslacharger only — within 200 ms of boot, `vcuchglim` re-set by VCU's next periodic frame (steady-broadcast verification) - [ ] Power-cycle VCU only — after VCU EEPROM read, next 0x212 frame carries the persisted user value 🤖 Generated with [Claude Code](https://claude.com/claude-code)
bastian added 1 commit 2026-05-10 10:45:26 -04:00
feat: receive ChargeLimit_pct from VCU on CAN 0x212 (SKUDAK-516)
Build Firmware / Build stm32-teslacharger (pull_request) Successful in 58s
7acbd66907
Pairs with stm32-hal-vcu PR #42. Adds a runtime "vcuchglim" param that
the VCU broadcasts every 200 ms; on receipt, the param's Change()
callback translates 20-100% to the charger's udcspnt (DC voltage
setpoint) using a linear cell-voltage ramp.

Without this, the teslacharger had no path for the user's app-set
charge limit to actually limit charging — udcspnt was effectively
a compile-time setpoint.

What's added
------------

- Param::vcuchglim in include/param_prj.h:
    PARAM_ENTRY(CAT_CHARGER, vcuchglim, "%", 20, 100, 80, 23)
  - Range 20-100 (matches VCU validation)
  - Default 80% if VCU never connects
  - ID 23 (next free per the comment, bumped to 24)
  - Saveable so a flashed charger paired with a silent VCU still
    behaves predictably across reboots

- can->AddRecv(Param::vcuchglim, 0x212, 0, 8, 1) registration in
  ChargerCAN::MapMessages() — libopeninv's CanMap dispatches to the
  Change() callback on each received frame.

- Param::Change(vcuchglim) handler in src/main.cpp:
    cell_target = 3.30 + (4.15 - 3.30) * (pct - 20) / 80
    udcspnt     = cell_target * 96
  Endpoints chosen to be safe across Tesla LDU / VW MEB / Volt2:
    20%  → 3.30 V/cell × 96 = 316.8 V (deep-cycle storage floor)
    80%  → 3.94 V/cell × 96 = 378.0 V (recommended daily ceiling)
    100% → 4.15 V/cell × 96 = 398.4 V (matches existing udclim default)
  Tuneable here as constants if a particular pack needs a tighter range.

- VER bump 1.19.R → 1.20.R. Combined with the pre-existing -S1 suffix
  the OI web UI shows "4=1.20.R-S1", visually distinct from upstream
  and from the previous Skudak build.

Build
-----

text=25288, links clean. The "RWX LOAD segment" linker warning is
pre-existing on this template, unrelated. No new warnings from this
change.

Version string in the binary verified as "4=1.20.R-S1".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bastian force-pushed feat/skudak-516-receive-vcu-chargelimit from 307853bea8 to 7acbd66907 2026-05-10 10:45:26 -04:00 Compare
bastian merged commit ee59f69890 into main 2026-05-10 10:50:21 -04:00
bastian deleted branch feat/skudak-516-receive-vcu-chargelimit 2026-05-10 10:50:21 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Skudak/stm32-openinverter-teslacharger#4