Announce: node-red-contrib-ui-heatmap (beta release)

@BartButenaers, my flow (using your heatmap) works, but I am not happy as on top of 1Hz update rate, there is about 5s lug most likely due to nodered reading output from command line. Without nodered the application produces output smoothly every 1s. Anyhow, I am looking for another way of reading data from MLX90640 to nodered to make it better (faster, more reliable, higher frame rate). Thank you for your support.
Cheers

[{"id":"27b40865.2d54b","type":"exec","z":"1f1e04cf.2f4383","command":"/temp/RPiMLX90640/./MLX90640 0.5","addpay":true,"append":"0.5","useSpawn":"true","timer":"","oldrc":true,"name":"","x":370,"y":160,"wires":[["d58d97fa.e54e2"],[],[]]},{"id":"d58d97fa.e54e2","type":"function","z":"1f1e04cf.2f4383","name":"Concatenate","func":"if (msg.payload)\n{\n    console.log(\"here\");\n    var status = flow.get('status') || 0;\n    var goodmsg = flow.get('goodmsg') || \"\";\n    var tempmsg = flow.get('tempmsg') || \"\";\n    \n// status:    \n// 0 - searching for 'HERE'\n// 1 - 'HERE' found, searching for 'END'\n// 2 - no 'END' within the msg.payload, concat next msg.payload  \n\n    var fullpayload = msg.payload;\n    \n    if(status === 0)\n    {('')\n        // searching for start ('HERE') PRINTING IMAGE FROM DISPALY IMAGE MAIN')\n        var start1 = fullpayload.indexOf(\"DISPALY IMAGE MAIN\"); \n        if (start1 >= 0)\n        {\n            var s1 = fullpayload.substring(start1+48, fullpayload.length); // was start+49\n            // see if 'END' within the same msg\n            var end1 = s1.indexOf(\"END\");\n            if (end1 >= 0)\n            {\n                // full message within a single output\n                var res1 = s1.substring(0, end1);\n                goodmsg = res1;\n                msg.payload = \"FIRST and FULL\" + goodmsg;\n                // reset for a new search\n                // ?????\n                goodmsg = \"\";\n                tempmsg = \"\";\n                status = 0;\n                flow.set('goodpmsg', goodpmsg);\n                flow.set('tempmsg', tempmsg);\n                flow.set('status', status);\n                return msg;\n            } else\n                // found \"HERE\", not \"END\"\n                {\n                    status = 1;\n                    tempmsg = s1.substring(0, s1.length);\n                    flow.set('tempmsg', tempmsg);\n                    flow.set('status', status);\n                    //msg.payload = status + \" HERE not END\";\n                    //return msg;\n                }\n        }\n\n    }else\n        // status > 0, search for \"END\" ************COLORIZED Data\n        {\n            var end2 = fullpayload.indexOf(\"************COLORIZED Data\");\n            if (end2 >= 0)\n            {\n                var s2 = fullpayload.substring(0, end2);\n                tempmsg = tempmsg + s2;\n                goodmsg = tempmsg;\n                tempmsg = {};\n                status =  0; // ready for a new search                \n                flow.set('tempmsg', tempmsg);                \n                flow.set('goodmsg', goodmsg);\n                flow.set('status', status);\n                msg.payload = goodmsg; \n                \n///////////////////////////////////////////////////////////                \n                // search for a start of new frame at the end of current frame               \n                var s3 = fullpayload.substring(end2, fullpayload.length);\n                var start2 = s3.indexOf(\"HERE\");\n                if (start2 >= 0)\n                    {\n                    var remaining = s3.substring(start2+48, s3.length);\n                    tempmsg = remaining;\n                    goodmsg =\"\";\n                    status = 1;\n                    flow.set('status', status);\n                    flow.set('tempmsg', tempmsg); \n                    flow.set ('goodmsg', goodmsg);\n                    //msg.payload = \"remaining\";\n                    //return msg;\n                    } \n                    //else {\n                    //        status = 0;\n                    //        tempmsg = \"\";\n                    //        goodmsg = \"\";\n                    //        flow.set('status', status);\n                    //        flow.set('tempmsg', tempmsg);\n                    //        flow.set('goodmsg', goodmsg);\n                    //        }\n                          \n/////////////////////////////////////////////////////////////////////                            \n                return msg; \n            }\n        }\n}","outputs":1,"noerr":0,"x":610,"y":140,"wires":[["303b4057.574518","4378e1b.4ed432"]]},{"id":"ddef02ab.ef6ae8","type":"inject","z":"1f1e04cf.2f4383","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":150,"y":80,"wires":[["4062222.84acadc"]]},{"id":"4062222.84acadc","type":"function","z":"1f1e04cf.2f4383","name":"Init flow variables","func":"flow.set('status', 0);\n// 0 - searching for 'HERE'\n// 1 - 'HERE' found, searching for 'END'\n// 2 - no 'END' within the msg.payload, concat next msg.payload\nflow.set('goodmsg', \"\");\nflow.set('tempmsg', \"\");","outputs":1,"noerr":0,"x":350,"y":80,"wires":[[]]},{"id":"303b4057.574518","type":"debug","z":"1f1e04cf.2f4383","name":"","active":false,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","x":820,"y":100,"wires":[]},{"id":"d2a2b2c5.3e0458","type":"ui_template","z":"1f1e04cf.2f4383","group":"58403db.6185d44","name":"Clock Toolbar","order":4,"width":"0","height":"0","format":"<script id=\"titleScript\" type=\"text/javascript\">\n    $('#clock').remove();\n    var toolbar = $('.md-toolbar-tools');\n    var div = $('<div/>');\n    var p = $('<p/ id=\"clock\">');\n    \n    $('#titleScript').parent().hide();\n    div.append(p);\n    div[0].style.margin = '5px 5px 5px auto';\n    toolbar.append(div);\n\n    function displayTitle(lh) {\n        p.text(lh); \n    }\n    \n    function upTime() {\n        var d = new Date();\n        p.text(d.toLocaleTimeString('de-AT'));\n    }\n\n    \n\n    // Watch the payload and update the title\n    (function(scope) {\n        scope.$watch('msg.payload', function(data) {\n            displayTitle(data);\n        });\n        setInterval(upTime,1000);\n    })(scope);\n</script>","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":140,"y":40,"wires":[[]]},{"id":"4378e1b.4ed432","type":"function","z":"1f1e04cf.2f4383","name":"Create Array","func":"if(msg.payload)\n    {\n    var s = msg.payload ;   \n    var start = 0;\n    var end = 0;\n    // create an array     \n    var heat = [];\n        for(i = 0; i < 768; i++) \n        {\n            end = s.indexOf(\";\", start)\n            //heat[i] = s.substring(start, end); \n            var temp = parseFloat(s.substring(start, end));\n            temp /= 10;\n            (Math.round(temp * 10) / 10).toFixed(1);\n            if (isNaN(temp)) temp = 0.1;\n            if(temp > 150) temp = 100;\n            if(temp < 0.1) temp = 0.1;\n            heat[i] = temp;\n            start = end+1;\n        }        \n    msg.payload = heat;\n    return msg;\n    }","outputs":1,"noerr":0,"x":810,"y":140,"wires":[["9c10f24c.3bc9","7c497721.125df8"]]},{"id":"9c10f24c.3bc9","type":"debug","z":"1f1e04cf.2f4383","name":"","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","x":1040,"y":80,"wires":[]},{"id":"7c497721.125df8","type":"heat-map","z":"1f1e04cf.2f4383","group":"58403db.6185d44","order":0,"width":"16","height":"12","name":"","rows":"32","columns":"24","minMax":false,"minimumValue":"1","maximumValue":"50","backgroundColor":"#ffffff","radius":"40","opacity":0.6,"blur":0.85,"showValues":true,"valuesDecimals":"1","showLegend":true,"legendDecimals":"0","legendCount":"5","x":1020,"y":140,"wires":[[]]},{"id":"4b743966.c26368","type":"inject","z":"1f1e04cf.2f4383","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":"2","x":120,"y":160,"wires":[["27b40865.2d54b"]]},{"id":"58403db.6185d44","type":"ui_group","z":"","name":"Heatmap","tab":"de218ad6.e9ef6","order":2,"disp":true,"width":"16","collapse":true},{"id":"de218ad6.e9ef6","type":"ui_tab","z":"","name":"NewMLX90640","icon":"dashboard","order":4,"disabled":false,"hidden":false}]