How to visualize temperature map MLX90640


#1

Hi,
I need some hints how to visualize a thermal sensor data https://www.melexis.com/en/product/MLX90640/Far-Infrared-Thermal-Sensor-Array on RPi nodered dashboard.
Jan


#2

Is the resolution 32x24?
What information do get from the sensor?
Do you get a color code for each pixel?

If so, perhaps create an HTML table with 32 columns and 24 rows. Size each cell to fit into whatever size you need.
Then decode each pixel color into a cell.

Might take a bit of time and trial and error. As long as you have the information, I would think it would be doable.


#3

Hi,
interesting sensor!!
You might also create a html canvas and draw colored rectangles on it. Then you can show the canvas in any size you want.
Bart


#4

Thanks, that was my initial though as well. I see the photo here https://github.com/sparkfun/SparkFun_MLX90640_Arduino_Example implemented with processing sketch. That is what I would like to create. Luckily Pimoroni have libraries running on RPi, so I am able to get a text string with pixels data.


#5

I'm running Melexis application using the Exec node in nodered and have a problem. The console output is 5155 characters and is heavily fragmented by the Exec node and some character lost. Is there a way to increase buffer size for msg.load?
It repeats every second and looks as below:

END


CORRECT Line 665 write reg_config
CORRECT Line 665 write reg_config
CORRECT Line 665 write reg_config
CORRECT Line 665 write reg_config
CORRECT Line 665 write reg_config
CORRECT Line 665 write reg_config
CORRECT Line 696 write ir_data
CORRECT Line 665 write reg_config
CORRECT Line 665 write reg_config
CORRECT Line 516 write write_config
time was: 20190213203755
time was: 20190213213755
time was: 20190213213755
HERE

PRINTING IMAGE FROM DISPALY IMAGE MAIN

  232;  232;  229;  228;  232;  235;  231;  233;  229;  233;  227;  230;  236;  232;  226;  229;  231;  230;  232;  231;  239;  237;  228;  226;  227;  224;  224;  229;  227;  229;  227;  234;
  230;  232;  229;  231;  230;  228;  232;  233;  228;  229;  231;  232;  237;  230;  229;  230;  230;  228;  239;  237;  240;  237;  225;  226;  230;  230;  230;  228;  231;  229;  230;  230;
  234;  237;  233;  233;  234;  232;  231;  232;  232;  231;  231;  234;  234;  236;  232;  235;  236;  232;  234;  235;  236;  235;  233;  230;  233;  230;  230;  228;  234;  230;  229;  232;
  232;  237;  233;  234;  237;  232;  236;  235;  237;  230;  235;  234;  239;  234;  238;  237;  239;  233;  235;  235;  238;  235;  229;  228;  230;  228;  232;  232;  234;  228;  229;  235;
  239;  236;  230;  235;  233;  232;  230;  232;  232;  231;  232;  230;  235;  234;  232;  231;  230;  232;  227;  230;  233;  232;  226;  225;  227;  226;  228;  234;  230;  233;  225;  232;
  237;  231;  237;  235;  234;  234;  229;  228;  229;  229;  230;  230;  236;  232;  229;  232;  229;  230;  230;  230;  235;  228;  224;  229;  226;  228;  226;  227;  233;  229;  225;  232;
  235;  231;  230;  234;  231;  233;  229;  231;  230;  231;  228;  229;  233;  232;  229;  229;  228;  230;  229;  230;  231;  229;  224;  231;  228;  226;  222;  229;  228;  226;  226;  233;
  233;  231;  230;  232;  232;  229;  232;  233;  231;  230;  230;  233;  234;  233;  231;  229;  234;  228;  229;  232;  231;  226;  228;  228;  231;  228;  227;  231;  226;  230;  226;  228;
  235;  229;  229;  232;  232;  234;  228;  229;  230;  231;  229;  231;  230;  235;  230;  232;  229;  228;  228;  230;  228;  229;  226;  230;  228;  228;  225;  230;  231;  232;  229;  230;
  233;  229;  233;  229;  229;  231;  228;  232;  231;  229;  228;  229;  236;  231;  232;  231;  230;  226;  228;  230;  230;  227;  230;  230;  230;  225;  229;  231;  233;  235;  235;  238;
  232;  232;  232;  229;  230;  232;  228;  230;  231;  229;  228;  230;  230;  235;  230;  232;  229;  227;  227;  233;  232;  230;  229;  229;  229;  232;  234;  233;  236;  235;  229;  233;
  233;  234;  230;  233;  229;  232;  226;  233;  230;  229;  228;  231;  231;  235;  234;  234;  230;  230;  230;  234;  231;  233;  227;  235;  235;  233;  234;  233;  232;  231;  235;  236;
  229;  231;  228;  234;  233;  232;  228;  231;  232;  232;  230;  234;  235;  233;  231;  235;  234;  233;  232;  234;  233;  233;  233;  232;  236;  234;  234;  233;  236;  236;  234;  239;
  232;  231;  230;  232;  233;  232;  234;  237;  233;  235;  235;  234;  232;  234;  232;  233;  235;  235;  229;  235;  234;  233;  236;  233;  234;  233;  232;  233;  236;  236;  238;  239;
  233;  235;  231;  235;  233;  238;  234;  235;  236;  238;  234;  234;  233;  236;  236;  237;  236;  237;  232;  232;  233;  231;  233;  232;  228;  230;  227;  232;  235;  236;  236;  237;
  237;  235;  237;  235;  235;  235;  236;  236;  237;  235;  237;  237;  237;  238;  232;  239;  233;  234;  234;  233;  230;  229;  229;  230;  229;  225;  232;  229;  235;  235;  233;  236;
  237;  238;  234;  239;  237;  241;  237;  237;  240;  236;  231;  239;  237;  237;  234;  237;  234;  231;  232;  234;  228;  230;  229;  228;  227;  230;  228;  231;  236;  233;  235;  240;
  235;  238;  235;  238;  241;  233;  235;  240;  240;  237;  241;  237;  236;  233;  234;  236;  232;  231;  236;  237;  228;  228;  229;  230;  231;  231;  230;  230;  234;  234;  234;  242;
  235;  233;  233;  234;  236;  233;  236;  238;  239;  237;  235;  236;  238;  235;  231;  236;  230;  231;  235;  235;  234;  236;  235;  237;  233;  237;  230;  231;  236;  242;  238;  236;
  233;  232;  235;  239;  239;  236;  234;  241;  235;  240;  240;  239;  235;  234;  233;  232;  232;  232;  238;  240;  235;  236;  239;  236;  235;  233;  231;  234;  234;  236;  239;  239;
  231;  231;  230;  243;  238;  234;  233;  236;  235;  234;  234;  233;  237;  235;  235;  238;  239;  235;  237;  236;  238;  237;  236;  238;  234;  234;  230;  230;  238;  240;  234;  242;
  231;  233;  239;  233;  238;  236;  235;  236;  234;  237;  236;  237;  237;  238;  235;  238;  236;  237;  239;  237;  238;  240;  237;  241;  236;  237;  230;  231;  234;  234;  236;  239;
  236;  234;  234;  242;  239;  234;  236;  240;  238;  239;  237;  237;  240;  236;  233;  238;  237;  236;  237;  240;  239;  237;  237;  237;  241;  237;  232;  232;  240;  238;  237;  240;
  234;  234;  241;  243;  240;  236;  240;  238;  239;  237;  242;  239;  237;  236;  237;  238;  238;  238;  235;  238;  238;  237;  241;  241;  240;  239;  232;  236;  236;  235;  239;  242;

END


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

Does it really lose data or just break it into chunks - that then need to be concatenated back together ? Sending it as a massive text string is not particularly efficient - you are sending 6x the amount of data you really need to - but either way it should still get there.

Also please note that the debug node only shows about 1000 characters in the sidebar at a time - the full data is available in the console if you configure the node to send to there.


#7

After concatenating messages,I am able to run the application ./MLX90640 from nodered and display values on the canvas. As the data is transfered through system console it is slow and unreliable. My question is how can I access the frame buffer and display on the canvas on nodered. There is an example ./fbuf in Pimoroni library, which will keep updating the frame buffer (/dev/fb0). How can I access it from another application like nodered exec.
Any advice, links will be appreciated.


#8

Hey @janek, I'm currently developing something that should allow you to show a heatmap in the Node-RED dashboard. Had an issue with that last night, but @dceejay already proposad a solution. If that works, I hope to be able to offer you something next week ...

About the frame buffer... It would be interesting if you could post here a diagram of your setup: how the sensors are hardwired to your Raspberry (or perhaps other hardware in between), which programs/scripts you call with the exec node, ... And an export of your flow of course. Then it is easier for us to know what you are doing, without having to read all the information on the Github page.


#9

@BartButenaers, sorry for the late reply, but I was away. My sensor (ML90640) is connected to I2C pins of RPI (SDA, SCL) plus GND and 3.3V . My flow currently displays random colours (work in progress)

[{"id":"de2fc7f7.c12df","type":"tab","label":"MLX90640","disabled":false,"info":""},{"id":"1c4af653.42a7f2","type":"inject","z":"de2fc7f7.c12df","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":"2","x":120,"y":180,"wires":[["9ded099b.8e8fb"]]},{"id":"9ded099b.8e8fb","type":"exec","z":"de2fc7f7.c12df","command":"/temp/RPiMLX90640/./MLX90640 0.5","addpay":true,"append":"0.5","useSpawn":"true","timer":"","oldrc":true,"name":"","x":350,"y":180,"wires":[["d252ba8a.eb61e8"],[],[]]},{"id":"d252ba8a.eb61e8","type":"function","z":"de2fc7f7.c12df","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'\n        var start1 = fullpayload.indexOf(\"HERE\"); \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\"\n        {\n            var end2 = fullpayload.indexOf(\"END\");\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":160,"wires":[["6cb5dca2.a1accc","d99ebb42.a280b8"]]},{"id":"19d94a91.76e19d","type":"inject","z":"de2fc7f7.c12df","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":130,"y":100,"wires":[["2131a4f8.e0a804"]]},{"id":"2131a4f8.e0a804","type":"function","z":"de2fc7f7.c12df","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":330,"y":100,"wires":[[]]},{"id":"6a3210ae.48b2b","type":"debug","z":"de2fc7f7.c12df","name":"","active":false,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","x":980,"y":160,"wires":[]},{"id":"6cb5dca2.a1accc","type":"function","z":"de2fc7f7.c12df","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 < 24; i++) {\n            heat[i] = [];\n            for(j = 0; j < 32; j++) {\n                end = s.indexOf(\";\", start)\n                //var temp = parseFloat(s.substring(start, end));\n                //temp /= 10;\n                //temp = temp.toFixed(1);\n                heat[i][j] = s.substring(start, end);\n                //heat[i][j] = temp;\n                start = end+1;\n            }\n        }\n    msg.payload = heat;\n    return msg;\n    }","outputs":1,"noerr":0,"x":790,"y":160,"wires":[["6a3210ae.48b2b","72e082e6.ea94fc"]]},{"id":"72e082e6.ea94fc","type":"ui_template","z":"de2fc7f7.c12df","group":"b4dd4f99.74f6a8","name":"MLXHeatCam","order":2,"width":"12","height":"12","format":"<canvas id=\"myCanvas\" width=\"640\" height=\"520\" style=\"border:1px solid #000000;\"></canvas> \n<script>\n(function(scope) {\n    // watch msg object from Node-RED\n    scope.$watch('msg', function(msg) {\n        // new message received\n        var x = document.getElementById('myCanvas');\n        //x.setAttribute('src', msg.payload);\n        var ctx = x.getContext(\"2d\");\n        //ctx.beginPath();\n        //ctx.arc(95, 50, 40, 0, 2 * Math.PI);\n        //ctx.stroke();\n        // draw rectangles\n        var minTemp = 100;\n        var maxTemp = 0;\n        for (i=0; i<32; i++)\n            {\n                for(j=0; j<24; j++)\n                {\n                    var x = Math.floor(Math.random() * 256);\n                    var y = Math.floor(Math.random() * 256);\n                    var z = Math.floor(Math.random() * 256);\n                    var bgColor = \"rgb(\" + x + \",\" + y + \",\" + z + \")\";\n                    ctx.fillStyle = bgColor;\n                    ctx.fillRect(i*20,j*20, (i+1)*20, (j+1)*20);\n                    //ctx.rect(i*20,j*20, (i+1)*20, (j+1)*20);\n                    ctx.font = \"8px Arial\";\n                    ctx.fillStyle = \"#000000\";\n                    var degC = parseFloat(msg.payload[i,j]);\n                    degC /= 10;\n                    if(minTemp > degC) \n                        {minTemp = degC}\n                    if(maxTemp < degC) \n                        {maxTemp = degC}\n                    degC = degC.toFixed(1);\n                    //var degC = msg.payload[i,j];\n                    var degtext = degC.toString();\n                    //ctx.fillText(\"12.5\", i*20, j*20+15); \n                    ctx.fillText(degtext, i*20, j*20+15); \n                }\n            }\n        // draw legend\n        ctx.fillStyle = \"#000000\";\n        ctx.fillRect(0,480, 640,520);\n        ctx.font = \"28px Arial\";\n        ctx.fillStyle = \"#FFFFFF\";\n        \n        var step = (maxTemp - minTemp)/5;\n\n//        ctx.fillText(\"TEMP °C\", 0, 510); \n        minTemp = Math.round(minTemp);\n        ctx.fillText(minTemp + \"°\", 0, 510);\n        \n        var dispTemp = minTemp + step;\n        dispTemp = Math.round(dispTemp);\n        ctx.fillText(dispTemp + \"°\", 100, 510); \n        \n        var dispTemp1 = minTemp + 2*step; \n        dispTemp1 = Math.round(dispTemp1);\n        ctx.fillText(dispTemp1 + \"°\", 200, 510);\n        \n        var dispTemp2 = dispTemp + 3*step;\n        dispTemp2 = Math.round(dispTemp2);\n        ctx.fillText(dispTemp2 + \"°\", 300, 510); \n        \n        var dispTemp3 = dispTemp + 4*step; \n        dispTemp3 = Math.round(dispTemp3);\n        ctx.fillText(dispTemp3 + \"°\", 400, 510);\n    \n        maxTemp = Math.round(maxTemp);    \n        ctx.fillText(maxTemp + \"°\", 500, 510); \n\n       \n        ctx.stroke();        \n        });\n})(scope);\n</script> ","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":980,"y":220,"wires":[[]]},{"id":"9a8c3998.861d1","type":"ui_template","z":"de2fc7f7.c12df","group":"b4dd4f99.74f6a8","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":120,"y":140,"wires":[[]]},{"id":"d99ebb42.a280b8","type":"debug","z":"de2fc7f7.c12df","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":790,"y":120,"wires":[]},{"id":"b4dd4f99.74f6a8","type":"ui_group","z":"","name":"MLXHeatCam","tab":"638b0699.8b44f8","order":1,"disp":true,"width":"12","collapse":true},{"id":"638b0699.8b44f8","type":"ui_tab","z":"","name":"Home","icon":"dashboard","order":1,"disabled":false,"hidden":false}]

#10

Hey @janek,

What a coincidence. Just posted my latest UI node: see here. Will have a look at your flow tomorrow. Have to get up early tomorrow :sleeping:


#11

Hi @BartButenaers,
Thank you for sharing. I haven't try it yet, but my first feedback is:


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

Hey @janek,
I'm going to continue here, to keep the other discussion focussed on the UI-node.

Do I understand this correctly? Your ML9060 sensor is connected via I2C to the RPI pins of your Raspberry, and then you use a third-party application (which you trigger via the Exec-node) to read data from the RPI pins into your Node-RED flow.

I have no experience with the I2C yet, but have you already tried for example the node-red-contrib-i2c node. It might (??) be faster to read the I2C data directly from Node-RED? I'm pretty sure that other people on this forum might be able to help you with this ...


#13

It’s mlx90640 module from Pimoroni https://shop.pimoroni.com/products/mlx90640-thermal-camera-breakout connected with 4 wires to RPi (GND, +3.3V, SCL, SDA)

. The code coming soon. Works fast and reliable


#14

Great! Looking forward to it !!!!


#15

@BartButenaers, download the code from here https://github.com/caenrigen/mlx90640-library and install as per README.md (BCM2835 library is needed for RPi). I modified slightly the file acquisition.cpp and make it to check if it runs. Next create your nodered flow

[{"id":"1a6b4be2.b14294","type":"file in","z":"3c9210a8.7582a","name":"Read File","filename":"/dev/shm/test0","format":"utf8","chunk":false,"sendError":true,"x":340,"y":280,"wires":[["f89b04a4.ca0578","ee49a604.5fe798"]]},{"id":"324e53c4.eb9fb4","type":"exec","z":"3c9210a8.7582a","command":"/temp/mlx90640-library_caenrigen/./acquisition folderForFrames","addpay":true,"append":"folderForFrames","useSpawn":"true","timer":"","oldrc":true,"name":"","x":430,"y":160,"wires":[["76f82d66.46d6c4"],["82bfbc81.d68db8"],["be8e3492.9cfd8"]]},{"id":"2baecec1.29a7d2","type":"inject","z":"3c9210a8.7582a","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":"1","x":110,"y":160,"wires":[["324e53c4.eb9fb4"]]},{"id":"76f82d66.46d6c4","type":"debug","z":"3c9210a8.7582a","name":"","active":false,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","x":820,"y":100,"wires":[]},{"id":"82bfbc81.d68db8","type":"debug","z":"3c9210a8.7582a","name":"","active":false,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","x":820,"y":160,"wires":[]},{"id":"be8e3492.9cfd8","type":"debug","z":"3c9210a8.7582a","name":"","active":false,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","x":820,"y":220,"wires":[]},{"id":"34393fd8.5af5d8","type":"inject","z":"3c9210a8.7582a","name":"","topic":"","payload":"","payloadType":"date","repeat":"0.5","crontab":"","once":true,"onceDelay":"1","x":190,"y":280,"wires":[["1a6b4be2.b14294"]]},{"id":"f89b04a4.ca0578","type":"debug","z":"3c9210a8.7582a","name":"","active":false,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","x":540,"y":280,"wires":[]},{"id":"a258f02a.cfcda","type":"heat-map","z":"3c9210a8.7582a","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":720,"y":400,"wires":[[]]},{"id":"ee49a604.5fe798","type":"function","z":"3c9210a8.7582a","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":530,"y":400,"wires":[["a258f02a.cfcda","e410b6f2.069a38"]]},{"id":"e410b6f2.069a38","type":"debug","z":"3c9210a8.7582a","name":"","active":false,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","x":760,"y":340,"wires":[]},{"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}]
,,,  
<a class="attachment" href="/uploads/nodered/original/2X/3/3ab18cd98bd0d169c35eddbfcc1fc29bc913a06a.txt">acquisition.txt</a> (4.2 KB)
 
Rename the attached file acquisition.txt to acquisition.cpp

Good luck

#16
#include <stdint.h>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <string> 
#include <fstream>
#include <chrono>
#include <thread>
#include "headers/MLX90640_API.h"
#include "headers/MLX90640_RPI_I2C_Driver.h"

#define MLX_I2C_ADDR 0x33
#define REFRESH_RATE MLX90640_CR1_REFRESH_16_HZ // Subpages rate is half

// WAIT_TIME_MILLISECONDS = 2*0.68*1000/fps
// 2: there are 2 subframes
// 0.68: wait 68% (a bit less then 80% from the datasheet)
// 1000: s -> ms
#define WAIT_TIME_MILLISECONDS 85

int main(int argc, char *argv[]){
    static uint16_t eeMLX90640[832];
    float emissivity = 1;
    uint16_t frame[834];
    static float image[768];
    float eTa;
    static uint16_t data[768*sizeof(float)];

    int error;
    int64_t max_elapsed=0;
    int64_t max_calcs=0;
    int16_t CtrlReg1 = 0;
    static int64_t t_start=0,\
    t_end=0,\
    elapsed = 0;
    static float mlx90640To[768];
	
	static uint8_t tick = 0;	// new

    std::ofstream f;
    std::ofstream f1;	// new
    std::ofstream fTimings;
    std::ofstream fReadErrors;
    std::string base_path="./";

    std::string thermalVals = "";
    
    printf("Starting I2C...\n");
    error = MLX90640_I2CInit();
    printf("Error: %d\n", error);
    printf("Setting I2C freq...\n");
    MLX90640_I2CFreqSet(1000000); // Hz
    
    // Configure Control Register 1
    CtrlReg1 =  MLX90640_CR1_SUBPAGE_MODE_ON &\
    MLX90640_CR1_TRANSFER_TO_RAM_ON &\
    MLX90640_CR1_SUBPAGE_REPEAT_OFF &\
    REFRESH_RATE &\
    MLX90640_CR1_ADC_RESOLUTION_16BIT &\
    MLX90640_CR1_CHESS_MODE;
    printf("Setting Control Register 1...\n");
    error = MLX90640_ConfReg1(MLX_I2C_ADDR, CtrlReg1);
    printf("Error: %d\n", error);

    paramsMLX90640 mlx90640;
    printf("Damping EE...\n");
    error = MLX90640_DumpEE(MLX_I2C_ADDR, eeMLX90640);
    printf("Error: %d\n", error);
    printf("Extracting parameters...\n");
    error = MLX90640_ExtractParameters(eeMLX90640, &mlx90640);
    printf("Error: %d\n", error);

    if (argc>1)
    {
        base_path += argv[1];
        system(("mkdir " + base_path).c_str());
    }
    else 
	std::cout << "First argument must be the acquisition name! Nothing done." << std::endl;
    base_path += "/";
    std::cout << "Output dir: " << base_path << std::endl;
    printf("Starting Loop...\r\n");
    while(1){
        do{
            t_start = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
            error = MLX90640_GetFullFrameData(MLX_I2C_ADDR, frame);
            if(error) std::this_thread::sleep_for(std::chrono::milliseconds(5));
            if (error != 0 && error != -100){
                fReadErrors.open(base_path + "acqLog.txt", std::ios_base::app);
                fReadErrors << "[" << t_start <<"]: " <<  error << "\n";
                fReadErrors.close();
            }
        }while(error);

        MLX90640_InterpolateOutliers(frame, eeMLX90640);

        frame[833] = 0;
        eTa = MLX90640_GetTa(frame, &mlx90640);
        MLX90640_CalculateTo(frame, &mlx90640, emissivity, eTa, mlx90640To);

        frame[833] = 1;
        eTa = MLX90640_GetTa(frame, &mlx90640);
        MLX90640_CalculateTo(frame, &mlx90640, emissivity, eTa, mlx90640To);

        // was f.open(base_path + std::to_string(t_start));
	f.open("/dev/shm/test0");	// new
	f1.open("/dev/shm/test1");	//new
        f << std::setprecision(1) << std::fixed;
        f1 << std::setprecision(1) << std::fixed;	// new

	if(tick == 0) tick = 1; 	// new
		else tick = 0;	// new
	printf ("%d;", tick);	// new
	printf (" ");	// new
 
        for(int y = 23; y > -1; y--){
         for(int x = 31; x > -1; x--){
             float val = mlx90640To[32 * (23-y) + x];
             f << val << " ";
             //printf("%5d;",val);	// new
             mlx90640To[32 * (23-y) + x] = 0;
         }
         f << std::endl;
	 f1 << std::endl;	// new
     }
     f.close();
     f1.close();

     t_end = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
     elapsed = t_end - t_start;

     if( elapsed < WAIT_TIME_MILLISECONDS){
        std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_MILLISECONDS-elapsed));
    };
};
MLX90640_I2CEnd();
return 0;
}

,,,

#17

Hi, I'm a new learner and using MLX90640 to interface with Raspberry pi. can l ask how to use the functions inside this https://github.com/melexis/mlx90640-library ? Thank you