Evse15118D20

ISO 15118 SECC module on the C++ ISO 15118 stack: DIN SPEC 70121, ISO 15118-2 and ISO 15118-20 (AC and DC), selectable per protocol generation. Successor of the deprecated EvseV2G and IsoMux modules.

Handwritten Documentation

This module implements the SECC side of ISO 15118-20 charging, including both AC DER services of ISO 15118-20 Amendment 1, AC_DER_IEC (Annex L) and AC_DER_SAE (Annex M), and a grid_support provider that accepts active DER directives and raises grid alarms from EV-reported grid-event conditions. It also offers ISO 15118-2 and DIN SPEC 70121 (see supported_ISO15118_2 and supported_DIN70121). DIN SPEC 70121 sends the EVSEID from EvseManager’s evse_id_din (DIN SPEC 91286 as hexBinary, e.g. 49A80737A45678); when that is empty the eMI3 evse_id is packed instead, which only works for ids made of digits and *, so an eMI3 id like DE*PNX*E12345*1 is sent as 0x00.

TLS and SECC leaf certificates

ISO 15118-2 and ISO 15118-20 prescribe different TLS profiles that cannot be served by one certificate:

  • ISO 15118-2: TLS 1.2, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, SECC leaf on secp256r1 (prime256v1), no client certificate.

  • ISO 15118-20: TLS 1.3, TLS_AES_256_GCM_SHA384 / TLS_CHACHA20_POLY1305_SHA256, SECC leaf on secp521r1 or Ed448, mutual TLS with the vehicle certificate.

The TLS version and cipher follow whatever the EV offers in its ClientHello (unless enforce_tls_1_3 pins TLS 1.3). To also present the right leaf, install both SECC leaf certificates (with their keys) in the EvseSecurity SECC leaf directory – or let OCPP provision them (V2GCertificate and, on OCPP 2.1, V2G20Certificate; see the OCPP module documentation). The two are the EvseSecurity leaf types V2G (ISO 15118-2 profile) and V2G20 (ISO 15118-20 profile, secp521r1 / Ed448 key). The module fetches every valid leaf of both types – newest per issuing root – and tags each chain by its key: prime256v1 leaves are presented on TLS 1.2 connections, secp521r1 / Ed448 leaves on TLS 1.3 connections, anything else on both. Within a version the EV’s advertised CA list (certificate_authorities on TLS 1.3, trusted_ca_keys on TLS 1.2) picks among the chains, so the two leaves may chain to different sub-CAs or roots. When only one leaf is installed it is presented on every connection as before; a warning is logged at startup if a protocol is offered without a leaf on its mandated curve. The chain list is rebuilt on every certificate_store_update event, so a renewed leaf of either type is served from the next connection on without a restart.

The two DER flavors are mutually exclusive per EVSE, so at most one is ever advertised. EvseManager enforces that when it builds the energy transfer mode set; see “DER flavor selection” below.

Plug and Charge

Contract-certificate authorization is offered whenever EvseManager passes Contract in session_setup (payment_enable_contract). For ISO 15118-2 this is the Contract payment option; for ISO 15118-20 the SECC lists PnC next to EIM in AuthorizationSetupRes and issues the 128 bit GenChallenge ([V2G20-697]). The CertificateInstallationService flag follows supported_certificate_service.

An ISO 15118-20 AuthorizationReq with PnC is checked in this order: session id, offered service ([V2G20-2209]), GenChallenge match ([V2G20-2565], WARNING_ChallengeInvalid), header signature over PnC_AReqAuthorizationMode with the contract leaf key ([V2G20-1063]; ecdsa-sha512 with SHA-512 digests or Ed448 with SHAKE256 digests, r||s per [V2G20-1000]; a failure is FAILED_SignatureError and ends the session), then the contract chain. The chain is validated locally against the MO and V2G root bundles of EvseSecurity (get_verify_file): validity window of every element (WARNING_CertificateExpired / WARNING_CertificateNotYetValid), the Annex B profile (secp521r1 or Ed448 keys, ecdsa-with-SHA512 or Ed448 signatures, critical KeyUsage and BasicConstraints, no ExtendedKeyUsage, no extension outside Table B.9/B.10, CN = EMAID and O in the subject, CRL or OCSP pointer; WARNING_CertificateValidationError) and the trust chain. Annex B marks the key identifiers and the revocation pointers critical where IETF RFC 5280 does not, so those extensions are decoded and accepted; any other unknown critical extension still fails the chain. A chain whose eMSP root is not installed is WARNING_eMSPUnknown unless central_contract_validation_allowed is set, in which case it is still handed to the backend.

An accepted chain is published as require_auth_pnc (eMAID plus the PEM chain) and answered OK / Ongoing until authorization_response arrives. While that poll is open the EV repeats the request unaltered apart from the header timestamp ([V2G20-1582]) and a freshly computed header signature, which is verified on every repetition; a changed request is a sequence error, so a pending result can never be consumed by a different contract. The wait is bounded by auth_timeout_pnc ([V2G20-2102]); an EIM authorization left Ongoing stops the session after the 55 s V2G_SECC_Ongoing_Performance_Time (the next repetition is answered FAILED, [V2G20-2102]). Accepted gives OK, or OK_CertificateExpiresSoon when the leaf expires within 14 days ([V2G20-2218]), which keeps CertificateInstallationReq available so the EV can install a fresh contract ([V2G20-2232]); a rejection maps the CertificateStatus onto WARNING_CertificateExpired, WARNING_CertificateRevoked, WARNING_CertificateValidationError (chain, signature, no certificate) or WARNING_GeneralPnCAuthorizationError (everything else, including ContractCancelled, which has no code of its own in the -20 schema), and an AuthorizationStatus of Unknown onto WARNING_eMSPUnknown. After a WARNING the EV may retry with another chain, switch to EIM or send a CertificateInstallationReq ([V2G20-1583]). Once authorized the eMAID and contract certificate are fixed for the session ([V2G20-2702]) and survive a pause ([V2G20-1844]).

CertificateInstallationReq is accepted when the service was offered. The SECC checks the validity period of every OEM provisioning chain element (WARNING_CertificateExpired / WARNING_CertificateNotYetValid, [V2G20-2203], [V2G20-2204]), verifies the header signature over OEMProvisioningCertificateChain against the OEM provisioning leaf ([V2G20-1548], FAILED_SignatureError) and relays the raw request over iso15118_extensions with iso15118_schema_version urn:iso:std:iso:15118:-20:CommonMessages. Until set_get_certificate_response arrives every repetition is answered OK / Ongoing ([V2G20-1972]); the backend’s complete, CPS-signed CertificateInstallationRes then goes out verbatim, and its RemainingContractCertificateChains decides whether another CertificateInstallationReq is allowed ([V2G20-1973], [V2G20-1975]), while a relayed WARNING_eMSPUnknown allows a further attempt ([V2G20-2224]). A backend failure, timeout, or a response that is not a CertificateInstallationRes for this session is WARNING_NoCertificateAvailable ([V2G20-2207], [V2G20-2225]). Algorithm profile and trust of the OEM provisioning chain are the certificate provisioning service’s job ([V2G20-1548] NOTE 2).

Not implemented: SupportedProviders in AuthorizationSetupRes (optional, omitted), MeteringConfirmationReq on the multiplexed side stream, and the OCPP 2.1 maximumContractCertificateChains / prioritizedEMAIDs / remainingContracts pass-through (the RemainingContractCertificateChains the EV sees is whatever the backend encoded).

DER grid support

This section describes both directive relays. The SAE negotiation, the default grid code and the mid-session path are covered in “AC_DER_SAE grid code” below.

Active DER directives received via grid_support::set_active_directives are stored and relayed to the EV per annex.

The IEC relay maps three curve families, VoltVar to VoltVarMode, WattVar to WattVarMode and WattPF to WattCosPhiMode, each as an absolute-unit control curve; a type that appears twice is last-wins. Non-curve directives (frequency, volt-watt, setpoint, ride-through) have no counterpart here and are logged and skipped. Application is next-session-dynamic: a directive arriving mid-session takes effect at the next V2G session. The DER transfer limits advertised at each session’s charge parameter discovery derive from ac_limits.

The SAE relay maps the directive table onto the inert default grid code, so a directive that leaves the set falls back to the shipped default rather than lingering: HVMustTrip, HVMayTrip, HVMomCess, LVMustTrip, LVMayTrip, LVMomCess, HFMustTrip, HFMayTrip, LFMustTrip, VoltVar, WattVar, FixedVar, VoltWatt, FreqDroop, LimitMaxDischarge, FixedPFAbsorb, FixedPFInject and EnterService. FixedPFAbsorb and FixedPFInject share the single ConstantPowerFactor element, so one wins and the other is shadowed. Each Annex M element takes one writer: the lowest priority wins, array order breaks ties, and shadowed directives are logged by id (unlike the IEC relay, which ignores priority). A winner that fails validation frees its element for the next candidate. Only the single-phase fields are written. LimitMaxDischarge is an approximation: the percentage is clamped to 0..100 and rounded, not rescaled against the EV’s maximum discharge power. The mapped grid code reaches the next session and, after validation, the running one; see “Mid-session updates” below.

The relay owns every unit and orientation conversion, so the library below it knows nothing about OCPP:

  • grid_support trip curves arrive in OCPP orientation, threshold on x and seconds on y. The relay swaps them into the Annex M duration-first form (x the duration in seconds, y the threshold) and sorts by duration per M.2.2.1.10 and M.2.2.1.11; two points sharing a duration reject the directive. HV/LV thresholds stay percentages of nominal (PercentageV) and HF/LF stay Hz, so no volt conversion happens.

  • EnterService voltage bands and the VoltVar ReferenceVoltage pass through in volts, because AMD1 Table 1 gives them in volts and so does grid_support.

  • The FixedVar setpoint sign is inverted into VarSetpoint. The two sign conventions are opposed and [V2G20-3183] mandates the conversion.

  • The directive priority is forwarded into every written element that carries one; EnterService has none. A value outside the Annex M uint16 range is omitted with a warning rather than wrapped.

  • A curve with more than ten data points is rejected, never truncated: dropping points would silently reshape the grid code the operator asked for.

This module does not publish DER capability; an empty one is expected for AC_DER_IEC.

The DER control functions the EV supports are surfaced in ChargingNeeds.der_charging_parameters.ev_supported_dercontrol, so the backend learns what the EV supports without the EVSE guessing. For AC_DER_IEC they come from what the EV negotiates during service selection; for AC_DER_SAE from the SupportedModes bitmap in the charge parameter discovery request ([V2G20-3409]). See “Known gaps” for what a SAE session leaves out.

Discharge power limits follow ISO 15118-20 8.3.5.2.1: negative when the negative_bidirectional_limits config key is set, with nominal discharge power set equal to maximum discharge power so nominal never exceeds maximum in magnitude. That constraint is normative only for AC_DER_SAE ([V2G20-3228], [V2G20-3229], Annex M); Annex L has no equivalent numbered requirement, so the IEC path applies it defensively.

AC_DER_SAE grid code

Negotiation

SAE negotiation is declare, enable, acknowledge. The EV declares its SupportedModes bitmap in the charge parameter discovery request. The SECC masks it with SAE_MODE_BITMAP_MASK (0x05FFFDFB; bits 2, 9, 25 and 27 to 31 are unused by the specification and must be ignored), then sets the per-function Enable booleans inside DERControlCPDRes, clearing every Enable whose function the EV did not declare. There is no SECC-side mode bitmap; the Enable flags are the SECC’s only expression channel.

The EV acknowledges in EnabledModes. The charge loop compares that against the modes the SECC enabled, not against the EV’s declaration, masked with SAE_ENABLED_MODE_MASK to ignore unused bits and bits with no Enable in DERControlCPDRes. A mismatch is not an error: no requirement makes the SECC enforce the acknowledge, so it emits one latched warning naming the missing and extra functions and continues.

The diagram shows which side owns each step, and where the mask and the per-function gating sit relative to the response carrying them.

        sequenceDiagram
    participant EV
    participant SECC as SECC AC_DER_SAE states
    participant Config as DerSaeSetupConfig

    Note over Config: default der_control is inert:<br/>every enable false, permit_service false

    rect rgb(238, 244, 250)
    Note over EV, Config: charge parameter discovery
    EV->>SECC: DER_SAE_AC_ChargeParameterDiscoveryReq<br/>SupportedModes, EnabledModes, EV limits
    Note over SECC: declare: SupportedModes AND 0x05FFFDFB<br/>bits 2, 9, 25 and 27 to 31 are unused and ignored<br/>EnabledModes in the request is not consumed
    SECC->>SECC: session.set_ev_supported_sae_functions(masked)
    Config-->>SECC: der_control, required_der_operating_mode,<br/>grid_connection_mode, revision,<br/>der_control_update_time (UpdateTime on the wire only)
    Note over SECC: enable: gate_enables_by_supported_modes clears<br/>every Enable whose function the EV did not declare.<br/>There is no SECC-side mode bitmap.
    SECC-->>EV: DER_SAE_AC_ChargeParameterDiscoveryRes<br/>DERControlCPDRes with per-function Enable,<br/>SAE transfer limits, UpdateTime
    SECC->>SECC: session.record_der_control_sent(revision)
    end

    rect rgb(240, 248, 240)
    Note over EV, Config: charge loop
    loop every DER_SAE_AC_ChargeLoopReq
        EV->>SECC: DER_SAE_AC_ChargeLoopReq<br/>EnabledModes, DERAlarmStatus
        Note over SECC: acknowledge: EnabledModes, masked with<br/>SAE_ENABLED_MODE_MASK, is compared against the modes<br/>the SECC enabled. A mismatch is one latched warning, not an error.
        alt revision changed since discovery
            SECC-->>EV: DERControlCLRes: EnterServiceCLRes plus voltage_trip,<br/>frequency_trip, reactive_power_support_cl_res,<br/>active_power_support_cl_res
            SECC->>SECC: session.record_der_control_sent(revision)
        else unchanged
            SECC-->>EV: DERControlCLRes: EnterServiceCLRes.permit_service only
        end
        Note over SECC: each mode field is checked on its own,<br/>[V2G20-3358] to [V2G20-3361], an unchanged field is left unset
        opt RequiredDEROperatingMode differs from the last sent
            SECC-->>EV: RequiredDEROperatingMode re-sent in the response
        end
        opt GridConnectionMode differs from the last sent
            SECC-->>EV: GridConnectionMode re-sent in the response
        end
        SECC->>SECC: session.record_der_modes_sent(operating mode, connection mode)
    end
    end

    

Default grid code

get_default_sae_der_control(nominal_voltage_v) returns a complete but functionally inactive configuration: every enable and permit_service is false. DERControlCPDRes and all its sub-elements are mandatory, so a SAE SECC must emit a full grid code at charge parameter discovery or not offer the service at all. An inert one keeps a station that was never given a grid code from claiming one.

The volt-valued fields are derived from the nominal voltage passed in: the enter-service window is 1.05 and 0.917 of nominal (AMD1 Table 1 gives both bands in volts) and the volt-var ReferenceVoltage is the nominal itself. A FrequencyDroop carrying a zeroed over-frequency branch is present but disabled, because Table M.33 obliges UnderFrequencyDroop whenever OverFrequencyDroop is absent, and that holds regardless of enable. The frequency bands remain the shipped 50 Hz-family values and do not follow the nominal frequency.

The values are structurally valid, and the two mandatory curves per trip family carry the schema minimum of two data points, so a SAE station is conformant without any DSO directive.

Mid-session updates

DERControlCLRes updates parameters already sent in DERControlCPDRes ([V2G20-3236]). Only EnterServiceCLRes is mandatory inside it, and only permit_service inside that. The other four blocks (voltage_trip, frequency_trip, reactive_power_support_cl_res, active_power_support_cl_res) are emitted only when the grid code changed since charge parameter discovery. Sending a conditional element unconditionally is itself non-conformant, so the unchanged path clears them.

Change detection is keyed on DerSaeSetupConfig::revision, a producer-owned counter this module bumps only when the relay input changes: the sorted directive ids with their received_at stamps, plus the nominal voltage and frequency the mapping converts against. The session records the revision it sent at charge parameter discovery and after each re-send, and compares that against the installed config. der_control_update_time is the wall clock UpdateTime on the wire and nothing else; it is never compared, because a timestamp changes on every construction while the counter changes only with the content. A re-apply of an unchanged directive set, or an AC-limit re-derivation, therefore does not re-dictate the grid code.

RequiredDEROperatingMode and GridConnectionMode are handled per field and independently of the revision: each is re-sent in the charge loop response only when it differs from the value last sent to this EV and is left unset otherwise ([V2G20-3358] to [V2G20-3361]).

The path from a directive to the wire runs once AC_DER_SAE is advertised and its limits are derived; otherwise apply_active_der_directives logs at info and leaves the revision untouched. relay_sae_grid_code compares the relay input, maps the set with map_active_directives_to_sae_der_control, validates the result with validate_sae_der_setup against the SAE DER limits and AC limits held at that moment, stores it as the next-session setup and hands it to TbdController::update_der_sae_limits. The controller pushes it into the running session as a control event, where install_der_sae_setup_config validates it once more against the session’s own limits before installing it. The next DERControlCLRes then carries the four blocks, gated by the EV’s declared SupportedModes like the discovery response, and the EnabledModes mismatch warning re-arms. An invalid grid code is rejected with a warning at either stage. A module-stage rejection leaves the previous revision dictated. A session-stage rejection keeps the running session on its old grid code; the new revision is still the next-session setup. The controller also guards a caller passing no setup config: that withdrawal is logged once per withdrawal and does not reach the running session, which keeps its grid code until it ends. This module never withdraws; an empty directive set produces a new inert revision instead. The relayed setup config outlives AC-limit re-derivations: the derivation seeds it only while none is held or the held one is still the seed (revision 0).

        sequenceDiagram
    participant CSMS
    participant OCPP as OCPP module
    participant Module as Evse15118D20
    participant Ctrl as TbdController
    participant Session as running Session
    participant EV

    CSMS->>OCPP: SetDERControl
    OCPP->>Module: grid_support set_active_directives
    Module->>Module: apply_active_der_directives
    opt AC_DER_SAE not advertised or its limits not derived yet
        Note over Module: info log, revision untouched, stop here
    end
    Module->>Module: relay_sae_grid_code
    Module->>Module: sae_relay_input<br/>sorted (id, received_at) plus nominal voltage and frequency
    alt relay input unchanged
        Note over Module: revision stays in effect, nothing pushed
    else relay input changed
        Module->>Module: map_active_directives_to_sae_der_control<br/>default grid code plus winners per Annex M element,<br/>trip curves transposed to duration-first
        Module->>Module: validate_sae_der_setup against SAE DER limits and AC limits
        alt violation
            Note over Module: warning, previous revision remains dictated
        else valid
            Module->>Module: revision + 1, store as next-session setup
            Module->>Ctrl: update_der_sae_limits(limits, setup config)
            Ctrl->>Session: push_control_event(DerSaeSetupConfig)
            Session->>Session: install_der_sae_setup_config<br/>validate_sae_der_setup against the session's own limits
            alt violation
                Note over Session: warning, the running session keeps its old grid code,<br/>the new revision is still the next-session setup
            else installed
                EV->>Session: DER_SAE_AC_ChargeLoopReq
                Session-->>EV: DERControlCLRes: EnterServiceCLRes plus voltage_trip,<br/>frequency_trip, reactive_power_support_cl_res,<br/>active_power_support_cl_res
                Session->>Session: record_der_control_sent(revision),<br/>EnabledModes warning re-armed
            end
        end
    end

    

Transfer limits and the voltage window

Unlike the IEC limits, the SAE limits do not derive from ac_limits alone. They also use evse_max_reactive_power (from update_ac_parameters) and the nominal voltage from ac_setup_config. The nominal voltage has no fallback: until the AC parameters arrive it is unset and the service is withheld.

The advertised voltage window is the nominal voltage times 1.10 (GridMaximumVoltage) and 0.88 (GridMinimumVoltage). Both fractions must stay in step with the first breakpoints of the library’s default over- and under-voltage must-trip curves (110 % and 88 %): those curves carry duration on x and PercentageV on y, and the EV denormalizes the percentage against the base voltage for the function, usually the nominal voltage advertised here. Changing one side alone silently moves the EV’s trip thresholds.

PercentageV does not carry its base voltage on the wire. For trip thresholds, the relay assumes the EV uses the advertised nominal as its base voltage; an EV using a different function-specific base would interpret the same percentage differently.

Service withdrawal

If ac_limits.nominal_frequency or the nominal voltage is not yet positive when the DER limits are derived, the module logs an error naming the missing parameter and leaves der_sae_limits unset, which makes the library strip AC_DER_SAE from the advertised services. Advertising a placeholder grid is worse than withholding the service: the nominal voltage is the usual base for the percentage voltage curves, so a guess moves those thresholds on a 277 V or 120 V site, and the nominal frequency is advertised as the GridNominalFrequency the EV adopts as the frequency of the grid it is connecting to.

The frequency-trip curves are separate. Their y unit is Hz, so their breakpoints are absolute and not scaled by GridNominalFrequency. The shipped default carries 50 Hz-family constants (51.5 and 52 Hz over-frequency must-trip, 47.5 and 47 Hz under-frequency, a 49.5 to 50.1 Hz enter-service band). Setting the nominal frequency to 60 does not move them; a 60 Hz operator supplying a real setup config must supply 60 Hz curve values with it.

The withdrawal is not permanent. The derivation re-runs whenever the supported energy services, the AC limits or the AC parameters change, not only at ready(), so a service withheld at boot is picked up once they arrive.

Reactive power sign convention

Absorption fields are advertised non-negative and injection fields non-positive, per ISO 15118-20 AMD1 clause 8.3.5.2, which replaces Table 94 and binds both annexes. All four reactive power limits are mandatory on the wire, so an absent or zero EVSE capability yields explicit zeros rather than omitted elements.

Known gaps

  • A SAE session’s ChargingNeeds omits the reactive-power limits. SAE var absorption and injection do not map onto the IEC charge and discharge reactive fields of types::iso15118::DERChargingParameters, so those are left unset. The supported-modes bitmap, the four excitation fields and the optional session total discharge energy available do pass through.

  • required_der_operating_mode and grid_connection_mode are re-sent per field in the charge loop response when they differ from the value last sent ([V2G20-3358] to [V2G20-3361]). Nothing in this module changes them after boot; the relay carries the current values through, so in practice both are dictated at charge parameter discovery only.

  • Directive types with no Annex M target are logged once per applied set and skipped: FreqWatt, WattPF, ZeroCurrent, OvervoltageFaultRideThrough, UndervoltageFaultRideThrough, MaximumLevelDCInjection, DSOQSetpoint, DSOCosPhiSetpoint, PowerMonitoringMustTrip and Gradients exist in Annex L or in OCPP only.

  • The SAE relay writes the single-phase fields only; every _L2 and _L3 counterpart stays unset.

  • LimitMaxDischarge is approximated: pct_max_discharge_power lands in PercentageValue clamped to 0..100 and rounded, not rescaled against the EV’s maximum discharge power.

  • grid_support ramp_rate (%/s) is copied into EnterServiceRampTime (s) unconverted, by decision, and needs a follow-up. It reaches EnterServiceRampTime only when paired with delay; alone it is dropped at debug, and delay without ramp_rate rejects the directive.

DER flavor selection

The diagram answers “why is my station not advertising the DER service I configured?”. An AC DER service needs set_der_available asserted and an export-capable EVSE before the iso15118_der_flavor config key picks the annex, and SAE needs the AC parameters on top of that. The key defaults to NONE, so AC DER is opt-in even when availability is asserted.

Asserting availability does not require a wired grid_support provider. It normally arrives from OCPP via a grid_support capability publish, but the SIL configs assert it directly over MQTT with no provider wired, which advertises the service while leaving the directive path inert.

        flowchart TD
    subgraph EvseManager
        A["set_der_available(available)"] --> B{"DER available for this EVSE?"}
        B -- no --> Z["No AC DER service advertised"]
        B -- yes --> C{"EVSE export capable?<br/>max_current_A_export positive and<br/>max_phase_count_export at least 1"}
        C -- no --> Z
        C -- yes --> D{"config iso15118_der_flavor"}
        D -- "NONE (default)" --> Z
        D -- IEC --> E["EnergyTransferMode AC_DER_IEC"]
        D -- SAE --> F["EnergyTransferMode AC_DER_SAE"]
        D -- unrecognized --> W["warning logged,<br/>no AC DER advertised"]
        W --> Z
    end

    subgraph Evse15118D20
        E --> G["IEC DER transfer limits from ac_limits"]
        G --> H["AC_DER_IEC advertised"]
        F --> I{"ac_limits.nominal_frequency and<br/>nominal voltage both positive?"}
        I -- yes --> J["SAE DER transfer limits and the inert default<br/>grid code configuration built,<br/>AC_DER_SAE advertised"]
        I -- no --> K["error logged, der_sae_limits left unset,<br/>AC_DER_SAE stripped from the offered services"]
        K --> L["derivation re-runs on the next change of the<br/>supported energy services, AC limits or AC parameters"]
        L --> I
    end

    

SECC state machine

The ISO 15118-20 state machine of the underlying library, with the four AC DER states in place. Two things to take from it: the four AC DER states sit alongside the plain AC and DC ones rather than inside them, and AC_DER_SAE charge parameter discovery is the only charge parameter discovery state that can hand control back to ServiceDetail, which is how an EV that rejects the dictated grid code restarts service selection.

Terminations that any state performs on a SessionStopReq or a sequence error are omitted, since they end the session rather than move it to another state.

On a DC PowerDeliveryReq with Stop, or any PowerDeliveryReq while a shutdown is requested, current_demand_finished and dc_open_contactor are published before the response, and the response is held until ac_contactor_closed(false) reports the power path off, for at most 500 ms. The EV leaves state C as soon as it has the response, so this withdraws the power permissive first (IEC 61851-23-3 Table CC.111, t103 before t105). An MCS board support treats a C exit under a standing permissive as an emergency shutdown (CC.4.3).

A pause requested with pause_charging is sent as EVSENotification Pause with NotificationMaxDelay 60 s ([V2G20-1850]). In scheduled control mode it may only be sent while the applied entry of the EV’s power profile, from its latest PowerDeliveryReq, is 0 kW ([V2G20-1198]): until then every charge loop response goes out without it and the check is repeated. pause_notified is published once the notification has gone out. In dynamic control mode the SECC has to bring the power to 0 kW before asking ([V2G20-2115]); EvseManager does that before it requests the pause.

        stateDiagram-v2
    [*] --> SupportedAppProtocol

    SupportedAppProtocol --> SessionSetup : protocol namespace agreed

    SessionSetup --> AuthorizationSetup : new session
    SessionSetup --> AC_ChargeParameterDiscovery : resumed session, AC or AC_BPT
    SessionSetup --> AC_DER_IEC_ChargeParameterDiscovery : resumed session, AC_DER_IEC
    SessionSetup --> AC_DER_SAE_ChargeParameterDiscovery : resumed session, AC_DER_SAE
    SessionSetup --> DC_ChargeParameterDiscovery : resumed session, DC, DC_BPT, MCS or MCS_BPT

    AuthorizationSetup --> Authorization
    Authorization --> ServiceDiscovery : authorization accepted
    ServiceDiscovery --> ServiceDetail
    ServiceDetail --> ServiceSelection
    ServiceSelection --> ServiceSelection : further ServiceDetailReq

    ServiceSelection --> AC_ChargeParameterDiscovery : AC or AC_BPT selected
    ServiceSelection --> AC_DER_IEC_ChargeParameterDiscovery : AC_DER_IEC selected
    ServiceSelection --> AC_DER_SAE_ChargeParameterDiscovery : AC_DER_SAE selected
    ServiceSelection --> DC_ChargeParameterDiscovery : DC, DC_BPT, MCS or MCS_BPT selected

    AC_ChargeParameterDiscovery --> ScheduleExchange
    DC_ChargeParameterDiscovery --> ScheduleExchange

    AC_DER_IEC_ChargeParameterDiscovery --> AC_DER_IEC_ChargeParameterDiscovery : EV Processing Ongoing
    AC_DER_IEC_ChargeParameterDiscovery --> ScheduleExchange : EV Processing Finished

    AC_DER_SAE_ChargeParameterDiscovery --> AC_DER_SAE_ChargeParameterDiscovery : EV Processing Ongoing
    AC_DER_SAE_ChargeParameterDiscovery --> ScheduleExchange : EV Processing Finished
    AC_DER_SAE_ChargeParameterDiscovery --> ServiceDetail : ServiceDiscoveryReq, EV restarts service selection

    ScheduleExchange --> PowerDelivery : AC, AC_DER_IEC or AC_DER_SAE
    ScheduleExchange --> DC_CableCheck : DC, DC_BPT, MCS or MCS_BPT
    DC_CableCheck --> DC_PreCharge : cable check done
    DC_PreCharge --> PowerDelivery : EV Processing Finished

    PowerDelivery --> AC_ChargeLoop : contactor closed
    PowerDelivery --> AC_DER_IEC_ChargeLoop : contactor closed
    PowerDelivery --> AC_DER_SAE_ChargeLoop : contactor closed
    PowerDelivery --> DC_ChargeLoop
    PowerDelivery --> SessionStop : shutdown requested, AC family
    PowerDelivery --> DC_WeldingDetection : shutdown requested, DC family

    AC_ChargeLoop --> SessionStop : PowerDeliveryReq Stop or shutdown
    AC_DER_IEC_ChargeLoop --> SessionStop : PowerDeliveryReq Stop or shutdown
    AC_DER_SAE_ChargeLoop --> SessionStop : PowerDeliveryReq Stop or shutdown
    DC_ChargeLoop --> DC_WeldingDetection : PowerDeliveryReq Stop or shutdown, after power path off
    DC_WeldingDetection --> SessionStop : EV Processing Finished

    SessionStop --> [*]

    

Auto-Generated Reference

Module Configuration

device: string <optional>
default: “eth0”
Ethernet device used for HLC. Any local interface that has an ipv6 link-local and a MAC addr will work
logging_path: string <optional>
default: “.”
This option should no longer be used and will be removed in the future. Path to logging directory (will be created if non existent)
tls_negotiation_strategy: string <optional>
default: “ACCEPT_CLIENT_OFFER”
Select strategy on how to negotiate connection encryption. TLS needs a V2G leaf certificate from the security module; when none is installed the SECC still starts and offers unsecured sessions only (ISO 15118-2 with EIM, DIN SPEC 70121), except with ENFORCE_TLS, which then refuses to start.
enum:
- ACCEPT_CLIENT_OFFER
- ENFORCE_TLS
- ENFORCE_NO_TLS
enforce_tls_1_3: boolean <optional>
default: False
Enforcing tls version 1.3. Applies whenever a TLS connection is established, independently of tls_negotiation_strategy: it pins the min/max protocol version, the cipher list and the client_hello certificate-verify elevation. Set to true for ISO 15118-20 (mandates TLS 1.3 mTLS). Set to false to offer TLS 1.2 (cipher ECDHE-ECDSA-AES128-SHA256, no client-certificate request), which is required for ISO 15118-2 / DIN SPEC 70121. With it unset the TLS version follows the EV’s offer, and the SECC leaf is chosen per connection to match: a prime256v1 leaf (ISO 15118-2) is presented on TLS 1.2, a secp521r1 / Ed448 leaf (ISO 15118-20) on TLS 1.3, so install both leaves in the security module’s SECC leaf directory when offering both protocols. Multi-chain selection works on both TLS 1.2 (driven by the trusted_ca_keys extension) and TLS 1.3 (driven by the native certificate_authorities extension), so multiple V2G leaf chains can be installed concurrently regardless of this flag.
supported_DIN70121: boolean <optional>
default: False
Offer DIN SPEC 70121 (DC only) in the SupportedAppProtocol handshake. Negotiated with the lowest priority (after ISO 15118-20 and ISO 15118-2).
supported_ISO15118_2: boolean <optional>
default: False
Offer ISO 15118-2 (AC and DC) in the SupportedAppProtocol handshake. Negotiated with a lower priority than ISO 15118-20 but higher than DIN SPEC 70121.
supported_ISO15118_20: boolean <optional>
default: True
Offer ISO 15118-20 (AC and DC) in the SupportedAppProtocol handshake. Negotiated with the highest priority. At least one of supported_ISO15118_20, supported_ISO15118_2 and supported_DIN70121 must be enabled. ISO 15118-20 is TLS-only ([V2G20-2677]), so it is silently dropped from the offer when no V2G leaf certificate is available; ISO 15118-2 and DIN SPEC 70121 keep running unsecured in that case. When a certificate IS available, -20 stays in the offer even on unsecured connections (ENFORCE_NO_TLS, or an EV requesting no transport security): such a session works but is not standard-conformant, and is logged as a warning. Set this to false to offer only ISO 15118-2 / DIN SPEC 70121 on unsecured deployments.
auth_timeout_eim: integer <optional>
default: 300
Defines how many seconds the EVSE should wait for authorization in EIM (External Payment) case, before the charging session is aborted. Write 0 if the EVSE should wait indefinitely for EIM authorization. Applies to ISO 15118-2 and DIN SPEC 70121; ISO 15118-20 EIM stops the session after the 55 s V2G_SECC_Ongoing_Performance_Time ([V2G20-2102]).
auth_timeout_pnc: integer <optional>
default: 55
Defines how many seconds the EVSE should wait for authorization in PnC (Contract) case, before the charging session is aborted. Write 0 if the EVSE should wait indefinitely for PnC authorization. Applies to ISO 15118-2 and ISO 15118-20; DIN SPEC 70121 has no Plug-and-Charge. The default is the 55 s V2G_SECC_Ongoing_Performance_Time of [V2G2-712/713] and [V2G20-2102].
enable_ssl_logging: boolean <optional>
default: False
Verbosely log the ssl/tls connection
enable_tls_key_logging: boolean <optional>
default: False
Enable/Disable the export of TLS session keys (pre-master-secret) during a TLS handshake. Note that this option is for testing and simulation purpose only
tls_key_logging_path: string <optional>
default: “/tmp”
Output directory for the TLS key log file
enable_sdp_server: boolean <optional>
default: True
Enable the built-in SDP server
tcp_port: integer <optional>
default: 50000
TCP port used for ISO 15118-20 session communication
supported_dynamic_mode: boolean <optional>
default: True
The EVSE should support dynamic mode
supported_mobility_needs_mode_provided_by_secc: boolean <optional>
default: False
The EVSE should support the mobility needs mode provided by the SECC. Mobility needs mode provided by the EVCC is always provided.
supported_scheduled_mode: boolean <optional>
default: False
The EVSE should support scheduled mode
custom_protocol_namespace: string <optional>
default: “”
Providing a custom protocol namespace.
negative_bidirectional_limits: boolean <optional>
default: True
Some cars send or expect negative discharge limits. However, it is not clear from the standard whether the discharge limits should be negative. Until this is clarified, the positive limits can be converted using this option.
selecting_sap_based_on_energy_service: boolean <optional>
default: False
The SAP namespace matching the supported energy services is always preferred: a DC charger selects -20:DC (or -2 / DIN) and an AC charger -20:AC, even if the EV ranks the other -20 namespace higher. Based on ISO15118-20 AMD1, selecting the SAP namespace does not imply a final selection, so by default an EV offering only non-matching namespaces still gets one and selects its service later in the D20 service states. Set this option to true to refuse such namespaces instead (Failed_NoNegotiation).

Provides

This interface provides limited access to iso15118-20
This interface is used to share data between ISO15118 and OCPP modules to support the requirements of the OCPP protocol
grid_support: grid_support
Raises grid alarms from EV grid-event conditions and accepts active DER directives. Curve-family directives (VoltVar, WattVar, WattPF) are relayed to the EV as AC_DER_IEC control functions at the next V2G session; non-curve directives are logged and skipped.

Requirements

security: evse_security 1
iso15118_vas: ISO15118_vas 0..128

Metadata

Authors

Sebastian Lukas

License