Tesla Wall Connector Gen 3 via ESPHome + RS485 (dynamic current control, no WiFi)

After two charges, I can confirm that it works really well.



Gentlemen - @olaliv @Zany @PVi1 @Klangen82 and all the supporting team - I can’t tell you how happy I was today to finally mediate our Leaf’s charging with the ESP32 integration! I now have a stand alone ESPHome LoadPilot derived satellite capable of control based on demand which is then driven by a blueprint based automation taking into account powerwall state, solar excess and VPP event. Looking forward to optimising the latter - but the former is all you guys - thanks for your patience and ingenuity. I’m off to Mexico City now for a weeks holiday - already with a smile on my face. Cheers, DJ

@joyceda Congrats, very happy for you (nice card BTW)

Update: TWC3’s real reaction to reported mapped, new “zone steering” degradation strategy

Been running this a lot longer since my last post here, and learned a few things worth sharing back — plus a couple of real improvements to the control algorithm itself.

TWC3 doesn’t proportionally track reported below its own breaker limit

Confirmed live, repeatedly, on firmware 26.26.1: TWC3 completely ignores the exact value of reported as long as it stays under the configured breaker limit — it just charges toward its own internal ceiling regardless. It only reacts once reported crosses the limit by a margin.

TWC3’s own live plausibility/correlation check on reported (comparing it against its own ramping current) appears to only really be active during the initial charging ramp-up. Once the limit is reached and steering mode takes over, that check doesn’t seem to be a factor anymore — it’s driven purely by the reaction-curve behavior above at that point. Not 100% confirmed as a hard rule yet, but it’s consistent with everything observed so far.

That ruled out an entire family of designs I’d been iterating on (computing a precise “available current” per phase, water-filling, gain-damped self-balancing, EMA smoothing — all described in the repo’s design history for context). None of that fine-grained math has any effect on actual behavior on this firmware version — the car only cares whether reported is over or under the limit, and by how much.

The current design is adapted from: zany92/tesla-loadpilot.

Mapped the exact reaction curve

Built a dedicated diagnostic branch to precisely measure how TWC3 reacts to a fixed, manually-stepped reported value above the limit (instead of guessing from the self-balancing loop’s own noise). Turns out it’s not a sharp threshold — it’s closer to a time-integrated response:

excess over breaker limit reaction
+0.9A first reduction only after tens of seconds, can plateau for a while — but not indefinitely, held long enough it resumes declining
+1.0A reduction starts after ~20s in an isolated test — but this didn’t generalize to real dynamic conditions (one live stop after only ~4s when the car’s current was already low; another case held 8+ minutes before aborting anyway)
+1.1A and above fast, cascading reduction, full stop in ~3s; reaction saturates — going higher doesn’t react faster

Practical upshot: there’s no single “excess” value that’s both fast enough to react to a real deficit and gentle enough to never overshoot into a full stop.

New: “zone steering” — discrete bands instead of one continuous value

Instead of picking one operating point, the algorithm now switches to a 4-band discrete strategy once it’s about to publish at/above the limit: INCREASE / HOLD / SLOW (gentle, confirmed-safe braking) / HARD (fast, reserved for a persistent excess), decided by comparing the car’s actual measured current against the target. It auto-engages the instant the limit is reached and hands control straight back to the classic algorithm once the car’s current reaches a configurable floor — the goal being to gracefully ride out a passing cloud instead of the car dropping to a full stop and having to re-negotiate a new session.

Also fixed an asymmetric slew rate for the self-balancing target (cautious on the way down, fast recovery once conditions improve) and a couple of related correlation/hold-timing bugs found along the way.

Docs

Wrote up the whole thing as a diagrammed decision tree (mermaid flowcharts for every branch of the logic, plus the measured reaction-curve data) — docs/decision-tree.md in the repo.

Status

Still very much a work in progress — initial results are good (no more surprise full stops from a passing cloud, better surplus utilization), but I want more sessions across more weather/load conditions before calling any of this settled.

@PVi1 That reaction curve is the most useful thing posted in this thread so far. Thank you for doing the measurement properly instead of guessing.

It corroborates something I had measured on my own pilot from a completely different angle. I had characterised the dead band as a hysteresis rather than a threshold: from rest the wallbox only engages at about L+0.85, and once it is pulling it tracks all the way back down to published = L, the exact budget equilibrium, and holds there. Your “+0.9 A reduces after tens of seconds” is the same point reached by a different method. Two independent instrumentations, one threshold.

Your curve also caught a real defect in my own defaults, so this is not just agreement. LoadPilot shipped law_emax_default at 1.0 A, and the published value carries a permanent +/-0.05 A dither on top, so the law could peak at L+1.05. By your curve that is inside the inconsistent band and 0.05 A away from the band that stops the charge outright in three seconds. I had surprise charge stops on my pilot that I could never fully explain. That is a plausible mechanism, and I would not have looked there without your post.

I have capped the ceiling at 0.9 A (v1.4.0, pushed today). The peak becomes L+0.95, which still breaks the dead band while keeping 0.15 A of margin below the stop band. The ceiling now deliberately equals my stage-2 kick, so the continuous law and the anti-hysteresis kick target the same measured point.

On the strategy itself, I think we have converged on the same problem and split on the axis:

  • you escalate in amplitude, four discrete zones chosen on the measured vehicle current;
  • I escalate in time: the continuous law with its clamp, then a floor at L+0.1 after 120 s of sustained constraint, then a kick at L+0.9 after 240 s that deliberately bypasses the clamp to break the hysteresis.

Same observation, that no single excess value is both fast enough and gentle enough, two different ways out of it. Neither is obviously better on paper.

One thing your data makes me want to test: does the +1.0 A inconsistency correlate with the vehicle’s own current at that moment? Your zone decision reads the measured vehicle current, mine does not. If the inconsistency is explained by it, then your axis is the better one and I should follow rather than defend mine.

I will read the decision tree with interest. If it helps in the other direction, my behavioural model is condensed in docs/AI_KNOWLEDGE.md and llms.txt in the repo, written specifically so an assistant can pick it up without reading the firmware.