Raspberry Pi EVSE Hat

Use a Raspberry Pi to build an EV charging station

Similar projects worth following
I've done a couple of EV charging stations here on - to wit, OpenEVSE II and the Hydra. One difficulty with using either of those as a foundation is that they're microcontroller driven, which means the firmware is difficult to customize and external connectivity is harder to arrange. What if, instead, we gave the job of overall control over to a Raspberry Pi? Then most of the work could be done in Python or some other, friendlier language.

One issue with having any sort of Linux based system controlling an EVSE is that there are certain aspects of the design that must not be allowed to be software controlled.

In particular, with OpenEVSE/Hydra, responsibility for cutting off the vehicle power contactor when a ground-fault was detected was given to an interrupt service routine triggered by the GFI circuit. This was deemed acceptable, because code analysis could provide some assurance that there was no reasonable way this ISR would *not* be run. But we can't really do the same with a Linux machine controlling the show. So in this design, instead, the contactor power signal is gated by the GFI. A ground fault will cause a flip-flop to be set, and while it's set, the power relay is explicitly disabled. The software will need to first turn the relay power off, then reset the GFI state. This will insure an instant reaction to a ground-fault regardless of what Linux may be doing at the moment.

In addition, leaving the power turned on when there's no call for it is a serious safety error. To prevent that, there is a hardware watchdog that must be "fed" constantly whenever the EV power relay output is on (a GPIO pin toggled at least every 10 ms or so). If the watchdog isn't fed, it will bite, forcing the equivalent of a GFI event and turning the power off.

Linux can generate a PWM signal on GPIO18, which can be used for the pilot. We can use the same two level double MOSFET generator design to generate the ±12 volt pilot from the pin, and the boost converter and charge pump to generate the ±12 volt supply. We can also use the same voltage divider and an external ADC so the Pi can sense the voltage, and thus the impedance being imposed by the vehicle.

This project can use the same HV boards used by OpenEVSE II, which means that we have a ready made solution for relay test / GCM monitoring. The power boards also would be able to supply 5 volts to run the PI.

That's the complete list of features required for an EVSE, but we also added two more ADC channels to sense the input voltage and watch a current transformer and obtain between the two of those a power monitoring solution.

Everything else at this point becomes a software feature. Given the power and connectivity of a Raspberry Pi, there are a million different directions you could go from here.

Adobe Portable Document Format - 113.88 kB - 08/04/2020 at 03:04


sch - 569.05 kB - 08/04/2020 at 03:03


brd - 141.86 kB - 08/04/2020 at 03:03


Adobe Portable Document Format - 115.40 kB - 10/05/2019 at 22:57


sch - 564.85 kB - 10/05/2019 at 22:57


View all 6 files

  • PWM overlay

    Nick Sayer08/27/2020 at 04:26 0 comments

    It turns out you can also dynamically turn on hardware pwm. Just run this (as root):

    # dtoverlay pwm pin=18 func=2

    That will make /sys/class/pwm/ and all of the rest show up and work.

  • Tracking OpenEVSE II

    Nick Sayer08/04/2020 at 03:03 0 comments

    A while ago, I changed the Hydra design to use a primary 5 volt supply and a boost converter on the logic board to supply the pilot generator. I also made this change on OpenEVSE II, and at the time, the shared HV board dictated that design here.

    What was missed in that design choice was the fact that the Hydra only supports using contactors. There's no provision for DC relays because an L1 Hydra essentially makes no sense (you'd just plug two L1 EVSEs into two different 120V circuits). Asking a boost converter to make enough 12 VDC for both the pilot generator and to trigger a relay is on the border of asking too much, whereas a buck converter to make 5 volts for the logic system and the Pi is not a big deal at all.

    So since the OpenEVSE II HV boards are going to switch over to a 12 volt primary supply, that means swapping out the boost converter for a buck converter, and it has to be beefy enough to run a Pi as well.

  • Errors on the board

    Nick Sayer08/03/2020 at 21:06 0 comments

    Well, I've discovered that there's an error on most of the boards. Fortunately, it's easy to work around.

    I thought there would need to be a level shifter of some sort for the watchdog chip's WDI signal. So I added a MOSFET and pull-up to make this happen, but I used the footprint for a P channel MOSFET instead of an N, so it's wrong.

    It turns out that you don't need a level shifter - the 3.3 volt high from the Pi is good enough on its own. So the fix is to replace the MOSFET with a 0Ω 0805 resistor going from the gate to the source pin (which is supposed to be the drain, but for the error). You also should leave out the 5 volt pull-up resistor that's on the watchdog chip's input. The 3.3 volt pull-up is all that's required (though these resistors are 100kΩ, so there's little harm in leaving them).

    Version 4.0 has a similar error, but in this case the 0Ω resistor goes from source to drain.

    In every case the idea is to just connect the WDI pin on the watchdog chip directly to GPIO4 on the Raspberry Pi header, with a 100 kΩ pull-up to 3.3 volts. This will be the design with 4.0.1 and beyond.

  • Pin definitions

    Nick Sayer10/06/2019 at 01:02 0 comments

    To make this work, you need to manipulate these GPIO pins (note these are GPIO numbers, not the actual pin numbers on the Pi GPIO connector):

    • 4 - The power watchdog pin. Whenever the relay is powered on, you must toggle this pin at 1-10 kHz.
    • 17 - The power pin. Set this high to turn the vehicle power on. Whenever it is on, you must toggle the power watchdog pin or else a (synthetic) GFI event will occur.
    • 18 - the pilot pin. Set this high for +12, low for -12. See below for using this pin.
    • 22 - The GFI status pin. This is an input. When high, the GFI has been set and turning the power on is disallowed.
    • 23 - The relay test pin. This is an input. It gives the status of the HV relay / GCM test. Within 100 ms of turning the power on, this pin should go high and stay high. Within 100 ms of turning the power off, this pin should go low and stay that way.
    • 24 - The GFI test pin. This is an output pin. A wire connected to the GFI test pin should take two loops through the GFI CT and then connect to ground. Toggle this pin at 60 Hz to simulate a ground fault as part of the GFI test procedure.
    • 27 - The GFI reset pin. Pulse this pin high to clear the GFI. You cannot do this while the vehicle power is turned on.

    In addition to the above, the MCP3004 ADC is connected to the SPI system, on device zero (/dev/spidev0.0). The first three channels are connected to:

    • 0 - the pilot feedback. You should sample this a few hundred times, taking the high and low values found. The high value can be used to detect vehicle state changes. The low value should remain near -12v (when the pilot is oscillating) or else it's a "missing diode" test failure.
    • 1 - the current sense transformer. You should sample this pin to obtain samples across two zero crossings (512 is zero), perform an RMS calculation, and scale the result to determine how much current the vehicle is drawing.
    • 2 - the AC voltage sense. You should sample this looking for the peak. This will allow you to determine the AC voltage and with the current sense, determine how much power the vehicle is drawing.

    To make a proper pilot, you must configure the Raspberry Pi hardware PWM timers (the PWM functionality in the Python GPIO library won't do).

    To make the ADC available, you must use raspi-config and enable SPI. In order to turn on the hardware PWM on pin 18, you need to add:


    to /boot/config.txt. 

  • GFI testing

    Nick Sayer10/05/2019 at 23:05 0 comments

    To perform a GFI test, start with the GFI cleared (if it's set, clear it. If it won't clear, the test fails). It should stay clear for a few dozen ms or so. Then, toggle the GFI Test line at 60 Hz (so change it every 8.3 ms) for 10 cycles. At the end of that, the GFI should be set. If not, the test is a failure. Wait 100 ms or so and clear it. If it doesn't clear, that's a failure. Watch it for another 100 ms. If it doesn't stay clear, that's a failure. If after all of that it hasn't failed, it's a pass.

    This should be performed immediately before every attempt to turn the power on. Since the GFI is left in the clear state after the test, it's ok to turn the relay power on at that point and start toggling the power watchdog pin.

  • V1.1.1

    Nick Sayer10/05/2019 at 22:51 0 comments

    everything on this board works, so far as I can tell. I was able to write some python to force the relay out line low, toggle the GFI clear high briefly, then turn the relay out line on and toggle the watchdog line at 1 kHz and the little relay light on my test jig turned on. 

    Everything else was the same as 1.0 and worked then, so I am waving my hands a bit at it.

    So... for me, this one’s done!

  • Putting it all together

    Nick Sayer09/26/2019 at 23:17 0 comments

    I don't know if I want to sit down and write the code for a Pi EVSE. But essentially what it amounts to is a giant service loop:

    • If the pilot is on, then perform a hundred or so ADC conversions on channel 0 (pilot feedback) saving the highest and lowest value seen. If the lowest value seen does not correspond to -12V, that's an error. From the highest value you can figure out the state. If it's near 12v, that's A. 9 is B, 6 is C and 3 is D.
    • If the pilot feedback state has changed, then you may have to do something in response. A->B or B->A is just informational (plug in or plug out). B->C or B->D is a request for power. C->B or D->B is a request to end power.
    • For a request for power, you should perform a GFI self test and then turn the power on. For a request to end power, you should turn the power off.
    • If the power is on, you must toggle the power watchdog pin regularly (recommended frequency is 1 kHz, but no more than 100 kHz and no less than 100 Hz).
    • If the power is on, the relay test line must be high. If the power is off, the relay test line must be low. You should allow up to 100 ms of grace after power transitions. Otherwise, that's an error.
    • If the GFI line is high, that's a ground fault. Turn the power off and report an error on the pilot (stop oscillating - leave it either 0% or 100% duty). If you intend to perform an automatic GFI clearance, you should not reset the GFI until the clearance interval is elapsed. Reset the GFI before attempting to turn the power back on.
    • You can perform ADCs on channel 1 to observe the current drawn by the vehicle. Collect samples for two zero crossings (zero is 512), then perform an RMS on the samples and scale it to obtain an RMS current draw. You can also perform ADCs on channel 2 to figure out the AC voltage. Look for the peak and scale that to determine the voltage.
    • If you wish to change the pilot, you must give the car 5 seconds of grace to respond. That includes withdrawing the pilot while the vehicle is charging.
    • Other than in response to a GFI, you should not turn off the power other than in response to a [CD]->B state change. Yanking power away from the vehicle is bad for the contactor. The vehicle will arrange for current draw to be reduced to near zero before changing state. Other than in response to a B->[CD] transition, you should never turn the power on for rather obvious reasons.
    • In state A (no vehicle) the pilot should be a steady +12v. Do not oscillate the pilot in state A. A vehicle transition directly from state A to state C or D is illegal. A transition from state B to C or D is illegal unless the pilot is oscillating (an EVSE can deny permission to charge by refusing to oscillate the pilot).

  • Hardware PWM

    Nick Sayer09/26/2019 at 22:59 0 comments

    The good news is that hardware PWM works and the jitter observed with RPi.GPIO is nowhere to be seen.

    The bad news is that it doesn't appear to be supported by Python directly so far as I can see.

    The other good news, though, is that it's fairly easy to do. It's like doing manual GPIO manipulation via /sys/class. You write a "0" to /sys/class/pwm/pwmchip0/export and in response you'll see /sys/class/pwm/pwmchip0/pwm0/ show up. Inside pwm0 you'll find "period", "duty_cycle" and "enable". Write a cycle period in nanoseconds (1000000 for 1 kHz) into "period", write the number of nanoseconds you want it to be high (e.g. 100000 for 10%) into "duty_cycle" and a "1" into "enable".

  • Build report 1.0

    Nick Sayer09/26/2019 at 20:37 0 comments

    I built the first prototype and found a couple of errors on it:

    1. The quad NAND gate has the wrong pinout.
    2. There was no connection from the Pi GPIO header for the GFI test line

    Both of those have been corrected and in addition, I replaced 3 diode-plus-pull-up level shifters with a triple bus buffer chip. It may be slightly pricier, but placing a chip and a bypass cap beats 6 passives. I also replaced the dual JK flip-flop with a single D flip-flop chip. The fact that it's a D vs JK doesn't matter in our case because we only use the set/reset functionality as a latch.

    I was able to test the pilot generator and ADC and they work properly. Also the relay test input works (though there wasn't much chance it wouldn't).

    On to v1.1!

  • Some sample code

    Nick Sayer09/25/2019 at 17:50 0 comments

    Reading the ADC:

    import spidev
    spi = spidev.SpiDev(), 0)
    spi.max_speed_hz=1000000 # 1 MHz
    spi.mode = 0
    chan = 0 # 0..3 for MCP3004
    buf = [1, 0x80 | (chan << 4), 0xff]
    val = ((buf[1] & 0x3) << 8) | buf[2]
    # val is output, from 0-1023.

    While the ADC is rated (at Vdd=5V) for 3.6 MHz, it seems like going above 1 MHz makes its linearity suffer. Not sure why. Still, that's well over 40k samples per second.

    To test the pilot, do this in a tight loop, say, 500 times capturing the low and high reading. In theory a pilot voltage of -12v when presented to the divider network should result in an ADC voltage of about 900 mV and a +12v should read as about 4.55v. Those two values from the ADC are 184 and 932 respectively. Between those two values the scale should be linear.

View all 13 project logs

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates