Hab hier nur eine Votronic B2B Ladebooster und hab mir den so eingebunden:
[{"id":"6d30d4f2e67ca914","type":"ui_text","z":"9e99ed6f3458f738","g":"fec49eb90b97df35","group":"e9d7c6d1bf26c761","order":1,"width":6,"height":1,"name":"Voltage","label":"Voltage","format":"Input: {{msg.payload.ustarterV}} V<br>Board: {{msg.payload.uBoardV}} V","layout":"row-spread","className":"","style":false,"font":"","fontSize":"","color":"#000000","x":920,"y":540,"wires":[]},{"id":"d33cb1e4f1e7ca35","type":"ui_text","z":"9e99ed6f3458f738","g":"fec49eb90b97df35","group":"e9d7c6d1bf26c761","order":2,"width":6,"height":1,"name":"Current","label":"Current","format":"<font size=\"6\">{{msg.payload.iLadeA}} W</font>","layout":"row-spread","className":"","style":false,"font":"","fontSize":"","color":"#000000","x":920,"y":580,"wires":[]},{"id":"563c85fdc5a8b872","type":"ui_gauge","z":"9e99ed6f3458f738","g":"fec49eb90b97df35","name":"Power","group":"e9d7c6d1bf26c761","order":3,"width":"3","height":2,"gtype":"gage","title":"","label":"Power [W]","format":"{{msg.payload.b2bPowerW}}","min":0,"max":"400","colors":["#00ff00","#00ff00","#00ff00"],"seg1":"25","seg2":"50","diff":false,"className":"","x":910,"y":620,"wires":[]},{"id":"0838821e0be6fee7","type":"ui_text","z":"9e99ed6f3458f738","g":"fec49eb90b97df35","group":"e9d7c6d1bf26c761","order":4,"width":6,"height":1,"name":"Status","label":"Status","format":"{{msg.payload.chargingControllerStatusText}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":"","color":"#000000","x":910,"y":700,"wires":[]},{"id":"f94898a9969f8aa9","type":"debug","z":"9e99ed6f3458f738","g":"fec49eb90b97df35","name":"debug 241","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":930,"y":500,"wires":[]},{"id":"0192d6ee0474aa85","type":"ui_gauge","z":"9e99ed6f3458f738","g":"fec49eb90b97df35","name":"Power%","group":"e9d7c6d1bf26c761","order":3,"width":"3","height":2,"gtype":"gage","title":"","label":"Charging %","format":"{{msg.payload.chargingPowerPercentage}}","min":0,"max":"100","colors":["#0000ff","#0000ff","#0000ff"],"seg1":"100","seg2":"200","diff":false,"className":"","x":920,"y":660,"wires":[]},{"id":"ccbc6f2dbf426168","type":"ui_text","z":"9e99ed6f3458f738","g":"fec49eb90b97df35","group":"e9d7c6d1bf26c761","order":4,"width":6,"height":1,"name":"Charging Mode","label":"Charging Mode","format":"{{msg.payload.chargingMode}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":"","color":"#000000","x":940,"y":740,"wires":[]},{"id":"fd97709bcd7d6d22","type":"function","z":"9e99ed6f3458f738","g":"fec49eb90b97df35","name":"Votronic B2B","func":"// Helper-Funktionen\nconst readUInt16LE = (payload, offset) => (payload.readUInt16LE(offset) * 10) / 1000; // 10mV/Bit\nconst readInt16LE = (payload, offset) => payload.readInt16LE(offset) / 10; // 100mA/Bit\n\nlet buffer = msg.payload;\n\n// Check if the buffer is valid\nif (!Buffer.isBuffer(buffer) || buffer.length === 0) {\n node.error(\"Invalid buffer\");\n return null;\n}\n\n// Find the position of 0xaa\nlet targetValue = 0xaa;\nlet position = buffer.indexOf(targetValue);\n\n// If 0xaa is found, rotate the buffer\nif (position !== -1) {\n // Create a rotated buffer: start from the found position\n let rotatedBuffer = Buffer.concat([\n buffer.slice(position), // From target value to end\n buffer.slice(0, position) // From start to just before target value\n ]);\n \n // Set the rotated buffer as the payload\n msg.payload = rotatedBuffer;\n}\n\n// Check if Votronic is set\nif (global.get(\"multiuart1\") != 8) {\n return null\n}\n\nconst chargerStatusBitmaskToString = (mask) => {\n // Prüfe, ob die Maske 0 ist, und gebe \"Off\" zurück.\n if (mask === 0x00) {\n return \"Standby\";\n }\n\n let status = [];\n if (mask & (1 << 0)) status.push(\"Unused (Bit 0)\");\n if (mask & (1 << 1)) status.push(\"Unused (Bit 1)\");\n if (mask & (1 << 2)) status.push(\"Charging\");\n if (mask & (1 << 3)) status.push(\"Charging Battery 2 / Reverse Charging\");\n if (mask & (1 << 4)) status.push(\"Reduced\");\n if (mask & (1 << 5)) status.push(\"AC Power Limit\");\n\n return status.length ? status.join(\"; \") : \"Unknown\";\n};\n\n\n// Lademodus aus `data[12]` extrahieren\nconst chargingModeToString = (mode) => {\n switch (mode) {\n case 24:\n case 124:\n return \"Lead Acid (12V / 24V)\";\n case 53:\n case 153:\n return \"Gel (12V / 24V)\";\n case 34:\n case 134:\n return \"AGM 14.4V (12V / 24V)\";\n case 47:\n case 147:\n return \"AGM 14.7-14.8V (12V / 24V)\";\n case 89:\n case 189:\n return \"LiFePo4 13.8V (12V / 24V)\";\n case 82:\n case 182:\n return \"LiFePo4 14.2V (12V / 24V)\";\n case 84:\n case 184:\n return \"LiFePo4 14.4V (12V / 24V)\";\n case 86:\n case 186:\n return \"LiFePo4 14.6V (12V / 24V)\";\n case 88:\n case 188:\n return \"LiFePo4 14.8V (12V / 24V)\";\n default:\n return `Unknown (${mode})`;\n }\n};\n\n// Validate Input\nif (!msg.payload || msg.payload.length < 16) {\n throw new Error(\"Invalid payload: insufficient data\");\n}\n\n// Extract data\nconst data = msg.payload;\n\n// Ladecontroller Status\nconst chargingControllerStatus = data[14]; // Ladecontroller Status als Bitmaskenwert\nconst chargingPowerPercentage = data[10]; // Ladeleistung in Prozent (%)\nconst chargingMode = data[12]; // Lademodus aus `data[12]`\nconst controllerTemperature = data[11]; // Controller Temperatur aus `data[11]`\n\n// Entladestatus überprüfen (current < 0)\nconst current = readInt16LE(data, 6); // Current value, in Amperes\nconst isDischarging = current < 0.0;\n\n// Ladecontroller-Status in Text umwandeln\nconst chargingControllerStatusText = chargerStatusBitmaskToString(chargingControllerStatus);\n\n// Ladeleistung in Prozent (0-100%)\nconst chargingPower = chargingPowerPercentage; // Ladeleistung in Prozent\n\n// Lademodus in Text umwandeln\nconst chargingModeText = chargingModeToString(chargingMode);\n\n\n// Ladeleistung berechnen\nconst uBoardV = readUInt16LE(data, 2);\nconst uStarterV = readUInt16LE(data, 4);\nconst iLadeA = readInt16LE(data, 6);\nconst ladePower = uBoardV * iLadeA; // B2B Power in Watt\n\n// Create output message\nmsg.payload = {\n uBoardV: uBoardV.toFixed(1),\n ustarterV: uStarterV.toFixed(1),\n iLadeA: iLadeA.toFixed(1),\n b2bPowerW: ladePower.toFixed(2),\n chargingControllerStatusBitmask: chargingControllerStatus, // Bitmask des Ladecontrollers\n chargingControllerStatusText: chargingControllerStatusText, // Textuelle Beschreibung des Status\n chargingPowerPercentage: chargingPower, // Ladeleistung in Prozent\n chargingMode: chargingModeText, // Lademodus (Batterietyp / Ladeeinstellungen)\n controllerTemperature: (controllerTemperature / 10).toFixed(1), // Controller-Temperatur\n};\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":670,"y":500,"wires":[["f94898a9969f8aa9","6d30d4f2e67ca914","d33cb1e4f1e7ca35","563c85fdc5a8b872","0838821e0be6fee7","0192d6ee0474aa85","ccbc6f2dbf426168"]]},{"id":"09cf4f1b7d551d2a","type":"delay","z":"9e99ed6f3458f738","g":"fec49eb90b97df35","name":"","pauseType":"rate","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"allowrate":false,"outputs":1,"x":460,"y":500,"wires":[["fd97709bcd7d6d22"]]},{"id":"c5f1fd5df8b5c273","type":"inject","z":"9e99ed6f3458f738","g":"fec49eb90b97df35","name":"check activity 20s","props":[],"repeat":"20","crontab":"","once":true,"onceDelay":"0.5","topic":"","x":290,"y":540,"wires":[["387445cabb02b563"]]},{"id":"387445cabb02b563","type":"function","z":"9e99ed6f3458f738","g":"fec49eb90b97df35","name":"set to 0","func":"// Votronic MMPT Skript by Bock.auf.Raus\n// Check if Votronic is set\nif (global.get(\"multiuart1\") != 8) {\n return null\n}\nvar lastUpdate = flow.get('lastUpdate') || 0;\nvar currentTime = Date.now();\n\n// Check if last data is older than 10 seconds\nif (currentTime - lastUpdate > 10000) {\n // Simulate serial input with null values\n msg.payload = Buffer.from([\n 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00\n ]);\n node.send(msg);\n}\nreturn null;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":500,"y":540,"wires":[["fd97709bcd7d6d22"]]},{"id":"42b647e7f2e34398","type":"link in","z":"9e99ed6f3458f738","g":"fec49eb90b97df35","name":"Votronic in","links":["f07cec11c8757a4d"],"x":285,"y":500,"wires":[["09cf4f1b7d551d2a"]]},{"id":"e9d7c6d1bf26c761","type":"ui_group","name":"Votronic B2B","tab":"deee53a800de461d","order":11,"disp":true,"width":"6","collapse":false,"className":""},{"id":"deee53a800de461d","type":"ui_tab","name":"Info","icon":"mi-info","order":1,"disabled":false,"hidden":false}]
und der Shunt könnte damit klappen:
[{"id":"8429591959f6ef5d","type":"group","z":"9e99ed6f3458f738","name":"Votronic Shunt","style":{"fill":"#bfdbef","label":true},"nodes":["30e9924f1de9bf9f","9ef4f32d2765445b","7619fa583d7c4e25","a0415eccf9de181b","c949c4c357ebe7e7","4e0d3962443e8da5","bd33b8675183499c","a3175025f34b7077","843da462e80fb7e3","667252a9dbcafda2","27c53195e053563e","8df538d800e95c35"],"x":154,"y":799,"w":882,"h":242},{"id":"30e9924f1de9bf9f","type":"ui_text","z":"9e99ed6f3458f738","g":"8429591959f6ef5d","group":"b14491b449f1ee37","order":1,"width":6,"height":1,"name":"Voltage","label":"Voltage","format":"Board {{msg.payload.batteryVoltage}} V<br>Starter: {{msg.payload.secondaryBatteryVoltage}} V","layout":"row-spread","className":"","style":false,"font":"","fontSize":"","color":"#000000","x":920,"y":880,"wires":[]},{"id":"9ef4f32d2765445b","type":"ui_text","z":"9e99ed6f3458f738","g":"8429591959f6ef5d","group":"b14491b449f1ee37","order":2,"width":6,"height":1,"name":"Watt","label":"Power","format":"<font size=\"6\">{{msg.payload.power}} W</font>","layout":"row-spread","className":"","style":false,"font":"","fontSize":"","color":"#000000","x":910,"y":920,"wires":[]},{"id":"7619fa583d7c4e25","type":"ui_gauge","z":"9e99ed6f3458f738","g":"8429591959f6ef5d","name":"Current","group":"b14491b449f1ee37","order":3,"width":"3","height":2,"gtype":"gage","title":"","label":"Amps:","format":"{{msg.payload.current}}","min":"-200","max":"100","colors":["#ffd500","#ffffff","#00ff00"],"seg1":"0","seg2":"1","diff":false,"className":"","x":920,"y":960,"wires":[]},{"id":"a0415eccf9de181b","type":"debug","z":"9e99ed6f3458f738","g":"8429591959f6ef5d","name":"debug 16","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":930,"y":840,"wires":[]},{"id":"c949c4c357ebe7e7","type":"ui_gauge","z":"9e99ed6f3458f738","g":"8429591959f6ef5d","name":"Power%","group":"b14491b449f1ee37","order":3,"width":"3","height":2,"gtype":"gage","title":"","label":"SOC","format":"{{msg.payload.stateOfCharge}}","min":0,"max":"100","colors":["#ff0000","#ffc800","#59ff00"],"seg1":"10","seg2":"20","diff":false,"className":"","x":920,"y":1000,"wires":[]},{"id":"4e0d3962443e8da5","type":"delay","z":"9e99ed6f3458f738","g":"8429591959f6ef5d","name":"","pauseType":"rate","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"allowrate":false,"outputs":1,"x":460,"y":840,"wires":[["27c53195e053563e"]]},{"id":"bd33b8675183499c","type":"inject","z":"9e99ed6f3458f738","g":"8429591959f6ef5d","name":"check activity 20s","props":[],"repeat":"20","crontab":"","once":true,"onceDelay":"0.5","topic":"","x":290,"y":880,"wires":[["a3175025f34b7077"]]},{"id":"a3175025f34b7077","type":"function","z":"9e99ed6f3458f738","g":"8429591959f6ef5d","name":"set to 0","func":"// Votronic MMPT Skript by Bock.auf.Raus\n// Check if Votronic is set\nif (global.get(\"multiuart1\") != 8) {\n return null\n}\nvar lastUpdate = flow.get('lastUpdate') || 0;\nvar currentTime = Date.now();\n\n// Check if last data is older than 10 seconds\nif (currentTime - lastUpdate > 10000) {\n // Simulate serial input with null values\n msg.payload = Buffer.from([\n 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00\n ]);\n node.send(msg);\n}\nreturn null;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":500,"y":880,"wires":[["27c53195e053563e"]]},{"id":"843da462e80fb7e3","type":"link in","z":"9e99ed6f3458f738","g":"8429591959f6ef5d","name":"Votronic in","links":["f07cec11c8757a4d"],"x":285,"y":840,"wires":[["4e0d3962443e8da5"]]},{"id":"667252a9dbcafda2","type":"inject","z":"9e99ed6f3458f738","g":"8429591959f6ef5d","name":"Test","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":"3","topic":"","payload":"[170, 202, 3, 5, 15, 5, 199, 1, 32, 0, 99, 0, 123, 254, 255, 57]","payloadType":"bin","x":510,"y":920,"wires":[["27c53195e053563e"]]},{"id":"27c53195e053563e","type":"function","z":"9e99ed6f3458f738","g":"8429591959f6ef5d","name":"Votronic Shunt","func":"// Helper-Funktionen\nconst readUInt16LE = (payload, offset) => (payload.readUInt16LE(offset) * 10) / 1000; // 10mV/Bit\nconst readInt16LE = (payload, offset) => payload.readInt16LE(offset) / 10; // 100mA/Bit\n\nlet buffer = msg.payload;\n\n// Check if the buffer is valid\nif (!Buffer.isBuffer(buffer) || buffer.length === 0) {\n node.error(\"Invalid buffer\");\n return null;\n}\n\n// Find the position of 0xaa\nlet targetValue = 0xaa;\nlet position = buffer.indexOf(targetValue);\n\n// If 0xaa is found, rotate the buffer\nif (position !== -1) {\n let rotatedBuffer = Buffer.concat([ \n buffer.slice(position), // From target value to end\n buffer.slice(0, position) // From start to just before target value\n ]);\n \n msg.payload = rotatedBuffer;\n}\n\n// Validate Input\nif (!msg.payload || msg.payload.length < 16) {\n throw new Error(\"Invalid payload: insufficient data\");\n}\n\nconst data = msg.payload;\n\n// Votronic specific functions to decode data\nconst votronic_get_16bit = (index) => {\n return (data[index + 1] << 8) | data[index];\n};\n\n// Battery information\nconst batteryVoltage = votronic_get_16bit(2) * 0.01;\nconst secondaryBatteryVoltage = votronic_get_16bit(4) * 0.01;\nconst batteryCapacityRemaining = votronic_get_16bit(6) * 1.0;\n\n// Current and power calculations\nconst current = readInt16LE(data, 12); // Current in A\nconst power = current * batteryVoltage; // Power in W\n\n// Charging status checks\nconst chargingStatus = current > 0.0;\nconst dischargingStatus = current < 0.0;\n\n// Extract charging mode and status from data\nconst chargingMode = data[12]; // Lademodus aus `data[12]`\n// const chargingModeText = chargingModeToString(chargingMode);\nconst chargingControllerStatus = data[14]; // Ladecontroller Status als Bitmaskenwert\n// const chargingControllerStatusText = chargerStatusBitmaskToString(chargingControllerStatus);\n\n// Extract controller temperature\nconst controllerTemperature = data[11] / 10; // Controller Temperatur\n\n// Extract State of Charge (SOC) from byte 10\nconst stateOfCharge = data[10]; // Directly from byte 10\n\n// Pack results in msg.payload\nmsg.payload = {\n batteryVoltage: batteryVoltage.toFixed(2),\n secondaryBatteryVoltage: secondaryBatteryVoltage.toFixed(2),\n batteryCapacityRemaining: batteryCapacityRemaining.toFixed(1),\n current: current.toFixed(1),\n power: power.toFixed(2),\n chargingStatus: chargingStatus ? \"Charging\" : \"Not Charging\",\n dischargingStatus: dischargingStatus ? \"Discharging\" : \"Not Discharging\",\n //chargingModeText: chargingModeText,\n //chargingControllerStatus: chargingControllerStatusText,\n controllerTemperature: controllerTemperature.toFixed(1),\n stateOfCharge: stateOfCharge.toFixed(0) // Adding SOC to payload\n};\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":700,"y":840,"wires":[["a0415eccf9de181b","30e9924f1de9bf9f","9ef4f32d2765445b","7619fa583d7c4e25","c949c4c357ebe7e7","8df538d800e95c35"]]},{"id":"8df538d800e95c35","type":"function","z":"9e99ed6f3458f738","g":"8429591959f6ef5d","name":"Globel set Monitor","func":"global.set(\"MainBattSoc\", msg.payload.stateOfCharge);\nglobal.set(\"MainBattAmps\", msg.payload.current);\nglobal.set(\"MainBattVolt\", msg.payload.batteryVoltage);\n ","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":710,"y":900,"wires":[[]]},{"id":"b14491b449f1ee37","type":"ui_group","name":"Votronic Shunt","tab":"deee53a800de461d","order":14,"disp":true,"width":6,"collapse":false,"className":""},{"id":"deee53a800de461d","type":"ui_tab","name":"Info","icon":"mi-info","order":1,"disabled":false,"hidden":false}]
Hab keine Shunt da, sind nur Demo Daten.
Eigentlich sollte die daten reinlaufen sobald der MultiUrat auf Votronic Solar steht.