diy solar

diy solar

Decoding the DALY SmartBMS protocol

tanoshimini

Solar Enthusiast
Joined
Mar 28, 2021
Messages
110
I have a some SmartBMS units from Daly that I want to talk to. On other threads in this forum, I've seen some discussion of the protocol, and there have been some successes that people have had in getting some documentation out of them. Having read over the documentation, I've come to discover that it only covers a few aspects of the BMS, and does not cover at all, anything to do with setting or retrieving it's configuration parameters.

To that end, I decided to use a Teensy 3.2 to monitor both sides of the communication between the BMS and the official Daly BMS software as it goes about setting and retrieving various parameters. Here's a raw dump of the back and forth I captured:

Normal chatter about cell voltages, etc. This stuff is covered in the documentation, and served as a sanity check.
Code:
-> a5 40 90 08 00 00 00 00 00 00 00 00 7d
<- a5 01 90 08 02 10 00 00 75 30 01 f4 ea

-> a5 40 91 08 00 00 00 00 00 00 00 00 7e
<- a5 01 91 08 0e c8 05 0e 9c 09 01 f4 c2

-> a5 40 92 08 00 00 00 00 00 00 00 00 7f
<- a5 01 92 08 3d 01 3d 01 9c 09 01 f4 56

-> a5 40 93 08 00 00 00 00 00 00 00 00 80
<- a5 01 93 08 00 01 01 10 00 00 4e 20 c1

-> a5 40 94 08 00 00 00 00 00 00 00 00 81
<- a5 01 94 08 0e 01 00 00 02 00 00 20 73

-> a5 40 95 08 00 00 00 00 00 00 00 00 82
<- a5 01 95 08 01 0e c0 0e c2 0e c7 20 d7
<- a5 01 95 08 02 0e c8 0e c8 0e c6 20 e5
<- a5 01 95 08 03 0e c2 0e c2 0e 9c 20 b0
<- a5 01 95 08 04 0e c4 0e c4 0e c4 20 dd
<- a5 01 95 08 05 0e c3 0e c0 0e c4 20 d9

-> a5 40 96 08 00 00 00 00 00 00 00 00 83
<- a5 01 96 08 01 3d 00 00 00 00 00 00 82
<- a5 01 96 08 02 00 00 00 00 00 00 00 46

-> a5 40 97 08 00 00 00 00 00 00 00 00 84
<- a5 01 97 08 00 00 00 00 00 00 00 00 45

-> a5 40 98 08 00 00 00 00 00 00 00 00 85
<- a5 01 98 08 00 00 00 00 00 00 00 00 46

-> a5 40 d8 08 00 00 00 00 00 00 00 00 c5
<- a5 01 d8 08 00 00 00 00 00 00 00 00 86

This stuff is more interesting... It's the sequence that runs when you mash the "Read All" button on the "Read Settings" page of the official app. Attached below is a screenshot of the app after it's decoded this sequence. The app appears to helpfully group settings into things that can be retrieved with a single command, and these groups correspond 1-to-1 with the command sequence. It shouldn't be too hard to pick apart which one does what.

Code:
-> a5 40 50 08 00 00 00 00 00 00 00 00 3d // Query the rated pack capacity and nominal cell voltage.
<- a5 01 50 08 00 00 9c 40 00 00 0e 74 5c

-> a5 40 5f 08 00 00 00 00 00 00 00 00 4c // Query the voltage thresholds that control balancing.
<- a5 01 5f 08 0d 48 00 32 00 00 0e 74 16

-> a5 40 52 08 00 00 00 00 00 00 00 00 3f
<- a5 01 52 08 00 00 00 00 00 00 00 00 00

-> a5 40 60 08 00 00 00 00 00 00 00 00 4d // Query the short-circuit shutdown threshold and the current sampling resolution.
<- a5 01 60 08 07 d0 00 64 00 00 00 00 49

-> a5 40 51 08 00 00 00 00 00 00 00 00 3e // Query the "Number of acquisition board", "Cell counts" and "Temp Sensor counts" ???
<- a5 01 51 08 01 0e 00 00 01 00 00 00 0f

-> a5 40 53 08 00 00 00 00 00 00 00 00 40 // Query "Battery operation mode" / "Production Date" / Battery Type" and "Automatic sleep time" ???
<- a5 01 53 08 01 02 15 03 06 ff ff 00 20

-> a5 40 54 08 00 00 00 00 00 00 00 00 41 // ??? Query the "Firmware index number"
<- a5 01 54 08 32 30 32 30 30 33 30 36 8f

-> a5 40 57 08 00 00 00 00 00 00 00 00 44 // ??? Query the "Battery code"
<- a5 01 57 08 01 32 30 32 30 30 33 30 5d
<- a5 01 57 08 02 36 20 20 20 20 20 20 fd
<- a5 01 57 08 03 20 20 20 20 20 20 20 e8
<- a5 01 57 08 04 20 20 20 20 20 20 20 e9
<- a5 01 57 08 05 20 20 20 20 20 20 20 ea

-> a5 40 61 08 00 00 00 00 00 00 00 00 4e
<- a5 01 61 08 00 00 00 00 00 00 20 20 4f

-> a5 40 62 08 00 00 00 00 00 00 00 00 4f // Query Software Version
<- a5 01 62 08 01 32 30 32 31 30 32 32 6a
<- a5 01 62 08 02 32 2d 31 2e 30 31 54 85

-> a5 40 63 08 00 00 00 00 00 00 00 00 50 // Query Hardware Version
<- a5 01 63 08 01 44 4c 2d 42 4d 53 2d de
<- a5 01 63 08 02 52 33 32 2d 30 31 45 9d

-> a5 40 59 08 00 00 00 00 00 00 00 00 46 // Query the Level 1 and 2 alarm thresholds for high and low cell voltages
<- a5 01 59 08 10 36 10 04 0a f0 0b b8 1e

-> a5 40 5a 08 00 00 00 00 00 00 00 00 47 // Query the Level 1 and 2 alarm thresholds for high and low voltages for the pack as a whole
<- a5 01 5a 08 02 45 02 3e 01 88 01 b2 cb

-> a5 40 5b 08 00 00 00 00 00 00 00 00 48 // Query the Level 1 and 2 alarm thresholds for charge and discharge current for the pack.
<- a5 01 5b 08 6b d0 6f 54 7e 90 78 1e ab

-> a5 40 5e 08 00 00 00 00 00 00 00 00 4b // Query the Level 1 and 2 alarm thresholds for allowable difference in cell voltage and temperature sensor readings
<- a5 01 5e 08 01 f4 00 c8 0a 0f 78 1e 78

-> a5 40 5d 08 00 00 00 00 00 00 00 00 4a
<- a5 01 5d 08 03 e8 03 fc 00 c8 00 64 21

-> a5 40 5c 08 00 00 00 00 00 00 00 00 49
<- a5 01 5c 08 5f 69 05 00 69 6e 05 00 b3
DALY Data Page.PNG

Likewise, this is a dump of the sequence that from mashing the "Set All" button on the "Parameter Settings" page. It looks like they largely mirror the commands being send in the read section, and it shouldn't be too much work to prove that out.

Code:
-> a5 40 10 08 00 00 9c 40 00 00 0e 74 5b // Set the rated pack capacity and nominal cell voltage.
<- a5 01 10 08 01 10 00 00 75 30 01 f4 69

-> a5 40 1f 08 0d 48 00 32 00 00 0e 74 15 // Set the voltage thresholds that control balancing.
<- a5 01 1f 08 01 10 00 00 75 30 01 f4 78

-> a5 40 12 08 00 00 00 00 00 00 00 00 ff
<- a5 01 12 08 01 10 00 00 75 30 01 f4 6b

-> a5 40 20 08 07 d0 00 64 00 00 00 00 48 // Set the short-circuit shutdown threshold and the current sampling resolution.
<- a5 01 20 08 01 10 00 00 75 30 01 f4 79

-> a5 40 11 08 01 0e 00 00 01 00 00 00 0e // Set the "Number of acquisition board", "Cell counts" and "Temp Sensor counts" ???
<- a5 01 11 08 01 10 00 00 75 30 01 f4 6a

-> a5 40 13 08 01 02 15 03 06 ff ff 00 1f // Set "Battery operation mode" / "Production Date" / Battery Type" and "Automatic sleep time" ???
<- a5 01 13 08 01 10 00 00 75 30 01 f4 6c

-> a5 40 14 08 32 30 32 30 30 33 30 36 8e // ??? Set the "Firmware index number"
<- a5 01 14 08 01 10 00 00 75 30 01 f4 6d

-> a5 40 17 08 01 32 30 32 30 30 33 30 5c // ??? Set the "Battery code"
-> a5 40 17 08 02 36 20 20 20 20 20 20 fc
-> a5 40 17 08 03 20 20 20 20 20 20 20 e7
-> a5 40 17 08 04 20 20 20 20 20 20 20 e8
-> a5 40 17 08 05 20 20 20 20 20 20 20 e9
<- a5 01 17 08 01 10 00 00 75 30 01 f4 70

-> a5 40 19 08 10 36 10 04 0a f0 0b b8 1d // Set the Level 1 and 2 alarm thresholds for high and low cell voltages
<- a5 01 19 08 01 10 00 00 75 30 01 f4 72

-> a5 40 1a 08 02 44 02 3e 01 88 01 b2 c9 // Set the Level 1 and 2 alarm thresholds for high and low voltages for the pack as a whole
<- a5 01 1a 08 01 10 00 00 75 30 01 f4 73

-> a5 40 1b 08 6b d0 6f 54 7e 90 78 1e aa // Set the Level 1 and 2 alarm thresholds for charge and discharge current for the pack.
<- a5 01 1b 08 01 10 00 00 75 30 01 f4 74

-> a5 40 1e 08 01 f4 00 c8 0a 0f 78 1e 77 // Set the Level 1 and 2 alarm thresholds for allowable difference in cell voltage and temperature sensor readings
<- a5 01 1e 08 01 10 00 00 75 30 01 f4 77

-> a5 40 1d 08 03 e8 03 fc 00 c8 00 64 20
<- a5 01 1d 08 01 10 00 00 75 30 01 f4 76

-> a5 40 1c 08 5f 69 05 00 69 6e 05 00 b2
<- a5 01 1c 08 01 10 00 00 75 30 01 f4 75
 
Last edited:
Command 0x62 appears to query the "Software Version":
Code:
-> a5 40 62 08 00 00 00 00 00 00 00 00 4f 
<- a5 01 62 08 01 32 30 32 31 30 32 32 6a // "2021022"
<- a5 01 62 08 02 32 2d 31 2e 30 31 54 85 // "2-1.01T"
  • Byte 0: The frame number; Each frame carries 7 bytes of the string.
  • Byte 1-7: The characters that make up the version string.
 
Command 0x63 appears to query the "Hardware Version"
Code:
-> a5 40 63 08 00 00 00 00 00 00 00 00 50 
<- a5 01 63 08 01 44 4c 2d 42 4d 53 2d de // "DL-BMS-"
<- a5 01 63 08 02 52 33 32 2d 30 31 45 9d // "R32-01E"
  • Byte 0: The frame number; Each frame carries 7 bytes of the string.
  • Byte 1-7: The characters that make up the version string.
 
Command 0x51 appears to query the "Number of acquisition board", "Cell counts" and "Temp Sensor counts"
Code:
-> a5 40 51 08 00 00 00 00 00 00 00 00 3e
<- a5 01 51 08 01 0e 00 00 01 00 00 00 0f
  • Byte 0: Number of acquisition board? [01] = Board count of 1
  • Byte 1-3: Cell counts [0E 00 00] = (14, 0, 0)
  • Byte 4-6: Temp sensor (NTC thermistor) counts [01 00 00] = (1, 0, 0)
  • Byte 7: Reserved?
 
Last edited:
Command 0x59 appears to query the Level 1 and 2 alarm thresholds for high and low cell voltages.
Code:
-> a5 40 59 08 00 00 00 00 00 00 00 00 46
<- a5 01 59 08 10 36 10 04 0a f0 0b b8 1e
  • Byte 0-1: Level-1 alarm threshold for Cell High Voltage, in millivolts [10 36] = 4.15v
  • Byte 2-3: Level-2 alarm threshold for Cell High Voltage, in millivolts [10 04] = 4.1v
  • Byte 4-5: Level-1 alarm threshold for Cell Low Voltage, in millivolts [0A F0] = 2.8v
  • Byte 6-7: Level-2 alarm threshold for Cell Low Voltage, in millivolts [0B B8] = 3.0v
 
Command 0x5A appears to query the Level 1 and 2 alarm thresholds for high and low voltages for the pack as a whole.
Code:
-> a5 40 5a 08 00 00 00 00 00 00 00 00 47
<- a5 01 5a 08 02 45 02 3e 01 88 01 b2 cb
  • Byte 0-1: Level-1 alarm threshold for Pack High Voltage, in 10th's of a volt [02 45] = 58.1v
  • Byte 2-3: Level-2 alarm threshold for Pack High Voltage, in 10th's of a volt [02 3E] = 57.4v
  • Byte 4-5: Level-1 alarm threshold for Pack Low Voltage, in 10th's of a volt [01 88] = 39.2v
  • Byte 6-7: Level-2 alarm threshold for Pack Low Voltage, in 10th's of a volt [01 B2] = 43.4v
 
Command 0x5E appears to query the Level 1 and 2 alarm thresholds for allowable difference in cell voltage and temperature sensor readings
Code:
-> a5 40 5e 08 00 00 00 00 00 00 00 00 4b
<- a5 01 5e 08 01 f4 00 c8 0a 0f 78 1e 78
  • Byte 0-1: Level-1 alarm threshold for Cell Voltage Difference, in millivolts [01 F4] = 0.5v
  • Byte 2-3: Level-2 alarm threshold for Cell Voltage Difference, in millivolts [00 C8] = 0.2v
  • Byte 4: Level-1 alarm threshold for Temp Sensor Difference, in °C [0A] = 10°C
  • Byte 5: Level-2 alarm threshold for Temp Sensor Difference, in °C [0F] = 15°C
  • Byte 6-7: Reserved?
 
Command 0x5B appears to query the Level 1 and 2 alarm thresholds for charge and discharge current for the pack.
Code:
-> a5 40 5b 08 00 00 00 00 00 00 00 00 48
<- a5 01 5b 08 6b d0 6f 54 7e 90 78 1e ab
  • Byte 0-1: Level-1 alarm threshold for High Charge Current, in 10ths of an amp (with a zero-value of 30000) [6B D0] = 240A; (30000 - (240 * 10))
  • Byte 2-3: Level-2 alarm threshold for High Charge Current, in 10ths of an amp (with a zero-value of 30000) [6F 54] = 150A; (30000 - (150 * 10))
  • Byte 4-5: Level-1 alarm threshold for High Discharge Current, in 10ths of an amp (with a zero-value of 30000) [7E 90] = 240A; (30000 + (240 * 10))
  • Byte 6-7: Level-2 alarm threshold for High Discharge Current, in 10ths of an amp (with a zero-value of 30000) [78 1e] = 75A; (30000 + (75* 10))
 
Command 0x50 appears to query the rated pack capacity and nominal cell voltage.
Code:
-> a5 40 50 08 00 00 00 00 00 00 00 00 3d
<- a5 01 50 08 00 00 9c 40 00 00 0e 74 5c
  • Byte 0-3: Rated pack capacity, in mAh. [00 00 9C 40] = 40Ah
  • Byte 4-5: Reserved?
  • Byte 6-7: Nominal cell voltage, in millivolts. [0E 74] = 3.7v
 
Command 0x5F appears to query the voltage thresholds that control balancing.
Code:
-> a5 40 5f 08 00 00 00 00 00 00 00 00 4c
<- a5 01 5f 08 0d 48 00 32 00 00 0e 74 16
  • Byte 0-1: Cell voltage threshold at which balancing is enabled, in millivolts. [0D 48] = 3.4v
  • Byte 2-3: Voltage differential between cells that is considered acceptable, in millivolts. [00 32] = 0.05v (The software doesn't seem to allow a value below 0.05v, but the protocol would seem to allow it. Interesting.)
  • Byte 4-7: Reserved?
 
Command 0x60 appears to query the short-circuit shutdown threshold and the current sampling resolution.
Code:
-> a5 40 60 08 00 00 00 00 00 00 00 00 4d
<- a5 01 60 08 07 d0 00 64 00 00 00 00 49
  • Byte 0-1: Short-circuit shutdown threshold, in amps. [07 D0] = 2000A
  • Byte 2-3: Current sampling resistance, in milliohms. [00 64] = 0.1Ω
  • Byte 4-7: Reserved?
 
Excellent, I haven't had time to set up to sniff/capture the traffic myself. I suspect Seiko might have a data sheet somewhere, since they advertise it as using a Seiko chip. This is great information, thank you for sharing.

Now to figure out if my 12v and 24v share the same command/response set, likely they do.
 
Does anyone know what seiko chip they're using? I can't seem to find much information on that. Knowing the chip, there may be manufacturer datasheets and documentation that I can mine for additional features or functionality that Daly chose not to expose.
 
Does anyone know what seiko chip they're using? I can't seem to find much information on that. Knowing the chip, there may be manufacturer datasheets and documentation that I can mine for additional features or functionality that Daly chose not to expose.
I would like to know as well, I haven't found anything published by Daly that tells us the specific chip, unfortunately.
 
I took some inspiration from the SimpBMS for tesla modules, and I now have the Daly sending BMS.Can messages that my Victron equipment is reading successfully. Yay! From what I understand this would make my little adapter work with any inverter that is compatible with the SimpBMS or the ones from REC.

I'm going to streamline the code and push it to GitHub in the next day or so.
 
I wish all these chinese BMS brands would just use standard protocols like Modbus or SMbus, would make things so much easier!
 
Back
Top