uhi22 wrote: ↑Mon Jan 20, 2025 7:11 am
Looks like the Teslas 15Ah limit can be worked-around by pretending a charge current while actually discharging:
viewtopic.php?p=78883#p78883
Alas nope, there was a follow-up:
https://teslamotorsclub.com/tmc/threads ... st-8579714
What's interesting: with that device the battery heater turns on, which is boring quickly in cold weather (it's like ~7kW and trying to heat up to 50°C). Interestingly with pyPLC I haven't seen that ever. Ingineerx (the author for that teardown) was so kind to send me a trace of the PLC communication. It's on my list to check if any differences in the messages pyPLC sends avoids the heating, or if it's due to the Tesla BMS firmware (he is using an older firmware version on his car).
vbarrier wrote: ↑Sun Jan 19, 2025 9:20 pm
Did you be able to continue your testing after Christmas?
Yes! I'm planning to do a more structured write-up somewhen next month, but for now a braindump for curious folks must do it; basically all the information is out there, I just built something on top of giants (pyPLC and battery emulator) and put them together
NO FURTHER PYROFUSE HAS BEEN HARMED IN THE FOLLOWING ACTIVITIES (first good news

)
The first thing I tackled is the timeout issue. There are two ways to detect it: (1) Whenever the communication is in state
CurrentDemand the expectation is that the next message will arrive within 110ms, if not, a
stopDCSoft is triggered, and if it happens again a
stopDCHard is triggered:
https://github.com/lewurm/pyPLC/blob/cd ... #L473-L480 The difference is that
stopDCSoft tells the inverter via ModBus to reduce the power to zero, while
stopDCHard will open the contactors of the wallbox (ignoring any potential load and thus risk welding).
The (2) way is to hook into the sniffer
https://github.com/lewurm/pyPLC/blob/cd ... #L317-L320 which will also trigger a
stopDCSoft. This is however slower in terms of reaction time, it's somewhere around 300ms. I assume you could tune that value somewhere in the TCP settings of the kernel, but I guess the first variant is already good enough.
And
then I replaced the jumper cables that I used within the wallbox for CP with some 2 pair telephone cable that has shielding, and suddenly the timeout situation was much much better. It's still happening, and from the few observations I have it seems to only happen when there is a lot of sun; I assume the DC cables between wallbox<>inverter are carrying some noisy stuff when the inverter is more at work? I ordered some ferrite rings to put on the DC cables (please tell me if this a bad idea, I have no idea what I'm doing regarding EMI).
I also gave up on using the Battery Emulator, and instead ported the inverter communication to Python and use it directly within the pyPLC loop. This makes the coordination much easier, it's basically another variant in
hardwareInterface.py. It's also equipped with some additional tweaks that are easily possible in a Linux environment, e.g. I'm using the
pykoplenti command that uses the REST API of the inverter to turn on/off its battery support. Probably possible on an ESP32 too (which Battery Emulator runs on), but much more cumbersome. And then there is some interaction with my local Home Assistant instance for high level control, logging state transitions and bookkeeping. By the way, I had some "performance issues" because that involves network stuff, which can have delays... and that's not good to do on the same thread as the car communication (which is pretty sensitive to timeouts, roughly one second). So there is another thread that polls/push certain stuff, to unblock the main thread. Seems good enough, but a bit hacky due to the GIL in cpython.
Regarding the step-up converter, I followed the mod described here:
https://github.com/dalathegreat/Battery ... age-source
And have a little calibration script to figure out good frequencies for the PWM:
https://github.com/lewurm/pyPLC/blob/di ... ter_mod.py
It depends on your input voltage, but then didn't require much tuning, it rarely needs more than one attempt before the car is happy:
https://github.com/lewurm/pyPLC/blob/cd ... #L303-L339
It's pretty solid!
That's how it looks like currently.
Still janky... looks better if you hide it
So I did my first longer session last Sunday:
kostal SEM Power Saldierend is the sum of the three phases going to/from grid according to the Kostal smartmeter (an additional device).
Scb-kostal-battery Total Power is the power to/from the battery according to the inverter.
dcwb_strategy is the input to a script that controls via modbus regarding how much power should go to/from the battery (it runs as part of Home Assistant via PyScript
https://gist.githubusercontent.com/lewu ... 3e/dcwb.py yes yes, I'm a Home Assistant fanboi

).
So it's discharging for a while, and then doing a little "charge pump" to hopefully reset the BMS state and thus bumps the 15Ah threshold. And it seems to work, because I sucked out more than 15Ah in this session. It was pretty cold in the garage already and I was tired, so I stopped the session. I thought that was already pretty cool, but there seems more to this 15Ah limit...
On Monday (electricity spot prices were high), I felt confident enough to leave it unattended (well I put a security cam inside the garage

it's actually pretty useful for verifying the shutdown sequence on timeouts, the sound of the different contactors are distinctive

):
Here the car aborted with the infamous
BMS_a076_SW_Dch_While_Charging, but interestingly enough without payload (it doesn't mention -15Ah):
Still great, that was a 6h session and I was already very happy with that.
For easy restarting a charging session I put CP behind a relay. So flipping that one for a few seconds works in some scenarios to restart the charging session, for example if the EVSE initiates stopping the charging session. Interestingly enough on TCP retransmission timeouts the session can also be restarted this way. However, on this
BMS_a076_SW_Dch_While_Charging event the charge port LED pops up red. I
can initiate another session, but it even refuses to precharge. It requires physical unplugging.
So I started another session, but exactly at midnight something happened: The inverter restarted. Apparently that's a thing, haha. I just didn't handle that case well, but I added some code to consider that and started another session a few minutes after midnight... and amazingly, the very same session tested next midnight, that is,
it was running more than 24h
I want you to appreciate pyPLC for a second, it's amazing how well it works. Thank you uhi22!!!
What do we see here? So this
FZ60e Battery value comes from TeslaFi which in turn polls the Tesla API... at some point the battery level got stuck, but it didn't on the Tesla App. I submitted a bug report and it was actually fixed pretty quickly then (or it was just a coincidence? I didn't get a response yet). Anyway, I thought that was funny
From the charts you can probably guess the more expensive hours. Also towards the evening the oven was on and the control loop to adjust power oscillated, but fortunately the car nor the inverter seemed to mind. In the 26th hour the car stopped the session with our dear friend
BMS_a076_SW_Dch_While_Charging, this time showing the payload message again (see previous post) mentioning the 15Ah threshold. At this point I already added a session counter for charge/discharge amount, so you can see that those are almost equal. However, and here comes one of the drawbacks using a car as battery: There is some idle power used by the car if it's "online" (for example when it's charging). From what I've gathered from the CAN bus, it's around 200W. And that seems to match quite well: According to my session counters there was 500Wh more charged into the car than discharged. And then 200W for 25h => 200*25 + 500 = 5500Wh. Divided by 390V that's 14,1Ah. That seems to be the right ballpark. However, it's sort of surprising, given the "charge pumps", that have helped in the first chart in this post.
And an observation from today: I stopped the charging session after ~4.5kWh discharging and did the CP flipping, but unfortunately the next session aborted after ~1kWh of discharging with
BMS_a076_SW_Dch_While_Charging. Only physically replugging helped then. A bit of a bummer; alas I can't "disconnect" PP (= putting a relay inbetween) on my plug, but not sure if that would even help.
More experiments are needed

And maybe it's also time to have a closer look at the BMS firmware in a disassembler...
Anyway, I'm really really happy with the results so far. Thanks again to uhi22 and other pyPLC contributors for making all the work and information publicly available, and also shoutout to the folks over at the Battery Emulator Discord. It has been really fun so far and I learned a lot!
TODOs on my side:
- figure out EMI issue whenever there is too much PV energy
- figure out why inverter won't start battery usage when there is already PV energy there. My hope is that is related somehow with the previous point, but with the RS485 connection between inverter<>wallbox. There might be as well parts of the protocol that I'm missing in that situation. The current implementation is based on reverse engineering, so... if someone has good connections to Kostal, I would be very interesting in their custom protocol

- do a structured write-up so others can reproduce the setup more easily
- more experiments and automation fine tuning to work around the 15Ah limit