Votronic Solarregler über den MultiUART auslesen

Hallo,
in dem change-log von Version 2.0.4 taucht dieser Punkt mit auf: Es können Votronic Solarregler ab Baujahr 2014 über den MultiUART ausgelesen werden.

Bis jetzt habe ich diesbezüglich leider noch nichts gefunden, wie ich den Solarregler an den MultiUART anschließen/auslesen kann und wo anschließend die Werte dargestellt werden.

Weiß jemand etwas genauer darüber?

Vielen dank

Moin,
dafür wird ein RJ12 Kabel mit einem JST Kabel verbunden.
Hab hier mal was gemacht:


Und einmal als Grafik:

Und einmal Web UI:

Hoffe das hilft dir Weiter

1 Like

Moin, danke für die Info.
Verstehe ich das richtig: Das RJ12 Kabel kommt vom Solarregler!? wird mit dem JST Kabel verbunden, wo wird das JST-Kabel dann am Van-Pi angeklemmt?
Danke und Gruß

Genau am Multi Urat, sprich der ganz rechte vom Core
Und das RJ12 Kabel am Display Port vom Solarregler

“Genau am Multi Urat, sprich der ganz rechte vom Core”

das wäre dann nach der Skizze “Top View Pin Out” UART5, ist das richtig?

Richtig, der ganz außen am Board, UART5

Danke für die hilfreichen Infos!

Eine Frage habe ich aber noch: Wird es demnächst noch eine Anbindung des Votronic Shunts geben?

Gruß

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.

Hallo,
danke für die schöne Einbindung von den beiden Votronic Geräten. Kann ich aber erst testen, wenn das WoMo aus dem Winterschlaf kommt!
Für das Einlesen der Daten wird auch nur der UART5-Eingang benötigt, müssen dabei dann alle 3 Geräte parallel auf diesen Eingang verschaltet werden (mit RJ12-Y-Verbinder)?