Jutro coś podeśle, dopasowane do tego doc. Ale cały czas nie masz prawidłowo działającej strony statusowej więc słabo to widzę. Jesli to co otrzymałeś rozbiega cie z prawdą to nawet po modbus mogą byc problemy.
Poprosiłem zeby podesłali jeszcze inne dokumentację jeśli mają.
Co do statusu to w zrzutach ekranu na
Też nie ma statusów z falownika. Tak samo u mnie to wyglada
Tylko produkcja tam wygląda sensownie.
Dla porównania
Wszystkie napisane i testowane integracje opierają się na takiej adresacji.
W/g dokumentacji, którą załączyłeś w 2020r. powstała chyba jakaś nowa wersja softu - porównaj adresy i odpowiednie pola.
itd… mapa jest całkiem inna.
Do testów możesz spróbować edytować plik integracji by stefan
https://github.com/StephanJoubert/home_assistant_solarman/blob/main/custom_components/solarman/sofar_lsw3.yaml
Dostosowując rejestry do nowej dokumentacji. Nie musisz zmieniać wszystkich tylko możesz na początek zawęzić się do kilku, pamiętając również zmienić ted dwa odczytywane “banki” rejestrów
requests:
- start: 0x0000
end: 0x0027
- start: 0x0105
end: 0x0114
lub
Uruchomić mój załacznik i pokazać do wyrzuci debug.
[{"id":"916e4fd2.13596","type":"subflow","name":"Sofar inverter","info":"","category":"","in":[{"x":80,"y":80,"wires":[{"id":"e811032c.4d431"}]}],"out":[{"x":1140,"y":140,"wires":[{"id":"4f9bce54.66b91","port":0}]},{"x":1140,"y":200,"wires":[{"id":"ad8991f0.c505c","port":0}]}],"env":[{"name":"IP","type":"str","value":"","ui":{"type":"input","opts":{"types":["str"]}}},{"name":"serial_nr","type":"num","value":"","ui":{"label":{"en-US":"Logger S\\N"},"type":"input","opts":{"types":["num"]}}},{"name":"s_reg","type":"num","value":"0","ui":{"label":{"en-US":"From Register (dec)"},"type":"input","opts":{"types":["num"]}}},{"name":"e_reg","type":"num","value":"39","ui":{"label":{"en-US":"To Register (dec)"},"type":"input","opts":{"types":["num"]}}}],"meta":{},"color":"#FFAAAA","inputLabels":["in"],"outputLabels":["data","status byte"],"icon":"node-red/status.svg","status":{"x":1140,"y":260,"wires":[{"id":"ad8991f0.c505c","port":0}]}},{"id":"c43f4230.d1f43","type":"function","z":"916e4fd2.13596","name":"CRC16-modbus","func":"var CRCMaster = {\n StringToCheck: \"\",\n CleanedString: \"\",\n CRCTableDNP: [],\n init: function() {\n this.CRCDNPInit();\n },\n CleanString: function(inputType) {\n if (inputType == \"ASCII\") {\n this.CleanedString = this.StringToCheck;\n } else {\n if (this.StringToCheck.match(/^[0-9A-F \\t]+$/gi) !== null) {\n this.CleanedString = this._hexStringToString(this.StringToCheck.toUpperCase().replace(/[\\t ]/g, ''));\n } else {\n window.alert(\"String doesn't seem to be a valid Hex input.\");\n return false;\n }\n }\n return true;\n },\n CRCDNPInit: function() {\n var i, j, crc, c;\n for (i = 0; i < 256; i++) {\n crc = 0;\n c = i;\n for (j = 0; j < 8; j++) {\n if ((crc ^ c) & 0x0001) crc = (crc >> 1) ^ 0xA6BC;\n else crc = crc >> 1;\n c = c >> 1;\n }\n this.CRCTableDNP[i] = crc;\n }\n },\n CRC16Modbus: function() {\n var crc = 0xFFFF;\n var str = this.CleanedString;\n for (var pos = 0; pos < str.length; pos++) {\n crc ^= str.charCodeAt(pos);\n for (var i = 8; i !== 0; i--) {\n if ((crc & 0x0001) !== 0) {\n crc >>= 1;\n crc ^= 0xA001;\n } else\n crc >>= 1;\n }\n }\n return crc;\n },\n _stringToBytes: function(str) {\n var ch, st, re = [];\n for (var i = 0; i < str.length; i++) {\n ch = str.charCodeAt(i); // get char\n st = []; // set up \"stack\"\n do {\n st.push(ch & 0xFF); // push byte to stack\n ch = ch >> 8; // shift value down by 1 byte\n }\n while (ch);\n // add stack contents to result\n // done because chars have \"wrong\" endianness\n re = re.concat(st.reverse());\n }\n // return an array of bytes\n return re;\n },\n _hexStringToString: function(inputstr) {\n var hex = inputstr.toString(); //force conversion\n var str = '';\n for (var i = 0; i < hex.length; i += 2)\n str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));\n return str;\n },\n Calculate: function(str, inputType) {\n this.StringToCheck = str;\n if (this.CleanString(inputType)) {\n crcinputcrc16modbus=this.CRC16Modbus().toString(16).toUpperCase();\n crcinputcrc16modbus=crcinputcrc16modbus.substr(2) + crcinputcrc16modbus.substr(0, 2); //swap bytes\n \n }\n }\n};\n\nCRCMaster.init();\n\nvar inputType = \"HEX\";\nvar crcinputcrc16modbus;\nvar crcinput = msg.payload;\ncrcinput = crcinput.slice(-12);\nCRCMaster.Calculate(crcinput, inputType);\n\nmsg.payload = msg.payload + crcinputcrc16modbus.toLowerCase();\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":360,"y":80,"wires":[["c648833b.f8547"]]},{"id":"e811032c.4d431","type":"function","z":"916e4fd2.13596","name":"Out_Frame","func":"var a_start = env.get(\"s_reg\"); //adres startowy rejestru\nvar a_end = env.get(\"e_reg\"); //adres końcowy bloku rejestów\nvar oFrame = \"a5170010450000\"; //start bytes \n\nSN = env.get(\"serial_nr\");\nvar hexSN = SN.toString(16).toLowerCase();\n//seria No.\noFrame +=(hexSN[6]+hexSN[7]+hexSN[4]+hexSN[5]+hexSN[2]+hexSN[3]+hexSN[0]+hexSN[1]);\n//data field\noFrame +=\"020000000000000000000000000000\"\nvar businessfield=\"0103\"+('0000'+a_start.toString(16).toLowerCase()).slice(-4)+('0000'+(a_end-a_start+1).toString(16).toLowerCase()).slice(-4) ;\noFrame += businessfield;\nmsg.payload = oFrame;\nmsg.host = env.get(\"IP\");\nmsg.port = 8899;\nreturn msg;","outputs":1,"noerr":5,"initialize":"","finalize":"","libs":[],"x":190,"y":80,"wires":[["c43f4230.d1f43"]]},{"id":"c648833b.f8547","type":"function","z":"916e4fd2.13596","name":"CRC frame","func":"var l = parseInt(msg.payload.length)/2;\nvar bFrame =[l+2]; \nvar crc = 0;\nbFrame[0] = parseInt((msg.payload[0]+msg.payload[1]),16); \nfor (i=1; i<l; i++){\n bFrame[i] = parseInt((msg.payload[i*2]+msg.payload[(i*2)+1]),16); \n crc +=bFrame[i];\n crc &= 255; \n}\nbFrame[i] = crc;\nbFrame[i+1] = 0x15;\nmsg.payload = Buffer.from(bFrame);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":530,"y":80,"wires":[["82c505a3.984848"]]},{"id":"82c505a3.984848","type":"tcp request","z":"916e4fd2.13596","name":"Sofar","server":"","port":"","out":"time","ret":"buffer","splitc":"100","newline":"","tls":"","x":170,"y":140,"wires":[["4b49d7fe.e74338"]]},{"id":"4f9bce54.66b91","type":"function","z":"916e4fd2.13596","name":"Response To Data ","func":"var l=2*(env.get(\"e_reg\")-env.get(\"s_reg\"))+6;\nmsg.payload = msg.payload.slice(-l,-4)\nreturn msg;","outputs":1,"noerr":2,"initialize":"","finalize":"","libs":[],"x":890,"y":140,"wires":[[]]},{"id":"4b49d7fe.e74338","type":"switch","z":"916e4fd2.13596","name":"","property":"payload[0]","propertyType":"msg","rules":[{"t":"eq","v":"0xA5","vt":"num"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":290,"y":140,"wires":[["11f501f17764f76f"],["ad8991f0.c505c"]]},{"id":"ad8991f0.c505c","type":"change","z":"916e4fd2.13596","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"[0,9]","tot":"bin"}],"action":"","property":"","from":"","to":"","reg":false,"x":860,"y":240,"wires":[[]]},{"id":"11f501f17764f76f","type":"function","z":"916e4fd2.13596","name":"check length","func":"var l = parseInt(msg.payload.length)-13;\nmsg.length = l;\n//return [msg,null];\nif (msg.payload[1] == l){\n return [msg,null];\n}\nelse {\n msg.err = \"Length err\"\n return [null,msg];\n}","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":490,"y":140,"wires":[["6c16a8123255f98d"],["ad8991f0.c505c"]],"outputLabels":["OK","Error"]},{"id":"6c16a8123255f98d","type":"function","z":"916e4fd2.13596","name":"Check CRC","func":"var l = parseInt(msg.payload.length)-2;\nvar crc = 0;\nfor (i=1; i<l; i++){\n crc +=msg.payload[i];\n crc &= 255; \n}\nmsg.crc = crc;\nmsg.crc16 = parseInt((msg.payload[i-2]+msg.payload[i-1]),16); \nif (crc == msg.payload[i]){\n return [msg,null];\n}\nelse {\n msg.err = \"CRC err\"\n return [null,msg];\n}\n","outputs":2,"noerr":7,"initialize":"","finalize":"","libs":[],"x":690,"y":140,"wires":[["4f9bce54.66b91"],["ad8991f0.c505c"]],"outputLabels":["OK","Err"]},{"id":"323664626e5225fb","type":"inject","z":"f51d87293b3e5cc3","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"30","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":270,"y":460,"wires":[["7e31e5133b13a46a"]]},{"id":"7e31e5133b13a46a","type":"subflow:916e4fd2.13596","z":"f51d87293b3e5cc3","name":"","env":[{"name":"s_reg","value":"1152","type":"num"},{"name":"e_reg","value":"1213","type":"num"}],"x":480,"y":460,"wires":[["bb37a5e5dcc3f6cd"],["cf3ffd9f5fffd167"]]},{"id":"bb37a5e5dcc3f6cd","type":"debug","z":"f51d87293b3e5cc3","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":670,"y":440,"wires":[]},{"id":"cf3ffd9f5fffd167","type":"debug","z":"f51d87293b3e5cc3","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":670,"y":480,"wires":[]}]
Jeśli masz tą wersję to czeka Cię grubsza robota aby to ręcznie dostosować nawet po modbusie.
Nikt jeszcze nie zrobił tej wersji więc będziesz pionierem
Niestety nie mogę Ci inaczej pomóc , bo nie mam poligonu.
Rozumiem.
Dziękuję jutro się pobawię a jeśli mi się nie uda to zostaje zostać przy chmurze
Dziękuję za poświęcony czas
@paku2020 … uparłem się i nie odpuszczam
Powróćmy jeszcze raz do metody z regułą na mikrotiku.
Włącz tą regułę i przekieruj cały ruch na IP NR i port 10000
Skopiuj te 3 nody i uruchom.
[{"id":"854cd0c2b509654b","type":"tcp in","z":"41b46ab9.643494","name":"Inverter stick out","server":"server","host":"","port":"10000","datamode":"stream","datatype":"buffer","newline":"","topic":"","base64":false,"tls":"","x":420,"y":420,"wires":[["933cba5f353ba16b","763916ddd9ec5b3b"]]},{"id":"933cba5f353ba16b","type":"function","z":"41b46ab9.643494","name":"decode msg","func":"let buffer = msg.payload;\nlet data = {};\nswitch (buffer.length) {\n case 146:\n data.loggerSN = buffer.readUInt32LE(0x07);\n data.sensorTypeList = readSensorTypeList(buffer.slice(0x0C, 0x0E));\n data.tOperationTime = buffer.readUInt32LE(0x0E);\n data.inverterSN = (buffer.toString('utf8', 0x20, 0x30)).trimEnd();\n data.inverterTemp = buffer.readInt16LE(0x30)/10;\n data.VDC1 = buffer.readUInt16LE(0x32)/10;\n data.VDC2 = buffer.readUInt16LE(0x34)/10;\n data.IDC1 = buffer.readUInt16LE(0x36)/10;\n data.IDC2 = buffer.readUInt16LE(0x38)/10;\n data.IAC1 = buffer.readUInt16LE(0x3A)/10;\n data.IAC2 = buffer.readUInt16LE(0x3C)/10;\n data.IAC3 = buffer.readUInt16LE(0x3E)/10;\n data.VAC1 = buffer.readUInt16LE(0x40)/10;\n data.VAC2 = buffer.readUInt16LE(0x42)/10;\n data.VAC3 = buffer.readUInt16LE(0x44)/10;\n data.fAC = buffer.readUInt16LE(0x46)/100;\n \n data.currentPower = buffer.readUInt32LE(0x48);\n \n data.eToday = buffer.readUInt32LE(0x4C)/100;\n data.eTotal = buffer.readUInt32LE(0x50)/10;\n \n data.hTotal = buffer.readUInt32LE(0x54);\n \n data.loggerTemp = buffer.readInt16LE(0x70);\n data.Vbus = buffer.readUInt16LE(0x72)/10;\n data.VCPU1 = buffer.readUInt16LE(0x74)/10;\n data.countdownTime = buffer.readUInt16LE(0x78);\n data.PV1insulationResistance = buffer.readUInt16LE(0x7C);\n data.PV2insulationRsistance = buffer.readUInt16LE(0x7E);\n data.catod_groundInsulationImpedance = buffer.readUInt16LE(0x80);\n data.countryCode = buffer.readUInt16LE(0x82);\n data.A_phaseDCdistribution = buffer.readUInt16LE(0x8A);\n data.B_phaseDCdistribution = buffer.readUInt16LE(0x8C);\n data.C_phaseDCdistribution = buffer.readUInt16LE(0x8E);\n \n //data.firmware = buffer.toString('utf8', 0x90, 0x94);\n \n //data.year = buffer.readUInt8(0x98);\n //data.month = buffer.readUInt8(0x99);\n //data.day = buffer.readUInt8(0x9A);\n // data.hour = buffer.readUInt8(0x9B);\n // data.minute = buffer.readUInt8(0x9C);\n // data.second = buffer.readUInt8(0x9D);\n \n // node.warn(data);\n return {payload: data};\n break;\n case 99: // hello message\n data.loggerSN = buffer.readUInt32LE(0x07);\n data.tOperationTime = buffer.readUInt32LE(0x0C);\n data.uploadingFrequency = buffer.readUInt8(0x18);\n data.dataLoggingFrequency = buffer.readUInt8(0x19);\n data.heartbeatFrequency = buffer.readUInt8(0x1A);\n data.commandType = buffer.readUInt8(0x1B);\n data.signalQuality = buffer.readUInt8(0x1C);\n data.sensorTypeNr = buffer.readUInt8(0x1D);\n data.moduleVrsion = buffer.toString('utf8', 0x1E, 0x46);\n data.macAddress = readMacAddress(buffer.slice(0x46, 0x4C));\n data.localIP = buffer.toString('utf8', 0x4C, 0x5B);\n data.sensorTypeList = readSensorTypeList(buffer.slice(0x5F, 0x61));\n \n // node.warn(data);\n return {payload: data};\n break;\n case 41: // 41bytes message\n data.loggerSN = buffer.readUInt32LE(0x07);\n data.totalOperationTime = buffer.readUInt32LE(0x0C);\n \n // node.warn(data);\n return {payload: data};\n break;\n case 73: // 73bytes message\n data.loggerSN = buffer.readUInt32LE(0x07);\n data.totalOperationTime = buffer.readUInt32LE(0x0C);\n \n // node.warn(data);\n return {payload: data};\n break;\n \n default:\n // node.warn(buffer.length);\n return null\n break;\n}\n\nfunction readMacAddress (buffer) {\n let macAddressStr = '';\n for (let buff of buffer) {\n // node.warn(buff.toString(16));\n macAddressStr += `${`0${(buff.toString(16))}`.slice(-2)}:`;\n }\n return macAddressStr.substring(0, 17);\n}\nfunction readSensorTypeList (buffer) {\n let SensorTypeListStr = `${`0${(buffer[1].toString(16))}`.slice(-2)}`;\n SensorTypeListStr += `${`0${(buffer[0].toString(16))}`.slice(-2)}`;\n return SensorTypeListStr;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":630,"y":420,"wires":[["763916ddd9ec5b3b"]],"inputLabels":["wifi stick buffer in"],"outputLabels":["decoded message object"]},{"id":"763916ddd9ec5b3b","type":"debug","z":"41b46ab9.643494","name":"decoded message object","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":670,"y":480,"wires":[]}]
Przechwyciłem i zdekodowałem cała ramkę, którą wysyła do chmury.
Co 5min powinneś otrzymać 2 msg
Interesuje mnie długość buffer - to co jest w czerwonym kółku, oraz czy po kliknięciu trzech kropek otrzymasz coś takiego
Oki już działam.
zaraz odpale wszystko i będe aktualizował jak tylko coś wejdzie
edit
po 10min nie ma nic. To chyba musi potrwać chwilę zanim mt będzie przechwytywał dane. Wtedy to chyba po jakimś czasie było. HA pobrał dane z chmury więc poczekam jeszcze
Poki co nic. Ale wtedy kiedyś też nie było. Zapomniałem o tym i gdzieś się pojawiło
Pokaż kartę ip>firewall>NAT>rule zakładka general i action pozostałe nie wypełniasz.
Kurcze… mam tak samo !?
To jest głowny router, gdzie dodałeś tą regułę?
Tak to jest router brzegowy.
Może jakiś port po stronie ip loggera?
Nie wiadomo jaki port, ta reguła powinna przekierować wszystko? …spróbuj 80
Może reboot MT?
zrobiłem reboot mt.
Jakieś pakiety skoczyły
bede pisał za niedlugo bo musze wyjść
edit
U mnie całkowicie co innego niż u ciebie
840 …grubo i to nie jest dobra wiadomość
zmień w funkcji 146 na 840
Obserwuj długość buffer[xxx] i taką samą wpisz w case.
czy ta liczba xxx się powtarza?
tylko wtedy gdy buffer[xxx] = case xxx, jeśli nie ma takiej regularności to coś przerobimy.
jest już lepiej
zakomentuj te linijki
//data.A_phaseDCdistribution = buffer.readUInt16LE(0x8A);
//data.B_phaseDCdistribution = buffer.readUInt16LE(0x8C);
//data.C_phaseDCdistribution = buffer.readUInt16LE(0x8E);