Skip to content

Latest commit

 

History

History
407 lines (314 loc) · 14.4 KB

File metadata and controls

407 lines (314 loc) · 14.4 KB

ESPHome EG4 BMS Component

ESPHome component to monitor EG4 Battery Management Systems (EG4 BMS) via RS485/Modbus RTU.

This is a native C++ implementation that communicates directly with the EG4 BMS using the Modbus RTU protocol, providing better stability and lower resource usage compared to using the built-in ESPHome Modbus controller.

Supported Devices

  • EG4 LL (Lifeline) series batteries
  • EG4-LL-S V2 (formerly known as EG4-LL)
  • Any EG4 battery with RS485/Modbus RTU interface

Features

  • Native Modbus RTU implementation - Direct communication without external dependencies
  • Comprehensive monitoring - Voltage, current, temperature, capacity, and more
  • Individual cell voltages - Monitor all 16 cells independently
  • Status reporting - Real-time status, warnings, protection, and errors
  • Multi-BMS support - Connect multiple BMSes on the same RS485 bus
  • Low resource usage - Efficient C++ implementation
  • Home Assistant integration - Automatic device discovery and configuration

Hardware Requirements

  • ESP32 or ESP8266 board
  • RS485 to TTL converter module (e.g., MAX485, SP3485)
  • EG4 BMS with RS485 port

Wiring Diagram

                  RS485                      UART
┌────────────┐              ┌──────────┐                ┌─────────┐
│            │              │          │<----- RX ----->│         │
│   EG4      │<-----B- ---->│  RS485   │<----- TX ----->│ ESP32/  │
│   BMS      │<---- A+ ---->│  to TTL  │<----- GND ---->│ ESP8266 │
│            │<--- GND ---->│  module  │<----- 3.3V --->│         │
│            │              │          │                │         │
└────────────┘              └──────────┘                └─────────┘

Important: Power the RS485 module with 3.3V to match the ESP's logic level.

EG4 BMS RJ45 Pinout

Pin Signal Wire Color (T-568B)
1 RS485 B Orange/White
2 RS485 A Orange
3 GND Green/White
4 - Blue
5 - Blue/White
6 GND Green
7 RS485 A Brown/White
8 RS485 B Brown

The RS485 pair is bridged across pins 1/8 (B) and 2/7 (A), so either end of the connector works. CAN bus for inverter closed-loop is on a separate port — not on this connector.

Installation

Option 1: Using External Components (Recommended)

Add to your ESPHome YAML configuration:

external_components:
  - source: github://rar/esphome-eg4-bms@main
    refresh: 0s

Option 2: Local Installation

  1. Clone this repository
  2. Copy the components folder to your ESPHome configuration directory
  3. Reference it in your YAML:
external_components:
  - source: components

Configuration

Basic Example

uart:
  id: uart_0
  baud_rate: 9600
  tx_pin: GPIO16
  rx_pin: GPIO17
  rx_buffer_size: 256

eg4_modbus:
  id: modbus0
  uart_id: uart_0

eg4_bms:
  id: bms0
  address: 0x10  # Default address for master BMS
  eg4_modbus_id: modbus0
  update_interval: 10s

sensor:
  - platform: eg4_bms
    eg4_bms_id: bms0
    total_voltage:
      name: "Battery Voltage"
    current:
      name: "Battery Current"
    state_of_charge:
      name: "Battery SOC"

Complete Examples

See the example configurations in this repository:

Configuration Variables

eg4_modbus

Variable Type Default Description
id Required - ID of the Modbus component
uart_id Required - ID of the UART component
flow_control_pin Optional - GPIO pin for RS485 flow control (DE/RE)

eg4_bms

Variable Type Default Description
id Required - ID of the BMS component
eg4_modbus_id Required - ID of the parent Modbus component
address Optional 0x10 Modbus address of the BMS (0x01-0xF7; EG4 uses 0x01-0x10)
update_interval Optional 10s How often to poll the BMS
max_no_response_count Optional 5 Consecutive polls without a valid data block before the BMS is marked offline

Implausible Response Rejection

Some EG4 LL-V2 units intermittently return a complete, CRC-valid function-03 response whose entire register data area is zero. Accepting those frames publishes false zeroes for SOC, pack voltage and capacity, which can knock a battery out of a downstream aggregate such as YamBMS.

Each register block is therefore validated before anything is published, and rejected as a unit if it is implausible:

  • Electrical/cell block - rejected if the payload is entirely zero or the pack voltage reads 0.00 V.
  • Capacity/SOC block - rejected if the payload is entirely zero or remaining capacity, full capacity and SOH all read zero.

A rejected block publishes nothing, so all previous values are retained, and it does not reset the online tracker. Legitimate zeroes are unaffected: SOC = 0 on a discharged pack and max_charge_current = 0 on a charge-limited pack are still accepted as long as the rest of the block is plausible.

Expose the rejected_frame_count sensor to monitor how often this happens; the rejected frame is also logged as hex at WARN level.

BMS Addressing

  • Master BMS: Address 0x10 (default)
  • Battery Module 1: Address 0x01
  • Battery Module 2: Address 0x02
  • ...
  • Battery Module 15: Address 0x0F

Use DIP switches on the BMS to configure the address.

Available Sensors

Binary Sensors

  • online_status - BMS communication status
  • heating - Battery heating active
  • charging - Charging is permitted (no charge-blocking protection active)
  • discharging - Discharging is permitted (no discharge-blocking protection active)

Note that charging and discharging report whether the BMS permits each direction, derived from the protection bits - not whether current is currently flowing. An idle battery reports both as ON. Use current or status to tell what the pack is actually doing.

Sensors

Voltage:

  • total_voltage - Total battery pack voltage (V)
  • min_cell_voltage - Minimum cell voltage (V)
  • max_cell_voltage - Maximum cell voltage (V)
  • delta_cell_voltage - Voltage difference between min and max cells (V)
  • min_voltage_cell - Index (1-16) of the lowest cell
  • max_voltage_cell - Index (1-16) of the highest cell
  • cell_average_voltage - Average cell voltage (V)
  • cell_voltage_1 to cell_voltage_16 - Individual cell voltages (V)

Current and Power:

  • current - Battery current (A, positive = charging, negative = discharging)
  • power - Battery power (W)
  • charging_power - Charging power (W)
  • discharging_power - Discharging power (W)

Temperature:

  • pcb_temperature - PCB temperature (°C)
  • avg_temperature - Average temperature (°C)
  • max_temperature - Maximum temperature (°C)
  • temperature_1 to temperature_6 - Individual temperature sensors (°C)

Capacity:

  • remaining_capacity - Remaining capacity (Ah)
  • full_capacity - Full capacity (Ah)

State:

  • state_of_charge - State of charge (%)
  • state_of_health - State of health (%)
  • cycle_count - Charge/discharge cycles
  • max_charge_current - Maximum charging current (A)
  • cell_count - Number of cells reporting a valid voltage

Diagnostic:

  • errors_bitmask - Raw error register value
  • warnings_bitmask - Raw warning register value
  • protection_bitmask - Raw protection register value
  • rejected_frame_count - Count of implausible register blocks rejected since boot

Text Sensors

  • status - Battery status (Standby, Charging, Discharging, etc.)
  • warnings - Active warnings
  • protection - Active protections
  • error - Error codes
  • model - BMS model number
  • firmware_version - BMS firmware version
  • serial_number - BMS serial number

Status Codes

Battery Status

  • Standby - Battery is idle
  • Charging - Battery is charging
  • Discharging - Battery is discharging
  • Protect - Protection is active
  • Charging Limited - Charging current is limited
  • Heating+[Status] - Battery heating is active

Warnings and Protections

  • Pack OV - Pack overvoltage
  • Cell OV - Cell overvoltage
  • Pack UV - Pack undervoltage
  • Cell UV - Cell undervoltage
  • Charge OC - Charge overcurrent
  • Discharge OC - Discharge overcurrent
  • Abnormal Temp - Abnormal ambient temperature
  • MOS Overheat - MOSFET overheating
  • Charge OT - Charge over-temperature
  • Discharge OT - Discharge over-temperature
  • Charge UT - Charge under-temperature
  • Discharge UT - Discharge under-temperature
  • Low Capacity - Low capacity warning
  • Float Stopped - Float charging has stopped (warning only)
  • Discharge SC - Discharge short circuit (protection only)

Error Codes

  • Voltage Error - Voltage sensor error
  • Temperature Error - Temperature sensor error
  • Current Flow Error - Current sensor error
  • Cell Unbalance - Cell voltage unbalance error

Debugging

Enable debug logging to see communication details:

logger:
  level: DEBUG
  logs:
    eg4_modbus: DEBUG
    eg4_bms: DEBUG

uart:
  id: uart_0
  baud_rate: 9600
  tx_pin: GPIO16
  rx_pin: GPIO17
  debug:
    direction: BOTH
    dummy_receiver: false
    after:
      timeout: 50ms
    sequence:
      - lambda: UARTDebug::log_hex(direction, bytes, ':');

Troubleshooting

No data received

  1. Check wiring - Verify A+, B-, and GND connections
  2. Check baud rate - EG4 BMS default is 9600
  3. Check address - Make sure the address matches your BMS configuration
  4. Check power - RS485 module should be powered with 3.3V
  5. Check termination - Long cable runs may need 120Ω termination resistors

Incorrect readings

  1. Check register mapping - Different BMS models may have different register layouts
  2. Update firmware - Ensure your BMS has the latest firmware
  3. Check polling interval - Don't poll too frequently (minimum 1 second recommended)

CRC errors

  1. Check cable quality - Use shielded twisted pair cable for RS485
  2. Check cable length - Keep cables as short as possible (max 1200m for RS485)
  3. Check termination - Add 120Ω termination resistors at both ends

Protocol Information

This component implements the EG4 Battery Modbus RTU protocol as documented in the EG4-LL-MODBUS-Communication-Protocol documentation.

Protocol Details:

  • Type: Modbus RTU
  • Function Code: 0x03 (Read Holding Registers)
  • Baud Rate: 9600 (default), configurable up to 19200
  • Data Bits: 8
  • Stop Bits: 1
  • Parity: None

Comparison with Modbus Controller

Why use this component instead of the built-in Modbus controller?

Advantages:

  • Simpler configuration - No need to manually configure each register
  • Better stability - Native implementation with proper error handling
  • Lower resource usage - More efficient memory and CPU usage
  • Automatic calculations - Delta voltage, power, etc. calculated automatically
  • Status decoding - Human-readable status, warnings, and errors
  • Better Home Assistant integration - Proper device classes and units

Migration from Modbus Controller: Simply replace your modbus_controller configuration with this component. All sensors will be available with clearer naming and better organization.

Contributing

Contributions are welcome! Please feel free to submit pull requests or open issues.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Credits

Changelog

v1.1.0 (2026-08-10)

Breaking changes

  • designed_capacity has been removed. It was configurable but never populated and has no known register; remove it from your YAML.
  • address is now validated to 0x01-0xF7, rejecting the 0x00 broadcast address.

Changes

  • Reject CRC-valid but all-zero register blocks instead of publishing false zeroes, retaining the last good values (#7)
  • Only reset the online tracker on a plausible data block
  • Add the rejected_frame_count diagnostic sensor
  • Make the missed-update threshold configurable via max_no_response_count
  • Fix average_cell_voltage in the ESP32 examples and docs; the schema key is cell_average_voltage
  • Saturate the missed-update counter instead of letting it wrap, which flapped a long-dead BMS back online every 256 polls
  • Drop device_class: energy from the Ah capacity sensors, which Home Assistant rejects as a unit mismatch and excludes from long-term statistics
  • Publish cell_count (previously configurable but never populated)
  • Remove designed_capacity, which was never populated and has no known register
  • Text-sensor blocks no longer mark a BMS online; only a valid data block does, so a unit returning zeroed measurements can no longer be kept online by its model-string reply
  • Reject Modbus addresses outside 0x01-0xF7 and log an error at startup when two devices on one bus share an address
  • Mark the bitmask and cell-index sensors as diagnostic entities
  • Correct the register map in PROJECT_SUMMARY.md, document the missing sensors, clarify that charging/discharging mean "permitted" rather than "active", and fix the UART debug snippet, which never triggered on binary Modbus frames
  • Set device class battery for SoC and SoH sensors (#6)
  • Fix the BMS RJ45 pinout in the docs; RS485 is on pins 1/2 and 7/8, not 4/5 (#4)

v1.0.0 (2025-12-08)

  • Initial release
  • Native Modbus RTU implementation
  • Support for all EG4 LL series batteries
  • Comprehensive sensor suite
  • Multi-BMS support

Support

For issues, questions, or feature requests, please open an issue on GitHub.