Upload JK Jikong BMS voltage and current to pvoutput
This flow requests the JK Jikong BMS (mine is a JK-B2A24S20p) data via serial USB adapter and uploads it to pvoutput. This enables monitoring the daily battery in and out power.
You have to adapt the serial port and the pvoutput API key and sid in the flow.
The hardware I'm using is a: USB-TTL Serial Modul FT232RL FTDI 3,3V und 5V ISP USB Arduino Pro Mini Adapter that is connected to a Raspberry pi USB port an directly to the TTL out port of the BMS.E.g.: https://www.amazon.de/gp/product/B07G87GW4K/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1
The wiring is described here: https://forum.drbacke.de/viewtopic.php?p=26587&sid=2f287444a5f854d60080d1586b933581#p26587
The TTL Adapter is direclty connected via a 4-pin 1.25 Micro JST plug cable. Like this one: https://www.ebay.de/itm/185411283211?var=693203929249
[{"id":"7edcc5c2b2f40e74","type":"serial request","z":"3e20d16d6d150144","name":"","serial":"33a190bb5f257765","x":690,"y":800,"wires":[["25ab3190f361277a","5a9728861aea2322"]]},{"id":"66240475699d21f7","type":"inject","z":"3e20d16d6d150144","name":"trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[]","payloadType":"bin","x":110,"y":760,"wires":[["0177aa7fd0a231f2"]]},{"id":"0177aa7fd0a231f2","type":"function","z":"3e20d16d6d150144","name":"","func":"\nmsg.timeout = 10000;\n//msg.payload = [0x55, 0xaa, 0x01, 0xff, 0x00, 0x00, 0xff];\n//msg.payload = [0xdd, 0xa5, 0x03, 0x00, 0xff, 0xfd , 0x77];\n//msg.payload = [0x4E, 0x57, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x01, 0x29]\n\n//msg.payload = Buffer.from([0x55, 0xaa, 0x01, 0xff, 0x00, 0x00, 0xff])\n//msg.payload = Buffer.from([0xdd, 0xa5, 0x03, 0x00, 0xff, 0xfd , 0x77])\n\nmsg.payload = Buffer.from([0x4E, 0x57, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x01, 0x29])\n\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":340,"y":800,"wires":[["7edcc5c2b2f40e74","3729c9fbf751f535"]]},{"id":"25ab3190f361277a","type":"debug","z":"3e20d16d6d150144","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1030,"y":800,"wires":[]},{"id":"3729c9fbf751f535","type":"debug","z":"3e20d16d6d150144","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":490,"y":760,"wires":[]},{"id":"5a9728861aea2322","type":"function","z":"3e20d16d6d150144","name":"parse payload","func":"function chksum(buffer, len) {\n checksum = 0;\n for (ii = 0; ii < len; ii++) {\n checksum = checksum + buffer[ii];\n }\n return checksum;\n}\n\n\nmsg.buffer = msg.payload;\nif (msg.buffer[0] != 0x4E || msg.buffer[1] != 0x57) {\n node.warn ('Invalid header')\n //return;\n}\n\nmsg.jkbms = {};\nmsg.jkbms.data_len = msg.buffer[2] << 8 | msg.buffer[2 + 1] << 0;\nmsg.jkbms.function = msg.buffer[8];\nmsg.jkbms.computed_crc = chksum(msg.buffer, msg.jkbms.data_len);\n//msg.jkbms.computed_crc = chksum(msg.buffer, msg.buffer.length);\n\nmsg.jkbms.remote_crc = msg.buffer[msg.jkbms.data_len] << 8 | msg.buffer[msg.jkbms.data_len + 1] << 0;\n\nif (msg.jkbms.computed_crc != msg.jkbms.remote_crc) {\n node.warn('JkModbus CRC Check failed!');\n}\n\n//cell voltages\nlet cellbase = 11+1;\nmsg.jkbms.numberOfCells = msg.buffer[cellbase] / 3;\nmsg.jkbms.cell_voltages = {};\nfor (cell = 1; cell <= msg.jkbms.numberOfCells; cell++) {\n msg.jkbms.cell_voltages['cell' + cell] = (msg.buffer[cellbase + cell * 3 - 1] << 8 | msg.buffer[cellbase + cell * 3] << 0) / 1000;\n}\ncellbase = 11 + 1 + msg.jkbms.numberOfCells * 3;\n\n//temperatures\nmsg.jkbms.temperatur = {};\ncellbase+=1;\nif (msg.buffer[cellbase] != 0x80) {\n node.warn ('temperatur marker not correct!');\n}\nmsg.jkbms.temperatur.internal = msg.buffer[cellbase + 1] << 8 | msg.buffer[cellbase + 2] << 0;\nmsg.jkbms.temperatur.battery1 = msg.buffer[cellbase + 4] << 8 | msg.buffer[cellbase + 5] << 0;\nmsg.jkbms.temperatur.battery2 = msg.buffer[cellbase + 7] << 8 | msg.buffer[cellbase + 8] << 0;\ncellbase += 8;\n\n//voltage\ncellbase+=1;\nif (msg.buffer[cellbase] != 0x83) {\n node.warn ('voltage marker not correct!');\n}\nmsg.jkbms.voltage = (msg.buffer[cellbase + 1] << 8 | msg.buffer[cellbase + 2] << 0) / 100;\ncellbase+= 2;\n\n//current\ncellbase+=1;\nif (msg.buffer[cellbase] != 0x84) {\n node.warn ('current marker not correct!');\n}\nmsg.jkbms.current = (msg.buffer[cellbase + 1] << 8 | msg.buffer[cellbase + 2] << 0);\nmsg.jkbms.current_raw = msg.jkbms.current;\n\nlet CURRENT_ZERO_CONSTANT = 32768\nif (msg.jkbms.current < CURRENT_ZERO_CONSTANT)\n msg.jkbms.current = msg.jkbms.current / -100;\nelse\n msg.jkbms.current = (msg.jkbms.current - CURRENT_ZERO_CONSTANT) / 100;\n\n//print(current / -100 if current < CURRENT_ZERO_CONSTANT else (current - CURRENT_ZERO_CONSTANT) / 100)\n\n/*if ((msg.jkbms.current & 0x8000) == 0x8000) {\n msg.jkbms.current = msg.jkbms.current & 0x7FFF;\n} else {\n msg.jkbms.current = msg.jkbms.current & 0x7FFF * -1;\n}*/\n\n/*if (msg.jkbms.current > 10000) {\n msg.jkbms.current-=10000;\n msg.jkbms.current*=-1;\n}\nmsg.jkbms.current/=100;*/\nmsg.jkbms.power = msg.jkbms.current * msg.jkbms.voltage;\ncellbase+= 2;\n\n//remainig battery\ncellbase+=1;\nif (msg.buffer[cellbase] != 0x85) {\n node.warn ('remaining battery SOC marker not correct!');\n}\nmsg.jkbms.remainigBattery = msg.buffer[cellbase + 1];\ncellbase+= 1;\n\n//numberof NTC\ncellbase+=1;\nif (msg.buffer[cellbase] != 0x86) {\n node.warn ('number of NTC marker not correct!');\n}\nmsg.jkbms.numberOfNTC = msg.buffer[cellbase + 1];\ncellbase+= 1;\n\n//numberof battery cycles\ncellbase+=1;\nif (msg.buffer[cellbase] != 0x87) {\n node.warn ('number of battery cycles marker not correct!');\n}\nmsg.jkbms.numberOfBatteryCycles = (msg.buffer[cellbase + 1] << 8 | msg.buffer[cellbase + 2] << 0);\ncellbase+= 2;\n\n//battery cycle capacity Ah\ncellbase+=1;\nif (msg.buffer[cellbase] != 0x89) {\n node.warn ('battery cycle capacity Ah marker not correct!');\n}\nmsg.jkbms.batteryCycleCapacityAh = (msg.buffer[cellbase + 1] << 24 | msg.buffer[cellbase + 2] << 16 | msg.buffer[cellbase + 3] << 8 | msg.buffer[cellbase + 4] << 0);\ncellbase+= 4;\n\n//numberof strings\ncellbase+=1;\nif (msg.buffer[cellbase] != 0x8a) {\n node.warn ('number of strings marker not correct!');\n}\nmsg.jkbms.numberOfStrings = (msg.buffer[cellbase + 1] << 8 | msg.buffer[cellbase + 2] << 0);\ncellbase+= 2;\n\n//battery warning\nmsg.jkbms.warning = {};\n\nconst lowCapacity = 0x0000000000000001;\nconst powerTubeOvertemperature = 0x0000000000000010;\nconst chargingOvervoltage = 0x0000000000000100;\nconst dischargingUndervoltage = 0x0000000000001000;\nconst batteryOverTemperature = 0x0000000000010000;\nconst chargingOvercurrent = 0x0000000000100000;\nconst dischargingOvercurrent = 0x0000000001000000;\nconst cellPressureDifference = 0x0000000010000000;\nconst overtemperatureAlarmInTheBatteryBox = 0x0000000100000000;\nconst batteryLowTemperature = 0x0000001000000000;\nconst cellOvervoltage = 0x0000010000000000;\nconst cellUndervoltage = 0x0000100000000000;\nconst alarm309_AProtection = 0x0001000000000000;\nconst alarm309_BProtection = 0x0010000000000000;\n\ncellbase+=1;\nif (msg.buffer[cellbase] != 0x8b) {\n node.warn ('battery warning marker not correct!');\n}\nmsg.jkbms.warning.batteryWarning = (msg.buffer[cellbase + 1] << 8 | msg.buffer[cellbase + 2] << 0);\n\nmsg.jkbms.warning.lowCapacity = msg.jkbms.warning.batteryWarning & lowCapacity;\nmsg.jkbms.warning.powerTubeOvertemperature = msg.jkbms.warning.batteryWarning & powerTubeOvertemperature;\nmsg.jkbms.warning.chargingOvervoltage = msg.jkbms.warning.batteryWarning & chargingOvervoltage;\nmsg.jkbms.warning.dischargingUndervoltage = msg.jkbms.warning.batteryWarning & dischargingUndervoltage;\nmsg.jkbms.warning.batteryOverTemperature = msg.jkbms.warning.batteryWarning & batteryOverTemperature;\nmsg.jkbms.warning.chargingOvercurrent = msg.jkbms.warning.batteryWarning & chargingOvercurrent;\nmsg.jkbms.warning.dischargingOvercurrent = msg.jkbms.warning.batteryWarning & dischargingOvercurrent;\nmsg.jkbms.warning.cellPressureDifference = msg.jkbms.warning.batteryWarning & cellPressureDifference;\nmsg.jkbms.warning.overtemperatureAlarmInTheBatteryBox = msg.jkbms.warning.batteryWarning & overtemperatureAlarmInTheBatteryBox;\nmsg.jkbms.warning.batteryLowTemperature = msg.jkbms.warning.batteryWarning & batteryLowTemperature;\nmsg.jkbms.warning.cellOvervoltage = msg.jkbms.warning.batteryWarning & cellOvervoltage;\nmsg.jkbms.warning.cellUndervoltage = msg.jkbms.warning.batteryWarning & cellUndervoltage;\nmsg.jkbms.warning.alarm309_AProtection = msg.jkbms.warning.batteryWarning & alarm309_AProtection;\nmsg.jkbms.warning.alarm309_BProtection = msg.jkbms.warning.batteryWarning & alarm309_BProtection;\n\ncellbase+= 2;\n\n//battery status\nmsg.jkbms.status = {};\n\nconst chargingEnabled = 0x0000000000000001;\nconst dischargingEnabled = 0x0000000000000010;\nconst balancingEnabled = 0x0000000000000100;\nconst batteryConnected = 0x0000000000001000;\n\ncellbase+=1;\nif (msg.buffer[cellbase] != 0x8c) {\n node.warn ('battery warning marker not correct!');\n}\nmsg.jkbms.status.batteryStatus = (msg.buffer[cellbase + 1] << 8 | msg.buffer[cellbase + 2] << 0);\n\nmsg.jkbms.status.chargingEnabled = msg.jkbms.status.batteryStatus & chargingEnabled;\nmsg.jkbms.status.dischargingEnabled = msg.jkbms.status.batteryStatus & dischargingEnabled;\nmsg.jkbms.status.balancingEnabled = msg.jkbms.status.batteryStatus & balancingEnabled;\nmsg.jkbms.status.batteryConnected = msg.jkbms.status.batteryStatus & batteryConnected;\n\ncellbase+= 2;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":160,"y":900,"wires":[["a6b6a4d7373d9a48","6ca89b6a0455b99d"]]},{"id":"a6b6a4d7373d9a48","type":"debug","z":"3e20d16d6d150144","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":950,"y":900,"wires":[]},{"id":"6ca89b6a0455b99d","type":"function","z":"3e20d16d6d150144","name":"prepare pvoutput upload","func":"if (msg.skip)\n return;\n\nlet myDate = new Date();\n\nlet currdate = myDate.\n toLocaleString('en-us', {year: 'numeric', month: '2-digit', day: '2-digit'}).\n replace(/(\\d+)\\/(\\d+)\\/(\\d+)/, '$3$1$2');\n\nlet currtime = myDate.getHours()+ ':' + myDate.getMinutes();\n\nlet batteryVoltage = msg.jkbms.voltage;\nlet BatteryChargePower = 0;\nlet BatteryDischargePower = 0;\n\nif (msg.jkbms.power > 0)\n BatteryChargePower = msg.jkbms.power;\nelse\n BatteryDischargePower = msg.jkbms.power * -1;\n\nmsg.payload = {\n d: currdate,\n t: currtime,\n v2: BatteryChargePower,\n v4: BatteryDischargePower,\n v6: batteryVoltage\n};\n\nmsg.requestTimeout = 5000;\nreturn msg;\n\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":190,"y":960,"wires":[["4bd46a82994a68b7"]]},{"id":"4bd46a82994a68b7","type":"http request","z":"3e20d16d6d150144","name":"pvoutput addstatus","method":"GET","ret":"txt","paytoqs":"query","url":"https://pvoutput.org/service/r2/addstatus.jsp?key=yourapikeyhere&sid=yoursidhere","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"credentials":{"user":"","password":""},"x":430,"y":960,"wires":[["9794e593bdcbbe51"]]},{"id":"9794e593bdcbbe51","type":"debug","z":"3e20d16d6d150144","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":950,"y":960,"wires":[]},{"id":"8c919b0b02dad084","type":"inject","z":"3e20d16d6d150144","name":"every 5 minutes","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"*/5 5-22 * * *","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":150,"y":820,"wires":[["0177aa7fd0a231f2"]]},{"id":"33a190bb5f257765","type":"serial-port","serialport":"/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A50285BI-if00-port0","serialbaud":"115200","databits":"8","parity":"none","stopbits":"1","waitfor":"","dtr":"none","rts":"none","cts":"none","dsr":"none","newline":"500","bin":"bin","out":"interbyte","addchar":"","responsetimeout":"10"}]