Charging · 9 min

Reading the pilot with an Arduino — the actual circuit

You wired CP to A0 because a tutorial said read the PWM. The pin is 5 V. The pilot is ±12 V. Load it, then scale it, before a sketch means anything.

You wired CP to A0 because a tutorial said “read the PWM.” The pin is 5 V. The pilot is ±12 V. Sometimes the Arduino dies. Sometimes it lives and the number you print is garbage, and you spend a weekend chasing firmware while the wall box sits there refusing to close. A DC meter on CP says 9 V. The station still will not give you AC, because the negative half never made it to −12 V and the diode check failed. That is the pile this note is for.

The Arduino is not the inlet. Two jobs have to exist in hardware before a sketch means anything: you have to load the pilot so the EVSE thinks a vehicle is there, and you have to scale ±12 V so a 5 V (or 3.3 V) pin can see the wave without dying or lying. A bare GPIO does neither.

This is not a new schematic. The voltages and the vehicle resistors are already on pilot, proximity, and PWM. This page is the front-end people skip.

You are the car, not the wall

The EVSE writes the 1 kHz ±12 V square wave. The vehicle leans on it. If you bought a board that generates PWM, you bought a wall box. Leave it on the building. That mismatch is why off-the-shelf J1772 controllers often do not fit.

A conversion Arduino (or any MCU behind an inlet) has to do the vehicle half.

Load the pilot so the station sees a car

IEC 61851 / SAE J1772 vehicle network, same numbers as the PWM note:

  • About 2.74 kΩ (builders often use 2.7 kΩ) from CP to PE, through a series diode, is State B. Connected, not asking.
  • Switch 1.3 kΩ in parallel (~882 Ω) and you are State C. Ready.
  • Optional extra ~330 Ω is State D, ventilation required. Rare on a conversion.
  • The EVSE’s source impedance is about 1 kΩ. That is why those loads become ~9 V and ~6 V on the positive pulse.

The diode is not decoration. It lets the EVSE see that the negative half of the PWM still sits at −12 V. Resistor-only CP looks fine on a DC meter and still fails the check. A lot of stations will not close.

Craig Peacock’s Beyondlogic EVCC is the clearest published vehicle front-end. D2 + R7 (2.74 kΩ) is the connected load. A second resistor (1.3 kΩ) switches in for ready. D2 rectifies so only the positive pulse is loaded — that is the diode check, the puddle-versus-car test. D3 is a TVS that clamps stray voltage to about 16 V. His MCU is a dsPIC33, not an Arduino. The inlet network is still the one you copy.

Nick Sayer’s J1772 EV Simulator writes the same vehicle end in plain language: 2.7 kΩ + diode CP to GND for +9 V; +1.3 kΩ in parallel for +6 V; optional +330 Ω for +3 V. Same 1 kΩ EVSE source.

Do not invent a third set of resistors. Use the standard network or a published board that already did.

Then scale the wave so the Arduino can see it

±12 V on a 5 V ADC pin is how you let the smoke out. Even if the clamp saves the pin, a raw GPIO still cannot tell you duty cycle honestly.

Three published ways people actually read this wave:

Divider, then a buffer, then the ADC. Sayer’s simulator rescales −12…+12 V to 0–5 V with a resistor divider. The divider looking into the pin is about 200 kΩ. An AVR ADC wants the source at or under 10 kΩ. Without a unity-gain op-amp in front of the analog pin, the sample-and-hold capacitor drags the scaled voltage around for a few microseconds on every conversion — on the order of half a volt. Brief, and enough to wreck a duty-cycle read. He also feeds the analog comparator a voltage that represents about 0 V on the pilot (10 kΩ / 12 kΩ on a 5 V rail, about 2.71 V) and times the edges in an ISR. That is how you get frequency and duty without trusting a slow ADC sample of a 1 kHz edge.

A comparator, not a GPIO. Beyondlogic does not hang the dsPIC I/O on CP. A TL331 on 3.3 V (the part’s input can stand 38 V) turns the wave into a clean logic edge for duty. The MCU never eats ±12 V. If you are on an Arduino and you only need duty, this is the safer half of the problem.

A published EVSE still has to scale the same volts. OpenEVSE generates the wave, so it does not need to discover duty the way a car does. It still has to look at CP to see vehicle state and a missing diode. Their theory of operation feeds the pilot back to ADC1. On the DIY 30 A board the read-back divider is 200 kΩ, 100 kΩ, and 56 kΩ (R5 / R6 / R7), meant to put −12 V near 0 and +12 V near 5 V on an ATmega328P. Older write-ups mention a three-transistor scale into the same ADC. Copy those values only if you are building their station. Do not bolt the generate half into the car.

A lot of “Arduino J1772” pages skip the buffer, skip the diode, and treat analogRead() of a raw divider as a current command. That is how you get a sketch that prints 30 A while the station is still in State B.

What the sketch is allowed to do

Once the front-end is honest:

  • Voltage of the positive pulse is state (12 / 9 / 6 / 3). That is the load you presented, plus whatever the EVSE is sourcing.
  • Duty cycle of the 1 kHz wave is the amp advertisement. Formulas live on the PWM note. Cap the onboard charger to that number. Do not draw more.
  • The negative pulse is the diode check. If it does not sit near −12 V (after your scale), the station may never close. Fix the diode before you rewrite the ISR.

PP is a different pin. Latch, not PWM. Do not scope it looking for 1 kHz, and this page does not reprint an ohm-to-amp table.

Do not generate ±12 V from the Arduino and call that an inlet. Writing the wave is the EVSE. Reading it is the car.

What people get wrong

Hooking CP to a GPIO “just to see.”

Using a DC meter, seeing 9 V, and declaring the inlet finished.

Copying an EVSE-generate schematic (op-amp driven from ±12 V, TI EVSE apps, OpenEVSE’s drive half) into the frunk.

A high-Z divider straight into analogRead(), then blaming the sketch when duty jitters.

Forgetting S2. The Arduino can print a beautiful duty cycle all day in State B. The contactor on the wall will not close until you pull CP to 6 V.

Building this from a napkin instead of a published front-end. Start from Beyondlogic’s vehicle network, or Sayer’s simulator read path, and keep the numbers.

Sources