ISO 15118-20
ISO 15118-20 is the second-generation high-level communication protocol for CCS. ChargeLink plays either role: the charger (SECC) for a vehicle or EVCC, or the vehicle (EVCC) for your charger or SECC. It speaks IPv6 link-local SDP, TCP, mutual TLS 1.3, V2GTP and EXI, and runs DC EIM (the full session of 16 request/response pairs from SupportedAppProtocol to SessionStop) and AC EIM (Scheduled and Dynamic control mode). As charger, ChargeLink runs plant-coupled on the NI VeriStand bench: every response is computed from the session state and the simulated power stage of the bench. As vehicle, ChargeLink runs the DC session with evcc activate-profile and the AC session with iso20-ac-evcc.
Before you start
| Requirement | What you need |
|---|---|
| Hardware | A Windows PC for ChargeLink with an Ethernet adapter on the link to the other side. For the charger role: the NI VeriStand bench of the package. A powerline (PLC) link to a vehicle or charger needs the hardware described in Connecting to Real PLC Hardware. |
| Network | IPv6 enabled on that adapter; link-local addresses (fe80::/10), no router. UDP 15118 (SDP) and the TCP port of the session (default 50118) open in Windows Firewall for fe80::/10 (Install and Activate). The preflight checks interface index, link-local address, ports and clock. |
| Licence | An active ChargeLink licence on the ChargeLink PC (chargelink --json license status). A command that needs it stops with exit code 20 (CL-LICENSE-001). |
| TLS material | A V2G root CA, plus the chain and key of the side ChargeLink plays: a SECC chain and key when ChargeLink is the charger, a vehicle chain and key when ChargeLink is the vehicle. The other side's material must chain to the same root. See TLS and PKI. |
- TLS 1.3 with mutual authentication is mandatory. ChargeLink rejects plaintext, TLS 1.2 and connections without a verified client certificate before the SupportedAppProtocol exchange. It negotiates only the named groups secp521r1 and x448 and the cipher suites TLS_AES_256_GCM_SHA384 and TLS_CHACHA20_POLY1305_SHA256.
- Production and engineering material. Production material follows ISO 15118-20 Annex B (ECDSA P-521, SHA-512).
evse tls-material-initgenerates a fresh Annex B-shaped engineering chain on every run;evse startaccepts it as the charger identity only with--allow-engineering-tls-identity. - Legacy named groups. A peer that offers only other groups (for example P-256 or X25519) is refused before any V2G message.
--allow-legacy-tls-named-groupsadds secp256r1 after the standard groups; it is an engineering option outside ISO 15118-20, and the charger side then also needs--allow-engineering-tls-identity. - The charger identity is validated as an Annex B chain to the V2G root. The DNS name is SNI only; the vehicle-side default
--tls-server-name chargelink-secc.localdoes not need to match a SAN.
Every example on this page uses this block. The kit has a charger\ and a vehicle\ folder; chargelink.exe is the same binary in both.
$CL = 'C:\Program Files\DEVlink\ChargeLink\<VERSION>\charger\bin\chargelink.exe'
$Data = "$env:ProgramData\DEVlink\ChargeLink\data"
$IfIndex = <INTERFACE_INDEX>
$Advertise = 'fe80::<ADDRESS>%' + $IfIndex
$Pki = "$Data\pki"
ChargeLink as charger
The ISO 15118-20 charger runs plant-coupled on the NI VeriStand bench, with profile iso15118_20_dc (DC EIM) or iso15118_20_ac_eim (AC EIM, below). The plant values (voltage, current, contactor, isolation) come from the VeriStand model, and PreCharge, PowerDelivery and the DC charge loop follow the vehicle's targets and the simulated electrical behaviour. The charger is delivered and verified with the ChargeLink vehicle on the VeriStand bench. Contact DEVlink before connecting a third-party vehicle.
The plant-coupled charger session (DC EIM)
On the NI VeriStand bench, the VeriStand operator starts this session for you with the package's runtime profile (ChargeLink Operator (VeriStand), see NI VeriStand Bench); you do not type the command. For reference, the session it starts has this shape:
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse start --external-evcc-session --run-id $Run `
--protocol iso15118-20 --profile-id iso15118_20_dc --exi-mode chargelink-exi `
--plant-grpc-endpoint <BENCH_PLANT_ENDPOINT> --requested-power-kw <kW> --target-voltage-v <V> `
[--evse-rated-current-a <A>] [--evse-rated-voltage-v <V>] `
--host '::' --tcp-port 50118 --udp-port 15118 --advertise-ipv6 $Advertise --interface-index $IfIndex `
--tls --tls-cert "$Pki\secc-chain.pem" --tls-key "$Pki\secc-key.pem" --tls-cafile "$Pki\v2g-root-ca.pem" --require-ca
--plant-grpc-endpointis the plant endpoint of the bench; there is no other plant option.--requested-power-kwand--target-voltage-vmust be given explicitly (zero means unset and is rejected).- The environment variable
CHARGELINK_ISO20_RUNTIME_PROFILEpoints to the runtime profile JSON. The runtime profile sets the control modes offered (Scheduled, Dynamic, or both for the vehicle to choose); the package's runtime profile offers Scheduled. Its fields are described on the NI VeriStand Bench page. --tcp-port 0picks a free port from 49152–65535; the bound port is reported in the JSON result and inevse_listener_status.json.- Do not use
--external-evcc-windows-tlsor--external-evcc-dev-tlsfor ISO 15118-20: both select TLS 1.2, which the ISO 15118-20 admission check rejects. - ChargeLink enforces its response budget: ChargeLoop 250 ms, ServiceDetail 4500 ms, CableCheck 38 s, other messages 1500 ms. A budget overrun is recorded as a finding.
Watch the session from a second window:
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse status --run-id $Run
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse list-state --run-id $Run
Network, SDP and TLS check without the bench
evse start without --plant-grpc-endpoint starts a reference responder with fixed reference responses and no plant. It is not a charging session: a vehicle stops early, at AuthorizationSetup. Use it only to check the network, SDP discovery and the TLS 1.3 handshake with the other side.
$Run = "iso20-check-$(Get-Date -Format yyyyMMdd-HHmmss)"
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse live-preflight --source external-evcc `
--protocol iso15118-20 --profile-id iso15118_20_dc `
--host '::' --tcp-port 50118 --udp-port 15118 `
--advertise-ipv6 $Advertise --interface-index $IfIndex `
--tls --tls-cert "$Pki\secc-chain.pem" --tls-key "$Pki\secc-key.pem" --tls-cafile "$Pki\v2g-root-ca.pem" --require-ca `
--recover-stale
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse start --external-evcc-session --run-id $Run `
--protocol iso15118-20 --profile-id iso15118_20_dc `
--host '::' --tcp-port 50118 --udp-port 15118 `
--advertise-ipv6 $Advertise --interface-index $IfIndex `
--tls --tls-cert "$Pki\secc-chain.pem" --tls-key "$Pki\secc-key.pem" --tls-cafile "$Pki\v2g-root-ca.pem" --require-ca `
--max-runtime-ms 1800000 --heartbeat-interval-ms 1000
--external-evcc-session keeps the listener open until --max-runtime-ms elapses or you stop it, and defaults UDP to 15118. The check is good when sdp_diagnostics.json records the SDP exchange and tls_handshake_diagnostics.json a completed TLS 1.3 handshake with the other side's certificate.
This check applies to the DC profile. The AC charger has no check without a plant (see below).
The plant-coupled charger session (AC EIM)
The AC charger runs plant-coupled only, with profile iso15118_20_ac_eim. It needs a plant behind --plant-grpc-endpoint: on the NI VeriStand bench, the bench's plant endpoint. Started without the endpoint, it refuses to start with "protocol-only AC is not offered". The session runs over mutual TLS 1.3 only, as ISO 15118-20 requires; the vehicle must present a certificate chain to the same V2G root. SDP runs on UDP 15118.
$env:CHARGELINK_ISO20_AC_RUNTIME_PROFILE = "$Data\profiles\iso20-ac-charger.json"
Remove-Item Env:\CHARGELINK_ISO20_RUNTIME_PROFILE -ErrorAction SilentlyContinue
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse start --external-evcc-session --run-id $Run `
--protocol iso15118-20 --profile-id iso15118_20_ac_eim --exi-mode chargelink-exi `
--plant-grpc-endpoint <BENCH_PLANT_ENDPOINT> --requested-power-kw 11 --target-voltage-v 230 `
--host '::' --tcp-port 50118 --udp-port 15118 --advertise-ipv6 $Advertise --interface-index $IfIndex `
--tls --tls-cert "$Pki\secc-chain.pem" --tls-key "$Pki\secc-key.pem" --tls-cafile "$Pki\v2g-root-ca.pem" --require-ca
--requested-power-kwand--target-voltage-vare required with--plant-grpc-endpoint(zero means unset and is rejected); give the outlet's power and nominal voltage. The AC limits themselves come from the settings file.- The environment variable
CHARGELINK_ISO20_AC_RUNTIME_PROFILEnames the charger's AC settings file. The profile ID and the settings file belong together: either one alone is refused. Do not set the DC variableCHARGELINK_ISO20_RUNTIME_PROFILEin the same window. - The kit contains no AC settings file. Write your own, as described below.
- With engineering TLS material from
evse tls-material-init, add--allow-engineering-tls-identity.
The AC settings file
The settings file is JSON. A field the charger does not know is refused.
| Field | Meaning |
|---|---|
evse_id, parameter_set_id | The charger's EVSEID and the ID of the parameter set it offers. |
connector | 1 single phase, 2 three phase. |
nominal_voltage_v, nominal_frequency_hz | Nominal voltage, phase to neutral (1–500 V), and nominal frequency. |
evse_max_current_per_phase_a, cable_rating_a | The power stage's current per phase and the cable rating. The lower of the two is the per-phase limit. |
installation_max_power_w (optional) | An installation limit below the outlet's power. |
evse_minimum_charge_power_w | The minimum charge power the charger states; at least 0 and below the outlet's maximum power. |
schedule_tuple_id, schedule_duration_s | The ID and the length of the charging schedule (both above 0). |
meter_id, free_service | The meter ID (1–32 characters) and whether the service is free. |
terminate_notification_max_delay_s | The notification delay the charger states when it terminates the session. |
schedule_renegotiation_trigger (optional) | true: the charger asks for a new schedule at the schedule's horizon. |
dynamic (optional) | The Dynamic control mode; see below. Without it, the charger offers the Scheduled control mode only. |
The dynamic block:
| Field | Meaning |
|---|---|
offer_scheduled | false (default): the charger offers Dynamic only. true: it offers both, Scheduled under parameter_set_id and Dynamic under the next ID, and the vehicle chooses. |
policy_mode | max_available (default) or departure_spread: how the charger sets the power target. |
decision_period_ms, ramp_w_per_s, departure_margin_s | How often the target is recomputed (default 1000 ms), how fast it may change (default 4000 W/s), and the margin before the vehicle's departure time (default 60 s). |
available_power_steps | The available power over time: a list of steps, each with after_ms and power_w. |
Example: a three-phase 11 kW charger that offers both control modes.
{
"evse_id": "<EVSE_ID>",
"parameter_set_id": 1,
"connector": 2,
"nominal_voltage_v": 230,
"nominal_frequency_hz": 50,
"evse_max_current_per_phase_a": 16,
"cable_rating_a": 32,
"evse_minimum_charge_power_w": 1380,
"schedule_tuple_id": 1,
"schedule_duration_s": 3600,
"meter_id": "<METER_ID>",
"free_service": true,
"terminate_notification_max_delay_s": 10,
"dynamic": {
"offer_scheduled": true,
"policy_mode": "max_available",
"available_power_steps": [ { "after_ms": 0, "power_w": 11000 }, { "after_ms": 60000, "power_w": 7000 } ]
}
}
Control modes: the charger always offers Scheduled when dynamic is absent; with dynamic it offers Dynamic only, or both when offer_scheduled is true. In both modes the vehicle states its mobility needs (MobilityNeedsMode 1). The charger answers a power tolerance the vehicle confirms with OK_PowerToleranceConfirmed; it offers no tolerance itself.
What a good AC charger session looks like: protocol\iso20_ac_secc_session_evidence.json in the run folder reads "result": "session_stop" and "profile_id": "iso15118_20_ac_eim"; iso20_ac_frames.jsonl beside it has one row per frame, with the response code and response time of each response. evse_listener_status.json reads "status": "stopped" after evse stop.
The DC EIM session at a glance
What a good session looks like
The run folder is <artifact-root>\<run-id>\. After evse stop:
evse_listener_status.jsonreads"status": "stopped".command_result.jsonreads"status": "stopped".evse_message_trace.jsonlshows the request/response pairs in order, from SupportedAppProtocol to SessionStop;events.jsonl,diagnostics.jsonandconformance_traceability.jsoncomplete the evidence.- The process exits with code 0. The exit code alone is not the verdict: read the status fields.
ChargeLink as vehicle
Your DUT is a charger or SECC. Connect it to the ChargeLink PC's adapter. For DC EIM, the vehicle runs evcc activate-profile with profile iso15118_20_dc_evcc_reference (steps 1 and 2 below). For AC EIM, it runs the iso20-ac-evcc command, which takes no profile (step 3). Contact DEVlink before connecting the DC vehicle to a third-party charger.
Your charger must:
- offer SDP on the link (or accept a direct TCP connection, see below);
- offer TLS 1.3 with an ISO 15118-20 Annex B chain on secp521r1 or x448. A P-256 chain is refused unless you add
--allow-legacy-tls-named-groups, an engineering option outside ISO 15118-20; - chain to the V2G root you pass with
--tls-cafile.--tls-server-namesets the expected server name (defaultchargelink-secc.local).
1. Probe the charger (transport and TLS only)
Prove TCP reachability and the mutual TLS 1.3 handshake first. The probe deliberately takes no session arguments:
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evcc tls-handshake-probe `
--protocol iso15118-20 --profile-id iso15118_20_dc_evcc_reference `
--peer-ipv6 ('fe80::<SECC_ADDRESS>%' + $IfIndex) --tcp-port 50118 `
--tls --tls-cert "$Pki\vehicle-chain.pem" --tls-key "$Pki\vehicle-key.pem" --tls-cafile "$Pki\v2g-root-ca.pem" --require-ca
Judge the result on the reported status, not on the exit code: the exit code says whether the probe ran, the status says what the charger decided. The link-local zone index must be the ChargeLink PC's interface index, not the charger's.
2. Run the DC EIM session
The vehicle session refuses to run with hidden defaults. Name two files from the vehicle folder of the kit (vehicle\cases) in environment variables before you start evcc activate-profile. The EV values in them are test values, not a vehicle implementation.
| Variable | File | Purpose |
|---|---|---|
CHARGELINK_ISO20_LIVE_SEMANTIC_CASE | EV case JSON, for example vehicle\cases\nominal.json | The vehicle's identity (EVCCID with Annex C check digit) and electrical parameters: present, precharge and target voltage, target current, power, EV limits, tolerances, safe limits, precharge and charge-loop timeouts. |
CHARGELINK_ISO20_FULL_WIRE_SESSION_MANIFEST | session manifest, vehicle\cases\full-wire-session-manifest.json | The session manifest delivered with the kit. Use it unchanged. |
$env:CHARGELINK_ISO20_LIVE_SEMANTIC_CASE = 'C:\Program Files\DEVlink\ChargeLink\<VERSION>\vehicle\cases\nominal.json'
$env:CHARGELINK_ISO20_FULL_WIRE_SESSION_MANIFEST = 'C:\Program Files\DEVlink\ChargeLink\<VERSION>\vehicle\cases\full-wire-session-manifest.json'
# standard flow: SDP discovery on the adapter to the charger
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evcc activate-profile `
--protocol iso15118-20 --profile-id iso15118_20_dc_evcc_reference `
--use-sdp --interface-index $IfIndex --tcp-port <CHARGER_TLS_PORT> --exi-mode chargelink-exi `
--tls --tls-cert "$Pki\vehicle-chain.pem" --tls-key "$Pki\vehicle-key.pem" --tls-cafile "<V2G_ROOT_OF_THE_CHARGER_CHAIN>" --require-ca `
--max-steps 14 --requested-power-kw 80
# to isolate transport problems: direct TCP (no --use-sdp, explicit peer and port)
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evcc activate-profile `
--protocol iso15118-20 --profile-id iso15118_20_dc_evcc_reference `
--peer-ipv6 ('fe80::<SECC_ADDRESS>%' + $IfIndex) --tcp-port <CHARGER_TLS_PORT> --probe-peer --exi-mode chargelink-exi `
--tls --tls-cert "$Pki\vehicle-chain.pem" --tls-key "$Pki\vehicle-key.pem" --tls-cafile "<V2G_ROOT_OF_THE_CHARGER_CHAIN>" --require-ca `
--max-steps 14 --requested-power-kw 80
--interface-index is the ChargeLink PC's interface to the charger. Every response wait is bounded by the ISO 15118-20 V2G_EVCC_Msg_Timeout for that message type. The vehicle stays inside the schedule it negotiated with the charger.
What a good session looks like
The run folder is <artifact-root>\cl-evcc-<id>\ (the JSON result names it):
summary.jsonreads"status": "passed"and"session_stopped": true, andobserved_state_countequalsrequired_state_count(16 of 16 for a nominal DC session).command_result.jsonreads"status": "stopped".protocol_summary.json,protocol_session.json, the traces (evcc_message_trace.jsonland.log,frames.jsonl,trace.jsonl),events.jsonl,diagnostics.json,manifest.jsonandconformance_traceability.jsoncomplete the evidence.- The process exits with code 0. The exit code alone is not the verdict: read the status fields.
3. Run the AC EIM session
iso20-ac-evcc runs one ISO 15118-20 AC EIM session against your charger: SDP, mutual TLS 1.3 with the vehicle certificate chain, then one V2G communication session. It selects the AC service (ServiceID 1) and the offered parameter set whose control mode matches its own and whose mobility needs mode is 1. If the charger offers no matching set, the vehicle stops at once with SessionStopReq (Terminate).
# standard flow: SDP discovery
chargelink iso20-ac-evcc --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json `
--sdp-server ('[fe80::<SECC_ADDRESS>%' + $IfIndex + ']:15118') `
--tls-cert "$Pki\vehicle-chain.pem" --tls-key "$Pki\vehicle-key.pem" --tls-root "<V2G_ROOT_OF_THE_CHARGER_CHAIN>" `
--phases 3 --ev-max-power-w 11000 --ev-min-power-w 1400 --charge-loops 20
# without SDP: the charger's TLS port
chargelink iso20-ac-evcc --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json `
--peer ('[fe80::<SECC_ADDRESS>%' + $IfIndex + ']:50118') `
--tls-cert "$Pki\vehicle-chain.pem" --tls-key "$Pki\vehicle-key.pem" --tls-root "<V2G_ROOT_OF_THE_CHARGER_CHAIN>"
The kit contains no AC vehicle case files. Describe the vehicle with the options below, or with --case <FILE>, a JSON case file with the EV settings it names over the defaults (a field the vehicle does not know is refused; with --case, the EV options below are not used). The Dynamic control mode is selected in a case file, with "control_mode": "dynamic"; without it the vehicle uses the Scheduled control mode.
| Option | Meaning |
|---|---|
--sdp-server <ADDR> | The charger's SDP socket, for example [fe80::1%12]:15118. The zone index is the ChargeLink PC's interface index. |
--peer <ADDR> | Without SDP: the charger's TLS port, for example [fe80::1%12]:50118. Give either --sdp-server or --peer. |
--tls-cert, --tls-key, --tls-root | Required: the vehicle certificate chain (PEM, leaf first), its private key, and the V2G root the charger's chain must end in. |
--tls-server-name, --iso20-curve-profile, --allow-legacy-tls-named-groups | As for the DC vehicle: the expected server name (default chargelink-secc.local), the preferred named group (secp521r1 or x448), and the engineering option for P-256 test peers. |
--phases | The phases the vehicle's charger uses: 1 or 3 (default 3). |
--ev-max-power-w, --ev-min-power-w | The vehicle's maximum and minimum charge power as the sum over the phases (defaults 11000 W and 1400 W). |
--per-phase-cpd | Sends per-phase values in AC_ChargeParameterDiscoveryReq, for an asymmetric vehicle. |
--charge-loops, --energy-target-wh | The number of AC_ChargeLoopReqs before the vehicle stops (default 20; 0 runs until the energy target or a charger stop), and an energy target in Wh. |
--loop-period-ms, --meter-info-every, --no-loop-limits | The pause between charge-loop messages (below 250 ms, default 100 ms); MeterInfoRequested on every n-th charge-loop request; leave the power limits out of the charge-loop request. |
--renegotiate-after | The vehicle asks once for a new schedule after this many charge-loop requests. |
--standby-after, --pause | Once, after this many charge-loop requests, a PowerDeliveryReq with ChargeProgress Standby; a SessionStopReq with Pause instead of Terminate. Use them to test your charger's answer. |
--fault with --qualification-only | One faulty request to test your charger's handling: invalid_cpd, loop_maximum_above_cpd, per_phase_in_loop or dynamic_branch_in_scheduled. --qualification-only marks the run as a test case; a normal session refuses request faults. |
--connect-timeout-ms, --run-id | The connection timeout (default 2000 ms) and your run identifier. |
The vehicle confirms the power tolerance of the schedule it selected in every PowerDeliveryReq. Per-phase values that the charger sends without the vehicle having asked for them are ignored and recorded; the vehicle goes on with the base values.
What a good AC session looks like
- The run folder is
<artifact-root>\<run-id>\; without--run-idit is namediso20-ac-evcc-<number>, which the JSON result names. iso20_ac_evcc_report.jsonreads"passed": true. The command fails unless the session ended with SessionStopRes (or with your charger's FAILED answer to the fault the run asked for).traces\live-v2gtp-frames.jsonlholds one row per frame.
Results and evidence
Each run writes one folder under the artifact root: $Data\artifacts\<run-id>. Charger runs use the --run-id you pass; vehicle runs without one get a generated ID (cl-evcc-<id> for DC, iso20-ac-evcc-<number> for AC) that the JSON result names. For AC, the charger's session record is protocol\iso20_ac_secc_session_evidence.json with the frame list iso20_ac_frames.jsonl, and the vehicle's verdict is iso20_ac_evcc_report.json. Stop the charger, then check the evidence:
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse stop --run-id $Run
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json artifacts inspect --run-id $Run
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json artifacts integrity --run-id $Run
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse live-closure-gate --run-id $Run --source external-evcc `
--require-sdp --require-tcp-peer --require-tls --require-v2gtp --require-exi --write-artifacts
evse live-closure-gate reads the charger's run folder and reports whether SDP, a live TCP peer, TLS, V2GTP exchange and EXI decoding were observed. artifacts integrity confirms that no file changed after the run. Every artifact carries a claim boundary: it is engineering evidence, not certification. See Trace, Evidence and Reports.
| A passing run shows | A passing run does not show |
|---|---|
| IPv6 link-local discovery, TCP, TLS 1.3 mutual authentication, V2GTP framing and EXI decoding with the other side; message ordering and response times; for the plant-coupled charger session, protocol decisions correlated with simulated electrical behaviour. | Accredited ISO 15118 conformance; measured isolation (CableCheck is simulated by the plant model); physical PLC/SLAC, control pilot or power hardware; interoperability with vehicles or chargers not tested; a live control API (the gRPC GetProtocolState reports unobserved for a stand-alone listener). |
Limits
- EIM only. ChargeLink does not provide bidirectional power transfer (BPT) or Plug & Charge (Contract authorization):
evse startrefuses a profile whose ID names either (for examplebpt,bidirectional,v2g,pnc,contractorplug) with exit code 70 (CL-UNSUPPORTED-001) before any socket opens. - The TLS handshake accepts only the named groups secp521r1 and x448, unless
--allow-legacy-tls-named-groupsis given. - The charging session of the charger role runs plant-coupled on the NI VeriStand bench; there is no other plant option. The charger is delivered and verified with the ChargeLink vehicle on the VeriStand bench. In DEVlink's own tests, ChargeLink's charger has also completed ISO 15118-20 DC sessions over Ethernet with a third-party vehicle stack; this has not been verified on the VeriStand bench. Connecting a real vehicle needs a physical layer (control pilot, PLC) that this release does not include. Contact DEVlink before connecting a third-party vehicle.
- Without
--plant-grpc-endpoint, the DC charger is a reference responder with fixed responses and no plant. It is not a charging session; use it only to check the network, SDP and TLS. The AC charger does not start without a plant. - AC: one charger that offers AC and DC together is not available; the AC charger runs under
iso15118_20_ac_eimwith its AC settings file, the DC charger underiso15118_20_dc. - AC: the Dynamic control mode has been shown with the charger and the vehicle on one PC. On the NI VeriStand bench with the vehicle on a second PC, use the Scheduled control mode.
- AC: mobility needs mode 1 only (the vehicle states its needs). A charger that offers only mode 2 is stopped by the ChargeLink vehicle (Terminate) after ServiceDetailRes.
- AC: not offered are charger-initiated pause, service renegotiation, standby and pricing. The AC charger offers no power tolerance in its schedules; it answers a missing or unconfirmed tolerance with OK and records it.
- AC: a V2GTP frame with a wrong header ends the AC session on both sides instead of being ignored while the message timer runs; a wrong payload length also ends the session on a decode or timer error. Not safety-relevant.
- AC: faults inside the charger, such as a residual-current trip that opens the contactor, end the session through the charger's stop path, or the charger answers FAILED and contains the session; this behaviour is shown by DEVlink's own tests. The schema validity of each AC message is not checked during a run; it is covered by the AC codec's test vectors.
- Dynamic control mode supports MobilityNeedsMode 1 and pricing 0 only, with available power from a profile script. The charger never pauses or enters standby on its own.
- The package's runtime profile offers one 60 s charging schedule. Schedule renegotiation is answered when the vehicle requests it; the charger never triggers it. A longer session needs a vehicle that renegotiates or a runtime profile with a longer schedule.
- The charger does not handle CertificateInstallation or MeteringConfirmation.
- As vehicle, ChargeLink runs a live DC session with the profile
iso15118_20_dc_evcc_referenceand the EV case files of the kit; contact DEVlink before connecting the DC vehicle to a third-party charger. The live AC session runs withiso20-ac-evcc; the kit ships no AC vehicle case files, so you describe the vehicle with the command's options or your own case file. - No physical charging interface (PLC, control pilot, power stage) is included.
See Capability Boundaries for every standard. To check the installation without a DUT, see Self-test without a device under test.
Troubleshooting
| Symptom | Usually means | Action |
|---|---|---|
| TCP connects, then the connection closes before SupportedAppProtocol | The peer offered plaintext, TLS 1.2 or no client certificate | Use TLS 1.3 with mutual authentication on both sides; check tls_handshake_diagnostics.json. |
| Handshake refused with a P-256 or X25519 peer | The peer offers no secp521r1 or x448 group | Give the peer an Annex B identity, or add --allow-legacy-tls-named-groups (and --allow-engineering-tls-identity on the charger side) as an engineering option. |
evse start refuses the TLS identity | Engineering material from evse tls-material-init | Add --allow-engineering-tls-identity, or use production material. |
| TLS fails with a valid-looking chain | The chain does not lead to the shared V2G root, or the clock is wrong | See TLS and PKI; run evse tls-pki-doctor and check w32tm /query /status. |
Exit 70, CL-UNSUPPORTED-001 at evse start | The profile ID names BPT or Plug & Charge | Use an EIM profile such as iso15118_20_dc. |
| TLS 1.2 rejected by the admission check | --external-evcc-windows-tls or --external-evcc-dev-tls was given | Remove the option; ISO 15118-20 uses the default TLS adapter with TLS 1.3. |
| The vehicle stops at AuthorizationSetup | The charger was started without --plant-grpc-endpoint (reference responder) | Expected: the reference responder checks network, SDP and TLS only. Run the charging session plant-coupled on the NI VeriStand bench. |
evcc activate-profile refuses to start | An EV case variable is not set or names a missing file | Set CHARGELINK_ISO20_LIVE_SEMANTIC_CASE and CHARGELINK_ISO20_FULL_WIRE_SESSION_MANIFEST to the kit files. |
| The AC charger refuses to start: "protocol-only AC is not offered" | iso15118_20_ac_eim was started without --plant-grpc-endpoint | Start it plant-coupled, with the bench's plant endpoint, --requested-power-kw and --target-voltage-v. |
| The AC charger refuses its configuration | CHARGELINK_ISO20_AC_RUNTIME_PROFILE is missing, the DC variable is also set, or the settings file has an unknown or invalid field | Set only the AC variable, to a settings file with the fields above; the error message names the field. |
iso20-ac-evcc stops right after the service exchange with SessionStopReq (Terminate) | Your charger offers no parameter set with the vehicle's control mode and mobility needs mode 1 | Offer a matching parameter set, or select the other control mode in a case file. |
iso20-ac-evcc refuses to start: "give --sdp-server or --peer" | No charger address was given | Add --sdp-server (SDP) or --peer (the TLS port). |
| "unreachable network" (10051) on the vehicle side | The peer address carries the charger's zone index | Use fe80::<peer>%<LOCAL_INDEX>. |
| A response-time finding in the evidence | A response exceeded its budget | Find the message in the message trace; check CPU load and other traffic on the PC. |
| The plant-coupled session ends after about 60 s | The runtime profile offers one 60 s schedule | Let the vehicle renegotiate, or use a runtime profile with a longer schedule. |
| The plant-coupled session is rejected at start | --requested-power-kw or --target-voltage-v is zero (unset) | Give both values explicitly, with CHARGELINK_ISO20_RUNTIME_PROFILE set; on the VeriStand bench, start the session from the VeriStand operator (NI VeriStand Bench). |
General failures (licence, ports, firewall, role locks) are in Troubleshooting.

