Simple real time multi value chart for NodeRed

This flow is based on my project work when I have to display many (30) trends on one chart. I have to set up data refresh frequency around 4Hz. This is 120 points per second to display. Standard chart from Node-Red's dashboard was too greedy for resources, so I have to find solution. This example is based on flow: https://flows.nodered.org/flow/c3dc75c47323a2754f5285225bce64b5

My example is for chart with 3 values, 2 left axis, 4Hz refresh rate. Values go in vector. First element in vector is for "Topic0" AAA, second for "Topic1" BBB, an so. Want more data? Add value to vector and add another trend object to "const data = {datasets: ['here']}" with another label and topic.

To make this work you have to:

  1. Add static content path in your Node-Red settings file (on linux: home/USERNAME/.node-red/settings.js). After change restart Node-Red!

httpStatic: [ {path: '/home/USERNAME/.node-red/static/', root: "/st/"}, {path: '/home/USERNAME/.node-red/reports/', root: "/doc/"}, ],

  1. Download and install libraries in "/home/USERNAME/.node-red/static/". If folder does not exist, make one. Paste link from below in web browser, select all text, copy, paste in text file, save as .js):

2.1 chart.js (version 3.9.1), basic library for charts, save as chart.js https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.js

2.2 luxon (version 3.3.0), date and time, save as luxon.js https://cdn.jsdelivr.net/npm/[email protected]/build/global/luxon.min.js

2.3 luxon (version 1.3.1) adapter for chart.js, save as luxonA.js https://cdnjs.cloudflare.com/ajax/libs/chartjs-adapter-luxon/1.3.1/chartjs-adapter-luxon.esm.js

2.4 chartjs-plugin-streaming (2.0.0), timeseries, save as chartjsS.js https://cdn.jsdelivr.net/npm/chartjs-plugin-streaming@2

2.5 In Node-Red you have to have Dashboard installed.

[{"id":"c54c77472b55605b","type":"ui_template","z":"a63203244c0d1ccf","group":"c34b61ca.077f1","name":"chartMain","order":1,"width":13,"height":9,"format":"","storeOutMessages":false,"fwdInMessages":false,"resendOnRefresh":false,"templateScope":"local","className":"","x":840,"y":220,"wires":[[]]},{"id":"5f62758c7811a7ee","type":"template","z":"a63203244c0d1ccf","name":"","field":"template","fieldType":"msg","format":"html","syntax":"mustache","template":"\n<canvas id=\"myChart\" width=\"300\" height=\"200\"></canvas>\n<script src=\"/st/chart.js\"></script>\n<script src=\"/st/luxon.js\"></script>\n<script src=\"/st/luxonA.js\"></script>\n<script src=\"/st/chartjsS.js\"></script>\n<script>\n    {{{script}}}\n</script>","output":"str","x":700,"y":220,"wires":[["c54c77472b55605b"]]},{"id":"420fda75074a953d","type":"template","z":"a63203244c0d1ccf","name":"script","field":"script","fieldType":"msg","format":"javascript","syntax":"mustache","template":"(function () {\nconst ctx = document.getElementById('myChart').getContext('2d');\n\nvar payload;\n\n    function rewriteData(msg, scope) {\n        payload = msg.payload;\n    }\n\nconst data = {\n    datasets: [\n        {\n\n            topic: \"Topic0\", // used here not by chart.js\n            label: \"AAA\",\n            yAxisID: \"yAxis1\",\n            fill: false,\n            lineTension: 0,\n            borderColor: \"red\",\n            pointRadius: 1,\n            pointBorderColor: \"red\",\n            pointBackgroundColor: \"red\",\n            backgroundColor: \"red\",\n            borderWidth: 1,\n            data: [] // data is written here later\n        },\n        {\n            topic: \"Topic1\", // used here not by chart.js\n            label: \"BBB\",\n            yAxisID: \"yAxis2\",\n            fill: false,\n            lineTension: 0,\n            borderColor: \"blue\",\n            pointRadius: 1,\n            pointBorderColor: \"blue\",\n            pointBackgroundColor: \"blue\",\n            backgroundColor: \"blue\",\n            borderWidth: 1,\n            data: [] // data is written here later\n        },\n        {\n            topic: \"Topic3\", // used here not by chart.js\n            label: \"CCC\",\n            yAxisID: \"yAxis2\",\n            fill: false,\n            lineTension: 0,\n            borderColor: \"lightblue\",\n            pointRadius: 1,\n            pointBorderColor: \"lightblue\",\n            pointBackgroundColor: \"lightblue\",\n            backgroundColor: \"lightblue\",\n            borderWidth: 1,\n            data: [] // data is written here later\n        },\n    \n        \n    ]\n};\n\n\n\n    const onRefresh = chart => {\n        \n        const now = new Date();\n        for (let index = 0; index < chart.data.datasets.length; index++) {\n            \n\n            chart.data.datasets[index].data.push(\n                {\n                    x:now,\n                    y: payload[index]\n                }\n                )\n            \n        }\n    };\n\nconst chart = new Chart(ctx, {\n    type: 'line',\n    data: data,\n    options: {\n        scales: {\n            x: {\n                type: 'realtime',\n                realtime: {\n                    duration: 10000, //display time in ms \n                    ttl: 10000, //time after displayed data is deleted\n                    refresh: 250, //data refresh rate in ms\n                    delay: 1000, //delay in ms to make display fancy and start print before display\n                    onRefresh: onRefresh,\n                    frameRate: 30, //display refresh rate in FPS (frame per second), more == smoother display\n                    pause: false\n                }\n            },\n            yAxis1: {\n                type: 'linear',\n                display: true,\n                position: 'left',\n                ticks:\n                {\n                    count: 10,\n                },\n                title: {\n                    display: true,\n                    text: 'A[bar]'\n                },\n                \n                min: 0,\n                max: 60\n                \n            },\n            yAxis2: {\n                type: 'linear',\n                display: true,\n                position: 'left',\n                ticks:\n                {\n                    count: 10,\n                },\n                title: {\n                    display: true,\n                    text: 'B[L/min]'\n                },\n                min: 0,\n                max: 180\n            }\n        },\n        interaction: {\n            intersect: false\n        }\n    }\n\n});\n\n\n    function preloadMsg() {\n        var preMsg = { action: \"preload\", payload: \"preload\" };\n        return preMsg;\n    }\n\n    (function (scope) {\n        // this code gets run when the a view is opened on the node in the browser\n        // send a preload message back to node red to ask it send\n        // us a complete set of data. Pass down max points and time span to the helper node for it to use\n        // plus an array of the topics of interest\n        scope.send(preloadMsg());\n\n        scope.$watch('msg', function (msg) {\n            if (msg) {\n                rewriteData(msg, scope);\n            }\n        });\n    })(scope);\n\n})();","output":"str","x":550,"y":220,"wires":[["5f62758c7811a7ee"]]},{"id":"a9b07f1bd326c046","type":"inject","z":"a63203244c0d1ccf","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"0.1","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[$random()*50,$random()*150,$random()*150]\t","payloadType":"jsonata","x":410,"y":220,"wires":[["420fda75074a953d"]]},{"id":"c34b61ca.077f1","type":"ui_group","name":"Wykres w czasie rzeczywistym","tab":"8f1c9dab.c81588","order":1,"disp":true,"width":13,"collapse":false},{"id":"8f1c9dab.c81588","type":"ui_tab","name":"Główna","icon":"dashboard","order":1,"disabled":false,"hidden":false}]

Flow Info

Created 1 year, 11 months ago
Rating: not yet rated

Actions

Rate:

Node Types

Core
  • inject (x1)
  • template (x2)
Other
  • ui_group (x1)
  • ui_tab (x1)
  • ui_template (x1)

Tags

  • chart
  • timeseries
  • dashboard
  • luxon
  • chartJS
  • realtime
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option