Cordex HP Controller (Alpha / EnerSys)
The Cordex HP driver polls an Alpha Technologies (EnerSys) Cordex HP system controller — CXC HP, CXCi HP, or CXCM1 HP — over Modbus TCP and surfaces DC system telemetry as device attributes with history enabled. The Limited Data Set (battery voltage / current / runtime, total load, AC mains, alarm thresholds, system mode) lives at fixed input-register addresses; an extra_register_map lets you pull additional points out of the full DC System and AMPS HP2 register tables.
The integration is read-only. The Cordex HP does not implement Modbus Holding Registers (FC03), so setpoint changes need the controller's HTTP control plane and are out of scope here.
Status
Built against the Alpha Cordex HP Modbus Integrator Guide 0350114-J0 Rev K (08/2021). Limited Data Set addresses are stable across firmware revisions; full data set addresses are dynamic per install.
Prerequisites
- Modbus enabled on the CXC HP. By default it is off — turn it on under Controller > Configure Controller > Communications > Modbus in the controller web UI.
- Note the Limited Data Set Device ID (default
247) and any System Device IDs (one per DC system; one per AMPS HP2 inverter system). - Outbound TCP/502 from the GEM controller to the Cordex HP IP.
- For points outside the Limited Data Set: export the controller's Modbus Table CSV from the web UI and translate it into the
extra_register_mapJSON shape below.
Setup
- Enable Modbus on the controller. Note the configured byte order — the CXC HP default is Least significant bytes first, which maps to
little_endianon this driver. - Note the device IDs — Limited Data Set ID (default 247), DC System ID (typically 1), AMPS HP2 System ID(s) (typically 2+).
- Create the device under System > Devices with driver
cordex_hp. Set:ip— controller IPport—502(default)limited_data_set_unit_id— match the controller (default247)byte_order— match the controller (defaultlittle_endian)
- (Optional) Add an extra register map for points beyond the Limited Data Set. Paste a JSON array into the
extra_register_mapattribute. Each entry:{name, unit_id, register, format, unit?, scale?, codes?}. - Watch the device attributes populate after one polling cycle (default 10 s).
The full DC System and AMPS HP2 register addresses are dynamic per install — the controller assigns them as inventory is added or removed. Always pull the live map from the controller's web UI rather than copying from another install. Use the controller's Re-number Modbus Table By Name button before importing if you need address stability across identical sites.
Attributes
Device
| Attribute | Required | Type | Notes |
|---|---|---|---|
ip | yes | string | Controller IP. |
port | no | int | TCP port. Default 502. |
limited_data_set_unit_id | no | int | Modbus unit ID for the Limited Data Set. Default 247. |
byte_order | no | string | little_endian (default) | big_endian | mid_big | mid_little. Must match the controller. |
polling_rate | no | int | Poll interval in ms. Default 10000. Below 5000 ms can stress the controller under heavy alarm activity. |
timeout | no | int | Modbus request timeout in ms. Default 10000. |
reconnect_timeout | no | int | Delay between reconnect attempts in ms. Default 3000. |
extra_register_map | no | json | List of additional points to poll. See format below. |
Limited Data Set (auto-populated)
Each value is a 32-bit float spanning two consecutive input registers (30001..30036).
| Attribute | Unit |
|---|---|
system_voltage | V |
total_load_current | A |
total_capacity_installed | A |
system_mode_code | — (numeric) |
system_mode | string (resolved label — Charging, Discharging, etc.) |
estimated_rectifier_ac_voltage | V |
estimated_battery_runtime | min |
last_discharge_duration | min |
output_voltage_low_alarm_limit | V |
output_voltage_high_alarm_limit | V |
battery_runtime_low_alarm_limit | min |
ac_mains_voltage_low_alarm_limit | V |
ac_mains_voltage_high_alarm_limit | V |
battery_capacity_rating | Ah |
battery_average_temperature | °C |
battery_current | A |
battery_temp_low_alarm_limit | °C |
battery_temp_high_alarm_limit | °C |
battery_charge_current_high_limit | A |
Extra register map format
[
{ "unit_id": 1, "name": "dc_battery_voltage", "register": 35157, "format": "float32", "unit": "V" },
{ "unit_id": 1, "name": "dc_battery_current", "register": 35159, "format": "float32", "unit": "A" },
{ "unit_id": 2, "name": "ampshp2_inv1_loading_w", "register": 36773, "format": "float32", "unit": "W" },
{ "unit_id": 2, "name": "ampshp2_inv1_ac_output_status", "register": 36775, "format": "float32",
"codes": { "0": "On", "1": "ManualOff", "2": "IrrecoverableError", "3": "RecoverableError" } }
]
| Field | Notes |
|---|---|
unit_id | Modbus unit ID for this point. The driver flips the slave ID once per group, not per point. |
name | Attribute name written to the device. |
register | Input register address (Kohler extended format). |
format | float32 | float64 | uint32 | int32 | input_status | coil. |
unit | Optional unit label. |
scale | Optional numeric multiplier applied after read. |
codes | Optional integer→string lookup (e.g., AMPS HP2 AC Output Status). |
Commands
| Name | Args | Description |
|---|---|---|
get_status | — | Return last cached register values. |
poll_now | — | Force an immediate poll cycle. |
read_register | address, unit_id | One-off Modbus read. |
read_input_registers | address, length, unit_id | Bulk input register read. |
read_input / read_inputs | address, [length], unit_id | Discrete input read. |
read_coil / read_coils | address, [length], unit_id | Coil read. |
scan_unit_ids | start, end, timeout_per_unit, test_register | Probe a unit ID range. |
get_diagnostics / clear_diagnostics / get_comm_log | — | Driver-level Modbus diagnostics. |
Known limitations
- Read-only. The controller does not support Modbus Holding Registers (FC03), so setpoint writes are not possible over Modbus.
- Sentinel translation.
0xFFFFFFFF(NaN as float, -1 as int) means unknown and999999999means deprecated in the Cordex spec. Both are translated tonull. A bare0is left as-is, since it is indistinguishable from a legitimate zero reading. - Dynamic full data set. Full DC System / AMPS HP2 register addresses are not stable across installs — the controller assigns them as inventory changes.
- Single-master. Multiple unit IDs share one TCP connection and the driver flips slave ID per read group. Do not point a second Modbus master at the controller while this driver is polling.
Troubleshooting
| Symptom | Check |
|---|---|
connection refused on 502 | Modbus is disabled on the controller. Enable it under Controller > Configure Controller > Communications > Modbus. |
Limited Data Set values are all null | byte_order is wrong. The CXC HP default is Least significant bytes first → little_endian. |
| Extra register map values look scrambled | byte_order mismatch, or the addresses were copied from a different install. Re-export from the live controller's Modbus Table CSV. |
| Connection alternates up/down | Polling rate is too aggressive. Raise polling_rate to 10000+ ms during heavy alarm activity. |
Some values read null after firmware update | Inventory change may have shifted the dynamic register addresses. Re-export the Modbus Table CSV and update extra_register_map. |
Related Documentation
- Modbus — generic Modbus device, scanner, and register browser
- Devices — device management
- Subsystems — power subsystem assignment