Build a DIY Thermal Camera with ESP32 and MLX90640: Wiring, Calibration, and False-Color Display
A thermal camera used to mean a $2,000+ FLIR handheld. Today a 32x24 pixel thermal array sensor costs under $50, talks I2C just like any other breakout board, and can be driven entirely from an ESP32. It won't replace a professional imager for building inspections, but for finding a hot connector on a PCB, spotting a bearing about to fail, checking insulation gaps, or just seeing in the dark, a homebuilt thermal camera is a genuinely useful shop tool — and a fun weekend electronics build. This guide walks through wiring a MLX90640 thermal array to an ESP32, rendering a false-color heatmap on a TFT display, and calibrating the result so the numbers you read are numbers you can trust.
How These Sensors Actually Work
The MLX90640 is a far-infrared (FIR) thermopile array — not a camera in the visible-light sense. Each of its 768 pixels (32x24) is an independent thermocouple-like sensor that measures the infrared radiation emitted by whatever it's pointed at, and converts that to a temperature reading. The sensor outputs raw pixel data over I2C at up to 64 Hz (in practice most ESP32 setups top out around 4-8 Hz due to I2C bandwidth and processing time), along with factory calibration coefficients burned into its EEPROM that let you convert raw ADC counts into accurate temperatures.
The cheaper alternative, the AMG8833 (Panasonic Grid-EYE), is an 8x8 array — only 64 pixels. It's easier to find, cheaper, and plenty good for "is this thing hot" detection, but the image is blocky even after interpolation. The MLX90640's 32x24 resolution is the meaningful jump where the display starts looking like a recognizable thermal image rather than a colored grid. This guide targets the MLX90640, but the wiring and most of the software approach applies to the AMG8833 with a different library.
Parts and Wiring
The core build is three components: an ESP32 dev board, a MLX90640 breakout (get the wide-FOV 110°x75° or 55°x35° version depending on whether you want a wide view or more detail at distance), and a TFT display for standalone viewing. The MLX90640 communicates over I2C, and it draws enough current on its 3.3V rail that a dedicated regulator is worth having if your ESP32 board's onboard 3.3V regulator is anemic (many clone boards are).
MLX90640 PinESP32 PinNotes VIN / 3V33.3VDo not run at 5V — sensor is 3.3V only GNDGND SDAGPIO 21 (default)Add 4.7k pull-up if your breakout lacks one SCLGPIO 22 (default)Add 4.7k pull-up if your breakout lacks oneFor the display, a 2.4"-3.5" SPI TFT (ILI9341 or similar) wired to a separate set of GPIOs works well and leaves I2C free for the sensor. If you want the display and sensor to share a bus for a more compact build, use an I2C TFT/OLED instead, but be aware the MLX90640's I2C traffic is bursty and heavy — a busy shared bus can cause frame drops.
Software: Reading the Array
Use the Adafruit MLX90640 library (Arduino) or the community esp32-mlx90640 port — both wrap the Melexis reference driver and handle the EEPROM calibration extraction for you, which is not something you want to reimplement from scratch. Basic read loop:
- On boot, call mlx.begin(MLX90640_I2CADDR_DEFAULT, &Wire) and set the refresh rate — start at 4 Hz (MLX90640_4_HZ) since higher rates need faster I2C clocking (400 kHz) and more processing headroom than a stock Arduino sketch usually has.
- Call mlx.getFrame(frame) to pull all 768 float temperature values in °C into an array. This already applies the factory calibration — you get real temperatures, not raw ADC counts.
- Find the min and max of the frame to set your color scale range, unless you're locking to a fixed range (better for consistent comparisons between frames).
- Map each pixel's temperature to a color using a palette lookup (see below), then draw it to the display, scaling each of the 32x24 source pixels up to fill your screen — typically 8x-10x per axis using nearest-neighbor or bilinear interpolation for a smoother look.
False-Color Palettes
Raw grayscale thermal images are hard to read at a glance — human eyes are much better at picking out subtle differences in hue than in gray brightness. Implement at least an "Ironbow" palette (black → purple → red → orange → yellow → white, the FLIR-style default) as a 256-entry RGB565 lookup table indexed by normalized pixel value. A rainbow palette (blue → green → yellow → red) is higher-contrast for finding a single hot spot but reads less intuitively for general scanning. Store the palette as a PROGMEM array — computing gradient colors on the fly per pixel at 768 pixels x several fps will bog down an ESP32's main loop unnecessarily.
Calibration and Accuracy
The MLX90640's factory calibration gets you to within about ±1.5°C to ±2.5°C of absolute accuracy out of the box, which is fine for relative comparisons (this transistor is hotter than that one) but not lab-grade absolute measurement. A few things affect real-world accuracy:
- Emissivity — the sensor assumes a default emissivity (usually 0.95, close to most organic and painted surfaces). Shiny bare metal has much lower emissivity (0.1–0.3) and will read artificially cool; you can set a custom emissivity value in the library if you're measuring known low-emissivity surfaces.
- Ambient temperature compensation — the sensor uses its own die temperature as a reference point. Let it run for 60-90 seconds after power-on before trusting readings; the die needs to reach thermal equilibrium with the sensor's optical assembly.
- Distance and background reflection — thermal radiation from surrounding objects (including your own body) reflects off shiny targets and skews readings. Point the sensor away from strong heat sources like your face or a nearby laser cutter enclosure when calibrating.
- Reference check — verify against a known temperature (a cup of ice water at 0°C, or a thermocouple probe on the same surface) and note the offset. Most builds are consistent enough that you can apply a fixed correction offset in software.
Practical Uses in the Shop
Once built, this is a legitimately useful tool alongside a multimeter and oscilloscope: spotting a MOSFET or voltage regulator running hotter than its neighbors on a board you're debugging, checking a 3D printer's heated bed for cold spots or a failing heater cartridge, finding where a laser cutter's chiller line has an airlock, checking insulation gaps around a workshop's exterior walls in winter, or verifying a soldering iron tip has actually reached temperature before you start reflow work.
Safety Note
The MLX90640 emits nothing — it's a passive receiver, not a laser or radiation source, so there's no exposure hazard from the sensor itself. The only real caution is electrical: keep it on 3.3V only, and if you're using this camera to inspect anything at mains voltage (a breaker panel, a motor terminal box), treat it as a diagnostic aid, not a substitute for proper lockout/tagout and a rated multimeter — a thermal hot spot confirms a problem exists, it doesn't tell you it's safe to touch.
This build sits nicely between "sensor breakout tutorial" and "real tool" — a couple hours of wiring and calibration turns into something you'll actually reach for during troubleshooting sessions on every other project on this site, from checking a Ray5's driver board under load to spotting a marginal joint on a freshly reflowed PCB.
Related Guides
- I2C vs SPI vs UART: How to Choose and Use Serial Communication Protocols
- I2C Wiring and Protocol Guide for Arduino, ESP32, and Raspberry Pi
- Build a Wall-Mounted Smart Clock and Weather Display (ESP32 + RTC)
- Reading Sensors over I2C with Raspberry Pi
- Raspberry Pi GPIO: Complete Beginner Guide with Python Examples
- Shift Registers and I/O Expanders for Arduino and ESP32: 74HC595, MCP23017, and PCF8574 Explained
- Air Quality Monitoring with ESP32: Choosing and Wiring PM2.5, CO2, and VOC Sensors
- How to Use a Logic Analyzer for Digital Signal Debugging: Saleae, Sigrok, and Protocol Decoding