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
- Pack Status
- Pack Configuration Parameters
- Get current date/time from battery
- Set current date/time on battery
- Get protection parameters
- WiFi Module info
- Debug info
- Unknown
- Reset
- Set MOS control state(0x79) 0x2902
- Set sleep mode(0x79) 0x2902
- Set all debug params (?)
- Read history
- Command 0x43
- Status dump
- Aggregated status
- Alternate Pack Status
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.
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) |
| 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 |
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) |
| 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.
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) |
| 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 |
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) |
| 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) |
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) |
| 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) |
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) |
| 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) |
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) |
| 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) |
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) |
| 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) |
01 78 2900 2910 0000 77a0
017829002910000e000000000000000000000000000079d2
01 79 2914 2916 0006 114a 4244 6688 616b
01 79 2912 2914 0006 114a 4244 4a4a fbd2
0179290c290e0006114a424455aa2122
0179290e29100006114a42445a5aae8e
| 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 |
| 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 |
| 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 |
| 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 |
01792900292c0030114a42440000002000000000a5020000000000000000000066880000000000000000000000000000000000000000000097fb
(Untested)
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 |
| 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) |
Unknown untested master-only command
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 |
| 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 |
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.
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 |
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 50request 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 51starting 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 example01 79 24 20 24 22 00 06 11 4a 42 44 00 0a 3a 72.
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 |
| 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 |
This is the command master is using to collect information from the slave packs.
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) |
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 |
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.