HEART Embedded Systems logoHEART

Guide 8: A HEART-Embedded EV charger#

Reading tracks1 Basic
Basic: monitoring only, HEART Basic, A1. Which track am I?

A case study. A maker of home electric-vehicle chargers (wallboxes) wants its products to be HEART-embedded systems: each charger should know when it is unwell, say so in one standard language, tell the owner and the installer, and never leak anything it should not. This guide goes step by step from day 1 to a charger that can claim HEART Basic, Autonomy Level A1, and shows how an owner registers it and watches it on HEART Hub.

A working demo comes with it: evChargerSimulator/, one Python file that simulates the cars, the grid, a home charger and its HEART, and reports to https://heartcortex.ai.

Status. A design case study with a simulator. No real charger, car or grid was involved, and nothing here is electrical advice or a certification route. A real charger is mains equipment that a qualified electrician installs, and it is certified as such everywhere. Simulated in this guide: everything the simulator does. Needed for a real product: the items marked "real product" in each step.

The guide takes a home charger: one tethered connector, 32 A single-phase (about 7.4 kW), one household, cars mostly arriving in the evening. A small public site with two connectors would use the same HEART; the main differences are noted where they matter (the privacy table, Step 3).


Before you start: HEART changes nothing#

A charger's job, delivering current to a car safely, is control and protection. Its controller is certified, sits in the safety chain, and must never depend on anything else. HEART stays out of it completely.

Stays in the charger's certified controller and the backendGoes to HEART
Charging current: the control pilot offer to the car, never above the cable's ratingKnowing whether the charger can do its job
Start and stop, authorisation (RFID cards, app, plug-and-charge)Watching the meter, the connector temperature, the supply, the contactor and the network link
The residual current device (RCD), over-temperature derating, under-voltage pausing, the weld checkRaising alerts with standard fault codes, and saying what they mean
Talking to the operator's backend (real chargers use OCPP, the Open Charge Point Protocol)Telling the owner and the installer, even with no internet
Smart charging and load management, tariffs, billingAnswering "how is the charger?" in one standard format

This is the general rule of the standard (Part 6: HEART stays out of the safety chain and out of real-time control loops, and never bypasses an interlock). This HEART has no commands at all. It cannot set a current, start or stop a session, or reset a protection, not even with approval. Every change request is refused with 403 capability-not-allowed.

HEART does not implement OCPP, and does not replace it. OCPP is how the charger and its operator run the charging business: sessions, authorisation, tariffs, remote start and stop. HEART is how the charger says whether it is well. They sit side by side on the same board, and HEART reads the same status the charger already sends over OCPP.


Step 1: Decide where HEART runs#

A wallbox usually has two processors:

mains ──► RCD ── contactor ── meter ──► cable ──► car
           ▲         ▲          │
           └── safety controller (certified, real time) ──status──► communication board ──► backend (OCPP)
                                                                     HEART runs here    └──► HEART Hub
PartWhat it doesHEART's place
Safety controllerControl pilot, contactor, RCD and its self-test, temperature derating, weld checkNone. Read only, through the status it already sends
Communication board (Linux or a Wi-Fi microcontroller)Network link, OCPP to the backend, the owner's app, display or LEDsRuns the HEART (the Python template, heartd, or the ESP32 Full starter)
BackendThe operator's system: sessions, billing, remote start and stopReceives escalations if the maker wants; never commands through HEART

So the HEART-embedded system is the charger as seen by its own communication board, which adds no new path into the safety controller.

Power cuts. When the grid is lost, the communication board needs a small backup (a supercapacitor or a cell) to send one last report, power.supply-lost. The simulator assumes it has one (real product: size it, or accept that a power cut shows on HEART Hub as a Flatline instead).


Step 2: Give the charger an identity#

The communication board's HEART serves a Card at /.well-known/heart-card.json:

{ "system": { "id": "com.example.wallbox.home-7kw", "name": "Example home charger", "version": "1.0.0" },
  "level": "basic", "bindings": ["http-server"], "pulse": { "intervalSec": 30 },
  "modules": ["properties"], "shares": { "pulse": "public", "arrhythmia": "public", "property": "private" },
  "autonomy": { "level": 1 }, "template": "heart/embeddedstandard/python/v1.2611.2" }

The simulator's own is org.heartaispec.demo.ev-charger with the template heart/embeddedstandard/ev-charger-simulator/v1.2611.2. The system ID names the product, never the household or the address. The device ID used by HEART Hub (Step 8) is random, and nothing links it to a person except the owner's own registration. The charger's OCPP identity (its charge point ID) is a different thing, belongs to the backend, and is not part of HEART.


Step 3: List what the charger knows, and how private each item is#

A home charger says more about a household than it seems. When a car charges and how much shows when people are at home, when they leave for work and when they come back. Decide every value's privacy level before writing code.

ValueSourcePrivacyWhy
Healthy or not; open fault codesDerived by HEARTpublicThe only things HEART Hub ever receives (Step 8)
In service or out of serviceDerived by HEARTpublicNeeded to act; says nothing about use
Firmware versionControllerinternalFor the installer; a public version tells attackers which flaws to try
Grid voltageMeterinternalSupply quality, for the installer and the network operator
RCD state (ok, tripped)Controllerinternal
Contactor health (ok, not closing, welded), operations countControllerinternalWear, for the installer
Network link to the backend (up, down)Communication boardinternal
Session countControllerinternalA count, not a timeline: for wear and warranty
Outdoor temperatureSensorinternal
Connector state (available, charging, suspended, faulted)ControllerprivateShows whether a car is there, so whether someone is home
Current, offered current, powerController and meterprivateShows when a car charges
Energy this session, meter registerMeterprivateHow much, and over time a household's habits
Connector temperatureSensorprivateIt rises only while a car charges, so it reveals the same
Charging sessions (start, end, energy, peak current)ControllerprivateThe full timeline: owner only
RFID card IDs, vehicle IDs (for example the car's ISO 15118 identity), account numbersController and backendnever part of HEARTHEART does not need them, so it never has them. They stay in the controller and the OCPP backend

For a public site with two connectors, connector state is a service fact (drivers want to know a connector is free) and could be public; the sessions would still be private, and card and vehicle IDs still never part of HEART.

Note what is not an alert: a car that stops drawing current because its battery is full (suspended-ev) is normal; so is the charger pausing for a voltage dip. Only problems with the charger or its supply become alerts.


Step 4: Say what "doing its main job" means#

The main job of a charger is: when a car is plugged in and asks for charge, current flows, safely. The HEART checks this from the controller's status:

def check_main_function():
    """CHANGE: return (True, "") if the charger can do its job, else (False, "what is wrong, in plain words")."""
    s = controller.latest_status()
    if s.rcd_tripped:
        return False, "The RCD has tripped. A person must inspect the car and cable and reset it at the charger."
    if s.weld_detected:
        return False, "The contactor is welded. The charger has locked itself out: call the installer."
    if s.contactor_command and not s.contactor_mirror:
        return False, "The contactor does not close: the car cannot charge. Call the installer."
    return True, ""

A condition must hold for 5 seconds before it is raised, because one odd reading is not a fault.


Step 5: The faults it recognises#

Every alert carries a fault code from Part 3 §5.4, so the charger, the installer's tools and any monitor site read it the same way. Each code below was checked against that list.

What happensFault codeSeverityStatus of the system
The RCD tripped (earth leak in the car, cable or wiring); charging stopped until a person resets itsystem.main-function-lostcriticalfailing
The contactor will not close: a car waits and no current flowssystem.main-function-lost and actuators.not-respondingcritical, errorfailing
The contactor is welded (stays closed when told to open); the charger locks itself outsystem.main-function-lost and actuators.not-respondingcritical, errorfailing
Connector above 70 °C: the charger derates from 32 A to 16 A (and stops at 90 °C)performance.too-hotwarningdegraded
Grid voltage outside 207 to 253 Vpower.supply-unstablewarningdegraded
No grid voltagepower.supply-losterrordegraded
The network link to the backend is downconnectivity.link-lostwarningdegraded
Current flows but the energy meter does not count for 60 ssensors.stuckerrordegraded
Charger silent for three Pulse intervalssystem.flatlineraised by HEART Hub

Why these codes, honestly.

  • RCD trip. Part 3 has no "safety tripped" code, and none of power.* fits: the supply is fine, the charger's own protection has acted. What the owner needs to know is that the charger can no longer do its job, so the code is system.main-function-lost, and the explanation says why: the RCD tripped, and a person must inspect and reset it.
  • Contactor. It is a switch the controller commands, so actuators.not-responding ("does not react to commands") fits both a burnt coil and welded contacts; the charger cannot charge in either case, so system.main-function-lost is raised as well.
  • Meter. A register that does not move while current flows is exactly sensors.stuck. It is an error, not a warning: charging is still safe, but billing and energy records are wrong. HEART checks it itself, by comparing the current with the register.
  • Supply lost is an error, not critical: the charger is not broken, and there is usually nothing to fix at the charger. Its status says degraded, and the explanation says it cannot charge until power returns.

The thresholds here are illustrations (the voltage window is the usual ±10 % around 230 V). A real product takes them from its own specification.


Step 6: No commands, and what must never be automatic#

The HEART has no capabilities: the Card lists none, and every PUT, POST, PATCH or DELETE, with or without the owner key, is refused with 403 capability-not-allowed. Current, start and stop stay with the controller and the backend.

Two things must never be automatic, at any Autonomy Level, by HEART or anything else:

  1. Current above the cable's rating (or above the circuit's breaker). The controller enforces it, and no Playbook, model or person through HEART may raise it.
  2. Re-closing after an RCD trip. A trip means current may be flowing through something or someone it should not. Only a person at the charger, after looking, may reset it. The same holds for a weld lockout.

The standard requires a Stop control only from Autonomy Level A3 up, because A1 and A2 take no action and so have nothing to stop. This HEART takes no action, so it has none. The charger's own emergency stop and the backend's remote stop are not HEART's.


Step 7: Tell a person#

Autonomy Level A1 needs an escalation route. For a home charger there are three, and the first works with no internet:

  1. At the charger: its LED ring or display shows the fault, so whoever stands at it knows.
  2. The owner's app, as the charger already has.
  3. The installer, for faults only they can fix (RCD, contactor, meter, connector).

Change escalate() to use them. HEART Hub (Step 8) adds a fourth: an email when the charger goes silent.


Step 8: Register and watch it on HEART Hub#

HEART Hub lets an owner (or the installer they share with) see the charger's health from anywhere, on a private page.

  1. First start. The charger makes a random device ID, claim code and key, keeps them, and shows the claim code only to the person holding it: on a label inside the cover, or in the installer's commissioning app.
  2. Sign in. The owner opens https://heartcortex.ai/login and enters an email address. A 6-digit code arrives; there are no passwords.
  3. Register. At https://heartcortex.ai/register?id=<device ID> (a QR code on the label can open it), the owner enters the claim code. Holding the charger is the proof of ownership.
  4. Watch. The device's page, https://heartcortex.ai/device/<device ID>, shows its status and open alerts, private to the owner until they share it. Their list of devices is at https://heartcortex.ai/devices.
  5. Silence. If no report arrives for three Pulse intervals, the page shows a Flatline and the owner gets one email.

Only the public Pulse and public alerts are sent, and the site keeps only the status, timing and fault codes: never a current, an energy, a connector state or a session.

Privacy note for a real product. Some alerts only happen while a car charges (an RCD trip, an overheating connector, a stuck meter), so their times hint at when a car was there. That is far less than a session log, and acceptable for a page the owner chose to register; but a maker with many chargers should run its own monitor site (the code is open, in heart-monitor/), under its own data-protection duties.


Step 9: Try it with the simulator#

python3 evChargerSimulator/evChargerSimulator.py --scenario all

It prints a device ID and claim code; register them as in Step 8 and watch the page while a demo car charges and the simulator stages each fault in turn: a stuck meter, an overheating connector with derating, an unstable then lost supply, a lost network link (and so a gap in reports), an RCD trip, a contactor that will not close, and one that welds as the car leaves. Each is "fixed by the electrician" a minute or two later, so you can watch alerts open, escalate and resolve. Without --scenario, cars arrive on random timers, mostly in the evening. Details and every option are in evChargerSimulator/README.md.

What is simulated. The cars (battery, charging curve), the weather, the grid, the network, and the charger's controller and protections. What a real product needs instead: a read-only reader of the controller's status, its own thresholds, its own escalation route, and a backup supply on the communication board for the last report.


Step 10: Test it, rate it and claim only what passed#

  1. Run the HEART-Rating client (heart-rating/client/heart_rating.py) against the charger, and the hands-on tests of Part 9: cause each fault in the table of Step 5 on a test bench (an RCD test button, a heated connector, a variable supply, a disconnected network, a contactor with its coil removed), and check that nothing HEART does changes charging.
  2. Publish a Declaration with every result.
  3. Claim HEART Basic, Autonomy Level A1.
LevelNeeds (Part 2 §8)What it would mean for a charger
A1 (notices and tells a person)Basic, and an escalation routeThis guide
A2 (says why, with a confidence)Connected, cause and confidence on alerts"The connector reached 78 °C at 32 A on a 12 °C evening, 20 °C hotter than last month at the same current: likely a worn pin, confidence medium." Still no action
A3 (suggests a fix, a person approves)Full: approval and a Stop controlOnly for the communication board itself, for example "restart the network module?" after a link loss. Never current, start or stop of charging, which belong to the controller and the backend
A4 (acts by an adopted rule)Full, an adopted Playbook in each area, an allowlist entry for ruleA Playbook such as "if the link has been down 10 minutes, restart the Wi-Fi module", within the board's own limits
A5 (learns and evolves)Full, the ai and evolving modulesLearning what "normal" looks like for this connector and this supply, to raise warnings earlier. Its proposals would still be bound by everything above

At every level, current above the cable's rating and re-closing after an RCD trip (or a weld lockout) stay out of reach (Step 6).

HEART conformance says nothing about charger approval. A real charger still needs its own (for example IEC 61851 for conductive charging, IEC 62955 or an RCD to IEC 61008/62423 for DC leakage protection, IEC 62196 for the connector, the meter rules of each market, and the local wiring rules).


What the case study shows#

  • A HEART that changes nothing is still useful. Most of the value is noticing (a frozen meter, a hot pin, a welded contactor), explaining in plain words and telling the right person.
  • Some protections must stay with people. Resetting an RCD and exceeding a cable's rating are not things to automate at any level; the standard's safety split makes that easy to say.
  • Privacy is about patterns, not just numbers. No single current is secret, but a charging timeline shows when a household is home. Status and fault codes are public; the timeline is private; card and vehicle IDs are not part of HEART at all.
  • HEART sits beside existing protocols. The charger keeps OCPP for its business with the backend; HEART adds one standard answer to "is it well?".