diy solar

diy solar

Decoding the DALY SmartBMS protocol

I wish all these chinese BMS brands would just use standard protocols like Modbus or SMbus, would make things so much easier!
Naw... we’re still firmly in the BETA/VHS (or Blue-Ray/HD-DVD for younger people) stage of this tech. It’s okay, though, give it a little while and it’ll firm up. In the meantime, these little microcontrollers make for a nifty fix to the problem.

I’m working through the various messages and seeing what kind of junk I’m sending the Victron (Cerbo GX) through it’s UI / logging. As I find things that are missing or wrong, I’m rigging those messages 1st, with dummy / hard-coded values — just to see them come through and to work out the encoding / formatting of the message. 2nd, I’m wiring those messages up to real values as read from the Daly. 3rd, I’m cleaning up the code and refactoring it before I push it up to Github.

I’m going to try and build the microcontroller software in such a way that it could be easily adapted to pull from other types of BMS, run on other flavors of micro controller and perform the same general services. I’ve looked around, and all the things I’ve found (so far) are so hopelessly hard-coded with spaghetti logic. I’m pretty certain that it can be done better.
 
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.
Nice! I am very interested in this project, getting a Daly to talk to anything is an achievement, a Victron is fantastic.
 
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
View attachment 46920

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
Need help in decoding battery code!! Thanks in advance
 
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.

Have you already published the code you have on GitHub? I am very interested in it. Thanks.
 

Attachments

  • The list of compatibility between battery and Growatt Off-grid Inverter 2021.05 V1.9 (1) (1).pdf
    62.1 KB · Views: 98
  • PYLON LFP Battery communication protocol - RS485 V2.8 20161216.pdf
    226.6 KB · Views: 91
This is awesome work. What is the possibility of expanding this effort in order to enable the Dalys to talk to the Growatt inverters? It seems that emulating the Pylon BMS protocol would be the most practical way.

That's actually pretty doable. I'm playing around with pulling the data from multiple Daly's over the BLE (different packs, wired in parallel) and collating that to send to my victron quattro. Doing the same for PylonTech-speak shouldn't be all that difficult, and would still work with my inverter. Lemme look into this.
 
That's actually pretty doable. I'm playing around with pulling the data from multiple Daly's over the BLE (different packs, wired in parallel) and collating that to send to my victron quattro. Doing the same for PylonTech-speak shouldn't be all that difficult, and would still work with my inverter. Lemme look into this.
That would be awesome. I know there are many of us that could benefit from this. Attached a few docs that might be helpful.
 

Attachments

  • The list of compatibility between battery and Growatt Off-grid Inverter 2021.05 V1.9 (1) (1).pdf
    62.1 KB · Views: 52
  • PYLON LFP Battery communication protocol - RS485 V2.8 20161216.pdf
    226.6 KB · Views: 45
  • CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf
    250.4 KB · Views: 95
I did some diggin into the Charge/Discharge Control feature, i.e. options to enable/disable charging or discharging of a specific battery. As I have several batteries in parallel and hence need to do some grooming of each battery (e.g. to allow for balancing), this feature is crucial to let one battery charge to the limit in order to reach its balancing SoC level.
The Charge/Discharge status is shown on the "Data Monitoring" sheet, the controls however are on the "Engineering model" sheet.
 

Attachments

  • R16T-FK03 Monitor as Board2.gif
    R16T-FK03 Monitor as Board2.gif
    151.3 KB · Views: 76
  • R16T-FK03 Engineering Model Board2.gif
    R16T-FK03 Engineering Model Board2.gif
    124 KB · Views: 82
Command 0xDA appears to address the Charge Control feature with this command switching the MOSFET on
Code:
-> A540DA080100000000000000C8
<- A501DA0801xxxxxxxxxxxxxxyy
As it seems replies the BMS only with the first byte (0x01) as the following bytes are remains of the previous message.

To switch the charging MOSFET off again:
Code:
-> A540DA080000000000000000C7
<- A501DA0800xxxxxxxxxxxxxxyy

And here the example for board 2 (check out my post on changing board numbers). As you can see, the address from PC to BMS changes from 0x40 for Board1 to 0x41 for Board2. The BMS Board2 replies with 0x02 after 0xA5.

Switch Charge MOSFET On:
Code:
-> A541DA080100000000000000C9
<- A502DA0801xxxxxxxxxxxxxxyy
Switch Charge MOSFET Off:
Code:
-> A541DA080000000000000000C8
<- A502DA0800xxxxxxxxxxxxxxyy
 
Last edited:
Command 0xD9 appears to address the Discharge Control feature with this command switching the MOSFET on

Code:
-> A540D9080100000000000000C7
<- A501D90801xxxxxxxxxxxxxxyy
As it seems replies the BMS only with the first byte (0x01) as the following bytes are remains of the previous message.

To switch the charging MOSFET off again:
Code:
-> A540D9080000000000000000C6
<- A501D90800xxxxxxxxxxxxxxyy

And here the example for board 2. As you can see, the address from PC to BMS changes from 0x40 for Board1 to 0x41 for Board2. The BMS Board2 replies with 0x02 after 0xA5.

Switch Charge MOSFET On:
Code:
-> A541D9080100000000000000C8
<- A502D90801xxxxxxxxxxxxxxyy
Switch Charge MOSFET Off:
Code:
-> A541D9080000000000000000C7
<- A502D90800xxxxxxxxxxxxxxyy
 
Hi Guys,
you are all doing a great work! I'm currently trying just to read the temperature senor from the daly BMS but i didn't get it right. (Sorry i'm totaly new to the stuff).

I think i get it right how to adress the temperature sensor, but i'm not sure how to adress the correct handle. Maybe some of you guys can give me a short hint. (I'm trying now for the hole day but i think i'm missing some essential knowledge about bluetooth connections...).

gatttool -b XX:XX:XX:XX:XX:XX --char-write -a 0x0010 -n a5409408000000000000000081
--> i don't get a response

So i think it's only the wrong handle but i can not figure out which handle to write to...

I appreciate every response or hint what i'm doing wrong!
 
Last edited:
I don't know your tool. All I can say is, you are sending the right message. Check again if your tool sends the message as hex (and not ASCII).
 
I don't know your tool. All I can say is, you are sending the right message. Check again if your tool sends the message as hex (and not ASCII).
At the moment i try to do it directly in the terminal, from my raspberry with venus OS, later on i will sent the request using node-red (exec). So from the terminal should i use another syntaxe?
 
At the moment i try to do it directly in the terminal, from my raspberry with venus OS, later on i will sent the request using node-red (exec). So from the terminal should i use another syntaxe?
Again, I don’t know your tool! But start try monitoring what you send with another machine (e.g. a RS485-to-USB adaptor on a laptop). Most probably you’re sending not in Hex. To monitor I use hterm in Windows and I have to select “View in hex” not to see garbled stuff.
You can compare what you’re sending to something the Daly-software is sending!
 
Hey guy's,

i now figured out how to send the hex values correct via the "gatt" write command. it has to be formated like this:

0xa5 0x40 0x94 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x81

But now i still have a problem to adress the right handle. If i list all attributes i don't find a handle "0x0010" to write to. Have some else done this via "gatt"? These are all handles i get from the list command.

Primary Service (Handle 0x83cc)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service000a
00001801-0000-1000-8000-00805f9b34fb
Generic Attribute Profile
Characteristic (Handle 0x6dc8)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service000a/char000b
00002a05-0000-1000-8000-00805f9b34fb
Service Changed
Descriptor (Handle 0x0000)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service000a/char000b/desc000d
00002902-0000-1000-8000-00805f9b34fb
Client Characteristic Configuration
Primary Service (Handle 0x7568)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service000e
0000fff0-0000-1000-8000-00805f9b34fb
Unknown
Characteristic (Handle 0xb628)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service000e/char000f
0000fff2-0000-1000-8000-00805f9b34fb
Unknown
Characteristic (Handle 0xfec0)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service000e/char0011
0000fff1-0000-1000-8000-00805f9b34fb
Unknown
Descriptor (Handle 0x0000)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service000e/char0011/desc0013
00002902-0000-1000-8000-00805f9b34fb
Client Characteristic Configuration
Primary Service (Handle 0x8990)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0014
0000180a-0000-1000-8000-00805f9b34fb
Device Information
Characteristic (Handle 0x19e8)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0014/char0015
00002a29-0000-1000-8000-00805f9b34fb
Manufacturer Name String
Characteristic (Handle 0xfec0)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0014/char0017
00002a24-0000-1000-8000-00805f9b34fb
Model Number String
Characteristic (Handle 0xfec0)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0014/char0019
00002a25-0000-1000-8000-00805f9b34fb
Serial Number String
Characteristic (Handle 0xfec0)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0014/char001b
00002a27-0000-1000-8000-00805f9b34fb
Hardware Revision String
Characteristic (Handle 0xfec0)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0014/char001d
00002a26-0000-1000-8000-00805f9b34fb
Firmware Revision String
Characteristic (Handle 0xfec0)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0014/char001f
00002a28-0000-1000-8000-00805f9b34fb
Software Revision String
Characteristic (Handle 0xfec0)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0014/char0021
00002a23-0000-1000-8000-00805f9b34fb
System ID
Characteristic (Handle 0xfec0)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0014/char0023
00002a2a-0000-1000-8000-00805f9b34fb
IEEE 11073-20601 Regulatory Cert. Data List
Characteristic (Handle 0xfec0)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0014/char0025
00002a50-0000-1000-8000-00805f9b34fb
PnP ID
Primary Service (Handle 0x55a8)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service002b
f000ffc0-0451-4000-b000-000000000000
Vendor specific
Characteristic (Handle 0x6d80)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service002b/char002c
f000ffc1-0451-4000-b000-000000000000
Vendor specific
Descriptor (Handle 0x0000)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service002b/char002c/desc002e
00002902-0000-1000-8000-00805f9b34fb
Client Characteristic Configuration
Descriptor (Handle 0x0000)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service002b/char002c/desc002f
00002901-0000-1000-8000-00805f9b34fb
Characteristic User Description
Characteristic (Handle 0xfec0)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service002b/char0030
f000ffc2-0451-4000-b000-000000000000
Vendor specific
Descriptor (Handle 0x0000)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service002b/char0030/desc0032
00002902-0000-1000-8000-00805f9b34fb
Client Characteristic Configuration
Descriptor (Handle 0x0000)
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service002b/char0030/desc0033
00002901-0000-1000-8000-00805f9b34fb
Characteristic User Description
 
Are you trying this via Bluetooth? GATT is in my understanding a BT protocol profile
(https://learn.adafruit.com/introduction-to-bluetooth-low-energy/gatt: "GATT is an acronym for the Generic Attribute Profile, and it defines the way that two Bluetooth Low Energy devices transfer data back and forth ...")

I can't help you with that! I am using UART or RS485 ...
Yes im tryin it via bluetooth and the "gatt" protocol. As far as i understand it should be possible to get the values with "gatt", adressing the right service. So if i get it right the "a5409408000000000000000081" request is only for UART or RS485....

Nevertheless already many thanks for your support, now i'm already a little bit smarter.

I will further keep trying to figure out if it's possible somehow to get tha values via bluetooth.
 
Back
Top