diy solar

diy solar

BMS protocol and read-out

Is this Node-Red? Looks amazing!
Yes...:) THX.
I didn't know Node Red before and I had to find a solution in a week. Maybe is my solution not the smartest but works stably.
Atached the code...
Have fun.

PS: Ich schreibe hier weil auch nicht deutschsprachige die Interessen haben, davon profietieren.

1668599511488.png

And the Solution in FHEM over MTTQ

1668600213840.png
 

Attachments

  • 48v_Batterie.json.txt
    36.6 KB · Views: 154
Last edited:
This is a great thread. I too have been working on monitoring these batteries which appear to use an old version of the pylontech protocol which I cannot find any doco for. They use version "2.2" but I can only find doco for ver 2.8, so having this thread saved some time decoding the dumps!

I am mostly after some basic information such as SOC, voltage and current and have created a Node Red flow to publish to MQTT. Its just a different take on the same problem. I have shared in case it can save someone else some time. I have two batteries so the code may need adjustment depending on your situation.

No warranty and no support!


JSON:
[{"id":"e8741fa499dd6155","type":"tab","label":"Batteries From China","disabled":false,"info":"","env":[]},{"id":"b80ca6efc679cf53","type":"function","z":"e8741fa499dd6155","name":"request","func":"/*\nSAMPLE\n 1: ~   SOI\n 2: 22  VER\n 3: 01  ADR\n 4: 4A  CID1    \n 5: 42  CID2    Get analaog values\n 6: E0  LEN1    Length checksum = E\n 7: 02  LEN2    Length 2 hex bytes\n 8: 01  INFO    Get pack 1 data\n 9: FD  CHKSUM\n10: 28  CHKSUM\n11: CR  EOI \n*/\nvar cmd = []\n\ncmd[0] = \"~22\"      // SOI, VER\ncmd[1] = msg.addr   // ADDR: \"01\" or \"02\"\ncmd[2] = \"4A42\"     // CID1, CID2\ncmd[3] = \"E002\"     // LEN\ncmd[4] = \"01\"\n\nvar cmdstr = cmd.join('');\nvar C = 0;\nfor (let index = 1; index < cmdstr.length; index++) {\n    const c = cmdstr.charCodeAt(index);\n    C += c;\n}\nC = (~C & 0xffff) + 1\n \nmsg.payload = cmdstr + C.toString(16).toUpperCase() + \"\\x0d\"\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":370,"y":360,"wires":[["67f3703dd92df2ef"]]},{"id":"a79405afce98bcd2","type":"inject","z":"e8741fa499dd6155","name":"5s","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"5","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":70,"y":360,"wires":[["084f2bcfa9d4ad43"]]},{"id":"67f3703dd92df2ef","type":"serial request","z":"e8741fa499dd6155","name":"","serial":"91d1aae3823e1ef2","x":530,"y":360,"wires":[["e9e6b0f2ad058063","b42e1447116f89e2","d42f8c64bea41df4"]]},{"id":"e9e6b0f2ad058063","type":"function","z":"e8741fa499dd6155","name":"extract","func":"/*\n          ~               = SOI\n 01       22              = Ver = 22\n 03       01              = ADDR\n 05       4A              = CID1\n 07       00\n 09       E0C6            = Length + length checksum\n 13       00\n 15       2704            = SOC 99.88 (/100)\n 19       1533            = Pack voltage 54.27V (/100)\n 23       10              = 16 cells\n 25       0D44            = cell voltages, 3396mV\n 29       0D57            = 3415 mV\n        0D2D\n        0D14\n        0D11\n        0D13\n        0D6F\n        0D22\n        0D70\n        0D70\n        0D6D\n        0D34\n        0D33\n        0D0F\n        0D5C\n        0D55            = cell 16 voltage\n        010E            = 27 deg C (/10)\n        00FA            = 25 deg C (/10)\n        010E            = 27 deg C (/10)\n 101    04\n 103    0104\n 107    0104\n 111    0104\n 115    00FA\n 119    0000            = Current ?\n        0000\n        0064\n        01\n        30D4            = 125 Ahr total capacity\n        30C6            = 124.86 Ahr available\n        0005            = 5 cycles\n        0000\n        0000\n        0000\n        0000\n        0023            = 35\n        0000000000000000000000000000000000000000000000\nD563            = checksum\n*/\nif (msg.status != \"OK\")  return; // no data\n// check RTN code for valid (==\"00\")\nif (msg.payload.substr(7, 2) != \"00\") return; // no data\nmsg.D = msg.payload  // save original data\n\n/* it appears that sometimes we get results for different battery, maybe\n because of a timeout on previous call? Either way, we can use the data\n  as long as it is valid\n*/\n\nmsg.addr = msg.D.substr(3, 2)\n\n// Checksum validation\nvar C = 0;\nfor (let index = 1; index < msg.D.length-5; index++) {\n       const c = msg.D.charCodeAt(index);\n       C += c;\n}\nC = (~C & 0xffff) + 1\n\nvar rxC = parseInt(msg.D.substr(msg.D.length-5, 4), 16)\nmsg.valid = (C == rxC)\nif (!msg.valid) return;  // return with no data if invalid\n\nmsg.payload = {}\n\nmsg.payload.soc = parseInt(msg.D.substr(15, 4), 16) / 100\nmsg.payload.voltage = parseInt(msg.D.substr(19, 4), 16) / 100\n// Parse current\nvar a = parseInt(msg.D.substr(119, 4), 16)\nif ((a & 0x8000) > 0) a = a - 0x10000; // need to handle negative hex number for current\nmsg.payload.current = a/100;\n//\nmsg.payload.cellcount = parseInt(msg.D.substr(23,2),16)\nmsg.payload.cellvoltage  = []\nfor (let index = 0; index < msg.payload.cellcount; index++) \n{\n       msg.payload.cellvoltage[index] = parseInt(msg.D.substr(25+4*index, 4), 16)         \n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":280,"wires":[["b08a5271bceeaa98","0842c048ba4040fd"]]},{"id":"54a14510753a8c7f","type":"mqtt out","z":"e8741fa499dd6155","name":"Publish","topic":"","qos":"1","retain":"true","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"9cb0be1e.1c82a","x":960,"y":210,"wires":[]},{"id":"b08a5271bceeaa98","type":"split","z":"e8741fa499dd6155","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"key","x":650,"y":280,"wires":[["439a6141cc9dad27"]]},{"id":"cd8a4e22f0d435dc","type":"debug","z":"e8741fa499dd6155","name":"debug 12","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":950,"y":280,"wires":[]},{"id":"439a6141cc9dad27","type":"function","z":"e8741fa499dd6155","name":"make key","func":"msg.topic = \"NRED/Power/battery/china/\" + msg.addr + \"/\" + msg.key\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":790,"y":280,"wires":[["cd8a4e22f0d435dc","da41da0092a687b6"]]},{"id":"da41da0092a687b6","type":"rbe","z":"e8741fa499dd6155","name":"","func":"rbe","gap":"","start":"","inout":"out","septopics":true,"property":"payload","topi":"topic","x":820,"y":210,"wires":[["54a14510753a8c7f"]]},{"id":"b42e1447116f89e2","type":"debug","z":"e8741fa499dd6155","name":"debug 13","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":800,"y":360,"wires":[]},{"id":"d42f8c64bea41df4","type":"switch","z":"e8741fa499dd6155","name":"addr==01?","property":"addr","propertyType":"msg","rules":[{"t":"eq","v":"01","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":480,"y":500,"wires":[["7ccdb8358f62bed5"]]},{"id":"7ccdb8358f62bed5","type":"change","z":"e8741fa499dd6155","name":"addr=02","rules":[{"t":"set","p":"addr","pt":"msg","to":"02","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":500,"wires":[["20be21bf678fd47d"]]},{"id":"084f2bcfa9d4ad43","type":"change","z":"e8741fa499dd6155","name":"addr=01","rules":[{"t":"set","p":"addr","pt":"msg","to":"01","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":210,"y":360,"wires":[["b80ca6efc679cf53"]]},{"id":"f561167b68f27b78","type":"link in","z":"e8741fa499dd6155","name":"B-again","links":["20be21bf678fd47d"],"x":195,"y":260,"wires":[["b80ca6efc679cf53"]]},{"id":"20be21bf678fd47d","type":"link out","z":"e8741fa499dd6155","name":"link out 1","mode":"link","links":["f561167b68f27b78"],"x":725,"y":500,"wires":[]},{"id":"2ccc611d35c3595e","type":"comment","z":"e8741fa499dd6155","name":"loop for 2nd battery","info":"","x":650,"y":460,"wires":[]},{"id":"0842c048ba4040fd","type":"debug","z":"e8741fa499dd6155","name":"debug 14","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":620,"y":200,"wires":[]},{"id":"91d1aae3823e1ef2","type":"serial-port","serialport":"/dev/ttyUSB2","serialbaud":"19200","databits":"8","parity":"none","stopbits":"1","waitfor":"","dtr":"none","rts":"none","cts":"none","dsr":"none","newline":"300","bin":"false","out":"interbyte","addchar":"","responsetimeout":"2000"},{"id":"9cb0be1e.1c82a","type":"mqtt-broker","name":"MQTT_R1","broker":"192.168.7.101","port":"1883","clientid":"nodered_client1","usetls":false,"compatmode":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"sessionExpiry":""}]
 
Last edited:
Can I have a password please, because the given one doesn't work, I'm using 7zip.

The password works, sorry to disturb you.
 
Last edited:
I have a generic Chinese LifePo4 155AH Battery and it's performing OK. The Battery has a small LCD Panel to provide Battery status and Cell information (16 cells). I also have the DR Windows Software which reads the individual Cells and provides historical information etc.
One problem I'm having is that the Battery BMS Software doesn't appear to be "balancing" the cells.
Any suggestions??
I had to adapt a RS485 cable for the Battery to communicate with the Inverter Voltronic AXPERT VM IV 5600w 48v.
In Battery Lib mode (Li-Ion), Cell 7 goes over the 3650mv cutoff for OV whereas Cell 8 is sitting at 3370mv maximum with the rest in the 3385 to 3395 region.
I'm awaiting an Orange Pi 3 LTS, does anyone know if the "Solar Assistant App" will work with these Batteries?
 
I'm new in this forum. And this thread was very helpful to me. By that I'd like to share my Node-RED code:
NRa.pngNRb.png

I'm using three Pylontech batteries. By that I'm sending three commands:
NRc.png
 
hello,

i am new to this forum, too.

i am using iobroker, not node red.
i have the usb-rs485 cable.
in windows i am able to communicate with the software to the BMS.

But how could i get the information in Linux ?
Which program ?

Really appreciate your help.
 
since it is new to me and to understand it better:
So it is as you said? I have to convert the buffer to ASCII which then contains hex values which I have to decode to 16bit numbers? (well, it might contain status bits somewhere, too, no idea yet)

I think I made some progress, 2 data packages:

~22014A00E0C6002
71014E510

0D13 cell01 3347V
0D07 cell02 3335V
0D08 cell03 3336V
0D0A cell04 3338V
0D08 cell05 3336V
0D0E cell06 3342V
0D07 cell07 3335V
0D0C cell08 3340V
0D18 cell09 3352V
0D1A cell10 3354V
0D09 cell11 3337V
0D08 cell12 3336V
0D07 cell13 3335V
0D08 cell14 3336V
0D28 cell15 3368V
0D23 cell16 3363V

012C temp 30.0
0122 temp 29.0
012C temp 30.0

0401
2C012C012201220000
000000640128CA
28CA SoC/Capacity 104.42Ah
0006000000000000000000230000000000000000000000000000000000000000000000D56C


~22014A00E0C6002
39A150D10

0D27 cell01 3367V
0D29 cell02 3369V
0D2B cell03 3371V
0D29 cell04 3369V
0D28 cell05 3368V
0D29 cell06 3369V
0D2A cell07 3370V
0D28 cell08 3368V
0D28 cell09 3368V
0D29 cell10 3369V
0D28 cell11 3368V
0D2A cell12 3370V
0D29 cell13 3369V
0D2A cell14 3370V
0D28 cell15 3368V
0D26 cell16 3366V

0118 temp 28.0
010E temp 27.0
0118 temp 28.0

0401
0E010E010E010E0285
000000640128CA
252D SoC/Capacity 95.17Ah
0006000000010000000000230000000000000000000000000000000000000000000000D520

:)

That means I have to work with substrings after decoding to ASCII, I guess.
Hi,

i have a 105ah battery, too. The node Red is working.

But i am confused with the SOC. Ich the BMS Shows 56% at 45,25 v.

Could this be true?

Attach the manual.
 

Attachments

  • Screenshot_20230213_101351_Xodo Docs.jpg
    Screenshot_20230213_101351_Xodo Docs.jpg
    132.5 KB · Views: 91
The PW from my BMS is 666666. But be carefully with changes.
I am searching for a solution to readout the Parameters. I test the "solaranzeige.de" with a Pylontech Batterie Protokoll but the protocols are not compatible.

After i put the PW i became more Informations in the menu "Help":

View attachment 115507

And know the big question: If i change the protocol to pylon1363 then maybe then the "solaranzeige.de" software can read the parameters. If that is the case, then it will be possible to read the data via the MQTT server from solaranzeige.de

On the other hand i am afraid that it will be not more possible to have a communication with the own software to change the parameters of the BMS.

I will inform you if i have some news ...
Many Regards
Hi,

have you tried changing the protocol to pylon1363? I have the same battery from aliexpress and playing with the communication :)

Regards , Petr
 
so, you won't believe it but I just now had the time to try the password in the BMS software and to read the posts that have been written here after my last posting, lol ...
and the password works! :D

But did anyone of you try to change some of the BMS settings?
There's the "start voltage" (I don't have a screenshot here and have to write from memory).
It was set to 3.5V and I think it's the balancer start voltage, I set it to 3.42V

The next value below this is a voltage difference, it was set to 20mV here.
I suppose this is the hi/low-cell difference? Either this or it's the cell voltage offset from the "starting voltage".
Well, I lowered it to 12mV.

I balanced the cells manually and after that started charging the battery some more (55.1V).
But nothing happened so far, which is kinda sad and I have to wait for more sun to see if really nothing happens after changing the values.

I want the balancing to start earlier (than 3.5V), I never saw it balancing the cells as long as I have the battery.
During winter I did not balance it by hand, so I recently had a hi/low-cell difference of 30+ mV when the battery is full.
Being able to alter the BMS settings makes me hope to let the BMS do the balancing work.

Do you have any experiences with this?
 
An update from earlier -

I fitted the Orange Pi3 and purchased Solar Assistant from South Africa.

I must say the information given and the readout information is first class. The ability to change settings is very easy even with my "not so helpful" Voltronic Axpert Inverter.

30+ MV is nothing at the high end. It probably decreases when discharging - mine can be 280mv difference that causes an OV fault every time I charge.

I have two of these Batteries with identical make up and I'm convinced after using a Neey 4a on the wayward Battery that the BMS is actually faulty.

Neey gives me one set of Battery readings and the BMS another. The Battery Charges to around 85% SOC then immediately jumps to 100% SOC.
 
hehe, don't get me wrong, but 200+ mV difference between cells is a no-go ... lol
imagine, 3.5V is full, 3.3V is nearly empty ^^

I think the BMS may actually work now :eek: at least I saw the lower cells charge and the higher cells discharge today while being in CV mode with my charger.
I have to changes the values some more to test it, but I had around 16mV difference at ~55V battery voltage and about 20 at 56V.
Before altering the 2 values above the lower cells never increased when in CV mode :eek:
 
Hallo,

ich weiß, der Thread ist schon etwas älter, aber es sieht so aus als ob wir das gleiche BMS hätten (ich weiß nicht welches das genau ist, auch so ne Chinabox). Nun habe ich gerade das Problem, dass ich vom Verkäufer eine Firmware bekommen habe (DR01_16S100JC03_V2.0.0_T1_V.bin), weil bei mir eine Beta drauf ist (22.04.13-beta1) oder vermutlich war.

Das Update lief aber leider nicht, nun ist keine Kommunikation mehr möglich, die Batterie nicht mehr ansprechbar.

Gibt es eine Möglichkeit, um das wieder zu fixen?

Vielen Dank im Voraus.
 
I have two Chinese LifePo4 Batteries both with different Firmware.
Battery One's SOC reporting is poor whilst Battery Two is good.
Battery one Firmware
Version- DR-DR06 16S200JC03-V2.0.0T2_G
Protocol DR-1363
22.07.06-beta1
Battery two Firmware Version- DR-SAIENSI07 16S200JC03-V1.1.1T2_G
Protocol DR-1363
22.07.06-beta1

I have spoken to the supplier in China because Battery One gets to around 85% SOC, jumps immediately to 100% SOC and OV's.
They are sending me a new BMS.
I am also going to buy a JK BMS with Active Balancer built in to "piggy back" onto Battery One to see if I can get the best out of the Battery.
 
hello,

i am new to this forum, too.

i am using iobroker, not node red.
i have the usb-rs485 cable.
in windows i am able to communicate with the software to the BMS.

But how could i get the information in Linux ?
Which program ?

Really appreciate your help.
Hello,
based on information from this forum i created a simple python script that controls my boilers and disconnects the panels when everything is fully charged. All I need is to read the current from the battery and the SOC. I'm calculating checksums etc, someone might appreciate. Running on Armbian and NanoPI neo.
Everything is in progress :)
Python:
# BMS communication script

import serial
import time
import datetime
import syslog
import ftplib
import os


def chksum_data(str) :

   result=0;
   for znak in str:
      result = result + ord(znak);
   result = result ^ 0xFFFF;
   return result + 1;

def Lchksum(value) :

   value = value & 0x0FFF;
   n1=value & 0xF;
   n2=(value >> 4) & 0xF;
   n3=(value >> 8) & 0xF;
   chksum=((n1 + n2 + n3) & 0xF) ^ 0xF;
   chksum = chksum + 1;
   return value + ( chksum << 12 );

def CID2_decode(CID2):

   match CID2:
      case '00':
#        print ('CID2 response ok.');
         return 0;
      case '01':
         syslog.syslog ('VER error.');
      case '02':
         syslog.syslog ('CHKSUM error.');
      case '03':
         syslog.syslog ('LCHKSUM error.');
      case '04':
         syslog.syslog ('CID2 invalid.');
      case '05':
         syslog.syslog ('Command format error.');
      case '06':
         syslog.syslog ('INFO data invalid.');
      case '90':
          syslog.syslog ('ADR error.');
      case '91':
          syslog.syslog ('Battery communication error.');
#          print ('CID2 error response code: ', CID2);
   return -1;

syslog.syslog('Starting solar control.');
BOJLER_OFF = 1;
BOJLER_ON = 0
SOLAR_OFF = 0;
SOLAR_ON = 1;
BOJLER_PIN = 198;
SOLAR_PIN = 199;
bojler = BOJLER_OFF;
solar = SOLAR_ON;
sent=b'~22014A42E00201FD28\r';
ser = serial.Serial('/dev/ttyUSB0', 9600);
while True:
   rcv = '';
   ser.write(sent);
#   print('Request sent:', datetime.datetime.now());
   time.sleep(4);
   while ser.inWaiting() > 0:
      try:
         chr = ser.read();
         rcv+=chr.decode();
         if chr == b'\r':
            break;
      except:
#         syslog.syslog('Invalid character from serial port.');
         pass;
   valid_data = 1;
   try:
      CID2 = rcv[7:9];
      if CID2_decode(CID2) == -1:
         valid_data = -1;
   except:
      valid_data = -1;
   try:
      LENID = int(rcv[9:13], base=16);
      length = LENID & 0x0FFF;
      if Lchksum(length) == LENID:
         pass;
#         print('LEN_ID ok.');
      else:
#         print('LEN_ID error.');
         valid_data = -1;
   except:
      valid_data = -1;
   try:
      chksum = int(rcv[len(rcv)-5:], base=16);
      if chksum_data(rcv[1:len(rcv)-5]) == chksum:
#         print('CHKSUM ok.')
         valid_data = 1;
      else:
#         print('CHKSUM error.');
         valid_data = -1;
   except:
      valid_data = -1;
#      syslog.syslog("Invalid data from the battery.");
   if valid_data == 1:
      # Data z baterky jsou OK
      data = rcv[13:len(rcv)-5]
      SOC = int(data[2:6], base=16) / 100;
#      print('SOC:    ',SOC, '%');
      napeti = int(data[6:10], base=16) /100;
#      print('Napětí: ', napeti, 'V');
      proud = int(data[106:110], base=16);
      if proud  > 32767:
         proud = -(32768-(proud - 32768));
      proud /=100;
#      print('Proud:  ', proud, 'A');
#      print('--------------------------------\n');
      if (time.localtime().tm_min in [0, 15, 30, 45]) & (time.localtime().tm_sec < 5):
         syslog.syslog('INFO: ' + str(napeti) + 'V ' + str(proud) + 'A ' + str(SOC)+'% BOJLER: ' + ('OFF' if bojler == 1 else 'ON'));
##################################################################################################
      if (bojler == BOJLER_OFF) & (proud > 30) & (SOC > 80):
         bojler = BOJLER_ON;
         syslog.syslog('BOJLER ON: ' + str(napeti) + 'V ' + str(proud) + 'A ' +  str(SOC) + '%');
      if (bojler == BOJLER_ON) & (proud < -10) & (SOC < 90):
         bojler = BOJLER_OFF;
         syslog.syslog('BOJLER OFF: ' + str(napeti) + 'V ' + str(proud) + 'A ' +  str(SOC) + '%');
      if (solar == SOLAR_ON) & (SOC > 99):
         solar = SOLAR_OFF;
         syslog.syslog('SOLAR OFF: ' + str(napeti) + 'V ' + str(proud) + 'A ' +  str(SOC) + '%');
      if (solar == SOLAR_OFF) & (SOC < 90):
         solar = SOLAR_ON;
         syslog.syslog('SOLAR ON: ' + str(napeti) + 'V ' + str(proud) + 'A ' +  str(SOC) + '%');
      if (bojler == BOJLER_OFF) & (SOC > 95):
         bojler = BOJLER_ON;
         syslog.syslog('BOJLER ON: ' + str(napeti) + 'V ' + str(proud) + 'A ' +  str(SOC) + '%');


##################################################################################################
      o = open("/sys/class/gpio/gpio198/value", "w"); o.write(str(bojler)); o.close();
      o = open("/sys/class/gpio/gpio199/value", "w"); o.write(str(solar)); o.close();
 
   else:
#      print('Invalid data.\n----------------------\n');
#      syslog.syslog("Invalid data from the battery.");
      pass;
ser.close();
time.sleep(120);
os.system('reboot');
 
Back
Top