Solar Charge Controller Examples
Modbus TCP polling examples for Morningstar, Midnite and Outback solar charge controllers.
Outback Flexmax charge controller tested with 'AXS Port' device, has not been tested with Outback Mate.
Victron data via serial port using a VE.Direct to USB cable.
[{"id":"f5e24dbfed1c15df","type":"tab","label":"Flow 5","disabled":false,"info":"","env":[]},{"id":"1875107bda0e9243","type":"function","z":"f5e24dbfed1c15df","name":"Midnite Classic","func":"msg.payload = { 'fc': 3, 'unitid': 10, 'address': 4114, 'quantity': 20 }\nmsg.topic = \"midnite-classic\"\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":320,"y":160,"wires":[["67664a3ba6bcb0bf"]]},{"id":"67664a3ba6bcb0bf","type":"modbus-flex-getter","z":"f5e24dbfed1c15df","name":"Midnite Modbus Get","showStatusActivities":true,"showErrors":true,"showWarnings":true,"logIOActivities":false,"server":"087002bf20b20498","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"keepMsgProperties":false,"delayOnStart":false,"startDelayTime":"","x":580,"y":160,"wires":[["25fa710851bc9837"],[]]},{"id":"88183794e37814f6","type":"function","z":"f5e24dbfed1c15df","name":"Outback AXS","func":"msg.payload = { 'fc': 3, 'unitid': 255, 'address': 40384, 'quantity': 30 }\nmsg.topic = \"outback-axs\"\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":310,"y":220,"wires":[["6a8731447d44e2ef"]]},{"id":"6a8731447d44e2ef","type":"modbus-flex-getter","z":"f5e24dbfed1c15df","name":"Outback Modbus Get","showStatusActivities":true,"showErrors":true,"showWarnings":true,"logIOActivities":false,"server":"f3a135728011b42a","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"keepMsgProperties":false,"delayOnStart":false,"startDelayTime":"","x":580,"y":220,"wires":[["7ac0b913a57512c2"],[]]},{"id":"25fa710851bc9837","type":"function","z":"f5e24dbfed1c15df","name":"Parse Modbus","func":"let battV = msg.payload[0] / 10\nlet battI = msg.payload[2] / 10\nlet arrayV = msg.payload[1] / 10\nlet arrayI = msg.payload[6] / 10\nlet statenum = msg.payload[5] >> 8\nlet hsTemp = msg.payload[19] / 10\nlet rtsTemp = msg.payload[17] / 10\nlet outPower = msg.payload[4]\nlet kWh = msg.payload[3] / 10\n\n//change state values to match Tristar\nif (statenum == 0) { statenum = 3 } // Off/Night\nelse if (statenum == 10) { statenum = 4 } // Fault\nelse if (statenum == 4) { statenum = 5 } // Bulk\nelse if (statenum == 3) { statenum = 6 } // Absorb\nelse if (statenum == 5 || statenum == 6) { statenum = 7 } // Float\nelse if (statenum == 7 || statenum == 18) { statenum = 8 } // Equalise\n\nlet siteID = msg.topic\n\nmsg.topic = \"solar/\"+siteID\n\nmsg.payload = {\n \"battV\": battV,\n \"battI\": battI,\n \"arrayV\": arrayV,\n \"arrayI\": arrayI,\n \"state\": statenum,\n \"hsTemp\": hsTemp,\n \"rtsTemp\": rtsTemp,\n \"outPower\": outPower,\n \"kWh\": kWh\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":880,"y":160,"wires":[["a121ea75dd916c1d"]]},{"id":"7ac0b913a57512c2","type":"function","z":"f5e24dbfed1c15df","name":"Parse Modbus","func":"let battV = msg.payload[0] / 10\nlet battI = msg.payload[2] / 10\nlet arrayV = msg.payload[1] / 10\nlet arrayI = msg.payload[3] / 10\nlet statenum = msg.payload[4]\nlet outPower = msg.payload[5]\nlet kWh = msg.payload[10] / 10\n\n//change state values to match Tristar\nif (statenum == 0) { statenum = 3 } // Off/Night\nelse if (statenum == 10) { statenum = 4 } // Fault\nelse if (statenum == 3) { statenum = 6 } // Absorb\nelse if (statenum == 1 || statenum == 6) { statenum = 7 } // Float\nelse if (statenum == 4 || statenum == 18) { statenum = 8 } // Equalise\n\nlet siteID = msg.topic\n\nmsg.topic = \"solar/\"+siteID\n\nmsg.payload = {\n \"battV\": battV,\n \"battI\": battI,\n \"arrayV\": arrayV,\n \"arrayI\": arrayI,\n \"state\": statenum,\n \"outPower\": outPower,\n \"kWh\": kWh\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":880,"y":220,"wires":[["a121ea75dd916c1d"]]},{"id":"cd5b30045dcd18ac","type":"function","z":"f5e24dbfed1c15df","name":"Parse Modbus","func":"let V_PU_hi = msg.payload[0]\nlet V_PU_lo = msg.payload[1]\nlet I_PU_hi = msg.payload[2]\nlet I_PU_lo = msg.payload[3]\n\nlet V_PU = V_PU_hi + V_PU_lo\nlet I_PU = I_PU_hi + I_PU_lo\n\nlet v_scale = V_PU * 2**(-15)\nlet i_scale = I_PU * 2**(-15)\nlet p_scale = V_PU * I_PU * 2**(-17)\n\nlet battV = msg.payload[24] * v_scale\nlet battI = msg.payload[28] * i_scale\nlet arrayV = msg.payload[27] * v_scale\nlet arrayI = msg.payload[29] * i_scale\nlet statenum = msg.payload[50]\nlet hsTemp = msg.payload[35]\nlet rtsTemp\nif (msg.payload[36] > 100) {\n rtsTemp = 0\n}\nelse {\n rtsTemp = msg.payload[36] \n}\nlet outPower = msg.payload[58] * p_scale\nlet inPower = msg.payload[59] * p_scale\nlet kWh = msg.payload[68] * 0.001\n\nlet siteID = msg.topic\n\nmsg.topic = \"solar/\"+siteID\n\nmsg.payload = {\n \"battV\": battV,\n \"battI\": battI,\n \"arrayV\": arrayV,\n \"arrayI\": arrayI,\n \"state\": statenum,\n \"hsTemp\": hsTemp,\n \"rtsTemp\": rtsTemp,\n \"outPower\": outPower,\n \"inPower\": inPower,\n \"kWh\": kWh\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":880,"y":100,"wires":[["a121ea75dd916c1d"]]},{"id":"ce08ad79910c0c7e","type":"modbus-flex-getter","z":"f5e24dbfed1c15df","name":"Tristar Modbus Get","showStatusActivities":true,"showErrors":false,"showWarnings":true,"logIOActivities":false,"server":"0ab0bb5c9e3e86da","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"keepMsgProperties":false,"delayOnStart":false,"startDelayTime":"","x":570,"y":100,"wires":[["cd5b30045dcd18ac"],[]]},{"id":"7992977c5f1044d1","type":"function","z":"f5e24dbfed1c15df","name":"Morningstar Tristar","func":"msg.payload = { 'fc': 3, 'unitid': 1, 'address': 0, 'quantity': 80 }\nmsg.topic = \"morningstar-tsmppt60\"\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":100,"wires":[["ce08ad79910c0c7e"]]},{"id":"bb0cae00c9269f01","type":"function","z":"f5e24dbfed1c15df","name":"Parse VE.Direct","func":"let battV = msg.payload.V * 0.001\nlet battI = msg.payload.I * 0.001\nlet arrayV = msg.payload.VPV * 0.001\nlet arrayI = msg.payload.PPV /arrayV\nlet statenum = msg.payload.CS\nlet outPower = msg.payload.PPV\nlet kWh = msg.payload.H20 *0.01\n\n//change state values to match Tristar\nif (statenum == 0) { statenum = 3 } // Off/Night\nelse if (statenum == 2) { statenum = 4 } // Fault\nelse if (statenum == 3) { statenum = 5 } // Bulk\nelse if (statenum == 4) { statenum = 6 } // Absorb\nelse if (statenum == 5) { statenum = 7 } // Float\n\nlet siteID = \"victron-1\"\n\nmsg.topic = \"solar/\"+siteID\n\nmsg.payload = {\n \"battV\": battV,\n \"battI\": battI,\n \"arrayV\": arrayV,\n \"arrayI\": arrayI,\n \"state\": statenum,\n \"outPower\": outPower,\n \"kWh\": kWh\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":880,"y":300,"wires":[["a121ea75dd916c1d"]]},{"id":"536427ca.a24558","type":"function","z":"f5e24dbfed1c15df","name":"VE.Direct to JSON","func":"// name: VE.Direct to JSON \n// https://github.com/timokovanen/nodered-vedirect-to-json\n// outputs: 1\n// inputs: 1 serial node, split input \"on the character\" \"\\n\"\n// and deliver \"binary buffers\"\n\nvar block = context.get('block') || \"{\";\nvar checksum = context.get('checksum') || 0;\n\nvar fieldBuffer = msg.payload;\nvar fieldLenght = fieldBuffer.length;\n\nvar field = fieldBuffer.toString();\nfield = field.replace(/\\r?\\n?/g, '');\nfield = field.split(\"\\t\");\n\n// Drop null fields\nif (field[0] === \"\") {\n// node.warn(\"NULL field dropped!\");\n return null;\n}\n\nif (field[0] === \"OR\") {\n field[1] = parseFloat((field[1]).substr(7));\n}\n\n// Remove HEX messages and add CR+LF for checksum\nif (field[0] == \"Checksum\" && field[1].length > 1) {\n fieldLenght = 12;\n fieldBuffer[10] = 0xd;\n fieldBuffer[11] = 0xa;\n// node.warn(\"HEX message stripped!\");\n}\n\nfor (var i = 0; i < fieldLenght; i++) {\n checksum -= fieldBuffer[i];\n}\n\nchecksum = checksum & 0xff;\n\nif (field[0] == \"Checksum\") {\n\n context.set('checksum', null);\n context.set('block', null);\n\n if (checksum === 0) {\n block = block += \"}\";\n msg.payload = JSON.parse(block);\n return msg;\n } else {\n// node.warn('Checksum failed!')\n return null;\n }\n}\n\nif (block.length > 1) {\n block = block += \",\";\n}\n\nblock = block += \"\\\"\" + field[0] + \"\\\"\" + \":\";\n\n// PID and FW values detected incorrectly as number\nvar isANumber = isNaN(field[1]) === false;\nif (isANumber && (field[0] !== \"PID\") && (field[0] !== \"FW\")) {\n// block = block += \"\\\"\" + field[1] + \"\\\"\";\n block = block += field[1];\n} else {\n// block = block += field[1];\n block = block += \"\\\"\" + field[1] + \"\\\"\";\n}\n\ncontext.set('checksum', checksum);\ncontext.set('block', block);\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":500,"y":300,"wires":[["9d4ece0e827b1e02"]]},{"id":"9d4ece0e827b1e02","type":"trigger","z":"f5e24dbfed1c15df","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"30","extend":false,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":700,"y":300,"wires":[["bb0cae00c9269f01"]]},{"id":"df4acb7e3a248baa","type":"serial in","z":"f5e24dbfed1c15df","name":"VE.Direct","serial":"2e265788d73d0dc5","x":300,"y":300,"wires":[["536427ca.a24558"]]},{"id":"a121ea75dd916c1d","type":"mqtt out","z":"f5e24dbfed1c15df","name":"","topic":"","qos":"0","retain":"false","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"e09210091108c943","x":1170,"y":180,"wires":[]},{"id":"f6a60db2b7960f09","type":"inject","z":"f5e24dbfed1c15df","d":true,"name":"30s","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"30","crontab":"","once":true,"onceDelay":"30","topic":"","payload":"","payloadType":"date","x":90,"y":160,"wires":[["7992977c5f1044d1","1875107bda0e9243","88183794e37814f6"]]},{"id":"087002bf20b20498","type":"modbus-client","name":"Midnite","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"failureLogEnabled":true,"tcpHost":"127.0.0.1","tcpPort":"502","tcpType":"TCP-RTU-BUFFERED","serialPort":"/dev/ttyUSB","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","serialAsciiResponseStartDelimiter":"0x3A","unit_id":"1","commandDelay":"200","clientTimeout":"1000","reconnectOnTimeout":true,"reconnectTimeout":"5000","parallelUnitIdsAllowed":true,"showWarnings":true,"showLogs":true},{"id":"f3a135728011b42a","type":"modbus-client","name":"Outback","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"failureLogEnabled":true,"tcpHost":"127.0.0.1","tcpPort":"502","tcpType":"DEFAULT","serialPort":"/dev/ttyUSB","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","serialAsciiResponseStartDelimiter":"0x3A","unit_id":"255","commandDelay":"200","clientTimeout":"2000","reconnectOnTimeout":true,"reconnectTimeout":"20000","parallelUnitIdsAllowed":true,"showWarnings":true,"showLogs":true},{"id":"0ab0bb5c9e3e86da","type":"modbus-client","name":"Tristar","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"failureLogEnabled":true,"tcpHost":"127.0.0.1","tcpPort":"502","tcpType":"DEFAULT","serialPort":"/dev/ttyUSB","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","serialAsciiResponseStartDelimiter":"0x3A","unit_id":"1","commandDelay":"1","clientTimeout":"2000","reconnectOnTimeout":true,"reconnectTimeout":"2000","parallelUnitIdsAllowed":true,"showWarnings":true,"showLogs":true},{"id":"2e265788d73d0dc5","type":"serial-port","serialport":"/dev/ttyUSB0","serialbaud":"19200","databits":"8","parity":"none","stopbits":"1","waitfor":"","dtr":"none","rts":"none","cts":"none","dsr":"none","newline":"\\n","bin":"bin","out":"char","addchar":"","responsetimeout":"10000"},{"id":"e09210091108c943","type":"mqtt-broker","name":"","broker":"127.0.0.1","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""}]