View and process Pylontech battery data

I connected today my Pylontech batteries via its console port into my Raspberry via USB. This allows me to query them via the serial node.
Here is what the command looks like in PWR terminal emulation under Linux:

From NR, I run this same command and I get the data back fine. Now, for me begins the most difficult part.




image

I want to be able to display these values in a table, but also to be able to process each value to make further decisions according to their values.
Could you help me to

  1. create a dashboard table that will be refreshed regularly with the command
  2. store each values for later use, Obviously except when the lines are empty of data

Currently I have only one rack connected, if I connect another one, there will be values in the array No4 and following if more rack.

I personally am not very comfortable with handling bulk data. So I want to get started the right way.
And I'm not a programmer.Please be lenient.

The terminal output looks like tab separated data. You could use the join node to produce a string, instead of an array and try to feed that into a csv node and set the mode to tab.

The idea is good, I had never used this CSV node.
I removed the first 3 lines from the top.
It looks promising.


next step soon. Time to eat :plate_with_cutlery:

I think you can configure the csv node to use the first line as the column names, which in turn will use them as properties names for the values (ie, instead of col1:x, it will become Power:x} ) - in addition you can set the output to 'single array', which then can be mapped to a ui-table.

The result is not there yet, but we are getting closer.
The fields are not yet in the right places
Here is the CSV setting & debug informations


image

and the result on the screen

Is there any way to delete the last 2 rows of the matrix?
I think I need to do some formatting before the UI_TABLE in a Function node? Or to define each column in the Table node?
image

image

Hi JL...

This is how I am doing it.... (Yeh, I know my coding isnt the best, but it works for me...)

    {
        "id": "14161f8f.5bb748",
        "type": "function",
        "z": "a8cb3b26.8b8608",
        "g": "35635e2d.56d582",
        "name": "Extract Pylon req Bat Query Data",
        "func": "if(msg.payload == \"clear\"){//clear and initialise\n    context.set(\"arr\",undefined,\"file\") ;\n    return null\n}\n\nvar rT = 17; // Table max #rows(1st is headings)\n    cT = global.get('INVNoBattStrings','file')+1 ; // Table max #columns(was5)\n    r  = 0 ; // Row Counter\n    c  = 0 ; // Column Counter\n    \nif(context.get(\"arr\",\"file\") != undefined){\n    arr = context.get(\"arr\",\"file\")\n}\nelse{\n    arr = [];//Doesn't exist, so create the base array\n} \n//Layout Blank Table\nfor(r = 0; r < rT; r++) { // for each of rows(1st is headings)\n    if(arr [r] == undefined){//Create if it doesn't exist\n        arr[r] = []\n        }    \n    for(c = 0; c < cT; c++) {  // create columns\n        if(arr[r][c] == undefined){//Create if it doesn't exist\n            arr[r][c] = null\n        }\n    }    \n}\n\n//Put in Headings/Titles\narr[0][0] = \"Batt#\";//\"Power\";\narr[1][0] = \"Volt\";\narr[2][0] = \"Amps\";//\"Curr\";\narr[3][0] = \"ºC\"//\"Temp C\";//\"Tempr\";\narr[4][0] = \"SOC\";//\"Coulomb\"; \narr[5][0] = \"Tlow\";\narr[6][0] = \"Thigh\";\narr[7][0] = \"Vlow\";\narr[8][0] = \"Vhigh\";\narr[9][0] = \"Mos ºC\";//\"MosTempr\";\narr[10][0] = \"Base.St\";\narr[11][0] = \"Volt.St\";\narr[12][0] = \"Curr.St\";  \narr[13][0] = \"Temp.St\"; \narr[14][0] = \"B.V.St\";\narr[15][0] = \"B.T.St\";\narr[16][0] = \"M.T.St\";\n//arr[13][0] = \"Time\";\n\nData = msg.payload;\nBatt = \"\";\nBatt = msg.payload.substring(55,61);//Get Unique for this query\n\n//Look for trigger string\nif(Batt == \"Dischg\" || Batt == \"Charge\"||Batt == \"Idle  \"){\n    //Data Populate from msg.payload string\n    Address = Number(msg.payload.substring(0,5));//Battery\n    arr[0][Address]= Number(msg.payload.substring(0,5));//Battery\n    arr[1][Address]= Number(msg.payload.substring(6,12))/1000;//Volt\n    arr[2][Address]= Number(msg.payload.substring(13,19))/1000;//Curr\n    arr[3][Address]= Number(msg.payload.substring(20,26))/1000;//Tempr\n    arr[4][Address]= Number(msg.payload.substring(91,99).trim().replace('%', ''));//Coulomb\n    arr[5][Address]= Number(msg.payload.substring(27,33))/1000;//Tlow\n    arr[6][Address]= Number(msg.payload.substring(34,40))/1000;//Thigh\n    arr[7][Address]= Number(msg.payload.substring(41,47))/1000;//Vlow\n    arr[8][Address]= Number(msg.payload.substring(48,54))/1000;//Vhigh\n    arr[9][Address]= Number(msg.payload.substring(138,146))/1000;//MosTempr\n    arr[10][Address]= msg.payload.substring(55,63).trim();//Base.St\n    arr[11][Address]= msg.payload.substring(64,72).trim();//Volt.St\n    arr[12][Address]= msg.payload.substring(73,81).trim();//Curr.St\n    arr[13][Address]= msg.payload.substring(82,90).trim();//Temp.St\n    arr[14][Address]= msg.payload.substring(121,129).trim();//B.V.St\n    arr[15][Address]= msg.payload.substring(130,137).trim();//B.T.St\n    arr[16][Address]= msg.payload.substring(147,154).trim();//M.T.St\n    //arr[][]= msg.payload.substring(100,120).trim();//Time\n    context.set(\"arr\",arr,\"file\");\n    for(r = 0; r < rT; r++) { // for each of rows(1st is headings)\n        for(c = 0; c < cT; c++) { // for each of columns\n        if(arr[r][c] == null){ // Check it's populated\n            return; //Get out of here, table isnt full yet\n            }\n        }    \n    }\n    //Table is populated fully - Serve it!\n    msg.payload = arr;\n    return msg;\n}\n\nreturn;//Not a trigger string, just leave!\n\n//Power Volt   Curr   Tempr  Tlow   Thigh  Vlow   Vhigh  Base.St  Volt.St  Curr.St  Temp.St  Coulomb  Time                 B.V.St   B.T.St   MosTempr M.T.St\n//2     49020  -4748  27500  23900  24000  3268   3268   Dischg   Normal   Normal   Normal   36%      2021-05-20 22:32:13  Normal   Normal  25600    Normal\n//1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\n\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 330,
        "y": 1020,
        "wires": [
            [
                "b9680781.24a37"
            ]
        ]
    },
    {
        "id": "b9680781.24a37",
        "type": "delay",
        "z": "a8cb3b26.8b8608",
        "g": "35635e2d.56d582",
        "name": "",
        "pauseType": "rate",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "2",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": true,
        "outputs": 1,
        "x": 580,
        "y": 1020,
        "wires": [
            [
                "a1639b7e.8df1a8"
            ]
        ]
    },
    {
        "id": "a1639b7e.8df1a8",
        "type": "ui_table",
        "z": "a8cb3b26.8b8608",
        "g": "35635e2d.56d582",
        "group": "78ad4256.fda3d4",
        "name": "Batt Basic Info",
        "order": 2,
        "width": 7,
        "height": 10,
        "columns": [],
        "outputs": 0,
        "cts": false,
        "x": 770,
        "y": 1020,
        "wires": []
    },
    {
        "id": "2365c560.586fa2",
        "type": "link in",
        "z": "a8cb3b26.8b8608",
        "g": "35635e2d.56d582",
        "name": "",
        "links": [
            "a210e66a.672038",
            "166e9d46.bfd18b"
        ],
        "x": 155,
        "y": 1020,
        "wires": [
            [
                "14161f8f.5bb748"
            ]
        ]
    },
    {
        "id": "78ad4256.fda3d4",
        "type": "ui_group",
        "name": "Pylon Info",
        "tab": "ac8a95bb.741908",
        "order": 4,
        "disp": true,
        "width": "7",
        "collapse": true
    },
    {
        "id": "ac8a95bb.741908",
        "type": "ui_tab",
        "name": "Management & Diag",
        "icon": "dashboard",
        "order": 11,
        "disabled": false,
        "hidden": false
    }
]

Regds
Ed

it's cool. Is it for the "bat" command? I think so. it is written, I had not seen, it was too big :eyes:
I have a mini bug on the function node. Can you just guide me?
image
image

on the other hand nothing comes out of the function extract node, I have the "bat" data in input and on the "out" debug node but nothing into "extract" debug node

I saw in the meantime that another person proposed a flow with MQTT. To test ....
The link here Github

What if you skip the first 2 lines ?

Hi JL,

I inject a java string every 10sec - "pwr\r" - using an inject node,,,

on line7 - cT = global.get('INVNoBattStrings','file')+1 ; // Table max #columns(was5)
change it to: cT = 2
This is how many batteries you have linked together... I store it as a global parameter that is written to disk... (hence the <,'file' > bit of the command)

for testing and playing, you should just be able to modify the function node as follows:
search for all lines that contain the <,'file'> and delete the ,'file' bit of it...

ie: change arr = context.get("arr","file")
to arr = context.get("arr")

I will be online for a bit still... Just shout!!

Regds
Ed

1 Like

Sorry, I could not resume until now.
Ed, it works, I have a beautiful table with the values inside :grinning:
Thank you for sharing your flow with me.

2 Likes

The first lines don't have any useful information, so I configured the CSV node to skip them.
That was the question ? As I use a translator, I'm not sure I understood the remark

Hi JL,

Remember that you can change the number of Pylons you have by changing the value of the cT variable....

The table should automatically resize and add extra columns for your additional batteries when you do....

Glad it's working for you!!

Regds
Ed

1 Like

Salut Ed
I have quite a few things working, I still have the empty columns displayed when I don't want them.
But it's slowly taking shape

Test-Dashboard-Pylontech

Ed, do you have any idea why the columns stay at 8 on the dashboard?
I do store the value 2 in a global variable, but no matter what value I put, it stays at 8 columns. I'm confused. It must be in front of me but I don't see :flushed:

Value of the number of columns:

The function code:

The result:
image

The global value stored:
image

The Ui_Table setting:
image

Under linux, on the global context:
~/global-variables/context/global $ cat global.json, the variable is also OK
"Nbre_bat_Pylontech": 2,

Hi JL,

I wrote a clear command into the function, possibly the array/table isn't clearing fully... inject a payload "clear" into the function, see if that helps...

Regds
Ed

PS... Very nice indeed!! I like the way u are doing it!!

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.