Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EG4 Lifepower driver #212

Merged
merged 6 commits into from Sep 21, 2022
Merged

Add EG4 Lifepower driver #212

merged 6 commits into from Sep 21, 2022

Conversation

dchiquito
Copy link
Contributor

@dchiquito dchiquito commented Sep 17, 2022

Fixes #137

Marking as draft pending more complete testing.

  • I've tested this locally on my laptop with a EG4-LiFePower4 | 24V 200AH, but not on an actual Venus OS installation or any other EG4 batteries.
  • I have not yet figure out the alarm code formatting.
  • For some reason my battery also responds to the Revov serial requests, so possibly this PR is not necessary at all?

@Louisvdw
Copy link
Owner

Many batteries use the same BMS internally. The Revov is using a Tian Power BMS (so it should actually be renamed)
If you battery works with the same commands it is very possible that they have the same internals. In that case we can merge the 2 drivers.
I see that the command that you use are the same as Revov's command_two except for the second last byte which might be a checksum or something else. Check if you get valid data using the Revov driver. That one does need a bit more work as it has some missing values.

@dchiquito
Copy link
Contributor Author

I seem to get valid looking responses from the Revov driver, but they fail to parse because of different response lengths. It looks to me like revov.py doesn't account for different cell counts that would cause different offsets for all subsequent data.

@dchiquito
Copy link
Contributor Author

Assuming the Revov and Lifepower drivers both work with Tian Power BMS, then one of the implementations is redundant. It would be nice if this Lifepower driver also works on Revov batteries, but I only have a Lifepower battery though so I'm unable to verify that.

I registered the Lifepower driver over the Revov driver because it can accommodate different cell counts and has more fields filled out. It might break support for Revov batteries, though. Any modifications to unify the drivers are welcome.

@dchiquito dchiquito marked this pull request as ready for review September 20, 2022 15:12
@Louisvdw
Copy link
Owner

The Revov driver was not fully working yet, so it is very possible that you might have fixed a few things there. I was planning to rename it to Tian Power.
What I'll do is create a beta with this code and ask a few Revov battery owners to give your LifePower version a test. If it works for their setup as well we can replace it with your version and then rather rename that the Tian Power driver.

@Louisvdw Louisvdw merged commit 40ebe8c into Louisvdw:master Sep 21, 2022
@pchiquit
Copy link
Contributor

pchiquit commented Oct 9, 2022

To provide a more complete documentation of the protocol, I'm adding the content of this link below :

https://powerforum.co.za/topic/13120-narada-npfc-tian-power-bms-protocol/

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

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

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 September 13 by SolarConvert
Clarified charge current and temperatures, added protection data

@aaronsb
Copy link

aaronsb commented Oct 25, 2022

Am I correct in reading that the most recent commit is still hard coding battery addresses? Would it be possible to set a mode or work around that concatenates all the modules in series if there are multiple battery addresses available on the rs485 bus? Creating a data structure that listens to multiple batteries, then packing the returned data into that concatenated structure, then returning that as "one giant battery" back over dbus might be a way to do that.

@pchiquit
Copy link
Contributor

@aaronsb yes, right now the battery ID is hardcoded to 1 so there is no support for multiple batteries. While an interesting suggestion, your concept of a giant battery would work for reporting individual cell state, but wouldn't work for other attributes like temperature, SOC, etc. that needs to be one value per battery.
Unfortunately, I lack enough understanding of the whole package to figure out a way to support multiple batteries. Maybe @Louisvdw can suggest how that should be implemented? I also only have one battery, so testing would be an issue for me.
I have an updated version of the the EG4 driver that I'm testing that includes some fixes and that I'll submit in a PR soon, but I'm in dire need of finding more people with different batteries to test it.

@aaronsb
Copy link

aaronsb commented Oct 25, 2022

@pchiquit I sort of started this as a conversation continuation. (I'm also happy if this needs to move to another thread if needed)
If it is agreed that multiple battery instances need to be eventually supported, then starting by at least structuring the data acquisition in driver module would be a great start. Unfortunately I don't have ANY hardware to test on (yet).

Very shortly I will be getting my hands on 4 SOK 48 volt battery banks which I have a hunch use the same BMS controller, and will be paralleled together into one large bank.

Just trying to get started on thinking about how to make multi-pack work appropriately.

@aaronsb
Copy link

aaronsb commented Oct 25, 2022

I think this discussion is very relevant as well. It appears that @Louisvdw is actively working in integrating the multiple battery aspect of the driver. Aligning the EG4 driver with the multi battery design he is working on will help development in that topic too.

Link to multi battery feature:
#8

@Louisvdw
Copy link
Owner

There are 2 seperate requests. Both of these are not spesific to a BMS but would be for all BMS that can handle this

#8 is for handeling multiple battery banks and combining the data as one large battery. Each BMS will still need to have it's own serial port connection on the GX device. This is currently being worked on.

#142 is to be able to address different banks on the same RS485 device using different address for each. This will only be able to work on some BMS where their protocol has the ability for address built in.

@waelgho
Copy link

waelgho commented Jan 24, 2024

I have tian power bms i cant connect with voltronic inverter .
My bms .TP TL41HL-8S100-V1.00A
Can any one help me
Thank you.

@waelgho
Copy link

waelgho commented Jan 24, 2024

I need update my bms to connect with voltronic inverter
I use software bms-cz004-v1.1.634-37

1 similar comment
@waelgho
Copy link

waelgho commented Jan 24, 2024

I need update my bms to connect with voltronic inverter
I use software bms-cz004-v1.1.634-37

@Louisvdw
Copy link
Owner

This driver will work with the Victron inverter, but not with an Voltronic. The voltronic does not have a microcontroller that we can load the driver on too.

@waelgho
Copy link

waelgho commented Jan 24, 2024

Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support for Eg4 LifePower battery
5 participants