EVSE-DIN access via Modbus and mbpoll

Helmut Neukirchen, 16. December 2023

An Electric Vehicle Supply Equipment (EVSE) is the controller in a wallbox that checks how many Ampere the charging cable is able to transfer (as indicated by the PP resistor inside the cable) and communicates this via a PWM signal to the electric vehicle (EV) and when the EV signals that it wants to charge, then the EVSE finally closes the contactor inside the wallbox so that the charging current can flow to the onboard charger (OBC) inside the EV (the OBC then slowly ramps up the charging current and ramps it down at the end of charging, so contactor closing/connecting and opening/disconnecting does not involve an electric arc, so that contactor welding is not an issue).

After the Phoenix Contact EVSE in my Wallbe Eco 3.6 kW wallbox died, I replaced it with an EVSE-DIN (essentially the EVSE-WB in a DIN rail enclosure) from EV Racing. Note that there are types with a DC residual current monitoring and without. If you take the variant without, then you need to have a type B RCD.

I bought the version with Modbus from the seller OpenWB: that seller had Modbus already enabled (factory setting is disabled) and had the maximum current set to 13 A (more foolproof than the default 32 A), but had unfortunately also disabled PP detection and had it instead set to 32 Ampere (instead of measuring the PP resistor value).

I also bought an RS485 to USB converter so that I can access the EVSE-DIN's Modbus via USB.

The EVSE-DIN is very simple, but there exists a third party add-on that uses an ESP32 to add Wifi support, web server and other functionality: EVSE-WiFi. (And if you want to play around a lot with an EVSE: use an EV simulator to try out your EVSE, including checking error-handling: as any contactor, the contactors inside the EV have limited amount of open/close cycles, so using your car to test your EVSE may exhaust the cycles of the contactor inside your car).

Modbus is the protocol, RS485 is the physical layer (often, this is called RTU mode, but in fact, RTU mode means that each byte contains 8 bit of information instead of using ASCII character encoding where two ASCII characters would be needed for transmitting 8 bits) -- there exist also a TCP layer as lower layer for Modbus.

By default, 16 bit values are transmitted as values (when bit-level access is supported, then bits are called coils).

For EVSE-DIN with RS485, the baudrate is 9600 with 8N1, i.e. data bits are 8, parity is none, and stop bits is 1. The Modbus slave address of the EVSE-DIN is 1, but that can be changed via Modbus register 2001.

The registers that are accessible via Modbus can be found in the EVSE-DIN documentation.

On my Linux system, the RS485 to USB converter can be accessed via /dev/ttyUSB0 and that needs be accessed as super user.

I use mbpoll to read and write data:

To read the 10 registers from 1000 to 1009 via RS485, with addresses starting from 0 and a 1-time printout (using mbpoll's default slave address, i.e. 1):
sudo mbpoll -m rtu -b 9600 -d 8 -P none -s 1 -0 -1 /dev/ttyUSB0 -r 1000 -c 10

The result is
mbpoll 1.0-0 - FieldTalk(tm) Modbus(R) Master Simulator
Copyright © 2015-2019 Pascal JEAN, https://github.com/epsilonrt/mbpoll
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions; type 'mbpoll -w' for details.

Protocol configuration: Modbus RTU
Slave configuration...: address = [1]
start reference = 1000, count = 10
Communication.........: /dev/ttyUSB0, 9600-8N1
t/o 1.00 s, poll rate 1000 ms
Data type.............: 16-bit register, output (holding) register table

-- Polling slave 1...
[1000]: 13
[1001]: 0
[1002]: 1
[1003]: 32
[1004]: 0
[1005]: 18
[1006]: 1
[1007]: 0
[1008]: 0
[1009]: 0

To read the 17 registers from 2000 to 2016:
sudo mbpoll -m rtu -b 9600 -d 8 -P none -s 1 -0 -1 /dev/ttyUSB0 -r 2000 -c 17

The result is
[2000]: 13
[2001]: 1
[2002]: 5
[2003]: 1
[2004]: 0
[2005]: 521
[2006]: 0
[2007]: 32
[2008]: 65535 (-1)
[2009]: 3
[2010]: 6
[2011]: 10
[2012]: 16
[2013]: 25
[2014]: 32
[2015]: 48
[2016]: 63

When writing values, be aware that you need always to write at least two consecutive values (i.e. do a read to find out what the second value is and simply rewrite it). If you try to write only one single value to EVSE-DIN, you will get the following error:
Write output (holding) register failed: Illegal function

The reason is that Modbus function code 06 for writing a single value is not supported by EVSE-DIN.
From the above EVSE-DIN PDF:

NOTE#3: Only functions 03 (Read Holding Registers) and 16 (Preset Multiple Registers)
are implemented. For more details please check: http://www.simplymodbus.ca/FAQ.htm

Therefore, to write values, take care to write at least two values to make mbpoll use the Modbus function 16 (Preset Multiple Registers).

Changes that I did:

Set default (maximum) charging current to 17 Ampere in order to reflect the cable and circuit breaker rating in my garage (which is 16 A, but my EV is careful and uses always a little bit less, i.e. 16 A when the EVSE allows to use 17 A):
Change register 2000 from 13 to 17.

As single values cannot be written to EVSE-DIN, I had to write multiple values and the above read shows that 2001 is 1, so I wrote starting from 2000 the two values 17 1:
sudo mbpoll -m rtu -b 9600 -d 8 -P none -s 1 -0 /dev/ttyUSB0 -r 2000 17 1,

Make LED not quickly flashing when idle, but permanently on when idle (and slowly flashing when charging, i.e. contactor is on. Any remaining fast flashing is an indicator of an error):

Change bit 2 in register 2005 to gain constant LED ON while no EV is connected. Register 2005 had a value of 521, i.e. 0b1000001001 = (bit 1 set, i.e. button can be used to change charging current; bit 3 set, i.e. support to charge with ventilation for lead battery-based EVs; bit 9 set, i.e. pilot auto recover delay) and after setting bit 2, the new values is 525. Writing that value should work via

Enable PP detection (i.e. it takes the maximum charge current from the connected charging cable):

Change register 2007 from 32 to 0.

As single values cannot be written to EVSE-DIN, I wrote multiple values and the above read shows that 2006 is 0, so I wrote starting from 2005 the three values 525 0 0:

sudo mbpoll -m rtu -b 9600 -d 8 -P none -s 1 -0 /dev/ttyUSB0 -r 2005 525 0 0,
which gives

bpoll 1.0-0 - FieldTalk(tm) Modbus(R) Master Simulator
Copyright © 2015-2019 Pascal JEAN, https://github.com/epsilonrt/mbpoll
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions; type 'mbpoll -w' for details.

Protocol configuration: Modbus RTU
Slave configuration...: address = [1]
start reference = 2005, count = 3
Communication.........: /dev/ttyUSB0, 9600-8N1
t/o 1.00 s, poll rate 1000 ms
Data type.............: 16-bit register, output (holding) register table

Written 3 references.

A new read confirms that the changes have been applied:

sudo mbpoll -m rtu -b 9600 -d 8 -P none -s 1 -0 -1 /dev/ttyUSB0 -r 2000 -c 17
mbpoll 1.0-0 - FieldTalk(tm) Modbus(R) Master Simulator
Copyright © 2015-2019 Pascal JEAN, https://github.com/epsilonrt/mbpoll
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions; type 'mbpoll -w' for details.

Protocol configuration: Modbus RTU
Slave configuration...: address = [1]
start reference = 2000, count = 17
Communication.........: /dev/ttyUSB0, 9600-8N1
t/o 1.00 s, poll rate 1000 ms
Data type.............: 16-bit register, output (holding) register table

-- Polling slave 1...
[2000]: 17
[2001]: 1
[2002]: 5
[2003]: 1
[2004]: 0
[2005]: 525
[2006]: 0
[2007]: 0
[2008]: 65535 (-1)
[2009]: 3
[2010]: 6
[2011]: 10
[2012]: 16
[2013]: 25
[2014]: 32
[2015]: 48
[2016]: 63

The LED is now on when idle (instead of nervously blinking). Also, after having enabled PP detection, register 1003 has then the value 5 with no cable attached and 20 with an 20 Ampere cable attached.

In future, I might try enabling a second LED (via pin AN) as a dedicated error LED (need to find out what the normal LED does then display). Currently, the single LED is on when the wallbox is idle, i.e. no EV connected, flashes two times quickly followed by a short pause when the EV is connected (PWM generated by EVSE), but does not charge (e.g. EV finished charging or EV is initiating charging), and flashes slowly while the EV is charging, i.e. the contactor is closed.

I might also re-use the keyswitch of the Wallbe Eco case to interrupt charging: interrupting the CP signal should be the way to go...

Another future work is to upgrade the wallbox to 20 Amps and 3 phases. I already added a 3L+N contactor and the charging port cabling supports 20 Amps as well as the Phoenix terminal blocks do. Just all internal wiring needs to be upgraded to 3 phase 20 Amps.