Enexa
Simulation Overview
  • Input SummaryAll configuration parameters
  • Simulation AlgorithmsReactive vs Smart EMS explained
  • Result OverviewSide-by-side comparison
Configuration
  • Location SetupEquipment specs & constraints
  • FinancialsCosts, income & margins
  • Solar ProductionPV generation profile
  • Charging SessionsEV demand profile
  • Not ModeledKnown gaps & limitations
Reactive BMS
  • BMS AlgorithmHow the reactive BMS works
  • BMS ReactiveRule-based simulation results
SmartEMS
  • SmartEMS AlgorithmHow the 2-layer optimizer works
  • SmartEMS ConfigPlanner tuning parameters
  • SmartEMS ResultsOptimized simulation output
  • SmartEMS DispatchingGate logic & energy flow rules
Solution Overview
  • Solution OverviewArchitecture & responsibilities
  • Comm ArchitectureAPI integration patterns
  • Middleware APITelemetry & command schema
  • Onboarding & ConfigMaster data & config API
  • Exception HandlingFallbacks & failure scenarios
Prototype
  • Site MonitoringReal-time telemetry dashboard
  • Dispatch LogsCommand execution & verification
  • Impact DashboardSavings & value demonstration
  • What-If ScenariosScenario analysis & comparison

Middleware API Specification

Interface contract between Enexa Platform and Amperio Middleware

Integration Architecture
How Enexa optimization communicates with Amperio infrastructure

Enexa Platform

Optimization engine, asset registry, configuration repository, and monitoring dashboards

Enexa Responsibility

Amperio Middleware

Centralized backend that routes commands to site controllers via Modbus/TCP

Amperio Team Responsibility

ADS-TEC ChargePost

Ultrafast charging hardware with integrated battery storage (up to 300 kW)

Amperio Team
EnexaCommandsAmperio MiddlewareTelemetryEnexa
Hardware Reference: ADS-TEC ChargePost
Equipment specifications and Modbus interface (v2.6 / FW 1.10.2+)

Grid Connection

  • Max intake: 87 kVA
  • Max feed-in: 87 kVA
  • Aux reserve: 8 kW (HVAC, displays)

Power Units (x2)

  • Per unit: 110 kW battery discharge
  • Coupled mode: 300 kW to single EV
  • Single mode: 2 EVs simultaneously

Battery Storage

  • Two independent battery strings
  • Auto SoC balancing in automatic mode
  • Configurable max SoC limits

Operating Modes

station.mgmt.operation_mode
0 = Off1 = On
station.mgmt.grid_mgmt_mode
0 = Automatic1 = Manual
charger.X.mgmt.charging_mode
0 = Off1 = Single (max 150kW)2 = Dual (max 300kW)3 = Disabled (grid ops only)
Watchdog Timer

Register station.mgmt.watchdog_interval (addr 2500) must be written every 2-60 seconds. If timer expires, system reverts to fallback configuration values. Amperio Middleware must maintain this heartbeat.

Data Field Purpose Categories
Each telemetry and command field is tagged with its primary purpose. Fields may have multiple tags if they serve multiple purposes.
OPT

Optimization

MON

Monitoring

DSP

Dispatch

IMP

Impact

OPTOptimization Engine Input

Data required by the Enexa optimization algorithm to compute optimal dispatch schedules. These fields directly influence charge/discharge decisions.

When:Polled every 15 minutes (aligned with EPEX market intervals)
Used for:SOC curve calculation, arbitrage decisions, EV load management, grid constraint adherence
Examples:soc_pct, max_charge_w, max_discharge_w, P_EV_max_w, P_grid_clearance_w
Critical:Missing OPT data = optimizer cannot run, falls back to safe defaults
MONOperational Monitoring

Data displayed on NOC dashboards for real-time operational awareness. Enables operators to monitor system health and troubleshoot issues.

When:Streamed continuously (1-5 second intervals for live dashboards)
Used for:Status indicators, alert generation, health checks, diagnostic displays
Examples:charging_state, contactor_state, temp_min/max, operation_state, warnings
Screens:Site Overview, Fleet Status, System Health Dashboard
DSPDispatch Verification

Data used to verify that dispatched commands were executed correctly. Compares commanded setpoints to actual system response.

When:Captured before/after each command (command-response pairing)
Used for:Command acknowledgment, deviation detection, audit logs, troubleshooting
Examples:power_w (actual vs commanded P_grid_w), charging_state, operation_mode
Screens:Dispatch Log, Command History, Execution Status
IMPImpact Analysis & KPIs

Data used to calculate cost savings, demonstrate optimization value, and generate customer reports showing ROI of Smart Enexa layer.

When:Aggregated hourly/daily for reporting, stored historically for trend analysis
Used for:Cost calculation, uplift metrics, counterfactual comparison, ROI dashboards
Examples:E_grid_imp_kwh, E_grid_exp_kwh, E_EV_chg_kwh, P_grid_w (time-series)
Screens:Savings Dashboard, What-If Scenarios, Monthly Reports, Uplift Analysis

Multi-Purpose Fields: Some fields serve multiple purposes. For example, P_grid_w is taggedOPT (optimization uses current grid power) andIMP (time-series used for cost calculation). When a field has multiple tags, it means the same data point flows to multiple systems.

API Payload Identification
Every API call includes identifiers linking payloads to the asset registry configured during onboarding
site_id

Site Identifier

Unique identifier for the physical location. Assigned during site registration in Enexa portal.

"site_id": "site_de_munich_01"
asset_id

ChargePost Identifier

Unique identifier for the ADS-TEC ChargePost unit at this site. Maps to hardware serial number.

"asset_id": "cp_adstec_4872"
unit_id

Power Unit Identifier

Sub-component within ChargePost (1 or 2). Each unit has independent battery + charger.

"unit_id": 1

Hierarchy: Site → Asset → Unit

// Every telemetry POST includes envelope with identifiers
POST /api/v1/telemetry
{
  "site_id": "site_de_munich_01",      // From Enexa Site Registry
  "asset_id": "cp_adstec_4872",        // ChargePost serial/asset ID
  "timestamp": "2024-01-15T14:30:00Z", // ISO8601 UTC
  
  "batteries": [
    { "unit_id": 1, "soc_pct": 65, ... },
    { "unit_id": 2, "soc_pct": 62, ... }
  ],
  "chargers": [...],
  "grid": {...}
}

// Dispatch commands are routed using same identifiers
POST /api/v1/dispatch
{
  "site_id": "site_de_munich_01",
  "asset_id": "cp_adstec_4872",
  "command_id": "cmd_20240115_143000_001",  // Unique for ACK tracking
  "timestamp": "2024-01-15T14:30:00Z",
  
  "station": {...},
  "units": [
    { "unit_id": 1, "charging_mode": 1, ... },
    { "unit_id": 2, "charging_mode": 1, ... }
  ]
}

Telemetry Direction

Middleware → Enexa: Middleware includes site_id + asset_id in every telemetry push. Enexa uses these to route data to correct site dashboard and optimizer instance.

Dispatch Direction

Enexa → Middleware: Enexa includes site_id + asset_id in dispatch commands. Middleware validates these match its configured identity before executing.

Telemetry Endpoint
Amperio Middleware pushes aggregated telemetry to Enexa every 1 second
POST /api/v1/telemetry

Why Telemetry Matters

Enexa cannot see the physical hardware directly. The Amperio Middleware acts as the "eyes" of the optimization system, aggregating data from all site controllers and pushing it to Enexa. Without accurate, real-time telemetry, Enexa cannot make optimal dispatch decisions. Every field below directly influences how the algorithm allocates energy between grid, battery, and EVs.

Battery State (per power unit)

ADS-TEC ChargePost has two independent battery strings. Data mapped from Modbus registers charger.X.status.battery.*

{
  "batteries": [
    {
      "unit_id": 1,
      "soc_pct": 65,
      "power_w": -25000,
      "temp_min_c": 28.5,
      "temp_max_c": 32.5,
      "max_charge_w": 110000,
      "max_discharge_w": 110000,
      "energy_empty_kwh": 45.2,
      "energy_full_kwh": 12.8,
      "contactor_state": "closed"
    },
    {
      "unit_id": 2,
      "soc_pct": 62,
      "power_w": -22000,
      ...
    }
  ]
}
FieldPurposeModbus SourceWhy Needed & How Used
unit_idMONcharger.1 / charger.2Identifies which power unit (1 or 2). Used in dashboards to display per-unit status and in dispatch logs to track which unit executed commands.
soc_pctOPTsoc_cp (7006/17006)Critical optimization input. Compared to target SOC curve to decide charge/discharge actions. Drives arbitrage decisions - if SOC low during cheap hours, charge; if SOC high during expensive hours, available for EV boost.
power_wDSPP_bat (7000/17000)Dispatch verification. Compares actual power to commanded setpoint. Negative = discharge. Used to confirm commands executed correctly and detect deviations for alert generation.
temp_min_c / temp_max_cMONT_bat_min/max (7008-7010)Safety monitoring only. Displayed on dashboard for operator awareness. Not used in optimization - hardware handles thermal derating automatically via P_bat_chg_max limits.
max_charge_wOPTP_bat_chg_max (7012/17012)Optimization constraint. Real-time available charging headroom. Optimizer uses this to cap scheduled charge power - never commands more than hardware can accept. Reflects thermal/SOC derating.
max_discharge_wOPTP_bat_dischg_max (7014/17014)Optimization constraint. Real-time available discharge capacity (up to 110kW/unit). Used to calculate maximum EV boost power and grid export potential during peak pricing.
energy_empty_kwhOPTE_empty (7016/17016)Optimization input. Usable energy available for discharge. Critical for calculating: (1) How long can we boost EVs at a given power? (2) Total arbitrage potential in EUR during expensive hours.
energy_full_kwhOPTE_full (7018/17018)Optimization input. Usable energy capacity for charging. Used to calculate: (1) How much cheap energy can we store? (2) Time required to reach target SOC at given charge rate.
contactor_stateMONbattery_contactor_state (7024/17024)Operational monitoring. 0=undefined, 1=open, 2=closed. Displayed on dashboard. Must be closed for power flow - open state indicates fault or startup sequence.

Grid Meter State

Data from ChargePost integrated smart meter. Registers in "grid.*" namespace (addresses 1000+).

{
  "grid": {
    "P_grid_w": 35000,
    "E_grid_imp_kwh": 245.6,
    "E_grid_exp_kwh": 12.3,
    "P_aux_w": 3200,
    "f_grid_hz": 50.01,
    "cos_phi": 0.98
  }
}
FieldPurposeModbus SourceWhy Needed & How Used
P_grid_wOPT IMPgrid.P_grid (1000)Primary cost driver. Real-time grid import/export. Multiplied by EPEX price for cost calculation. Negative = export. Used in: (1) Optimization for real-time adjustments, (2) Impact dashboards showing actual vs baseline costs.
E_grid_imp_kwhIMPgrid.E_grid_imp (1022)Cost settlement. Cumulative import counter. Used for: (1) Daily/monthly cost calculations, (2) EPEX settlement validation, (3) KPI: total grid consumption vs baseline. Essential for uplift reporting.
E_grid_exp_kwhIMPgrid.E_grid_exp (1024)Revenue tracking. Cumulative export counter. Used for: (1) Feed-in tariff calculations if applicable, (2) Arbitrage profit calculation (sold high, bought low), (3) Grid support revenue attribution.
P_aux_wOPTgrid.P_aux (1026)Optimization constraint. Auxiliary load (HVAC, displays) up to 8kW. Subtracted from P_grid_clearance to get actual available capacity for charging. Important during hot/cold weather when HVAC load increases.
f_grid_hzMONgrid.f_grid (1008)Future use - monitoring only. Grid frequency (nominal 50Hz). Displayed on dashboard for grid health awareness. Phase 2: May trigger frequency containment reserve (FCR) participation if contracted.
cos_phiMONgrid.cos_phi (1006)Grid compliance monitoring. Power factor. Displayed on dashboard. Low values may indicate power quality issues. Some grid operators penalize poor power factor - useful for compliance reporting.

Note: Per-phase voltages (U_L1/L2/L3) and currents (I_L1/L2/L3) are available in registers 1010-1020 but not included in Phase 1 scope. These are diagnostic data points useful for fault analysis but not required for optimization or standard monitoring. Add if grid quality analysis becomes a requirement.

EV Charging State (per charger unit)

ChargePost has 2 charging points (charger.1 and charger.2). In coupled mode, both power units serve one connector.

{
  "chargers": [
    {
      "unit_id": 1,
      "charging_state": "InProgress",
      "charging_process_state": "Charging",
      "plug_state": "Plugged",
      "P_EV_w": 145000,
      "P_EV_max_w": 150000,
      "P_cp_max_w": 150000,
      "soc_EV_pct": 45,
      "E_EV_chg_kwh": 23.5,
      "boost_contactor": "closed"
    },
    {
      "unit_id": 2,
      "charging_state": "Available",
      ...
    }
  ]
}
FieldPurposeModbus SourceWhy Needed & How Used
unit_idMONcharger.1 / charger.2Charger identification. Identifies which charging unit (1 or 2). Used in dashboards to show per-connector status and in logs to track which unit served which session.
charging_stateMON DSPcharging_state (3000/13000)Dashboard & dispatch prerequisite. 0=NotAvailable, 1=Available, 2=InProgress. Displayed on monitoring dashboard. Optimizer checks state before sending EV-related commands - no commands sent if NotAvailable.
charging_process_stateMONprocess_state (3001/13001)Detailed monitoring only. States: Offline, ReadyToCharge, Authorization, ChargingSetup, Charging, ChargingTeardown, ChargingFinished, ChargingError. Enables detailed dashboard status and error diagnosis.
plug_stateOPTplug_state (3002/13002)Optimization trigger. 0=Unplugged, 1=Plugged. When plug_state changes to 1, optimizer immediately re-evaluates: Should we boost from battery? Throttle charging to cheap hours? Key event for real-time decisions.
P_EV_wDSP IMPP_EV (3008/13008)Dispatch verification + KPI. Actual charging power in watts. Compared to P_cp_lim setpoint for verification. Summed over session for impact analysis: "Session delivered X kWh at Y EUR average cost."
P_EV_max_wOPTP_EV_max (3010/13010)Optimization constraint. Maximum power EV currently accepts (ISO 15118/CHAdeMO). Optimizer never commands P_cp_lim above this - would be wasted headroom. Used to calculate: Can we shift load to cheaper hours and still finish in time?
P_cp_max_wOPTP_cp_max (3004/13004)Optimization constraint. Maximum power deliverable by chargepoint (hardware limit). Combined with P_EV_max and battery availability to calculate actual charging headroom for optimization.
soc_EV_pctOPTsoc_EV (3020/13020)Optimization input (if available). EV battery SOC communicated by vehicle. Used with energy_target (if V2G/smart charging) to calculate remaining kWh needed. Not all vehicles report this - optional field.
E_EV_chg_kwhIMPE_EV_chg (3021/13021)Impact KPI. Energy delivered this session. Used for: (1) Session billing, (2) Calculating per-session cost with time-weighted EPEX prices, (3) Impact dashboard: "Saved X EUR vs flat-rate charging."
boost_contactorMONboost_contactor_state (3031)Monitoring only. 0=undefined, 1=open, 2=closed. Shows on dashboard whether 300kW coupled mode is active. Hardware manages coupling automatically based on charging_mode command.

Station Status (per site)

Station-level status from registers in station.status.* namespace. Important for operational awareness.

{
  "station": {
    "operation_state": "Ready",
    "P_grid_consumption_limit_w": 79000,
    "P_grid_generation_limit_w": 79000,
    "warnings": [],
    "errors": []
  }
}
FieldPurposeModbus SourceWhy Needed & How Used
operation_stateMONoperation_state (2000)Dashboard status. Values: Off, Startup, Ready, LeftCharge, RightCharge, BothCharge, Shutdown. Shows operational state on monitoring dashboard and helps diagnose why commands may not execute.
P_grid_consumption_limit_wOPTP_grid_consumption_limit (2010)Optimization constraint. Maximum grid power consumption allowed without violating clearance. Dynamic value reflecting current limits. Optimizer uses this as upper bound for charge commands.
P_grid_generation_limit_wOPTP_grid_generation_limit (2012)Optimization constraint. Maximum grid export power allowed. Limits how much battery discharge power can be fed back to grid. Critical for arbitrage optimization.
warningsMONstation.status.warnings (2100)Alert generation. Bitfield: watchdog_triggered, no_modbus_ctrl, manual_ctrl, P_clearance_violation, etc. Mapped to dashboard alerts. Helps diagnose control issues.
errorsMONstation.status.errors.* (2110+)Critical alerts. Hardware errors: CrashSensor_Triggered, USV_GridSupplyFailure, HVAC errors. Triggers immediate alerts to operators. Not used in optimization - system is typically non-functional.

Dispatch Commands
Enexa sends control commands to Amperio Middleware via REST API
POST /api/v1/dispatch

Who Calculates What?

Enexa Platform calculates: Optimal battery SOC targets, charge/discharge timing, price-aware scheduling, and energy allocation between sources.

Amperio Middleware executes: Receives commands via API, routes to site controllers internally, enforces safety limits, handles real-time adjustments within commanded bounds.

Command Frequency

Every 15 minutes (aligned with EPEX slots) + event-driven updates when conditions change

Acknowledgment Required

Middleware must ACK within 5 seconds or Enexa retries (3x then alert)

Station-Level Control Commands

Commands mapped to ADS-TEC Modbus holding registers in station.mgmt.* namespace. Middleware writes these via Modbus/TCP.

{
  "station": {
    "operation_mode": 1,
    "grid_mgmt_mode": 1,
    "P_grid_clearance_w": 79000,
    "external_control_id": "ENEXA_PROD"
  }
}
FieldPurposeModbus RegisterWhy Needed & How Used
operation_modeDSPoperation_mode (2501)Master on/off switch. 0 = Off (system offline), 1 = On (Enexa control enabled). Set to 1 on deployment, rarely changed. Used in dispatch logs to confirm system is controllable.
grid_mgmt_modeOPTgrid_mgmt_mode (2502)Optimization control mode. 0 = Automatic (system manages using P_grid_clearance), 1 = Manual (Enexa sets per-unit P_grid targets). For price optimization, use Manual mode to control charge/discharge timing precisely.
P_grid_clearance_wOPTP_grid_clearance (2506)Primary optimization lever. Maximum grid power limit in watts. In Automatic mode: controls how much grid power to use before battery kicks in. Higher value = more grid, less battery. Set based on EPEX price - low during cheap hours, high during expensive hours.
external_control_idDSPexternal_control_id (2514)Audit trail. Identifier string (max 32 chars). Middleware writes "ENEXA_PROD" or "ENEXA_TEST". Visible on hardware UI and logs - helps diagnose who is controlling the system.

Not included: silent_mode, power_gradient_mode, energy_saving_mode_reaction_speed, adv_display_priority_mode - these are static configuration fields set once at commissioning. Not relevant for dynamic energy optimization.

Per-Unit Control Commands (charger.1 / charger.2)

Commands for each power unit. In manual grid_mgmt_mode, Enexa can control power independently per battery string.

{
  "chargers": [
    {
      "unit_id": 1,
      "charging_mode": 2,
      "P_grid_w": -30000,
      "P_cp_lim_w": 150000,
      "soc_cp_max_pct": 90
    },
    {
      "unit_id": 2,
      "charging_mode": 2,
      "P_grid_w": -30000,
      "P_cp_lim_w": 150000,
      "soc_cp_max_pct": 90
    }
  ]
}
FieldPurposeModbus RegisterWhy Needed & How Used
unit_idDSPcharger.1 / charger.2Target unit identifier. Specifies which power unit (1 or 2) receives the command. Required to route commands to correct Modbus address space (12xxx vs 22xxx).
charging_modeOPTcharging_mode (12000/22000)EV charging strategy. 0 = Off (unit disabled), 1 = Single (max 150kW), 2 = Dual (max 300kW coupled), 3 = Disabled (grid ops only). Use mode 3 during expensive hours to block EV charging while still allowing arbitrage.
P_grid_wOPTP_grid (12005/22005)Core dispatch command (manual mode). Target grid power in producer counting. Negative = discharge to grid (export during expensive hours). Positive = charge from grid (cheap hours). Optimizer calculates based on EPEX price and SOC.
P_cp_lim_wOPTP_cp_lim (12001/22001)EV load management. Maximum EV charging power (0-300000W). Set low during expensive hours to throttle EV charging. Set high during cheap hours to maximize grid-to-EV charging. 0 = block charging entirely.
soc_cp_max_pctOPTsoc_cp_max (12009/22009)Battery target SOC. Maximum battery SOC to charge to. Set high (90%) during cheap hours to store energy. Set lower during volatile periods to leave room for arbitrage opportunities. Must be below station backup reserve.

Not included: I_cp_lim (current limit) - redundant with P_cp_lim for most use cases. Power limit is the primary constraint; current limit only relevant for specific cable/connector limitations.

P_clearance Violation Handling

If sum of charger.X.mgmt.P_grid exceeds station.mgmt.P_grid_clearance:

  • <10% difference: Warning on station.status.warnings Bit 3. EMS has 60s to correct.
  • >10% difference: Immediate scale-down. System reduces charger.X.control.P_grid to stay within clearance.

EV Charging Strategy

How ADS-TEC ChargePost Handles EV Charging

Automatic Battery Assist: When EV demand exceeds grid clearance (P_grid_clearance), the internal battery automatically supplements power. This is hardware-level behavior.

Enexa's Role: Control the constraints that shape this behavior:

  • P_cp_lim: Maximum power to EV (0-300kW). Set low during expensive hours.
  • charging_mode: Single (150kW max) vs Dual (300kW max) vs Disabled.
  • soc_cp_max: Battery reserve level. Keep high to have more buffer for EV boost.
  • P_grid_clearance: How much grid to use before battery kicks in.

Grid Priority Mode: ChargePost prioritizes grid over battery. If EV demands 100kW and grid clearance is 80kW, it takes 80kW from grid and 20kW from battery.

// Example: Peak pricing - limit EV charging, preserve battery
{
  "station": {
    "P_grid_clearance_w": 40000  // Reduce grid usage
  },
  "chargers": [
    {
      "unit_id": 1,
      "P_cp_lim_w": 80000,       // Limit EV to 80kW (forces battery use)
      "soc_cp_max_pct": 95       // Keep battery full for boost
    }
  ]
}

// Example: Cheap hours - maximize grid charging
{
  "station": {
    "P_grid_clearance_w": 87000  // Full grid capacity
  },
  "chargers": [
    {
      "unit_id": 1,
      "P_cp_lim_w": 300000,      // No limit on EV
      "soc_cp_max_pct": 95       // Charge battery to max
    }
  ]
}
Coupled Mode (charging_mode = 2)
  • Both power units serve one connector
  • Up to 300kW to single EV
  • 220kW battery discharge available
  • Use for ultrafast charging demands
Single Mode (charging_mode = 1)
  • Each power unit serves its own connector
  • Up to 150kW per EV
  • Can charge 2 EVs simultaneously
  • Use when multiple vehicles arrive

Grid Import Gate

Purpose of Grid Import Gate

What is the gate? A control mechanism that limits or blocks grid power import during expensive price periods. Think of it as a "valve" that Enexa opens or closes based on electricity prices.

Why is this needed? Without the gate, the system would draw grid power whenever demanded, regardless of price. The gate enforces price-aware behavior:

  • CHEAP zone (green): Gate fully open - charge battery at max rate, buy cheap power
  • MODERATE zone (yellow): Gate throttled - charge slowly, don't overbuy
  • EXPENSIVE zone (orange): Gate mostly closed - only emergency charging
  • PEAK zone (red): Gate fully closed - use battery/PV only, zero grid import

Emergency override: Even when gate is closed, if battery SOC drops below emergency threshold (e.g., 15%), the gate opens to prevent system failure.

{
  "grid": {
    "import_allowed": true,
    "max_import_kw": 80.0,
    "export_allowed": false,
    "max_export_kw": 0.0,
    "price_zone": "peak",
    "gate_reason": "peak_pricing"
  }
}
FieldCalculated ByPurpose & Logic
import_allowedEnexaMaster switch for grid import. When false, middleware must not draw any power from grid for battery charging. EV charging may still use grid if source_priority includes it and other sources insufficient.
max_import_kwEnexaPower limit for grid import. During cheap hours = full 80 kW. During moderate = throttled (e.g., 30 kW). During expensive/peak = 0 kW (unless emergency). This is the "valve opening" amount.
export_allowedEnexaWhether grid export (selling power back) is permitted. Usually false unless feed-in tariff is favorable or V2G is enabled.
max_export_kwEnexaPower limit for grid export. Set to 0 if no export contract. If V2G enabled, this limits how much power can be sold back.
price_zoneEnexaCurrent price classification. Enexa calculates this from EPEX prices using percentile thresholds. Informational for middleware logging and operator dashboards.
gate_reasonEnexaHuman-readable explanation: "cheap_slot", "peak_pricing", "emergency_override", "demand_limit". Helps operators understand why gate is in current state.

Complete Dispatch Command

POST /api/v1/dispatch
Content-Type: application/json
Authorization: Bearer <site_token>

{
  "site_id": "site_enexa_001",
  "command_id": "cmd_20240115_103000_001",
  "timestamp": "2024-01-15T10:30:00Z",
  "valid_until": "2024-01-15T10:45:00Z",
  "battery": { ... },
  "connectors": [ ... ],
  "grid": { ... }
}

// Expected Response (within 5 seconds):
{
  "command_id": "cmd_20240115_103000_001",
  "status": "accepted",           // "accepted" | "rejected" | "partial"
  "applied_at": "2024-01-15T10:30:00.234Z",
  "warnings": [],                 // Non-fatal issues
  "errors": []                    // Reasons for rejection
}
Emergency Override Commands
High-priority commands that override normal dispatch
{
  "emergency": {
    "type": "low_soc",
    "action": "force_charge",
    "target_soc_pct": 25.0,
    "ignore_price": true,
    "max_rate_kw": 60.0
  }
}

Emergency commands take precedence over all other dispatch instructions. The middleware must execute immediately regardless of current state or price zone. Types: "low_soc" (battery critical), "grid_fault" (switch to island), "thermal" (reduce power), "ev_priority" (must charge EV now).

ADS-TEC ChargePost Modbus Register Reference
Complete register map for Modbus/TCP interface v2.6. Function codes: 03 (read holding), 04 (read input), 06/16 (write holding).

Station Status Registers (Input - FC 04)

AddressRegister NameTypeDescription
2000station.status.operation_stateuint160=Off, 1=Startup, 2=Ready, 3=LeftCharge, 4=LeftConCharge, 5=RightCharge, 6=RightConCharge, 7=BothCharge, 8=Shutdown
2006station.status.T_ambientfloatAmbient temperature near heat exchanger (°C)
2009station.status.watchdog_timeoutuint16Seconds until watchdog expires
2010station.status.P_grid_consumption_limituint32Max grid consumption power (W) without clearance violation
2012station.status.P_grid_generation_limituint32Max grid feed-in power (W) without clearance violation
2100station.status.warningsbitlistBit 0: watchdog_triggered, Bit 1: no_modbus_ctrl, Bit 2: manual_ctrl, Bit 3: P_clearance_violation, Bit 4: I_clearance_violation
2110station.status.errors.generalbitlistBit 0: error_flags_corrupted, Bit 1: CrashSensor_Triggered, Bit 2: USV_GridSupplyFailure

Station Management Registers (Holding - FC 03/06/16)

AddressRegister NameTypeDescription
2500station.mgmt.watchdog_intervaluint16CRITICAL: Write every 2-60s to maintain control. Expiry reverts to fallback config.
2501station.mgmt.operation_modeuint160=Off (system offline), 1=On (system active)
2502station.mgmt.grid_mgmt_modeuint160=Automatic (system manages), 1=Manual (per-unit P_grid setpoints)
2504station.mgmt.energy_saving_mode_reaction_speeduint160=Fast (always active), 1=Slow (sleep after 60s idle)
2506station.mgmt.P_grid_clearanceuint32Maximum grid power (W). Can exceed config limit with active Modbus control. Max 87kVA.
2512station.mgmt.power_gradient_modeuint160=No gradient, 1=VDE4105 third party gradient (for direct marketing)
2514station.mgmt.external_control_idstringIdentifier of external EMS (max 32 chars). Write "ENEXA_PROD" for audit trail.

Per-Unit Charger Registers (charger.1 / charger.2)

charger.1 addresses shown. Add 10000 for charger.2 (e.g., 3000 → 13000, 7000 → 17000, 12000 → 22000).

AddressRegister NameTypeDescription
Status Registers (Input - FC 04)
3000charger.1.status.charging_stateuint160=NotAvailable, 1=Available, 2=InProgress
3001charger.1.status.charging_process_stateuint160=Offline, 1=ReadyToCharge, 2=Authorization, 3=ChargingSetup, 4=Charging, 5=ChargingTeardown, 6=ChargingFinished, 7=ChargingError
3002charger.1.status.plug_stateuint160=Unplugged, 1=Plugged
3008charger.1.status.P_EVint32Current EV charging power (W)
3010charger.1.status.P_EV_maxint32Maximum power EV accepts (W)
3020charger.1.status.soc_EVuint16EV battery SOC (%)
3021charger.1.status.E_EV_chgfloatEnergy delivered this session (kWh)
Battery Status Registers (Input - FC 04)
7000charger.1.status.battery.P_batfloatBattery power in producer counting (W). Negative = discharge.
7006charger.1.status.battery.soc_cpuint16Internal battery SOC (%)
7008charger.1.status.battery.T_bat_minfloatMin battery string temperature (°C)
7010charger.1.status.battery.T_bat_maxfloatMax battery string temperature (°C)
7012charger.1.status.battery.P_bat_chg_maxfloatMax battery charging power available (W)
7014charger.1.status.battery.P_bat_dischg_maxfloatMax battery discharge power available (W)
Management Registers (Holding - FC 03/06/16)
12000charger.1.mgmt.charging_modeuint160=Off, 1=Single (150kW), 2=Dual (300kW), 3=Disabled (grid only)
12001charger.1.mgmt.P_cp_limuint32Max EV charging power (W). 0=no charging. >300000 treated as 300000.
12003charger.1.mgmt.I_cp_limuint32Max EV charging current (A). 0=no charging. >500 treated as 500.
12005charger.1.mgmt.P_gridint32Target grid power in manual mode (W). Negative = feed to grid.
12009charger.1.mgmt.soc_cp_maxuint16Max battery SOC target (%). Must be < backup config value.

Grid Meter Registers (Input - FC 04)

AddressRegister NameTypeDescription
1000grid.P_gridfloatActive grid power in producer counting (W). Negative = export.
1002grid.Q_gridfloatReactive grid power (VA)
1008grid.f_gridfloatGrid frequency (Hz)
1010-1014grid.U_L1/L2/L3_gridfloatPer-phase grid voltages (V)
1016-1020grid.I_L1/L2/L3_gridfloatPer-phase grid currents (A)
1022grid.E_grid_impfloatGrid import energy counter (kWh)
1024grid.E_grid_expfloatGrid export energy counter (kWh)
1026grid.P_auxfloatAuxiliary consumer power - HVAC, displays (W)
API Versioning & Error Handling

API Versioning

Base URL: https://api.enexa.io/v1/

Current Version: v1 (stable)

Deprecation Policy: 12 months notice

Version is included in the URL path. Breaking changes will increment the major version. Enexa will maintain backwards compatibility within a major version.

Rate Limits

Telemetry: 2 requests/second per site

Commands: 10 requests/minute per site

Burst: 5 requests allowed

HTTP 429 returned when exceeded. Retry-After header indicates wait time. Contact Enexa to increase limits for high-throughput sites.

Error Response Codes

4xx Client Errors

  • 400 - Invalid JSON or schema violation
  • 401 - Missing or invalid auth token
  • 403 - Token valid but not authorized for site
  • 404 - Site ID not found
  • 409 - Command conflicts with active emergency
  • 422 - Semantically invalid (e.g., SOC > 100%)
  • 429 - Rate limit exceeded

5xx Server Errors

  • 500 - Enexa internal error (retry later)
  • 502 - Upstream service unavailable
  • 503 - Enexa maintenance mode
  • 504 - Timeout processing request

On 5xx errors: retry with exponential backoff (1s, 2s, 4s, 8s, max 60s). After 5 failures, switch to LOCAL AUTONOMOUS mode.

Heartbeat & Health Check Protocol

Middleware Heartbeat

POST /api/v1/heartbeat

Frequency: Every 10 seconds

{ "site_id": "...", "status": "healthy", "uptime_s": 3600, "mode": "cloud_connected" }

If Enexa receives no heartbeat for 30 seconds, the site is marked OFFLINE. Alerts are sent to configured operators.

Enexa Health Check

GET /api/v1/health

Response: { "status": "ok", "time": "..." }

Middleware should check Enexa health before critical operations. If unhealthy, cache commands locally and retry later.

Implementation Notes for Middleware Developers

Connection Requirements

  • - HTTPS with TLS 1.3 minimum
  • - Bearer token authentication per site
  • - WebSocket option for low-latency bidirectional
  • - Retry with exponential backoff on failure

Fallback Behavior

  • - If no command in 60s: enter local auto mode
  • - Local mode: serve EVs, maintain 50% SOC target
  • - Resume cloud control when connection restored
  • - Log all local decisions for reconciliation

Data Quality

  • - Timestamps must be UTC ISO 8601
  • - Power values in kW, energy in kWh
  • - Missing fields should be null, not omitted
  • - Include measurement accuracy metadata

Safety Constraints

  • - Hardware limits always override cloud commands
  • - Battery BMS has final say on charge/discharge
  • - Grid connection limits are physical constraints
  • - Never exceed equipment ratings
Data-to-Dashboard Mapping
How telemetry fields power specific screens in the Enexa platform

Real-Time Monitoring Dashboard

Live status of all sites - used by NOC operators and site managers

batteries.soc_pctSOC gauge per unit (0-100%)
batteries.power_wPower flow diagram (charge/discharge)
grid.P_grid_wGrid import/export indicator
chargers.charging_stateConnector availability status
chargers.P_EV_wActive charging session power
batteries.temp_*Thermal status indicators

Dispatch Status & Logs Screen

Command execution tracking - used for troubleshooting and audit

command.P_grid_wCommanded setpoint (what Enexa sent)
batteries.power_wActual power (verify execution)
station.operation_modeSystem controllability status
chargers.charging_stateWhy command may not execute
ack_timestampCommand acknowledgment time

Impact & Savings Dashboard

Value showcase - used for customer reporting and ROI validation

grid.E_grid_imp_kwhTotal energy consumed (cost calculation)
grid.E_grid_exp_kwhEnergy sold back (revenue)
grid.P_grid_w + timestampTime-weighted cost: SUM(P * price * dt)
chargers.E_EV_chg_kwhEnergy delivered to EVs per session
batteries.energy_*_kwhArbitrage potential calculation
Uplift Calculation: Compare actual cost (using smart schedule) vs baseline cost (flat 50% SOC, no optimization) using same EV sessions.

What-If & Simulation

Scenario analysis - used for demonstrating optimization value

batteries.max_*_wHardware limits for simulation bounds
chargers.P_EV_max_wEV demand constraints
batteries.soc_pct (history)Actual SOC curve vs optimal
grid.P_grid_w (history)Actual vs counterfactual grid usage
Counterfactual: "If we had not optimized, your cost would have been X EUR higher."

Optimization Engine Input Summary

The optimizer runs every 15 minutes and requires these exact fields to compute optimal dispatch:

// Required inputs for optimization (per 15-min slot)
{
  // Battery State (per unit)
  "soc_pct": 65,              // Current SOC - where are we now?
  "max_charge_w": 110000,     // Can we charge? How much headroom?
  "max_discharge_w": 110000,  // Can we discharge? How much capacity?
  "energy_empty_kwh": 45.2,   // How much can we sell?
  "energy_full_kwh": 12.8,    // How much can we buy?
  
  // Grid State
  "P_grid_w": 35000,          // Current grid power (cost driver)
  "P_aux_w": 3200,            // Reserved for HVAC (reduces capacity)
  
  // EV State (if active session)
  "plug_state": 1,            // Is EV connected?
  "P_EV_max_w": 150000,       // What does EV want?
  "soc_EV_pct": 45,           // Where is EV at? (optional)
  
  // External Inputs (from Enexa)
  "epex_price_eur_mwh": 85.5, // Current electricity price
  "forecast_prices": [...],   // Next 24h price forecast
  "grid_carbon_g_kwh": 320    // Optional: carbon intensity
}