Introduction
Build a Level 2 AC EV charging station with SAE J1772 pilot signal, RFID auth, and cloud management. This comprehensive guide covers everything from design through implementation, testing, and deployment.
Build a Level 2 AC EV charging station with SAE J1772 pilot signal, RFID auth, and cloud management.
Build a Level 2 AC EV charging station with SAE J1772 pilot signal, RFID auth, and cloud management. This comprehensive guide covers everything from design through implementation, testing, and deployment.
Level 2 AC charging (SAE J1772 / IEC 61851) uses a pilot signal — a 1kHz PWM square wave on the Control Pilot (CP) wire — to communicate between EVSE (charger) and EV. The duty cycle encodes the available current: 16% duty = 10A, 25% duty = 16A, 51.3% duty = 32A. The EV responds by pulling the CP line to different voltage levels: 9V (ready to connect), 6V (ready to charge), 3V (charging with ventilation needed).
10 components required for this project.
| # | Component | Purpose | Qty |
|---|---|---|---|
| 1 | 32A 7kW Single-Phase EVSE Controller Board | Core charging control logic | x1 |
| 2 | MFRC522 RFID Reader | User authentication | x1 |
| 3 | Raspberry Pi 4 | OCPP gateway and management | x1 |
| 4 | INA3221 Triple Current Sensor | Energy metering per session | x1 |
| 5 | 32A 250V Earth Leakage Contactor | Main power switching | x1 |
| 6 | Type 2 EV Socket (IEC 62196) | Vehicle connector | x1 |
| 7 | 6mm² 5-Core Flex Cable | Power delivery to socket | x5m |
| 8 | 32A RCD/GFCI Protection | Earth fault protection (mandatory) | x1 |
| 9 | 7" Touchscreen LCD | User interface and session info | x1 |
| 10 | 4G LTE Module (SIM7600) | Cloud connectivity for OCPP | x1 |
Follow these 8 steps carefully.
Level 2 AC charging (SAE J1772 / IEC 61851) uses a pilot signal — a 1kHz PWM square wave on the Control Pilot (CP) wire — to communicate between EVSE (charger) and EV. The duty cycle encodes the available current: 16% duty = 10A, 25% duty = 16A, 51.3% duty = 32A. The EV responds by pulling the CP line to different voltage levels: 9V (ready to connect), 6V (ready to charge), 3V (charging with ventilation needed).
Generate the 1kHz PWM pilot signal using Arduino/ESP32 with a ±12V level shifter (CP must swing ±12V). Set duty cycle based on your circuit breaker rating. Monitor CP line voltage with a precision voltage divider to detect EV connection state machine transitions. State A: 12V (no vehicle), State B: 9V (vehicle connected), State C: 6V (charging), State D: 3V (needs ventilation), State E: 0V (fault).
Connect MFRC522 RFID reader via SPI. Store authorized RFID card UIDs in a local whitelist on Raspberry Pi. On card tap, check against whitelist and remote OCPP backend. Display session start on touchscreen with user name and allowed charging limit. Log all authentication events with timestamp. Support multiple user cards with individual kWh allowances for managed fleet charging.
Install OCPP-J (JSON over WebSocket) client on Raspberry Pi using the ocpp Python library. Connect to a central management system (CMS) like SteVe or ChargePoint cloud. Implement mandatory OCPP messages: BootNotification, Heartbeat, Authorize, StartTransaction, MeterValues, StopTransaction, StatusNotification. This enables remote start/stop, remote firmware updates, and central billing.
Read INA3221 for real-time power measurement every second. Accumulate kWh per session: energy += (power_W × 1s) / 3600000. Record session data: start time, end time, user ID, kWh delivered, peak power. Calculate session cost at configured rate (e.g., ₹8/kWh). Generate session receipt on touchscreen. Send session summary via OCPP StopTransaction message to CMS for centralized billing.
The RCD/GFCI must trip within 30ms for ground faults > 30mA (IEC 62955 requirement). Test RCD monthly using the test button. Implement software monitoring of CP line for fault states. Add overcurrent detection — if current exceeds 110% of set point for >5 seconds, open the contactor. Monitor cable temperature with NTC thermistor (max 90°C). All safety trips must be logged with cause and timestamp.
Build the UI using PyQt5 or Tkinter on Raspberry Pi. Home screen: CATB logo, Tap card to start. During session: real-time kW, kWh delivered, session time, cost, animated charging bar. Completion screen: total kWh, total cost, carbon saved (kgCO₂ = kWh × 0.82 for India grid). Include admin mode (PIN protected) for setting tariff rates, viewing logs, and network configuration.
Implement dynamic load management: monitor house's main supply current via a clamp meter. When house approaches its supply limit, reduce EV charging current via pilot signal duty cycle. Schedule charging during off-peak hours (11 PM – 6 AM) when tariff is lower. Support solar surplus charging — when solar generation exceeds house load, automatically start/increase EV charging to use excess generation.
Core code for ocpp_client.py:
import asyncio import logging from datetime import datetime from ocpp.v16 import ChargePoint as cp from ocpp.v16 import call from ocpp.v16.enums import RegistrationStatus, Action, AuthorizationStatus import websockets logging.basicConfig(level=logging.INFO) class CATBChargePoint(cp): async def send_boot_notification(self): request = call.BootNotificationPayload( charge_point_model="CATB-EVSE-L2", charge_point_vendor="CATB.in" ) response = await self.call(request) if response.status == RegistrationStatus.accepted: logging.info(f"Registered. Heartbeat interval: {response.interval}s") asyncio.ensure_future(self.send_heartbeat(response.interval)) async def send_heartbeat(self, interval): while True: await asyncio.sleep(interval) await self.call(call.HeartbeatPayload()) async def authorize_card(self, id_tag): response = await self.call(call.AuthorizePayload(id_tag=id_tag)) return response.id_tag_info.status == AuthorizationStatus.accepted async def start_transaction(self, id_tag, connector_id=1): if not await self.authorize_card(id_tag): logging.warning("Card not authorized") return None response = await self.call(call.StartTransactionPayload( connector_id=connector_id, id_tag=id_tag, meter_start=0, timestamp=datetime.utcnow().isoformat() )) logging.info(f"Transaction {response.transaction_id} started") return response.transaction_id async def stop_transaction(self, tx_id, meter_wh): await self.call(call.StopTransactionPayload( transaction_id=tx_id, meter_stop=meter_wh, timestamp=datetime.utcnow().isoformat() )) async def main(): async with websockets.connect( "ws://cms.catb.in/ocpp/CATB-001", subprotocols=["ocpp1.6"] ) as ws: charger = CATBChargePoint("CATB-001", ws) await asyncio.gather( charger.start(), charger.send_boot_notification() ) asyncio.run(main())
Test Electric Vehicle Charging Station by verifying each subsystem individually before full integration.
Verify power voltages, check ground connections, use serial monitor for debug.
An interactive simulator will be available here — simulate circuits and run code in-browser without hardware.