I recently got my hands on an MLX90640 thermal sensor. Sparkfun has a good tutorial to get started. However, I wanted to use Numpy and OpenCV to toy around with the data instead of the Processing IDE. I took their example and wrote a Python script that replaces the Processing IDE and loads the data into Numpy and OpenCV.

A few seconds in I hold my soldering iron over the sensor, and you can see the colormap adjust to the temperature range dynamically.

Prerequisites

Hardware

Software

All the software I use is open source and free. Besides Python 3, I use the following packages:

To program the Teensy you can use the Arduino IDE or -- what I prefer -- PlatformIO in VSCode.

Hardware Setup

The MLX90640 has an I2C interface. Connect the SCL and SDA pins of the Teensy to the corresponding pins on the MLX90640 and add a pull-up resistor to 3.3 V (e.g. 10 kOhm). Also connect GND and 3.3 V power. Then connect the serial to USB converter to your PC. That's it for hardware.

Software Setup

The script can be found on GitHub. The folder Example2_OutputToProcessing contains the Sparkfun firmware for connecting to the Processing IDE. Open Example2_OutputToProcessing.ino in the Arduino IDE, compile, and flash it to the Teensy. Arduino's serial monitor should now show the raw data coming from the MLX90640.

To consume the data with Numpy, use the test.py script. Make sure to update the serial port on line 5.

import numpy as np
import serial, cv2, math

serialport = "COM34" # or something like '/dev/ttyS0'
scaling = 20

width = scaling * 32
height = scaling * 24

img = np.zeros([height,width,3])
imgGray = np.zeros([height,width,3])

try:
    ser = serial.Serial(serialport,115000)
except serial.SerialException:
    print("Cannot open serial port")
    quit()

try:
    print("press Ctrl-C to end")
    while True:
        # read data from serial port
        cc = str(ser.readline())
        cc = cc[2:-6]
        data = np.fromstring(cc, sep=',')

        # reshape data into matrix
        output = data.reshape(24,32)

        # scale to 0-255
        minValue = math.floor(np.amin(output))
        maxValue = math.ceil(np.amax(output))
        output = output - minValue
        output = output * 255 / (maxValue - minValue)

        # resize image
        dim = (width, height)
        output = cv2.resize(output, dim, interpolation=cv2.INTER_LINEAR)

        # apply colormap
        imgGray = output.astype(np.uint8)
        img = cv2.applyColorMap(imgGray, cv2.COLORMAP_JET)

        # put min/max text on image
        text = "Min: " + str(minValue) + " C  Max: " + str(maxValue) + " C"
        font = cv2.FONT_HERSHEY_SIMPLEX
        org = (20, 50)
        image = cv2.putText(img, text, org, font, 1, (255, 255, 255), 2, cv2.LINE_AA)

        cv2.waitKey(50)
        cv2.imshow("image", img)

except KeyboardInterrupt:
    print("Bye bye :)")
    ser.close()