Skip to content

Instantly share code, notes, and snippets.

@dmitrych5
Forked from PhracturedBlue/ecoworthy.md
Last active January 21, 2026 17:05
Show Gist options
  • Select an option

  • Save dmitrych5/e2fa4ef16b0b483808e4f4089846d0d0 to your computer and use it in GitHub Desktop.

Select an option

Save dmitrych5/e2fa4ef16b0b483808e4f4089846d0d0 to your computer and use it in GitHub Desktop.
Ecoworthy 50V server battery Modbus-RTU protocol

Info related to Eco Worthy JBD UP16S010

BMS: JBD UP16S010, UP16S015 Decoding based on firmware versions 10.2.1, 12.1.7, 13.2.4, 13.3.2

Commands can be sent via the RS485-1 port, by the RS232 port, or UART (9600baud, N81). At least with these firmware versions, the Bluetooth/Wifi UART port is the only port on master that forwards all 0x78 and 0x79 commands to the slaves. On other ports, only the pack status is available for slaves.

CRC calculation: crc16 with initial_value=0xffff and polynomial=0xa001, LSB 1st

The Modbus data-frames are generally MSB-1st except for the CRC. The CRC is calculated from the entire frame (minus the crc itself)

NOTE: The Eco-Worthy software uses the Modbus-RTU protocol. Most other software uses the JBD protocol documented here: https://github.com/syssi/esphome-jbd-bms/blob/main/docs/Jiabaida.communication.protocol.pdf There is also a variant of the JBD protocol where the battery bank is selectable (instead of dd a5, packets start dd 01 a5 where 01 is the bank address). At least on my batteries, only the direct-connected battery is accessible even using the 'addressable JBD' protocol, the address is ignored, and the modbus-RTU protocol appears to be the only one capable of reading multiple banks with a single connection

Per marionw, here are the valid modbus function codes:

code function
0x42 ReadHostStartupData
0x50 ReadHostData
0x45 ReadSlaveData
0x46 SlaveResetCommand
0x47 SlaveChargingInstruction
0x78 ReadBMSInsideInformation
0x79 SetBMSInsideInformation

If the first byte (battery #) of a request is set to FF, the request appears to be treated as a broadcast to all batteries. Broadcasts are available only with some commands and possibly when sent only to some BMS ports, such as the Bluetooth/Wifi UART port. When a broadcast is received, only the master sends a response back, so broadcasts aren't intended for reading settings.

Pack Status

Request

01 78 10 00 10 a0 00 00 7f b2

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
10 00 0x1000 2 2 start address
10 a0 0x10a0 4 2 end address
00 00 0 6 2 data length
7f b2 0xb27f 8 2 crc (LSB 1st)

Response

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
10 00 0x1000 2 2 start address
10 a0 0x10a0 4 2 end address
00 a2 162 6 2 data length
14 8f 52.63V 8 2 pack voltage, V = val / 100
00 00 - 10 2 unknown, contains pack voltage for slave packs and 0 for master, V = val / 100
00 04 93 E0 0.00A 12 4 pack current, A = (val - 300000) / 100
1c 98 73.20% 16 2 state of charge in 0.01% units
1c 98 73.20Ah 18 2 residual pack capacity, Ah = val / 100
27 10 100.00Ah 20 2 full pack capacity, Ah = val / 100
27 10 100.00Ah 22 2 rated pack capacity, Ah = val / 100
02 78 13.2°C 24 2 MOSFET temperature: °C = (val - 500) / 10
02 82 14.2°C 26 2 ambient temperature: °C = (val - 500) / 10
00 00 - 28 2 Operation status (0: Idle, 1: Charging, 2: Discharging)
00 64 100% 30 2 state of health, %
00 00 00 00 - 32 4 level 2 protected state fault code. bitmask
0: Cell over-voltage
1: Cell undervoltage
2: Total overvoltage
3: Total undervoltage
4: charge overcurrent 1 (slow trigger)
5: charge overcurrent 2 (fast trigger)
6: discharge overcurrent 1 (slow trigger)
7: discharge overcurrent 2 (fast trigger)
8: charge high temperature
9: charge low temperature
10: discharge high temperature
11: discharge low temperature
12: MOS high temperature
13: ambient high temperature
14: ambient low temperature
15: cell voltage difference too large
16: temperature difference too large
17: SOC too low
18: short circuit protection
19: monomer offline
20: temperature sensor failure
21: charge MOS fault
22: discharge MOS fault
23: current limiting anomaly
24: aerosol fault
25: full charge protection
26: abnormal AFE communication
27: reverse protection
00 00 00 00 - 36 4 level 1 alarm code. bitmask
0: Cell overvoltage
1: Cell undervoltage
2: Total overvoltage
3: Total undervoltage
4: Charge overcurrent
5: Discharge overcurrent
6: charge high temperature
7: charge low temperature
8: discharge high temperature
9: discharge low temperature
10: MOS high temperature
11: Ambient high temperature
12: Ambient low temperature
13: cell voltage difference is too large
14: temperature difference is too large
15: SOC is too low
16: EEP Fault
17: RTC Abnormal
18: full charge protection
00 03 - 40 2 MOSFET state bitmask
0: discharge
1: charge
2: precharge
3: heat
4: fan
5: dry contact 1
6: dry contact 2
7: limiting
00 04 - 42 2 bitmask:
0: Charger
1: LOAD
2: SW
00 02 2 44 2 Number of charge cycles
00 09 9 46 2 cell # with highest voltage
0c db 3.291V 48 2 highest cell voltage, mV
00 01 1 50 2 cell # with lowest voltage
0c d9 3.289V 52 2 lowest cell voltage, mV
0c d9 3.289V 54 2 avg cell voltage, mV
00 04 4 56 2 cell temp sensor # with highest temperature
02 76 13.0°C 58 2 highest cell temperature: °C = (val - 500) / 10
00 01 1 60 2 cell temp sensor # with lowest temperature
02 74 12.8°C 62 2 lowest cell temperature: °C = (val - 500) / 10
02 75 12.9°C 64 2 avg cell temperature: °C = (val - 500) / 10
02 48 58.4V 66 2 instruction to the charger: Charge Voltage Limit ("MAX CHG CV"), V = val / 10
07 d0 200.0A 68 2 instruction to the charger: Charge Current Limit. Originates from "MAX CHG CC" config value and changes dynamically depending on the battery status. When request is to the master, this value is returned aggregated from all batteries. A = val / 10
01 c0 44.8V 70 2 instruction to the inverter: Discharge Voltage Limit ("MIN DSG DV"), V = val / 10
07 d0 200.0A 72 2 instruction to the inverter: Discharge Current Limit. Originates from "MAX DSG CC" config value and changes dynamically depending on the battery status. When request is to the master, this value is returned aggregated from all batteries. A = val / 10
00 10 16 74 2 number of cells (length of the list below)
0c d9 3.289 76 2 cell voltage 1
0c d9 3.289 78 2 cell voltage 2
0c da 3.290 80 2 cell voltage 3
0c d9 3.289 82 2 cell voltage 4
0c da 3.290 84 2 cell voltage 5
0c da 3.290 86 2 cell voltage 6
0c d9 3.289 88 2 cell voltage 7
0c da 3.290 90 2 cell voltage 8
0c db 3.291 92 2 cell voltage 9
0c d9 3.289 94 2 cell voltage 10
0c db 3.291 96 2 cell voltage 11
0c da 3.290 98 2 cell voltage 12
0c da 3.290 100 2 cell voltage 13
0c db 3.291 102 2 cell voltage 14
0c da 3.290 104 2 cell voltage 15
0c d9 3.289 106 2 cell voltage 16
00 04 4 76+cells*2 2 number of temperature sensors (length of the list below)
02 74 12.8°C 2 temperature 1: °C = (val - 500) / 10
02 75 12.9°C 2 temperature 2: °C = (val - 500) / 10
02 75 12.9°C 2 temperature 3: °C = (val - 500) / 10
02 76 13.0°C 2 temperature 4: °C = (val - 500) / 10
00 00 - 78+cells*2+temps*2 2 constant, unknown
00 00 - 2 balance status bitmask, cell 1 at the least significant bit. Depending on the firmware version, might be available only when connected to the battery directly rather than through the master.
0d 02 13.2 2 firmware version. Appears to be available only when connected to the battery directly rather than through the master.
4a 42 44 34 38 31 30 30 30 30 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 JBD48100000 30 pack sn code
00 02 2 2 number of parallel packs (master only)
00 03 0x0003 2 bitmask of which packs are available (master only)
5a a6 - 2 constant, unknown
00 00 0 2 CAN protocol: 0=Pylon, 1=Growatt, 2=Goodwe, 3=Sofor, 4=Victron, 5=Voltronic, 6=LXP, 7=DEYE, 8=Ginlong, 9=SMA, 10=VMII, 11=SRNE, 12=INVT, 13=Soroups, 14=Must, 15=Aiswei
00 00 0 2 RS485 protocol: 0=Pylon, 1=Growatt, 2=Voltronic, 3=LXP, 4=DEYE, 5=INVT, 6=SRNE, 7=IY-Power, 8=SMK, 9=PACE, 10=HNJD, 11=SAKO, 12=EXT-06, 13=EXT-07, 14=EXT-08, 15=EXT-09
00 00 - 2 bits 16-31 of the bitmask of which packs are available
00 00 00 00
bc 9a 0x9abc 130+cells*2+temps*2 2 crc

Pack Configuration Parameters

Request

01 78 1c 00 1c a0 00 00 7c 2e

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
1c 00 0x1c00 2 2 start address
1c a0 0x1ca0 4 2 end address
00 00 0 6 2 data length
7c 2e 0x2e7c 8 2 crc (LSB 1st)

Response

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
1c 00 0x1c00 2 2 start address
1c a0 0x1ca0 4 2 end address
00 88 136 6 2 data length
00 00 - 8 2
00 00 - 10 2
0d 48 3.400V 12 2 balance voltage, mV
00 1e 30mV 14 2 balance difference, mV
01 f4 0°C 16 2 heater start temp, °C = (val - 500) / 10
02 58 10°C 18 2 heater stop temp, °C = (val - 500) / 10
16 30 56.80V 20 2 full charge voltage, V = val / 100
05 dc 1500mA 22 2 full charge current, mA
55 50 31 36 53 30 31 39 xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx 00 00 00 00 UP16S019xxxxxxxxxxxxxxxxxx 24 30 bms sn code
07e8 2024 54 2 bms year
000c 12 56 2 bms month
00xx xx 58 2 bms day
4a 42 44 34 38 31 30 30 30 30 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 JBD48100000 60 30 pack sn code
07e8 2024 90 2 pack year
0006 6 92 2 pack month
00xx xx 94 2 pack day
45 43 4f 2d 4c 46 50 34 38 31 30 30 2d 33 55 2d xx xx xx xx 00 00 00 00 00 00 00 00 00 00 ECO-LFP48100-3U-xxxx 96 30 mfg code ("barcode")
00 00 - 126 2 CAN protocol: 0=Pylon, 1=Growatt, 2=Goodwe, 3=Sofor, 4=Victron, 5=Voltronic, 6=LXP, 7=DEYE, 8=Ginlong, 9=SMA, 10=VMII, 11=SRNE, 12=INVT, 13=Soroups, 14=Must, 15=Aiswei
0b b8 3000 128 2 sleep voltage, mV
0b 40 2880 130 2 sleep delay time, minutes
00 00 - 132 2 balance mode: 0=Charging, 1=Idle
00 00 - 134 2 RS485 protocol: 0=Pylon, 1=Growatt, 2=Voltronic, 3=LXP, 4=DEYE, 5=INVT, 6=SRNE, 7=IY-Power, 8=SMK, 9=PACE, 10=HNJD, 11=SAKO, 12=EXT-06, 13=EXT-07, 14=EXT-08, 15=EXT-09
00 00 - 136 2 CAN baud rate
00 00 - 138 2 RS485 baud rate
00 0a 10 140 2 sleep current, A = val / 10
00 00 - 142 2
8e bf 0xbf8e 144 2 crc

There's also 2-byte "BMS Software Address" at position 144. It's called this way in JBD ES UP software. JBD BMS Tools instructions hint that it might be used as the address in Modbus/RS-485 protocol, but none of the known UP16S models return a response that's long enough to include this field.

Request

01 78 20 00 20 50 00 00 75 71

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
20 00 0x2000 2 2 start address
20 50 0x2050 4 2 end address
00 00 0 6 2 data length
75 71 0x7175 8 2 crc (LSB 1st)

Response

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
20 00 0x2000 2 2 start address
20 50 0x2050 4 2 end address
00 40 64 6 2 data length
27 10 100.00Ah 8 2 rated capacity
27 10 100.00Ah 10 2 full capacity
1c 72 72.82Ah 12 2 remaining capacity
1c 72 72.82Ah 14 2 state of charge in 0.01% units
00 64 100 16 2 state of health
00 02 2 18 2 charge cycle count
00 96 150 20 2 cycle falloff factor
00 50 80 22 2 cycle factor, %
00 00 0d dd 354.9 Ah 24 4 total charge, Ah = val / 10
00 00 08 e6 227.8 Ah 28 4 total discharge, Ah = val / 10
00 1e 20 32 2 self power factor
0c 1c 3.100V 34 2 ocv adjust voltage 1
00 14 20% 36 2 ocv adjust soc 1
0c 80 3.200V 38 2 ocv adjust voltage 2
00 1e 30% 40 2 ocv adjust soc 2
0d 7a 3.450V 42 2 ocv adjust voltage 3
00 50 80% 44 2 ocv adjust soc 3
0d de 3.550V 46 2 ocv adjust voltage 4
00 5a 90% 48 2 ocv adjust soc 4
02 48 58.4V 50 2 configured Charge Voltage Limit (MAX CHG CV), V = val / 10
03 e8 100.0A 52 2 configured maximum value for Charge Current Limit (MAX CHG CC), A = val / 10
Note: this value does not change dynamically. Charge Current Limit in Pack Status responses does, and uses this value as the maximum
01 c0 44.8V 54 2 configured Discharge Voltage Limit (MIN DSG DV), V = val / 10
03 e8 100.0A 56 2 configured maximum value for Discharge Current Limit (MAX DSG CC), A = val / 10
Note: this value does not change dynamically. Discharge Current Limit in Pack Status responses does, and uses this value as the maximum
00 10 16 58 2 number of cells in pack
00 d2 210 uOhms 60 2 Rsns (current sense shunt resistance) in microohms
00 04 4 62 2 number of temperature sensors
05 34 1332 64 2 likely hardware short circuit protection current threshold in amps. Not configurable
00 7d 125 66 2 likely hardware short circuit protection delay in microseconds. Not configurable
00 00 - 68 2 Capacity algorithm:
bits 0-3 set to "5": disables capacity learning (default 0 is enabled)
bits 4-7 set to "5": disables voltage-based SOC correction (default 0 is enabled)
10 00 - 70 2 Function config:
bits 0-1 set to 1: Heating
bits 2-3 set to 1: Buzzer
bits 4-5 set to 1: Anti-theft
bits 6-7 set to 1: GPS
bits 8-9 set to 1: SOC-N4
bits 10-11 set to 1: EXT-N6
bits 12-13 set to 1: Func 03
bits 14-15 set to 1: Func 04
f8 4c 0x4cf8 72 2 crc

Get current date/time from battery

Request

01 78 28 00 28 0F 00 00 46 4B

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
28 00 0x2800 2 2 start address
28 0f 0x280f 4 2 end address
00 00 0 6 2 data length
46 4b 0x4b46 8 2 crc (LSB 1st)

Response

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
28 00 0x2800 2 2 start address
28 0f 0x280f 4 2 end address
00 0c 0 6 2 data length
07 e9 2025 8 2 year
00 0a 10 10 2 month
00 1a 26 12 2 day
00 06 6 14 2 hour
00 2a 42 16 2 minute
00 07 7 18 2 second
c9 e1 0xe1c9 20 2 crc (LSB 1st)

Set current date/time on battery

Request

01 79 28 00 28 0C 00 10 11 4A 42 44 07 E9 00 05 00 14 00 06 00 27 00 1B 16 D0

bytes value position count description
01 1 0 1 address (battery #)
79 0x79 1 1 function code (set)
28 00 0x2800 2 2 start address
28 0c 0x280c 4 2 end address
00 10 16 6 2 data length
11 4A 42 44 \x11 JBD 8 4 identifier(?)
07 E9 2025 12 2 year
00 05 5 14 2 month
00 14 20 16 2 day
00 06 6 18 2 hour
00 27 39 20 2 minute
00 1B 27 22 2 second
16 d0 0xd016 24 2 crc (LSB 1st)

Response

bytes value position count description
01 1 0 1 address (battery #)
79 0x79 1 1 function code (set)
28 00 0x2800 2 2 start address
28 0c 0x280c 4 2 end address
00 0c 0 6 2 data length
07 E9 2025 8 2 year
00 05 5 10 2 month
00 14 20 12 2 day
00 06 6 14 2 hour
00 27 39 16 2 minute
00 1B 27 18 2 second
89 26 0x2689 20 2 crc (LSB 1st)

Get protection parameters

Request

01 78 18 00 19 00 00 00 7d 44

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
18 00 0x1800 2 2 start address
19 00 0x1900 4 2 end address
00 00 0 6 2 data length
7d 44 0x447d 8 2 crc (LSB 1st)

Response

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
18 00 0x1800 2 2 start address
19 00 0x1900 4 2 end address
00 d0 208 6 2 data length
0e100d480bb80e420d480bb80a8c0b54
0bb809c40bb80bb8168015400bb816d0
15400bb8106812c003e80fa012c00bb8
041a03e807d004b007d00258000a0514
01f40258041a03e807d004b007d0003c
000a057800c8003c041a03e80bb8047e
03e80bb80226025803e801f4022603e8
041a03e803e8047e03e803e8015e0190
03e8012c015e03e805aa051403e8060e
054603e8044c03e80bb8047e044c0bb8
00fa012c0bb8012c015e0bb800500050
0bb8005000500bb8025801f41f400320
01f41f40000a000f07d00003000507d0
17 82 0x8217 8 2 crc (LSB 1st)

WiFi Module info

Request

01 78 24 00 24 50 00 00 75 c5

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
24 00 0x2400 2 2 start address
24 50 0x2450 4 2 end address
00 00 0 6 2 data length
75 c5 0xc575 8 2 crc (LSB 1st)

Response (V2 battery has no WiFi module)

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
24 00 0x2400 2 2 start address
24 50 0x2450 4 2 end address
00 0e 14 6 2 data length
00 00
00 00
00 00
00 00
00 00
00 00
00 00
f7 b5 0xb5f7 8 2 crc (LSB 1st)

Product information

Request (0x78 0x2810)

01 78 28 10 28 3c 00 00 77 87

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
28 10 0x2810 2 2 start address
28 3c 0x283c 4 2 end address
00 00 0 6 2 data length
77 87 0x8777 8 2 crc (LSB 1st)

Response

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
28 10 0x2810 2 2 start address
28 3c 0x283c 4 2 end address
00 2c 44 6 2 data length
00 21 33 8 2 appears to be some device type identifier or product model ID. Doesn't change with firmware updates
00 03 3 10 2 could be hardware revision. In two available samples, the value "3" matches the text on the PCB, "V1.3". Need more samples to confirm or reject this theory.
00 0d 13 12 2 firmware major version
00 03 3 14 2 firmware minor version
00 02 2 16 2 firmware patch version: "13.3.2"
00 26 38 18 2 constant, unknown
55 50 31 36 53 30 31 39 2e 31 36 53 31 30 30 41 UP16S019.16S100A 20 16 BMS model
58 4d 44 5a 31 35 00 00 00 00 00 00 00 00 00 00 XMDZ15 36 16 maybe customer/project code. BMSes with non-Ecoworthy UP16S firmware return variations of "JBD" here
xx xx - 52 2 crc (LSB 1st)

Debug info

Request

01 78 2900 2910 0000 77a0

Response

017829002910000e000000000000000000000000000079d2

Unknown

Request

01 79 2914 2916 0006 114a 4244 6688 616b

Reset

Request Restart

01 79 2912 2914 0006 114a 4244 4a4a fbd2

Request Param default

0179290c290e0006114a424455aa2122

Request Factory reset

0179290e29100006114a42445a5aae8e

Set MOS control state

Request

bytes value position count description
01 1 0 1 address (battery #)
79 0x79 1 1 function code (write)
29 02 0x2902 2 2 start address
29 04 0x2904 4 2 end address
00 06 6 6 2 data length
11 4a 42 44 \x11 JBD 8 4 identifier
00 01 0x0001 12 2 bitmask:
bit 1: turn on CHG MOS
bit 2: turn on DSG MOS
bit 3: turn on Precharge MOS
bit 4: turn on HOT MOS
bit 5: turn on FAN MOS
bit 6: Turn on CHG Limit
15 85 0x8515 14 2 crc

Set sleep mode

bytes value position count description
01 1 0 1 address (battery #)
79 0x79 1 1 function code (write)
29 08 0x2908 2 2 start address
29 0a 0x290a 4 2 end address
00 06 6 6 2 data length
11 4a 42 44 \x11 JBD 8 4 identifier
a5 01 0xa501 12 2 0xa501 = Standby sleep
0xa502 = Deep sleep
03 6d 0x6d03 14 2 crc

Debug Mode

bytes value position count description
01 1 0 1 address (battery #)
79 0x79 1 1 function code (write)
29 00 0x2900 2 2 start address
29 02 0x2902 4 2 end address
00 06 6 6 2 data length
11 4a 42 44 \x11 JBD 8 4 identifier
00 00 0x0000 12 2 0x0000 = Normal mode
0x5a01 = Tooling mode
0x5a02 = DV/DP mode
f4 5d 0x5df4 14 2 crc

Dry Contact

bytes value position count description
01 1 0 1 address (battery #)
79 0x79 1 1 function code (write)
29 04 0x2904 2 2 start address
29 06 0x2906 4 2 end address
00 06 6 6 2 data length
11 4a 42 44 \x11 JBD 8 4 identifier
00 00 0x0000 12 2 bitmask
bit 1: dry contact 1
bit 2: dry contact 2
bit 3: ADDR OUT
bit 4: trip
d3 ad 0xadd3 14 2 crc

Set all debug params (?)

Request

01792900292c0030114a42440000002000000000a5020000000000000000000066880000000000000000000000000000000000000000000097fb

Read history

(Untested)

Request (0x78 0x4000)

01 78 40 00 40 01 00 02 00 00 b5 30

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
40 00 0x4800 2 2 start address
40 01 0x4001 4 2 end address
00 02 2 6 2 data length
00 00 0 8 2 control:
0: start reading (reset pointer to newest)
1: read next (decrement pointer to an older record)
2: read previous (increment pointer)
3: end reading? (doesn't appear to be strictly needed)
b5 30 0x30b5 10 2 crc

Response

bytes value position count description
01 1 0 1 address (battery #)
78 0x78 1 1 function code (read)
40 00 0x4800 2 2 start address
40 01 0x4001 4 2 end address
00 65 101 6 2 data length
01 1 8 1 Status: 0=no records, 1=more records available, 3=last record
... (the actual record data continues here)

Command 0x43

Unknown untested master-only command

Request

01 43 00 00 00 08 00 00 72 c1

bytes value position count description
01 1 0 1 always 1
43 0x43 1 1 function code
00 00 0x0000 2 2 start address
00 08 0x0008 4 2 end address
00 00 0 6 2 data length
72 c1 0xc172 8 2 crc

Response

bytes value description
01 1 always 1
43 0x43 function code
00 00 0 start address
00 08 8 end address
16 80 5760 maybe some voltage parameter? (57.6V?)
05 17 1303 constant, might indicate the firmware version? (13.3 matches)
00 00 - always 0
00 00 - always 0
xx xx - crc

Status dump

Untested master-only command that appears to dump the information about all 32 parallel battery slots, regardless of how many batteries are actually connected. Note that the response is about 4 KB long.

Request(0x44)

01 44 00 00 00 00 00 00 85 c3

bytes value position count description
01 1 0 1 always 1
44 0x44 1 1 function code
00 00 0 2 2 start address
00 00 0 4 2 end address
00 00 0 6 2 length
85 c3 0xc385 8 2 crc

Aggregated status

Untested master-only command that returns aggregated information from all connected packs. There appears to be two ways to get this data:

  • send a 01 50 request as described below,
  • or make the master report this data automatically on the Bluetooth/Wifi UART port only, in which case master sends this data with 01 51 starting bytes. This can be achieved by setting the value at address 0x241e to 0x5aa5: 01 79 24 1e 24 20 00 06 11 4a 42 44 5a a5 92 26. Any other value at this address disables it. Additionally, auto-reporting interval can be adjusted by writing it (in seconds?) to address 0x2420, for example 01 79 24 20 24 22 00 06 11 4a 42 44 00 0a 3a 72.

Request(0x50)

01 50 00 00 00 36 00 00 31 cc

bytes value position count description
01 1 0 1 always 1
50 0x50 1 1 function code
00 00 - 2 2 start address
00 36 - 4 2 end address
00 00 0 6 2 data length
31 cc 0xcc31 8 2 crc

Response

bytes value count description
01 1 1 always 1
50 0x50 1 function code
00 00 0 2 start address
00 36 54 2 end address
00 36 54 2 data length
xx xx - 2 average pack voltage, V = val / 100
00 00 0 2 total current, A = val / 10
xx xx - 2 average SOC
xx xx - 2 average SOH
xx xx - 2 ambient temp, T(Kelvin) = val / 10
xx xx - 2 max cell voltage, mV
xx xx - 2 min cell voltage, mV
xx xx - 2 max cell temp, T(Kelvin) = val / 10
xx xx - 2 min cell temp, T(Kelvin) = val / 10
00 03 0x0003 2 MOS status bitmask (bit 0: charge MOS on, bit 1: discharge MOS on)
xx xx - 2 total residual capacity
xx xx - 2 total full capacity
00 02 2 2 number of packs online
00 03 0x0003 2 bitmask of which packs are available
00 05 5 2 average cycle count
00 00 00 00 - 4 level 2 protected state fault code, aggregated from all packs
0: Cell over-voltage
1: Cell undervoltage
2: Total overvoltage
3: Total undervoltage
4: Charge overcurrent 1 (slow trigger)
5: Charge overcurrent 2 (fast trigger)
6: discharge overcurrent 1 (slow trigger)
7: discharge overcurrent 2 (fast trigger)
8: charge high temperature
9: charge low temperature
10: discharge high temperature
11: discharge low temperature
12: MOS high temperature
13: Ambient high temperature
14: Ambient low temperature
15: Cell voltage difference too large
16: Temperature difference too large
17: SOC too low
18: Short circuit protection
19: Monomer offline
20: Temperature sensor failure
21: Charge MOS fault
22: Discharge MOS fault
23: Current limiting anomaly
24: Aerosol Fault
25: Full Charge Protection
26: Abnormal AFE communication
27: Reverse protection
01 1 1 max voltage cell pack index (1-based)
01 1 1 max voltage cell index (1-based)
01 1 1 min voltage cell pack index (1-based)
05 5 1 min voltage cell index (1-based)
01 1 1 max cell temperature pack index (1-based)
02 2 1 max cell temperature sensor index (1-based)
01 1 1 min cell temperature pack index (1-based)
04 4 1 min cell temperature sensor index (1-based)
00 00 0 2 CAN protocol: 0=Pylon, 1=Growatt, 2=Goodwe, 3=Sofor, 4=Victron, 5=Voltronic, 6=LXP, 7=DEYE, 8=Ginlong, 9=SMA, 10=VMII, 11=SRNE, 12=INVT, 13=Soroups, 14=Must, 15=Aiswei
00 00 0 2 RS485 protocol: 0=Pylon, 1=Growatt, 2=Voltronic, 3=LXP, 4=DEYE, 5=INVT, 6=SRNE, 7=IY-Power, 8=SMK, 9=PACE, 10=HNJD, 11=SAKO, 12=EXT-06, 13=EXT-07, 14=EXT-08, 15=EXT-09
5A A6 - 2 constant, unknown
00 00 - 2 bits 16-31 of the bitmask of which packs are available
00 00 00 00 - 4
xx xx - 2 crc

Alternate Pack Status

This is the command master is using to collect information from the slave packs.

Request

01 45 00 00 00 54 00 00 d4 d3

bytes value position count description
01 1 0 1 address (battery #)
45 0x45 1 1 function code (read)
00 00 0x0000 2 2 start address
00 54 0x0054 4 2 end address
00 00 0 6 2 data length
d4 d3 0xd3d4 8 2 crc (LSB 1st)

Response

The decoding comes from marionw @ https://diysolarforum.com/threads/eco-worthy-48v-100ah-5120wh-lifepo4-server-rack-battery.92299/post-1447436

bytes value position count description
01 1 0 1 address (battery #)
45 0x45 1 1 function code (read)
00 00 0x0000 2 2 start address
00 54 0x0054 4 2 end address
00 7c 124 6 2 data length
00 10 16 8 2 number of cells, max 16. Regardless of this value, the length of the list below is always 16*2 bytes
0c d8 3.288V 10 2 cell voltage 1
0c d8 3.288V 12 2 cell voltage 2
0c d8 3.288V 14 2 cell voltage 3
0c d8 3.288V 16 2 cell voltage 4
0c d8 3.288V 18 2 cell voltage 5
0c d9 3.289V 20 2 cell voltage 6
0c d8 3.288V 22 2 cell voltage 7
0c d8 3.288V 24 2 cell voltage 8
0c da 3.290V 26 2 cell voltage 9
0c d7 3.287V 28 2 cell voltage 10
0c da 3.290V 30 2 cell voltage 11
0c d9 3.289V 32 2 cell voltage 12
0c d9 3.289V 34 2 cell voltage 13
0c d9 3.289V 36 2 cell voltage 14
0c d9 3.289V 38 2 cell voltage 15
0c d8 3.288V 40 2 cell voltage 16
0c da 3.288V 42 2 maximum cell voltage
0c d7 3.287V 44 2 minimum cell voltage
14 8d 52.61V 46 2 total pack voltage
00 00 00 00 0A 48 4 pack current
0b 23 28.51°C 52 2 maximum cell temperature
0b 21 28.49°C 54 2 minimum cell temperature
27 10 100.00% 56 2 full charge capacity
1c 7e 72.94% 58 2 remaining capacity
00 02 2 60 2 number of charge cycles
00 48 72% 62 2 state of charge
00 64 100% 64 2 state of health
00 00 00 00 - 66 4 level 2 protection fault bitmask
0: charge low temperature
1: charge high temperature
2: discharge low temperature
3: discharge high temperature
4: MOS high temperature
5: ambient high temperature
6: ambient low temperature
7: charge MOS fault
8: discharge MOS fault
9: temperature sensor failure
10-15: unused
16: cell over-voltage
17: cell undervoltage
18: discharge overcurrent 1 (slow trigger)
19: discharge overcurrent 2 (fast trigger)
20: charge overcurrent 1 (slow trigger)
21: unused, always 0
22: total overvoltage
23: total undervoltage
24: monomer offline
25: abnormal AFE communication
26: cell voltage difference too large
27: reverse protection
28: full charge protection
00 03 - 70 2 MOSFET state bitmask
0: discharge MOSFET is on
1: charge MOSFET is on
2: operation status: discharging
3: operation status: charging
4: limiting
5: dry contact 2
6: dry contact 1
7: fan
00 00 - 72 2 level 1 protection warning bitmask
0: cell overvoltage
1: cell undervoltage
2: discharge overcurrent
3: charge overcurrent
4: total overvoltage
5: total undervoltage
6: charge high temperature
7: charge low temperature
8: discharge high temperature
9: discharge low temperature
10: MOS high temperature
11: SOC is too low
12: ambient high temperature
13: ambient low temperature
14: cell voltage difference too large
15: full charge protection
42 4a 34 44 31 38 30 30 30 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 JBD48100000 74 30 pack sn code, bytes stored LSB in 16bit pairs, null padded, but not guaranteed to be null terminated
03 e8 100.0A 104 2 Charge Current Limit, non-aggregated (this battery only) even on master, A = val / 10
03 e8 100.0A 106 2 Discharge Current Limit, non-aggregated (this battery only) even on master, A = val / 10
00 02 2 108 2 firmware patch version. Unusual location, and major version is not reported, but the values do match across 3 different samples, making it very unlikely that this value is something else
0b 25 28.53°C 110 2 MOSFET temperature
0b 30 28.64°C 112 2 ambient temperature
04 4 114 1 number of temperature sensors, max 4. Regardless of this value, the length of the list below is always 4*2 bytes
02 2 115 1 firmware minor version
0b 21 28.49°C 116 2 temperature sensor 1
0b 21 28.49°C 118 2 temperature sensor 2
0b 22 28.50°C 120 2 temperature sensor 3
0b 23 28.51°C 122 2 temperature sensor 4
00 00 - 124 2 balance status bitmask, cell 1 at the least significant bit. Older firmware 10.2.1 skips this field going directly to "rated capacity" instead, 12.1.7 does not return this offset at all ending the data right after the temperatures, but 13.3.2 does have it.
27 10 - 126 2 rated capacity, Ah = val / 100
00 00 - 128 2
d3 c9 0xc9d3 130 2 crc
@PhracturedBlue
Copy link

Thank you for adding the extra information. Can you clarify the UART part? You are working directly with the UART port on the JBD? this is not exposed on my Ecoworhty pack, so I assume you hook up directly to the module (or maybe some modules expose it)? It is interesting that the RS232 behaves differently to the UART with resepct to slaves. The 0xff case is good to know. I need to test if that will work with configuring the MOSFETs.

@dmitrych5
Copy link
Author

Thank you for adding the extra information. Can you clarify the UART part? You are working directly with the UART port on the JBD? this is not exposed on my Ecoworhty pack, so I assume you hook up directly to the module (or maybe some modules expose it)? It is interesting that the RS232 behaves differently to the UART with resepct to slaves. The 0xff case is good to know. I need to test if that will work with configuring the MOSFETs.

Yes, I have non-Ecoworthy JBD BMS purchased separately, from what I can tell the only difference is identification strings like SN codes and model names, the behavior appears to be the same. The BMS has two UART ports on its main PCB: for the screen, and for the Wifi/Bluetooth module. So in a way this port is exposed on your pack via bluetooth. You can try sending commands over bluetooth - I never tried it, but I suspect from the restrictions point of view it might behave exactly the same way as if you used wired UART connection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment