The Tesla OBC hardware already publishes 9 temperature probes (3
modules × {tmp1, tmp2, tmpin}) on CAN 0x237/0x239/0x23B which the
teslacharger receives as c[123]tmp[12,in]. Until now none of these
were forwarded downstream — the Polarity VCU's STATUS_SCREEN OBC-temp
and DCDC-temp fields rendered 0 °C on OI-OBC cars.
This commit:
- Adds Param::tmpobcmax — derived value computed each Ms100Task tick
as the max across the 9 probes, skipping any that read -40 °C
(sensor absent / module not present).
- Maps it to 0x211 byte 7 (previously reserved, the last unused byte
of the 7-byte frame). Wire encoding uses the Tesla OBC convention
of offset 40 (wire = °C + 40), matching c[123]tmp* upstream so
receivers can decode with the same formula.
- Bumps VERSTR to -S5 so the right firmware is visually confirmable
in the OpenInverter web UI.
The new byte is purely additive — existing 0x211 consumers that read
only bytes 0-6 continue to work unchanged.
The Polarity VCU's 0x211 RX handler grows a corresponding decode +
display path in a paired stm32-hal-vcu commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
src/main.cpp:161:16: warning: cannot declare '::main' with a linkage
specification [-Wpedantic]
Long-standing upstream OpenInverter code (predates SKUDAK). C++ forbids
linkage specifications on main; with -pedantic in CPPFLAGS this fires
every build.
Confirmed safe to drop:
- libopencm3 vector.c:35 forward-declares int main(void) with default
linkage and invokes it at boot.
- Linker resolves main by symbol name, not by linkage attribute.
- stm32-sine has the same upstream issue; same fix applies if we ever
push upstream.
Bump VERSTR -S3 -> -S4 so the OI web UI version field shows the new
build. Verified in the built binary as "4=1.20.R-S4".
Build: clean, no compiler warnings (only the long-standing linker RWX
notice remains, unrelated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Upstream openinverter bug surfaced after recent SKUDAK PRs.
Symptom (reported by Kyle on the bench): teslacharger detects EVSE,
shows the AC limit, but stays stuck in WaitStart and never proceeds
to Enable / Activate / Run. State machine looks like it's the OFF
state but it's actually one step further on.
Root cause in src/charger.cpp:85-91:
bool CheckDelay()
{
uint32_t now = rtc_get_counter_val();
uint32_t start = Param::GetInt(Param::timedly) * 60;
return start <= 0 || (now - startTime) > start;
}
With timedly = -1 (the param's documented "no delay" sentinel and its
default value), the math is:
Param::GetInt(timedly) -> -1 (signed int)
-1 * 60 -> -60 (signed)
assigned to uint32_t -> 0xFFFFFFC4 (~4.29 billion)
start <= 0 -> false (uint32_t can't be negative)
(now - startTime) > 4294967236 -> false for ~136 years
So CheckDelay() returns false forever; charger wedges in WaitStart.
Why "it was working before": every SKUDAK PR that bumped the param
table (uaux broadcast, expanded 0x210, 0x210->0x211, version suffix)
invalidates the saved-params CRC and resets timedly to its -1 default.
Kyle had a non-default value (probably 0) saved before.
Fix: signed math for the short-circuit so timedly <= 0 still means
"start immediately." Cast to uint32_t only on the right operand, which
is only evaluated when start > 0 — can't reinterpret negative as huge.
The companion CheckTimeout() at line 75 has the same uint32_t pattern
but works "correctly by accident" because timelim = -1 makes timeout
huge and (now - startTime) > timeout is false, which matches the
intended "no timeout" semantics. Leaving that alone — fixing it would
change behaviour, not bugs.
VER suffix bumped -S1 -> -S2 so the OI web UI clearly shows the
deployed firmware (post-fix). Confirmed in built binary as
"4=1.19.R-S2".
Build: text=25192, links clean, pre-existing pedantic warning unchanged.
This bug is also present upstream (johanneshuebner/stm32-charger);
worth a PR upstream once we've confirmed the fix on the bench.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The OpenInverter web UI displays the firmware version from the 'version'
parameter (entry 2001), which expands VERSTR. Out of the box this reads
'4=1.19.R' (upstream version, no Skudak indicator). With this change it
reads '4=1.19.R-S1' so we can visually confirm we're running the Skudak
fork and not stock OpenInverter firmware.
Matches the convention already used in stm32-sine ('-sine-S6' / '-foc-S6')
where the -S<N> suffix bumps on each Skudak-side change.
Verified: 'strings stm32_charger.bin | grep S1' shows '4=1.19.R-S1' in the
built artifact.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bench capture on the green car showed a non-Polarity module also
broadcasting at 0x210 (DLC=8 frame with mostly-zero payload, byte 5 = 0x85
static — likely a Tesla OEM DI module). Even after a clean reflash + 'can c'
to clear persisted maps, the rogue 0x210 frame keeps appearing on the wire,
racing our intended frame and intermittently corrupting the VCU's read.
Confirmed via 'can p' on the OBC that this firmware's CanMap has exactly
one 0x210 entry with the 5 expected fields — the conflict is external.
Moves all five AddSends to 0x211, an adjacent free ID. The VCU's
CHARGER_CAN_ID_OI_OBC_AUX constant is updated to match in a paired
stm32-hal-vcu PR. Same 7-byte expanded layout (state/udc/idc/uaux@5/soc),
same 100ms cadence — only the arbitration ID changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the single uaux-at-byte-0 broadcast (commit d209a47) with a
7-byte expanded telemetry frame. Bench capture on green car showed dual
0x210 frames colliding (DLC=8 simple + DLC=7 expanded) because Kyle's
flashed image had both AddSend sets active; standardising on the
expanded layout eliminates that ambiguity.
Byte layout:
byte 0 state (0=Off, 1=WaitStart, 2=Enable, 3=Activate, 4=Run, 5=Stop)
bytes 1-2 udc (V, gain=1)
bytes 3-4 idc (0.1A, gain=10)
byte 5 uaux (decivolts, gain=10) — 12V aux battery rail
byte 6 soc (%, gain=1)
byte 7 reserved
Consumed by stm32-hal-vcu Core/Src/main.c CHARGER_CAN_ID_OI_OBC_AUX
handler. The state byte enables future VCU_IsCharging integration via
state == 4 (Run) without requiring a second CAN ID.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Builds firmware on PR/push to main. Initializes libopencm3 and
libopeninv submodules then runs make. Uploads stm32_charger.bin
as a 30-day retained artifact.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds AddSend for Param::uaux at CAN ID 0x210, bit 0, length 8, gain=10.
Transmits 12V aux battery voltage in decivolts/0.1V steps (e.g. 125 = 12.5V).
VCU reads byte 0 of 0x210 for 12V battery protection logic per SKUDAK-448.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>