Skip to content
View in the app

A better way to browse. Learn more.

Power Forum - Renewable Energy Discussion

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Narada NPFC Tian Power BMS protocol

Featured Replies

Hi all, I thought I would document my findings of the Tian Power BMS protocol used in my Narada 48NPFC100 battery whilst I was attempting to establish why it did not work with my Deye inverter. Of note is that the exact same battery model is known to use a Shinwa BMS as well, but mine uses a Tian Power one, specifically TP-ND1530.

Firstly I wanted to thank @shanghailoz for documenting the same thing here for the Revov batteries, which also use a Tian Power BMS, without which I would have struggled a lot more. Also thank you @zivva for pointing me in the right direction for the BMS manufacturer.

I also peeked at the code of the BMS software to decipher how certain fields are calculated.

The protocol follows.

 

Preamble

Firstly, all data sent appears to have a header, body and CRC check. For example:

7e 01 01 00 fe 0d

The first byte is either 7c or 7e according to the software. In my case it is always 7e.

The second byte is the DIP switch setting on the battery and corresponds to the following in the software:

image.png.5bfa076f6f2d4d684c6e235c855ce4b6.png

The third byte is the function code, which you will see several examples of below, such as "read data", "read BMS time", "read BMS version", "read serial number".

The fourth byte is the length of the payload supplied as part of the function call - most "read" type functions will not include a payload and this parameter will be 00, but the "write" functions would have a payload. If there is a payload, it would follow this byte.

The last two bytes are the CRC check.

The response is structured in a similar way:

7e 01 01 56 ... f2 0d

The first three bytes are an echo of what the request was, the fourth byte indicates the response length followed by the response bytes, and finally we have the CRC check.

 

Function 1 - Read data

7e 01 01 00 fe 0d

This allows you to read almost everything that is displayed on the first tab of the BMS software.

The response will look as follows:

7e 01 01 56 01 0f 0c e1 0c e2 0c e2 0c e3 0c e3
0c e5 0c e5 0c e4 0c e4 0c e4 0c e2 0c e2 0c e2
0c e3 0c e5 02 01 75 30 03 01 16 74 04 01 27 10
05 06 00 44 00 45 00 45 00 44 40 46 20 45 06 05
00 00 00 00 00 00 00 00 00 00 07 01 00 81 08 01
13 54 09 01 27 10 0a 01 00 00 f2 0d

The response body for this function is a data map structured as follows: data entry index, number of high/low byte pairs, the high/low byte pairs.

Data 1 - Cell count and cell voltages

01 0f 0c e1 0c e2 0c e2 0c e3 0c e3 0c e5 0c e5 0c e4 0c e4 0c e4 0c e2 0c e2 0c e2 0c e3 0c e5

The second byte is the number of cells - this is 15 for a 15 cell battery like mine or 16 cells for Revov. The individual cell voltages follow as high/low pairs which need to be divided by 1000, for example, 0ce1 maps to 3297 which means 3.297V. Since my battery has 15 cells, there would be 15 individual cell voltages.

Data 2 - Current

02 01 75 30

This is a tricky one. 7530 hex maps to 30000 decimal. The BMS software works it out as:

current_in_amps = (30000 - value_of_item_2) / 100

This item therefore has an offset of 30000 and a scaling factor of 0.01. So when the charge current is 0, the BMS will return 30000, therefore (30000 - 30000) / 100 = 0 amps. When the charge current is 10 amps, the BMS would return 29000, therefore (30000 - 29000) / 100 = 10 amps. When the charge current is -10 amps, the BMS would return 31000, therefore (30000 - 31000) / 100 = -10 amps. Furthermore, charge/discharge time remaining is calculated based on max battery capacity in amps divided by current in amps.

Data 3 - Remaining capacity

03 01 16 74

This value needs to be divided by 100 to get Ah. In this case I have 1674 hex or 5748 decimal, so 57.48Ah left of my 100Ah battery, therefore the SOC is 57.48%.

Data 4 - Full capacity

04 01 27 10

Divide by 100 to get full battery capacity in Ah. 2710 in hex is 10000 in decimal, so I have a 100Ah battery.

Data 5 - Temperatures

05 06 00 44 00 45 00 45 00 44 40 46 20 45

In my case there are 6 temperatures corresponding to the following screenshot:

image.png.a3819eaf342c03a3b421c9da01608e41.png

In the Revovs the number of temperatures listed are different. The last two appear to be important ones.

Each temperature value is calculated as:

temp_value = (bms_temp_value & 0xFF) - 50

Data 6 - Alarm bits

06 05 00 00 00 00 00 00 00 00 00 00

These can be mapped to (a) very specific alarm code(s). I can get these if there is interest.

Data 7 - Cycles

07 01 00 81

This represents the number of cycles of the battery, so 129 in my case.

Data 8 - Voltage

08 01 13 54

The total voltage of the battery. The number needs to be divided by 100, so 49.48V in my case.

Data 9 - State of Health

09 01 27 10

The SOH of the battery. Needs to be divided by 100, so 100% in my case.

Data 10 - ALM bytes

0a 01 00 00

This corresponds to the ALM light on the battery and what it means.

 

Function 67 - Read protection parameters

Request:

7e 01 43 00 fe 0d

Response:

7e 01 43 94 00 02 0e 10 01 02 03 e8 02 02 0d ac
03 02 0a f0 04 02 03 e8 05 02 0c 1c 06 02 15 4a
07 02 03 e8 08 02 14 c8 09 02 11 94 0a 02 03 e8
0b 02 13 88 0c 02 1f 40 0d 02 03 e8 0e 02 1b 58
0f 02 1f 40 10 02 03 e8 11 02 1b 58 12 02 00 69
13 02 0f a0 14 02 00 5f 15 02 00 32 16 02 0f a0
17 02 00 3c 18 02 00 69 19 02 0f a0 1a 02 00 5f
1b 02 00 32 1c 02 0f a0 1d 02 00 3c 1e 02 00 8c
1f 02 0f a0 20 02 00 87 21 02 00 0a 22 02 00 0f
23 02 03 20 24 02 01 f4 de 0d

This is another data map which ultimately gets translated into the following, with the necessary scaling factors applied:

{
  "cell_ov_start": 3.6,
  "cell_ov_delay": 1000,
  "cell_ov_stop": 3.5,
  "cell_uv_start": 2.8,
  "cell_uv_delay": 1000,
  "cell_uv_stop": 3.1,
  "pack_ov_start": 54.5,
  "pack_ov_delay": 1000,
  "pack_ov_stop": 53.2,
  "pack_uv_start": 45.0,
  "pack_uv_delay": 1000,
  "pack_uv_stop": 50.0,
  "charge_oc_start": 80.0,
  "charge_oc_delay": 1000,
  "charge_oc_stop": 70.0,
  "discharge_oc_start": 80.0,
  "discharge_oc_delay": 1000,
  "discharge_oc_stop": 70.0,
  "cell_ot_start": 55,
  "cell_ot_delay": 4000,
  "cell_ot_stop": 45,
  "cell_ut_start": 0,
  "cell_ut_delay": 4000,
  "cell_ut_stop": 10,
  "env_ot_start": 55,
  "env_ot_delay": 4000,
  "env_ot_stop": 45,
  "env_ut_start": 0,
  "env_ut_delay": 4000,
  "env_ut_stop": 10,
  "mos_ot_start": 90,
  "mos_ot_delay": 4000,
  "mos_ot_stop": 85,
  "capacity_low_start": 10,
  "capacity_low_stop": 15,
  "volt_diff_start": 800,
  "volt_diff_stop": 500
}

 

Function 51 - Read BMS version

The request is as follows:

7e 01 33 00 fe 0d

The response body is a string, such as:

7e 01 33 18 54 50 2d 4e 44 31 35 33 30 2d 31 35 53 31 30 30 41 2d 56 31 2e 30 2e 30 2e 0d

Which in my case maps to: TP-ND1530-15S100A-V1.0.0

 

Function 66 - Read PCB barcode

Request:

7e 01 42 00 fc 0d

Response body is a string as above.

 

Function 220 - Read serial number

Request:

7e 01 dc 03 06 00 00 c2 0d

Response body is a string as above.

 

Function 69 - Read BMS time

Request:

7e 01 45 00 fe 0d

Response:

7e 01 45 06 16 07 08 14 3b 16 48 0d

To convert the above to a valid date time, prepend "20" and then concatenate the rest of the bytes which represent yy, MM, dd, hh, mm, ss, so in the above example: 2022-07-08 20:59:22.

 

There are many "write"-type functions too but I would be wary of using those.

I hope the above is useful to someone. I will post more useful ones if I find any.

Edited by SolarConvert
Clarified charge current and temperatures, added protection data

  • 1 month later...
  • Author

Thanks @shanghailoz

One command that the Deye inverter does issue is the following, but I have yet to decipher what this one does.

Command:

7e 01 43 00 fe 0d

Response:

7e 01 43 94 00 02 0e 10 01 02 03 e8 02 02 0d ac
03 02 0a f0 04 02 03 e8 05 02 0c 1c 06 02 15 4a
07 02 03 e8 08 02 14 c8 09 02 11 94 0a 02 03 e8
0b 02 13 88 0c 02 1f 40 0d 02 03 e8 0e 02 1b 58
0f 02 1f 40 10 02 03 e8 11 02 1b 58 12 02 00 69
13 02 0f a0 14 02 00 5f 15 02 00 32 16 02 0f a0
17 02 00 3c 18 02 00 69 19 02 0f a0 1a 02 00 5f
1b 02 00 32 1c 02 0f a0 1d 02 00 3c 1e 02 00 8c
1f 02 0f a0 20 02 00 87 21 02 00 0a 22 02 00 0f
23 02 03 20 24 02 01 f4 de 0d                  

I can see the data map but do not yet know what each item means.

Reformatted:

7e 01 43 94
00 02 0e 10
01 02 03 e8
02 02 0d ac
03 02 0a f0
04 02 03 e8
05 02 0c 1c
06 02 15 4a
07 02 03 e8
08 02 14 c8
09 02 11 94
0a 02 03 e8
0b 02 13 88
0c 02 1f 40
0d 02 03 e8
0e 02 1b 58
0f 02 1f 40
10 02 03 e8
11 02 1b 58
12 02 00 69
13 02 0f a0
14 02 00 5f
15 02 00 32
16 02 0f a0
17 02 00 3c
18 02 00 69
19 02 0f a0
1a 02 00 5f
1b 02 00 32
1c 02 0f a0
1d 02 00 3c
1e 02 00 8c
1f 02 0f a0
20 02 00 87
21 02 00 0a
22 02 00 0f
23 02 03 20
24 02 01 f4
de 0d

Since this is the first call that the inverter makes to the BMS, these must be the parameters that the inverter expects back from the BMS.
When I have time I will try to dig deeper, just posting it here for now.

  • 4 weeks later...

Hey, can you please elaborate on that current calculation with an example .. its not making sense to me ?

I assume this could be negative (discharging) or positive (charging) ? But I cant follow your logic ?

Thx

  • Author

So to get the current in amps, you need the following formula

Current = (30000 - value_of_item_2) / 100

30000 is a static value from which you need to subtract the value of item 2 returned by the BMS. I assume that Tian Power did this so that only unsigned values are returned by the BMS when queried, therefore some items not only have a scaling factor but also an offset to subtract from, if that makes sense?

I'll update the first post with the latest info I have - there is a lot of new info.

  • Author
On 2022/08/19 at 3:43 PM, SolarConvert said:

One command that the Deye inverter does issue is the following, but I have yet to decipher what this one does.

Command:

7e 01 43 00 fe 0d

I have managed to decode the response and have amended my first post to include BMS protection data.

3 hours ago, SolarConvert said:

So to get the current in amps, you need the following formula

Current = (30000 - value_of_item_2) / 100

30000 is a static value from which you need to subtract the value of item 2 returned by the BMS. I assume that Tian Power did this so that only unsigned values are returned by the BMS when queried, therefore some items not only have a scaling factor but also an offset to subtract from, if that makes sense?

I'll update the first post with the latest info I have - there is a lot of new info.

Awesome, got it now .. thx

  • 3 months later...

I am trying to send command 7e 01 01 00 fe 0d to BMS in c+ programming language. But all that sin received was silence. I have reached the end. Hope to get help from you

  • 1 month later...
On 2022/07/12 at 3:08 AM, SolarConvert said:

Hi all, I thought I would document my findings of the Tian Power BMS protocol used in my Narada 48NPFC100 battery whilst I was attempting to establish why it did not work with my Deye inverter. Of note is that the exact same battery model is known to use a Shinwa BMS as well, but mine uses a Tian Power one, specifically TP-ND1530.

Firstly I wanted to thank @shanghailoz for documenting the same thing here for the Revov batteries, which also use a Tian Power BMS, without which I would have struggled a lot more. Also thank you @zivva for pointing me in the right direction for the BMS manufacturer.

I also peeked at the code of the BMS software to decipher how certain fields are calculated.

The protocol follows.

 

Preamble

Firstly, all data sent appears to have a header, body and CRC check. For example:

7e 01 01 00 fe 0d

The first byte is either 7c or 7e according to the software. In my case it is always 7e.

The second byte is the DIP switch setting on the battery and corresponds to the following in the software:

image.png.5bfa076f6f2d4d684c6e235c855ce4b6.png

The third byte is the function code, which you will see several examples of below, such as "read data", "read BMS time", "read BMS version", "read serial number".

The fourth byte is the length of the payload supplied as part of the function call - most "read" type functions will not include a payload and this parameter will be 00, but the "write" functions would have a payload. If there is a payload, it would follow this byte.

The last two bytes are the CRC check.

The response is structured in a similar way:

7e 01 01 56 ... f2 0d

The first three bytes are an echo of what the request was, the fourth byte indicates the response length followed by the response bytes, and finally we have the CRC check.

 

Function 1 - Read data

7e 01 01 00 fe 0d

This allows you to read almost everything that is displayed on the first tab of the BMS software.

The response will look as follows:

7e 01 01 56 01 0f 0c e1 0c e2 0c e2 0c e3 0c e3
0c e5 0c e5 0c e4 0c e4 0c e4 0c e2 0c e2 0c e2
0c e3 0c e5 02 01 75 30 03 01 16 74 04 01 27 10
05 06 00 44 00 45 00 45 00 44 40 46 20 45 06 05
00 00 00 00 00 00 00 00 00 00 07 01 00 81 08 01
13 54 09 01 27 10 0a 01 00 00 f2 0d

The response body for this function is a data map structured as follows: data entry index, number of high/low byte pairs, the high/low byte pairs.

Data 1 - Cell count and cell voltages

01 0f 0c e1 0c e2 0c e2 0c e3 0c e3 0c e5 0c e5 0c e4 0c e4 0c e4 0c e2 0c e2 0c e2 0c e3 0c e5

The second byte is the number of cells - this is 15 for a 15 cell battery like mine or 16 cells for Revov. The individual cell voltages follow as high/low pairs which need to be divided by 1000, for example, 0ce1 maps to 3297 which means 3.297V. Since my battery has 15 cells, there would be 15 individual cell voltages.

Data 2 - Current

02 01 75 30

This is a tricky one. 7530 hex maps to 30000 decimal. The BMS software works it out as:

current_in_amps = (30000 - value_of_item_2) / 100

This item therefore has an offset of 30000 and a scaling factor of 0.01. So when the charge current is 0, the BMS will return 30000, therefore (30000 - 30000) / 100 = 0 amps. When the charge current is 10 amps, the BMS would return 29000, therefore (30000 - 29000) / 100 = 10 amps. When the charge current is -10 amps, the BMS would return 31000, therefore (30000 - 31000) / 100 = -10 amps. Furthermore, charge/discharge time remaining is calculated based on max battery capacity in amps divided by current in amps.

Data 3 - Remaining capacity

03 01 16 74

This value needs to be divided by 100 to get Ah. In this case I have 1674 hex or 5748 decimal, so 57.48Ah left of my 100Ah battery, therefore the SOC is 57.48%.

Data 4 - Full capacity

04 01 27 10

Divide by 100 to get full battery capacity in Ah. 2710 in hex is 10000 in decimal, so I have a 100Ah battery.

Data 5 - Temperatures

05 06 00 44 00 45 00 45 00 44 40 46 20 45

In my case there are 6 temperatures corresponding to the following screenshot:

image.png.a3819eaf342c03a3b421c9da01608e41.png

In the Revovs the number of temperatures listed are different. The last two appear to be important ones.

Each temperature value is calculated as:

temp_value = (bms_temp_value & 0xFF) - 50

Data 6 - Alarm bits

06 05 00 00 00 00 00 00 00 00 00 00

These can be mapped to (a) very specific alarm code(s). I can get these if there is interest.

Data 7 - Cycles

07 01 00 81

This represents the number of cycles of the battery, so 129 in my case.

Data 8 - Voltage

08 01 13 54

The total voltage of the battery. The number needs to be divided by 100, so 49.48V in my case.

Data 9 - State of Health

09 01 27 10

The SOH of the battery. Needs to be divided by 100, so 100% in my case.

Data 10 - ALM bytes

0a 01 00 00

This corresponds to the ALM light on the battery and what it means.

 

Function 67 - Read protection parameters

Request:

7e 01 43 00 fe 0d

Response:

7e 01 43 94 00 02 0e 10 01 02 03 e8 02 02 0d ac
03 02 0a f0 04 02 03 e8 05 02 0c 1c 06 02 15 4a
07 02 03 e8 08 02 14 c8 09 02 11 94 0a 02 03 e8
0b 02 13 88 0c 02 1f 40 0d 02 03 e8 0e 02 1b 58
0f 02 1f 40 10 02 03 e8 11 02 1b 58 12 02 00 69
13 02 0f a0 14 02 00 5f 15 02 00 32 16 02 0f a0
17 02 00 3c 18 02 00 69 19 02 0f a0 1a 02 00 5f
1b 02 00 32 1c 02 0f a0 1d 02 00 3c 1e 02 00 8c
1f 02 0f a0 20 02 00 87 21 02 00 0a 22 02 00 0f
23 02 03 20 24 02 01 f4 de 0d

This is another data map which ultimately gets translated into the following, with the necessary scaling factors applied:

{
  "cell_ov_start": 3.6,
  "cell_ov_delay": 1000,
  "cell_ov_stop": 3.5,
  "cell_uv_start": 2.8,
  "cell_uv_delay": 1000,
  "cell_uv_stop": 3.1,
  "pack_ov_start": 54.5,
  "pack_ov_delay": 1000,
  "pack_ov_stop": 53.2,
  "pack_uv_start": 45.0,
  "pack_uv_delay": 1000,
  "pack_uv_stop": 50.0,
  "charge_oc_start": 80.0,
  "charge_oc_delay": 1000,
  "charge_oc_stop": 70.0,
  "discharge_oc_start": 80.0,
  "discharge_oc_delay": 1000,
  "discharge_oc_stop": 70.0,
  "cell_ot_start": 55,
  "cell_ot_delay": 4000,
  "cell_ot_stop": 45,
  "cell_ut_start": 0,
  "cell_ut_delay": 4000,
  "cell_ut_stop": 10,
  "env_ot_start": 55,
  "env_ot_delay": 4000,
  "env_ot_stop": 45,
  "env_ut_start": 0,
  "env_ut_delay": 4000,
  "env_ut_stop": 10,
  "mos_ot_start": 90,
  "mos_ot_delay": 4000,
  "mos_ot_stop": 85,
  "capacity_low_start": 10,
  "capacity_low_stop": 15,
  "volt_diff_start": 800,
  "volt_diff_stop": 500
}

 

Function 51 - Read BMS version

The request is as follows:

7e 01 33 00 fe 0d

The response body is a string, such as:

7e 01 33 18 54 50 2d 4e 44 31 35 33 30 2d 31 35 53 31 30 30 41 2d 56 31 2e 30 2e 30 2e 0d

Which in my case maps to: TP-ND1530-15S100A-V1.0.0

 

Function 66 - Read PCB barcode

Request:

7e 01 42 00 fc 0d

Response body is a string as above.

 

Function 220 - Read serial number

Request:

7e 01 dc 03 06 00 00 c2 0d

Response body is a string as above.

 

Function 69 - Read BMS time

Request:

7e 01 45 00 fe 0d

Response:

7e 01 45 06 16 07 08 14 3b 16 48 0d

To convert the above to a valid date time, prepend "20" and then concatenate the rest of the bytes which represent yy, MM, dd, hh, mm, ss, so in the above example: 2022-07-08 20:59:22.

 

There are many "write"-type functions too but I would be wary of using those.

I hope the above is useful to someone. I will post more useful ones if I find any.

I want Alarm code message. from Alarm code byte and Alarm bit.  thank you

  • 1 month later...

Thanks so much for this post @SolarConvert – this was key in getting prometheus hooked up to my system. Have you (or anybody else here) been able to reverse engineer the CRC or checksum algorithm? I've spent hours on this and have been totally defeated. Right now I have a hardcoded map of requests I care about for each battery address, which I just copied from the software... but it's a very dirty way to go about things.

  • 1 year later...
	public static byte[] gabyCRCHi = new byte[256]
	{
		0, 193, 129, 64, 1, 192, 128, 65, 1, 192,
		128, 65, 0, 193, 129, 64, 1, 192, 128, 65,
		0, 193, 129, 64, 0, 193, 129, 64, 1, 192,
		128, 65, 1, 192, 128, 65, 0, 193, 129, 64,
		0, 193, 129, 64, 1, 192, 128, 65, 0, 193,
		129, 64, 1, 192, 128, 65, 1, 192, 128, 65,
		0, 193, 129, 64, 1, 192, 128, 65, 0, 193,
		129, 64, 0, 193, 129, 64, 1, 192, 128, 65,
		0, 193, 129, 64, 1, 192, 128, 65, 1, 192,
		128, 65, 0, 193, 129, 64, 0, 193, 129, 64,
		1, 192, 128, 65, 1, 192, 128, 65, 0, 193,
		129, 64, 1, 192, 128, 65, 0, 193, 129, 64,
		0, 193, 129, 64, 1, 192, 128, 65, 1, 192,
		128, 65, 0, 193, 129, 64, 0, 193, 129, 64,
		1, 192, 128, 65, 0, 193, 129, 64, 1, 192,
		128, 65, 1, 192, 128, 65, 0, 193, 129, 64,
		0, 193, 129, 64, 1, 192, 128, 65, 1, 192,
		128, 65, 0, 193, 129, 64, 1, 192, 128, 65,
		0, 193, 129, 64, 0, 193, 129, 64, 1, 192,
		128, 65, 0, 193, 129, 64, 1, 192, 128, 65,
		1, 192, 128, 65, 0, 193, 129, 64, 1, 192,
		128, 65, 0, 193, 129, 64, 0, 193, 129, 64,
		1, 192, 128, 65, 1, 192, 128, 65, 0, 193,
		129, 64, 0, 193, 129, 64, 1, 192, 128, 65,
		0, 193, 129, 64, 1, 192, 128, 65, 1, 192,
		128, 65, 0, 193, 129, 64
	};

	public static byte[] gabyCRCLo = new byte[256]
	{
		0, 192, 193, 1, 195, 3, 2, 194, 198, 6,
		7, 199, 5, 197, 196, 4, 204, 12, 13, 205,
		15, 207, 206, 14, 10, 202, 203, 11, 201, 9,
		8, 200, 216, 24, 25, 217, 27, 219, 218, 26,
		30, 222, 223, 31, 221, 29, 28, 220, 20, 212,
		213, 21, 215, 23, 22, 214, 210, 18, 19, 211,
		17, 209, 208, 16, 240, 48, 49, 241, 51, 243,
		242, 50, 54, 246, 247, 55, 245, 53, 52, 244,
		60, 252, 253, 61, 255, 63, 62, 254, 250, 58,
		59, 251, 57, 249, 248, 56, 40, 232, 233, 41,
		235, 43, 42, 234, 238, 46, 47, 239, 45, 237,
		236, 44, 228, 36, 37, 229, 39, 231, 230, 38,
		34, 226, 227, 35, 225, 33, 32, 224, 160, 96,
		97, 161, 99, 163, 162, 98, 102, 166, 167, 103,
		165, 101, 100, 164, 108, 172, 173, 109, 175, 111,
		110, 174, 170, 106, 107, 171, 105, 169, 168, 104,
		120, 184, 185, 121, 187, 123, 122, 186, 190, 126,
		127, 191, 125, 189, 188, 124, 180, 116, 117, 181,
		119, 183, 182, 118, 114, 178, 179, 115, 177, 113,
		112, 176, 80, 144, 145, 81, 147, 83, 82, 146,
		150, 86, 87, 151, 85, 149, 148, 84, 156, 92,
		93, 157, 95, 159, 158, 94, 90, 154, 155, 91,
		153, 89, 88, 152, 136, 72, 73, 137, 75, 139,
		138, 74, 78, 142, 143, 79, 141, 77, 76, 140,
		68, 132, 133, 69, 135, 71, 70, 134, 130, 66,
		67, 131, 65, 129, 128, 64
	};	
	private byte check(byte[] buf, byte len)
	{
		byte b = 0;
		byte b2 = 0;
		int num = 0;
		for (b = 0; b < len; b++)
		{
			b2 ^= buf[b];
			num += buf[b];
		}
		return (byte)((uint)(b2 ^ num) & 0xFFu);
	}

	private ushort checkcrc(byte[] buf, int len)
	{
		byte b = byte.MaxValue;
		byte b2 = byte.MaxValue;
		for (int num = len; num > 0; num--)
		{
			byte b3 = (byte)(b ^ buf[len - num]);
			b = (byte)(b2 ^ gabyCRCHi[b3]);
			b2 = gabyCRCLo[b3];
		}
		ushort num2 = b;
		num2 <<= 8;
		return (ushort)(num2 + b2);
	}

 

Delphi

function TShotoBMS.CheckCRC(Buf: PByteArray; Len: Integer): Word;
var
  b, b2, b3: Byte;
  num2: Word;
  i: Integer;
begin
  b   := $FF;
  b2  := $FF;
  for i := Len downto 1 do
  begin
    b3  := b xor Buf[Len - i];
    b   := b2 xor gabyCRCHi[b3];
    b2  := gabyCRCLo[b3];
  end;
  num2 := b;
  num2 := num2 shl 8;
  Result := num2 + b2;
end;

function TShotoBMS.Check(Buf: PByteArray; Len: Byte): Byte;
var
  b, b2: Byte;
  num: Integer;
begin
  b2 := 0;
  num := 0;
  for b := 0 to Len - 1 do
  begin
    b2 := b2 xor Buf^[b];
    num := num + Buf^[b];
  end;
  Result := Byte((b2 xor num) and $FF);
end;

 

  • 2 months later...

Has anybody managed to find out how CRC is calculated here?
Keep in mind that the last two algorithms posted by@Diego Carrilho are not for this particular protocol,

CRC here is only 1 byte, which is located before 0D

Function 0x01

Read Data

Start of the message always starts with 7C

DIP switch setting*

Function

 

CRC-check

End of the message

Reply

7C

00

01

00

00

0D

7C00017401100CFB0CF90CFB0CFA0CFA0CFA0CFA0CFA0CFA0CFA0CFB0CFB0CFA0CFA0CFA0CFB020175300301233F0401298705060048004800480048404B2049060600000000000000000000000007010001080114C3090127100A0100000B020000006F1002000015FF0C02000000001101150C1201404BEE0D

7C

01

01

00

02

0D

7C 01 01 74 01 10 0D 01 0C FF 0D 01 0D 00 0D 00 0D 00 0D 01 0D 01 0D 00 0D 00 0D 01 0D 00 0D 01 0D 00 0D 01 0D 02 02 01 75 30 03 01 24 19 04 01 29 87 05 06 00 47 00 47 00 47 00 47 40 49 20 48 06 06 00 00 00 00 00 00 00 00 00 00 00 00 07 01 00 01 08 01 14 CD 09 01 27 10 0A 01 00 00 0B 02 00 00 00 6F 10 02 00 00 15 FF 0C 02 00 00 00 00 11 01 15 17 12 01 40 49 4A 0D

7C

02

01

00

00

0D

7C02017401100CFB0CF90CFB0CF90CFA0CFA0CFA0CFA0CFA0CFA0CFB0CFB0CFA0CFA0CFA0CFB020175300301233F0401298705060048004800480048404A2049060600000000000000000000000007010001080114C3090127100A0100000B020000006F1002000015FF0C02000000001101150C1201404AEC0D

7C

03

01

00

FE

0D

7C03017401100CFB0CF90CFA0CFA0CFA0CFA0CFA0CFA0CFA0CF90CFB0CFB0CFA0CF90CFA0CFB02017530030123310401298705060048004800480048404A2049060600000000000000000000000007010001080114C3090127100A0100000B020000006F1002000015FF0C02000000001101150C1201404A120D

7C

04

01

00

F8

0D

7C04017401100CFB0CF90CFA0CFA0CFA0CFA0CFA0CFA0CFA0CFA0CFB0CFA0CFA0CFA0CFA0CFB02017530030123300401298705060048004800480048404A2049060600000000000000000000000007010001080114C3090127100A0100000B020000006F1002000015FF0C02000000001101150C1201404A140D

7C

05

01

00

FA

0D

7C05017401100CFB0CF90CFA0CFA0CFA0CFA0CFA0CF90CFA0CF90CFB0CFA0CFA0CFA0CFA0CFB02017530030123300401298705060048004800480048404A2049060600000000000000000000000007010001080114C3090127100A0100000B020000006F1002000015FF0C02000000001101150A1201404A100D

 

Function 0x06

Start of the message always starts with 7C

DIP switch setting*

Function

 

CRC-check

End of the message

Reply

7C

00

06

00

00

0D

7C00060400000F0AE40D

7C

01

06

00

02

0D

7C01060400000EA6EC0D

7C

02

06

00

FC

0D

7C02060400000F0AD80D

7C

03

06

00

FC

0D

7C03060400000F0ADA0D

7C

04

06

00

F8

0D

7C04060400000F0ADC0D

7C

05

06

00

F8

0D

7C05060400000F0ADA0D

 Can you help me?

1 hour ago, Kaveh said:

Has anybody managed to find out how CRC is calculated here?
Keep in mind that the last two algorithms posted by@Diego Carrilho are not for this particular protocol,

CRC here is only 1 byte, which is located before 0D

Function 0x01

 

Read Data

 

Start of the message always starts with 7C

 

DIP switch setting*

 

Function

 

 

 

CRC-check

 

End of the message

 

Reply

 

7C

 

00

 

01

 

00

 

00

 

0D

 

7C00017401100CFB0CF90CFB0CFA0CFA0CFA0CFA0CFA0CFA0CFA0CFB0CFB0CFA0CFA0CFA0CFB020175300301233F0401298705060048004800480048404B2049060600000000000000000000000007010001080114C3090127100A0100000B020000006F1002000015FF0C02000000001101150C1201404BEE0D

 

7C

 

01

 

01

 

00

 

02

 

0D

 

7C 01 01 74 01 10 0D 01 0C FF 0D 01 0D 00 0D 00 0D 00 0D 01 0D 01 0D 00 0D 00 0D 01 0D 00 0D 01 0D 00 0D 01 0D 02 02 01 75 30 03 01 24 19 04 01 29 87 05 06 00 47 00 47 00 47 00 47 40 49 20 48 06 06 00 00 00 00 00 00 00 00 00 00 00 00 07 01 00 01 08 01 14 CD 09 01 27 10 0A 01 00 00 0B 02 00 00 00 6F 10 02 00 00 15 FF 0C 02 00 00 00 00 11 01 15 17 12 01 40 49 4A 0D

 

7C

 

02

 

01

 

00

 

00

 

0D

 

7C02017401100CFB0CF90CFB0CF90CFA0CFA0CFA0CFA0CFA0CFA0CFB0CFB0CFA0CFA0CFA0CFB020175300301233F0401298705060048004800480048404A2049060600000000000000000000000007010001080114C3090127100A0100000B020000006F1002000015FF0C02000000001101150C1201404AEC0D

 

7C

 

03

 

01

 

00

 

FE

 

0D

 

7C03017401100CFB0CF90CFA0CFA0CFA0CFA0CFA0CFA0CFA0CF90CFB0CFB0CFA0CF90CFA0CFB02017530030123310401298705060048004800480048404A2049060600000000000000000000000007010001080114C3090127100A0100000B020000006F1002000015FF0C02000000001101150C1201404A120D

 

7C

 

04

 

01

 

00

 

F8

 

0D

 

7C04017401100CFB0CF90CFA0CFA0CFA0CFA0CFA0CFA0CFA0CFA0CFB0CFA0CFA0CFA0CFA0CFB02017530030123300401298705060048004800480048404A2049060600000000000000000000000007010001080114C3090127100A0100000B020000006F1002000015FF0C02000000001101150C1201404A140D

 

7C

 

05

 

01

 

00

 

FA

 

0D

 

7C05017401100CFB0CF90CFA0CFA0CFA0CFA0CFA0CF90CFA0CF90CFB0CFA0CFA0CFA0CFA0CFB02017530030123300401298705060048004800480048404A2049060600000000000000000000000007010001080114C3090127100A0100000B020000006F1002000015FF0C02000000001101150A1201404A100D

 

 

 

Function 0x06

 

 

 

Start of the message always starts with 7C

 

DIP switch setting*

 

Function

 

 

 

CRC-check

 

End of the message

 

Reply

 

7C

 

00

 

06

 

00

 

00

 

0D

 

7C00060400000F0AE40D

 

7C

 

01

 

06

 

00

 

02

 

0D

 

7C01060400000EA6EC0D

 

7C

 

02

 

06

 

00

 

FC

 

0D

 

7C02060400000F0AD80D

 

7C

 

03

 

06

 

00

 

FC

 

0D

 

7C03060400000F0ADA0D

 

7C

 

04

 

06

 

00

 

F8

 

0D

 

7C04060400000F0ADC0D

 

7C

 

05

 

06

 

00

 

F8

 

0D

 

7C05060400000F0ADA0D

 

 

 Can you help me?

for sending data , you shift all the bytes thru the checksum below starting from the first 0x7C/0x7E character to the end of payload. You then append the checksum byte as well as an 0x0D  character.

uint8_t nerada_485_checksum(uint8_t *buf, uint8_t len)
{
    uint8_t b = 0;
    uint8_t b2 = 0;
    int num = 0;
    for (b = 0; b < len; b++)
    {
        b2 ^= buf[b];
        num += buf[b];
    }
    return (uint8_t)((uint)(b2 ^ num) & 0xFFu);

When receiving data , you shift all the bytes thru the checksum starting at the first character up to and excluding the checksum byte and the 0x0D character. You then compare your calculated checksum against the received check sum , and also check that the last character is indeed 0x0D.

5 hours ago, BritishRacingGreen said:

for sending data , you shift all the bytes thru the checksum below starting from the first 0x7C/0x7E character to the end of payload. You then append the checksum byte as well as an 0x0D  character.

uint8_t nerada_485_checksum(uint8_t *buf, uint8_t len)
{
    uint8_t b = 0;
    uint8_t b2 = 0;
    int num = 0;
    for (b = 0; b < len; b++)
    {
        b2 ^= buf[b];
        num += buf[b];
    }
    return (uint8_t)((uint)(b2 ^ num) & 0xFFu);

When receiving data , you shift all the bytes thru the checksum starting at the first character up to and excluding the checksum byte and the 0x0D character. You then compare your calculated checksum against the received check sum , and also check that the last character is indeed 0x0D.

Thank you for providing this, its been a while I am thinking about this!

5 hours ago, BritishRacingGreen said:

I have a question regarding the Nerada protocol .  Does anyone know the alarm bit details as supplied  in subcommand  6 and subcommand 10 of main main command 1 ( read analogue data) ?

Not sure what command are you talking about, can you explain a bit more?

On 2022/07/11 at 10:08 PM, SolarConvert said:

Function 67 - Read protection parameters

by the way, this command is worng, 0x43 is to read alarm Params, not protection parameters

On 2025/01/22 at 10:11 AM, BritishRacingGreen said:

I have a question regarding the Nerada protocol .  Does anyone know the alarm bit details as supplied  in subcommand  6 and subcommand 10 of main main command 1 ( read analogue data) ?

Now I got what you mean,
here is what you see more in detail
I do not have an answer to your question!

7C 02 01 74
01 10      0CFA 0CF8 0CFA 0CF9 0CF9 0CF9 0CFA 0CF9 0CF9 0CF9 0CFA 0CFA 0CF9 0CF9 0CF9 0CFB
02 01      7530
03 01      22E5
04 01      2987
05 06      0048 0048 0048 0048 404A 2049
06 06      0000 0000 0000 0000 0000 0000
07 01      0001
08 01      14C2  
09 01      2710
0A 01      0000
0B 02      0000 006F
10 02      0000 15FF
0C 02      0000 0000
11 01      150C
12 01      404A
9E
0D

7C 01 01 74
01 10 0CF9 0CF8 0CFA 0CFA 0CFA 0CFA 0CFA 0CFA 0CF9 0CFA 0CFA 0CFA 0CFA 0CF9 0CF9 0CFA
02 01 7530
03 01 2383
04 01 299F
05 06 0047 0047 0047 0047 404A 2048
06 06 0000 0000 0000 0000 0000 0000
07 01 0001
08 01 14C2
09 01 2710
0A 01 0000
0B 02 0000 006E
10 02 0000 15CC
0C 02 0000 0000
11 01 14D3
12 01 404A
4A
0D
Does anybody know why the order of these data are like this?
particularly after 0B, I would expect 0C, but there is 10,
0B
10
0C
11
12

Edited by Kaveh

  • 2 months later...

Hi, thank you so much for the effort of getting this info. I have written a small c# prpgram to decode the data. I am going to put a small panel on my wall to display all of my betteries'parameters. I did start this because i have one battery that come up with a non serious error all the time and i needed to find out where the problem is. So far all the data looks good , but i would like to try and find out what the error is. Can you still get hold of the data for the specific bits ?

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.