Here is a version of the thermal camera in Arduino with a Feather and discrete LCD display as an alternate to the Arcada-based devices like PyPortal and PyGamer.
This version uses an Adafruit Feather M0 Express and an ILI9341-based display in addition to the MLX90640 thermal sensor. The display connects over an SPI interface and the sensor over an I2C interface.
The above sensor has a 55 degree field of view. for a 110 degree field of view, use this version.
Wiring
See the Fritzing diagram above. The following connections are needed:
From the Feather to the display:
- Feather 3V pin to display Vin
- Feather GND pin to display GND
- Feather SCK to display CLK
- Feather MO to display MOSI
- Feather Pin 10 to display D/C
- Feather Pin 9 to display CS
- Feather or display 3V output (never 5V!) to IM3, IM2, and IM1 (but not IM0!) pins. This configures the breakout to use its SPI interface. See the breakout guide for details on soldering closed these connections to make the SPI interface the default.
Connections from the Feather to the sensor:
The sensor has STEMMA QT connectors. This Feather does not. A cable like STEMMA QT to male pins may be used if a Stemma connection is desired.
- Feather 3V to sensor VIN
- Feather GND to sensor GND
- Feather SCL to sensor SCL
- Feather SDA to sensor SDA
NOTE: The MLX90640 board in Fritzing is UPSIDE DOWN from the usual placement with the camera sensor up. Use the connection pins noted to get the wiring correct.
For the STEMMA QT cable:
- Red - 3.3VDC Power
- Black - Ground
- Blue - I2C SDA Data
- Yellow - I2C SCL Clock
Required Libraries
This project uses the Adafruit MLX90640 library, the Adafruit ILI9341 library, and the Adafruit GFX library. Be sure to install all library dependencies and ensure all libraries are at their latest versions. The process is the same as the Arduino Libraries page in this guide.
// SPDX-FileCopyrightText: 2025 Adafruit Industries
//
// Based on code submitted via feedback to learn.adafruit.com
//
// SPDX-License-Identifier: Unlicense
#include <SPI.h>
#include <Adafruit_MLX90640.h>
#include <Adafruit_ILI9341.h>
#include <Adafruit_GFX.h>
// Different display select pin numbers depending on the board used
#ifdef ESP8266
#define STMPE_CS 16
#define TFT_CS 0
#define TFT_DC 15
#define SD_CS 2
#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32C6)
#define STMPE_CS 6
#define TFT_CS 7
#define TFT_DC 8
#define SD_CS 5
#elif defined(ESP32) && !defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2)
#define STMPE_CS 32
#define TFT_CS 15
#define TFT_DC 33
#define SD_CS 14
#elif defined(TEENSYDUINO)
#define TFT_DC 10
#define TFT_CS 4
#define STMPE_CS 3
#define SD_CS 8
#elif defined(ARDUINO_STM32_FEATHER)
#define TFT_DC PB4
#define TFT_CS PA15
#define STMPE_CS PC7
#define SD_CS PC5
#elif defined(ARDUINO_NRF52832_FEATHER) /* BSP 0.6.5 and higher! */
#define TFT_DC 11
#define TFT_CS 31
#define STMPE_CS 30
#define SD_CS 27
#elif defined(ARDUINO_MAX32620FTHR) || defined(ARDUINO_MAX32630FTHR)
#define TFT_DC P5_4
#define TFT_CS P5_3
#define STMPE_CS P3_3
#define SD_CS P3_2
#else
// Anything else, defaults! This includes the Feather M0 Express
#define STMPE_CS 6
#define TFT_CS 9
#define TFT_DC 10
#define SD_CS 5
#endif
// Initialize the ILI9341 display
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
// Initialize thermal sensor
Adafruit_MLX90640 mlx;
float frame[32*24]; // buffer for full frame of temperatures
//low range of the sensor (this will be blue on the screen)
#define MINTEMP 20
//high range of the sensor (this will be red on the screen)
#define MAXTEMP 35
//the colors we will be using
const uint16_t camColors[] = {0x480F,
0x400F,0x400F,0x400F,0x4010,0x3810,0x3810,0x3810,0x3810,0x3010,0x3010,
0x3010,0x2810,0x2810,0x2810,0x2810,0x2010,0x2010,0x2010,0x1810,0x1810,
0x1811,0x1811,0x1011,0x1011,0x1011,0x0811,0x0811,0x0811,0x0011,0x0011,
0x0011,0x0011,0x0011,0x0031,0x0031,0x0051,0x0072,0x0072,0x0092,0x00B2,
0x00B2,0x00D2,0x00F2,0x00F2,0x0112,0x0132,0x0152,0x0152,0x0172,0x0192,
0x0192,0x01B2,0x01D2,0x01F3,0x01F3,0x0213,0x0233,0x0253,0x0253,0x0273,
0x0293,0x02B3,0x02D3,0x02D3,0x02F3,0x0313,0x0333,0x0333,0x0353,0x0373,
0x0394,0x03B4,0x03D4,0x03D4,0x03F4,0x0414,0x0434,0x0454,0x0474,0x0474,
0x0494,0x04B4,0x04D4,0x04F4,0x0514,0x0534,0x0534,0x0554,0x0554,0x0574,
0x0574,0x0573,0x0573,0x0573,0x0572,0x0572,0x0572,0x0571,0x0591,0x0591,
0x0590,0x0590,0x058F,0x058F,0x058F,0x058E,0x05AE,0x05AE,0x05AD,0x05AD,
0x05AD,0x05AC,0x05AC,0x05AB,0x05CB,0x05CB,0x05CA,0x05CA,0x05CA,0x05C9,
0x05C9,0x05C8,0x05E8,0x05E8,0x05E7,0x05E7,0x05E6,0x05E6,0x05E6,0x05E5,
0x05E5,0x0604,0x0604,0x0604,0x0603,0x0603,0x0602,0x0602,0x0601,0x0621,
0x0621,0x0620,0x0620,0x0620,0x0620,0x0E20,0x0E20,0x0E40,0x1640,0x1640,
0x1E40,0x1E40,0x2640,0x2640,0x2E40,0x2E60,0x3660,0x3660,0x3E60,0x3E60,
0x3E60,0x4660,0x4660,0x4E60,0x4E80,0x5680,0x5680,0x5E80,0x5E80,0x6680,
0x6680,0x6E80,0x6EA0,0x76A0,0x76A0,0x7EA0,0x7EA0,0x86A0,0x86A0,0x8EA0,
0x8EC0,0x96C0,0x96C0,0x9EC0,0x9EC0,0xA6C0,0xAEC0,0xAEC0,0xB6E0,0xB6E0,
0xBEE0,0xBEE0,0xC6E0,0xC6E0,0xCEE0,0xCEE0,0xD6E0,0xD700,0xDF00,0xDEE0,
0xDEC0,0xDEA0,0xDE80,0xDE80,0xE660,0xE640,0xE620,0xE600,0xE5E0,0xE5C0,
0xE5A0,0xE580,0xE560,0xE540,0xE520,0xE500,0xE4E0,0xE4C0,0xE4A0,0xE480,
0xE460,0xEC40,0xEC20,0xEC00,0xEBE0,0xEBC0,0xEBA0,0xEB80,0xEB60,0xEB40,
0xEB20,0xEB00,0xEAE0,0xEAC0,0xEAA0,0xEA80,0xEA60,0xEA40,0xF220,0xF200,
0xF1E0,0xF1C0,0xF1A0,0xF180,0xF160,0xF140,0xF100,0xF0E0,0xF0C0,0xF0A0,
0xF080,0xF060,0xF040,0xF020,0xF800,};
uint16_t displayPixelWidth, displayPixelHeight;
void setup() {
Serial.begin(115200); // Initialize serial terminal
delay(10);
// Initialize and Setup TFT Display
tft.begin();
tft.setRotation(1);
tft.fillScreen(ILI9341_BLACK);
displayPixelWidth = tft.width() / 32;
displayPixelHeight = tft.width() / 32; //Keep pixels square
delay(100);
//Serial.println("Adafruit MLX90640 Thermal Camera");
//
// Read the MLX90640 via I2C (Arduino Wire)
if (! mlx.begin(MLX90640_I2CADDR_DEFAULT, &Wire)) {
// MLX90640 not found
tft.setCursor(0, 0);
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(1);
tft.println("Thermal Sensor not found");
while(1) ; // Stop program with error
}
// Un-comment for debugging sensor set up
// Check sensor wiring if sensor isn't found and make sure it's an MLX90640
/*
Serial.println("Found Adafruit MLX90640");
Serial.print("Serial number: ");
Serial.print(mlx.serialNumber[0], HEX);
Serial.print(mlx.serialNumber[1], HEX);
Serial.println(mlx.serialNumber[2], HEX);
*/
// Set up Thermal Sensor
mlx.setMode(MLX90640_CHESS);
mlx.setResolution(MLX90640_ADC_18BIT);
mlx.setRefreshRate(MLX90640_8_HZ); // COuld also try MLX90640_16_HZ
Wire.setClock(1000000); // max 1 MHz
}
// Main loop: Run the project continuously
void loop() {
if (mlx.getFrame(frame) != 0) { // Get thermal sensor data
Serial.println("Failed"); // Look in serial for failed messages if issues
return; // will loop again
}
// For every square on the screen, get the value and map it into a temperature color
for (uint8_t h=0; h<24; h++) {
for (uint8_t w=0; w<32; w++) {
float t = frame[h*32 + w];
t = min(t, MAXTEMP); // Constrain the temp value between MAXTEMP and MINTEMP
t = max(t, MINTEMP);
uint8_t colorIndex = map(t, MINTEMP, MAXTEMP, 0, 255); // Get square color
colorIndex = constrain(colorIndex, 0, 255); // and make sure it's valid
// Draw the pixels!
tft.fillRect(displayPixelWidth * w, displayPixelHeight * h,
displayPixelHeight, displayPixelWidth,
camColors[colorIndex]);
}
}
}
You can change the line tft.setRotation(1); to one of the following values if you need to orient your display differently in relation to the sensor:
tft.setRotation(0); // Normal orientation tft.setRotation(1); // Rotate 90° clockwise tft.setRotation(2); // Upside down tft.setRotation(3); // Rotate 270° clockwise
Page last edited October 22, 2025
Text editor powered by tinymce.