Skip to content

Commit 14fc04f

Browse files
committed
docs(ref): add EG4 manuals, JK BMS RS-485 guide, and A5/5A empirical notes
Add reference materials to docs/ref/: - EG4-LL 48V 100Ah and 48V/24V user manuals - JK BMS RS-485 ESPHome wiring/addressing guide - README indexing all reference docs - EG4_A5_5A_BMS_notes.md with our live-captured frame layouts and checksum details for the proprietary A5/5A serial protocol. No inverter/datalogger serial numbers or credentials are included.
1 parent 22d3905 commit 14fc04f

5 files changed

Lines changed: 295 additions & 0 deletions

File tree

3.27 MB
Binary file not shown.

‎docs/ref/EG4-LL-48V-24V-Manual.pdf‎

3.35 MB
Binary file not shown.

‎docs/ref/EG4_A5_5A_BMS_notes.md‎

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# EG4 A5/5A Battery BMS Serial Protocol Notes
2+
3+
These notes are empirical. The protocol was reverse-engineered from a live EG4
4+
200 Ah battery BMS connected to the "PC ready" / display port on
5+
`/dev/ttyUSB0` at 115200 8N1.
6+
7+
No public protocol specification has been found for the A5/5A framing. The
8+
closest match is the LuxpowerTek / Voltronic family style: frames start with
9+
`0xA5 0x5A`, followed by a length byte, command, sub-command, payload, and a
10+
checksum byte at the end.
11+
12+
## Frame format
13+
14+
```text
15+
0xA5 0x5A | length | command | sub-command | payload... | checksum
16+
```
17+
18+
- `length` is the number of bytes *after* the length byte, i.e.
19+
`command + sub-command + payload + checksum`.
20+
- Checksum algorithm: sum of all bytes from `length` through the end of the
21+
payload, low byte only (`sum & 0xFF`). Verified against captured frames.
22+
- All 16-bit integers in the payload are big-endian.
23+
24+
## Observed command/sub-commands
25+
26+
| Command | Sub | Payload len | Description |
27+
|---------|-----|-------------|-------------|
28+
| 0x80 | 0x01| 0 | Heartbeat / keep-alive broadcast |
29+
| 0x82 | 0x10| 35 | Status frame (pack voltage, current, temperatures, average cell voltage, flags) |
30+
| 0x82 | 0x11| 57 | Cell-voltage frame (16 cells + trailing zeros) |
31+
| 0x82 | 0x13| 19 | All zeros in captures (possibly unused or configuration frame) |
32+
| 0x82 | 0x20| 3 | Small response; meaning unknown |
33+
| 0x82 | 0x21| 3 | Small response; meaning unknown |
34+
35+
The BMS broadcasts continuously; the host does not need to send requests to
36+
receive 0x80/0x01, 0x82/0x10, or 0x82/0x11 frames. Sending a poll request with
37+
command 0x82 and the desired sub-command appears to trigger an immediate
38+
response.
39+
40+
## Status frame (0x82 0x10) layout
41+
42+
Payload is 35 bytes. Verified by capturing 17 consecutive frames and
43+
correlating against the EG4 inverter's live values.
44+
45+
| Offset | Bytes | Field | Scale | Notes |
46+
|--------|-------|-------|-------|-------|
47+
| 0 | 1 | — | — | Always 0x00 in captures; may be a status/version byte |
48+
| 1–2 | 2 | voltage | 0.01 V | Pack voltage |
49+
| 3–4 | 2 | current | 0.01 A, signed | Positive = charge, negative = discharge |
50+
| 5–6 | 2 | temperature_pcb | 1 °C | Likely MOSFET/PCB temperature |
51+
| 7–8 | 2 | field_7_8 | — | Unknown |
52+
| 9–10 | 2 | field_9_10 | — | Unknown |
53+
| 11–12 | 2 | field_11_12 | — | Possibly battery/sensor temperature candidate |
54+
| 13–14 | 2 | field_13_14 | — | Possibly max cell temperature candidate |
55+
| 15–16 | 2 | field_15_16 | — | Unknown; always 0 in captures |
56+
| 17–18 | 2 | avg_cell_voltage | 1 mV | Matches average computed from 0x82 0x11 cell frame |
57+
| 19–20 | 2 | field_19_20 | — | Unknown; always 0 in captures |
58+
| 21–22 | 2 | status_word | — | Tentative; always 0x0001 in normal operation |
59+
| 23–24 | 2 | protection_word | — | Tentative; always 0x0001 in normal operation |
60+
| 25–26 | 2 | field_25_26 | — | Candidate for cycle_count: in one frame matched inverter cycle_count (53), but across 17 frames it varied 51–53. Left as raw field until stable correlation is confirmed. |
61+
| 27–28 | 2 | error_word | — | Tentative; always 0x0001 in normal operation |
62+
| 29–30 | 2 | field_29_30 | — | Unknown; always 0x0001 in captures |
63+
| 31–32 | 2 | field_31_32 | — | Unknown; always 0x0001 in captures |
64+
| 33–34 | 2 | field_33_34 | — | Unknown; always 0x0000 in captures |
65+
66+
### Open questions
67+
68+
- **SOC location is not yet known.** Because the observed battery stayed at
69+
0% SOC during decoding, every candidate SOC byte also reads 0. A capture
70+
while the battery charges is required to locate SOC.
71+
- The meaning of `field_25_26` (candidate cycle_count) is uncertain because
72+
it varied between frames.
73+
- Sub-commands 0x20 and 0x21 return 3-byte payloads that may contain
74+
capacity, SOH, or firmware information; not yet decoded.
75+
76+
## Cell-voltage frame (0x82 0x11) layout
77+
78+
Payload is 57 bytes.
79+
80+
| Offset | Bytes | Field | Scale |
81+
|--------|-------|-------|-------|
82+
| 0 | 1 | — | — |
83+
| 1–2 | 2 | cell_1_voltage | 1 mV |
84+
| 3–4 | 2 | cell_2_voltage | 1 mV |
85+
| ... | ... | ... | ... |
86+
| 31–32 | 2 | cell_16_voltage | 1 mV |
87+
| 33–56 | 24 | — | — | Trailing zeros in captures |
88+
89+
## Implementation
90+
91+
See `collector/rs485/eg4_a5_bms.py` for the lux-mon driver. Fields are written
92+
with the configured `LUX_RS485_PREFIX` (e.g. `eg4_bms_`) to MariaDB,
93+
InfluxDB, and MQTT so they do not collide with inverter registers.

‎docs/ref/JK_BMS_RS485_esphome.md‎

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# esphome gathering info from multiple JK-PB BMSs usign RS485 internal network
2+
3+
![GitHub stars](https://img.shields.io/github/stars/txubelaxu/esphome-jk-bms)
4+
![GitHub forks](https://img.shields.io/github/forks/txubelaxu/esphome-jk-bms)
5+
![GitHub watchers](https://img.shields.io/github/watchers/txubelaxu/esphome-jk-bms)
6+
7+
ESPHome component to monitor a Jikong Battery Management System (JK-PB) via ESP SERIAL (UART-TTL) + SERIAL TO RS485 + ETHERNET CABLE
8+
In theory 1 ESP can gather information of every BMS in the RS485 network: MAX 16.
9+
10+
The BMS bluetooth remains free to use with your mobile.
11+
12+
## REMINDER
13+
14+
* Use this at your own risk
15+
16+
## Supported devices
17+
18+
JK-PBx models with software version `>=14.0` are using the implemented protocol and should be supported.
19+
20+
* JK-PB2A16S-20P, hw 14.XA, sw 14.20, using `JK02_32S` (reported by [@txubelaxu])
21+
* JK-PBx, hw 15.XA, sw 15.11, using `JK02_32S` (reported by [@denveronly])
22+
23+
## Untested devices
24+
25+
* JK-PB1A16S-20P, hw 15.XA,
26+
27+
## Requirements
28+
29+
* [ESPHome 2022.11.0 or higher] (https://github.com/esphome/esphome/releases).
30+
* Generic ESP32 or ESP8266 board
31+
* SERIAL TTL to RS485 converter
32+
* Ethernet type cable (one side with RJ45, the other side 3 wires: A, B and GND)
33+
34+
35+
# IMPORTANT THINGS ABOUT RS485:
36+
* IN THEORY, IN A RS485 NETWORK ONE (ONLY ONE) DEVICE CAN BE THE MASTER.
37+
* In a JK-RS485 internal netwok:
38+
+ the MASTER is the one that has 0x00 (address) Use DIP switches to assign this address.
39+
+ other BMSs connected to the JK-RS485 internal network need a different address. So: 0x01 or 0x02 or 0x03... or 0x0F
40+
+ each BMS in the network must have a different address. Use DIP switches to assign these addresses.
41+
42+
The ESP with this code needs NO RS485 address to work.. But it needs a yaml file configured properly. The yaml content must be equal to the real situation of the amount of BMSs and their addresses in the network. How does ESP code work?
43+
44+
MODE 1. If there is a JK-BMS acting as REAL MASTER (i.e. it has set 0x00 address), the code will be sniffing all the traffic in the network. The problem is that there is some information that is NEVER in the sniffed traffic information (device info frame type 03): device-name, hw version, sw version, password, RCV Time, RFV Time...
45+
So, periodically, ESP code will act as a PSEUDO-MASTER to try to get this information. It works for every slave device, but not for master device. At this moment, master device does NOT broadcast by itself the "frame type 03" info. And it does not answer to a "frame type 03" request sent by PSEUDO MASTER.
46+
47+
MODE 2. If every JK-BMS are set as SLAVES (all of them), the ESP code will act as MASTER. So, if ESP code does not detect any REAL MASTER in the network, it will act as a MASTER. Remember to adapt DIP switches and the YAML (addresses) as well. Changing the addreses of the devices does not affect to the historic data gathered from that device, because the config is based on the NAME of the device and not on the address of the device. So, change the address, but not the name. In this mode, ESP will gather all the information (cell info, device settings and device info). If anytime, a REAL MASTER arrives to the network, ESP will detect it and will stop acting as MASTER.
48+
49+
In any of the two modes ESP needs to SPEAK to the JK-RS485 internal network. That is why it needs a "SPEAKING PIN OUT" to direct a signal to the "TTL to RS485" converter. There are schematics below. It is not mandatory to connect this pin to gather some info. But if it is not connected it could not ask for some information, and it can not act as a MASTER. But the code should work in the same way until this version. Any way, connected or not, it must be a "talk pin" in the config.
50+
51+
52+
53+
54+
## Schematics
55+
56+
```
57+
CONVERTER UART-TTL
58+
┌──────────┐ ┌───────────┐ ┌─────────┐
59+
│ │<----- A ----->│ SERIAL │<-----------------Vcc--│ │<--Vcc
60+
│ JK-BMS │<----- B ----->│ TTL TO │ │ ESP32/ │
61+
│ │ │ RS485 │--RO---------------RX->│ ESP8266 │
62+
│ │ │ │<-DI---------------TX--│ │
63+
│ │ │ CONVERTER │<-DE-----+ │ │
64+
│ │ │ │<-RE-----└---TALK PIN--│ │
65+
│ │ │ │ │ │
66+
| |<------GND----->| |<----------GND-------->| |<--GND
67+
└──────────┘ └───────────┘ └─────────┘
68+
69+
70+
# ESP32 UART-TTL (PINOUT ASIGNMENT EXAMPLE)
71+
┌──────────────────────────────┐
72+
│ │
73+
│ O O O O O │
74+
│ GND RX TX talk Vcc │
75+
└──────────────────────────────┘
76+
^ ^ ^ ^ ^
77+
│ │ │ | └──── Vcc
78+
│ │ | └────────── GPIO16
79+
│ │ └──────────────── GPIO17
80+
│ └────────────────────── GPIO4
81+
└──────────────────────────── GND
82+
83+
# HOW TO CONNECT RS485-2 BUS:
84+
FIRST: CONNECT ALL THE BMSs IN CHAIN
85+
SECOND: CONNECT THE SNIFFER (ESP32->RS485 CONVERTER->) AT ONE END OF THE CHAIN
86+
```
87+
![image](https://github.com/user-attachments/assets/94e6c464-df4b-4a0f-b6e6-dd013b574ea0)
88+
89+
![image](https://github.com/user-attachments/assets/5e12311d-1791-40c4-9302-a4130708c5a6) Thanks to [TheSmartGerman](https://github.com/TheSmartGerman) for previous picture. The RS485 connectors are the 2 connectors on the right.
90+
91+
pinout:
92+
![image](https://github.com/txubelaxu/esphome-jk-bms/assets/156140720/278abcac-c095-4ae2-8ba2-f82f1f152343)
93+
94+
```
95+
Usable any of the two connectors:
96+
┌─────────────────────────┐ ┌─────────────────────────┐ ┌─────────────────────────┐ ┌─────────────────────────┐
97+
│ │ │ │ │ │ │ │
98+
│ O O O O O O O O │ OR │ O O O O O O O O │ OR │ O O O O O O O O │ OR │ O O O O O O O O │
99+
│ 1 2 3 4 5 6 7 8 │ │ 9 10 11 12 13 14 15 16 │ │ 1 2 3 4 5 6 7 8 │ │ 9 10 11 12 13 14 15 16 │
100+
└─────────────────────────┘ └─────────────────────────┘ └─────────────────────────┘ └─────────────────────────┘
101+
│ │ │ │ │ │ │ │ │ │ │ │
102+
│ │ └──── GND │ │ └──── GND GND ───┘ │ │ GND ─┘ │ │
103+
│ └─────── A │ └─────── A A ──────┘ │ A ────┘ │
104+
└────────── B └────────── B B ─────────┘ B ───────┘
105+
```
106+
107+
108+
109+
## Installation
110+
111+
just use the `esp32-example-jkpb-rs485.yaml` as proof of concept:
112+
113+
```
114+
bash
115+
# Install esphome
116+
python3 -m pip install esphome
117+
118+
# Clone this external component
119+
git clone https://github.com/txubelaxu/esphome-jk-bms.git
120+
cd esphome-jk-bms
121+
122+
# Create a secrets.yaml containing some setup specific secrets
123+
cat > secrets.yaml <<EOF
124+
wifi_ssid: MY_WIFI_SSID
125+
wifi_password: MY_WIFI_PASSWORD
126+
EOF
127+
128+
There is a example configuration (0 master and 2 slaves) that you can change to your needs:
129+
- esp32-S3-example-jkpb-rs485_0master_2slaves.yaml <> a config of 0 master and 2 slave (total: 2 JK-PB BMSs)
130+
131+
132+
# Configure every BMS you have in this yaml, assigning a name and the address of each one.
133+
134+
135+
136+
## VALIDATE CODE + UPLOAD + LAUNCH + VIEW LOGS:
137+
138+
# Validate the configuration, create a binary, upload it, and start logs
139+
esphome run esp32-S3-example-jkpb-rs485_0master_2slaves.yaml
140+
141+
(previous example has 2 SLAVES at address 0x01 and at address 0x02).
142+
143+
144+
## Known issues
145+
146+
* ??
147+
148+
## TO DO
149+
150+
* Communication to INVERTER SYSTEM.
151+
152+
153+
## Goodies
154+
155+
A user of this project ([@dr3amr](https://github.com/dr3amr)) shared some [Home Assistant Lovelace UI cards for a beautiful dashboard here](https://github.com/syssi/esphome-jk-bms/discussions/230).
156+
157+
![Custom Lovelace UI cards](images/lovelace-cards-contribution.jpg "Home Assistant Lovelace UI cards")
158+
159+
## Debugging
160+
161+
If this component doesn't work out of the box for your device please update your configuration to enable the debug output of the UART component and increase the log level to the see outgoing and incoming serial traffic:
162+
163+
logger:
164+
level: DEBUG
165+
166+
```
167+
168+
## MUCH MORE
169+
Added a <a href="./home_assistant_dashboards/">Home Assistant Dashboard</a>
170+
![image](https://github.com/user-attachments/assets/cd561b9c-08ef-4ab8-8837-5855f91f27ae)
171+
![image](https://github.com/user-attachments/assets/d09ed79e-568a-4be1-8d24-a8dd1e1e1831)
172+
![image](https://github.com/user-attachments/assets/4d8a4db5-46c3-427d-a8f5-dc2a4a9a3b72)
173+
![image](https://github.com/user-attachments/assets/d9751048-762d-43db-9220-b5d49b05cb75)
174+
![image](https://github.com/user-attachments/assets/4659d9b9-4a1e-4148-b92a-7e5b926c5436)
175+
176+
177+
178+
## References
179+
180+
* https://secondlifestorage.com/index.php?threads/jk-b1a24s-jk-b2a24s-active-balancer.9591/
181+
* https://github.com/jblance/jkbms
182+
* https://github.com/jblance/mpp-solar/issues/112
183+
* https://github.com/jblance/mpp-solar/blob/master/mppsolar/protocols/jk232.py
184+
* https://github.com/jblance/mpp-solar/blob/master/mppsolar/protocols/jk485.py
185+
* https://github.com/sshoecraft/jktool
186+
* https://github.com/Louisvdw/dbus-serialbattery/blob/master/etc/dbus-serialbattery/jkbms.py
187+
* https://blog.ja-ke.tech/2020/02/07/ltt-power-bms-chinese-protocol.html

‎docs/ref/README.md‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Reference Documents
2+
3+
This directory contains protocol documentation, manuals, and notes for the
4+
hardware and protocols used by lux-mon.
5+
6+
| File | Description |
7+
|------|-------------|
8+
| `EG4_LifePower4_Communication_Protocol.pdf` | EG4 LifePower4 battery 0x7E serial protocol (BMS module addressing, voltage/current/temp/SOH registers). |
9+
| `EG4_LifePower4_Communication_Protocol.txt` | OCR/text extraction of the above PDF for easier searching. |
10+
| `EG4-LL-48V-100AH-Manual.pdf` | EG4-LL 48 V 100 Ah rack battery user manual. |
11+
| `EG4-LL-48V-24V-Manual.pdf` | EG4-LL 48 V / 24 V rack battery user manual. |
12+
| `JK_BMS_RS485_esphome.md` | ESPHome JK-PBx BMS RS-485 wiring/addressing guide from txubelaxu/esphome-jk-bms. |
13+
| `EG4_A5_5A_BMS_notes.md` | Empirical notes on the EG4 A5/5A proprietary serial protocol used by the 200 Ah battery BMS on `/dev/ttyUSB0`.
14+
15+
See `collector/rs485/README.md` for the driver implementations.

0 commit comments

Comments
 (0)