Projects / Thermal

Portable Thermal Camera

A 32×24 thermal image streamed to your phone over Wi-Fi.

Intermediate⏱ 2 hours2 ElectroTrical boards

Overview

The ET-S35 carries the Melexis MLX90640, a 768-pixel thermal camera with a wide 110° view. Point it at a fuse box, a leaking window, a hot motor or your AC vents to see temperature as colour.

The ESP32 creates its own Wi-Fi hotspot and serves a live heat map to any phone browser — no app needed. The ET-P09 power bank module makes it portable and USB-C rechargeable.

  • 32×24 thermal image, −40 to 300 °C
  • Live heat map on your phone, no app
  • Shows min, max and centre temperature
  • Battery powered, USB-C charging

What you need

ElectroTrical boards

MLX90640 Thermal Camera (110°)
ET-S35MLX90640 Thermal Camera (110°)MLX90640 32×24 thermal camera
AED 229.00Out of stock
LiPo Power Bank Module
ET-P09LiPo Power Bank ModuleLiPo power bank: USB-C charger + 5 V boost
AED 49.00Out of stock

Boards total: AED 278.00

These boards are being manufactured. Open a board's page to see when it's back, or ask us about a kit.

You'll also need

  • ESP32-S3 DevKitC-1 (or any ESP32-S3 board) with a USB-C data cable. Not sold by ElectroTrical.
  • 1S LiPo, 1,000–2,000 mAh
  • Handheld enclosure with an opening for the sensor

Wiring

Power

FromToNote
LiPoET-P09 battery socket
ET-P09 5VESP32 5V
ET-P09 GNDESP32 GND

ESP32 → ET-S35 header

FromToNote
ESP32 3V3VINVIN sets the I²C logic level. Keep it at 3.3 V for the ESP32.
ESP32 GNDGND
ESP32 GPIO8SDA
ESP32 GPIO9SCL

Build it

  1. 01

    Set up the Arduino IDE

    Install Arduino IDE 2, add the Espressif ESP32 board package (Boards Manager → “esp32” by Espressif), select “ESP32S3 Dev Module” and turn on USB CDC On Boot so Serial prints over USB. Install the libraries listed above the sketch from Library Manager.

  2. 02

    Upload and connect

    Upload the sketch. On your phone, join the Wi-Fi network “ElectroTrical-Thermal” (password electrotrical) and open http://192.168.4.1.

  3. 03

    Let it settle

    The MLX90640 needs about a minute after power-up for stable readings.

  4. 04

    Go portable

    Unplug USB and power from the ET-P09. Charge the LiPo through the ET-P09's USB-C socket.

  5. 05

    Read the image

    Each pixel covers a large area at a distance, so small hot spots (a single screw) read lower than they are. Get close to small objects.

Arduino code

Written for the ESP32-S3 DevKitC-1 with the Espressif Arduino core. Install these libraries from the Arduino Library Manager:

  • Adafruit MLX90640
thermal_camera.ino
#include <Wire.h>
#include <WiFi.h>
#include <WebServer.h>
#include <Adafruit_MLX90640.h>

Adafruit_MLX90640 mlx;
WebServer server(80);
float frame[32 * 24];

const char PAGE[] PROGMEM = R"HTML(
<!doctype html><meta name=viewport content="width=device-width">
<body style="margin:0;background:#111;color:#eee;font:16px sans-serif;text-align:center">
<canvas id=c width=32 height=24 style="width:100%;image-rendering:pixelated"></canvas>
<p id=t></p>
<script>
const c=document.getElementById('c').getContext('2d'),img=c.createImageData(32,24);
function col(x){x=Math.max(0,Math.min(1,x));return[255*Math.min(1,x*2),255*Math.max(0,x*2-1),255*Math.max(0,1-x*3)];}
async function tick(){
  const f=await (await fetch('/frame')).json();
  const lo=Math.min(...f),hi=Math.max(...f);
  f.forEach((v,i)=>{const [r,g,b]=col((v-lo)/(hi-lo||1));img.data.set([r,g,b,255],i*4);});
  c.putImageData(img,0,0);
  document.getElementById('t').textContent='min '+lo.toFixed(1)+'  centre '+f[12*32+16].toFixed(1)+'  max '+hi.toFixed(1)+' \u00b0C';
  setTimeout(tick,250);
}
tick();
</script>
)HTML";

void sendFrame() {
  String s = "[";
  for (int i = 0; i < 32 * 24; i++) {
    if (i) s += ',';
    s += String(frame[i], 1);
  }
  s += "]";
  server.send(200, "application/json", s);
}

void setup() {
  Serial.begin(115200);
  Wire.begin(8, 9);
  Wire.setClock(400000);                  // enough for 4 Hz through the level shifter

  if (!mlx.begin(MLX90640_I2CADDR_DEFAULT, &Wire)) {
    Serial.println("MLX90640 not found (0x33)");
    while (1) delay(100);
  }
  mlx.setMode(MLX90640_CHESS);
  mlx.setResolution(MLX90640_ADC_18BIT);
  mlx.setRefreshRate(MLX90640_4_HZ);

  WiFi.softAP("ElectroTrical-Thermal", "electrotrical");
  server.on("/", [] { server.send_P(200, "text/html", PAGE); });
  server.on("/frame", sendFrame);
  server.begin();
  Serial.println("Open http://192.168.4.1");
}

void loop() {
  mlx.getFrame(frame);
  server.handleClient();
}

Troubleshooting

Image is very noisy or stripes appear

Wait a minute after power-up and keep the refresh rate at 4 Hz. Higher rates add noise.

Shiny metal reads cold

Polished metal reflects infrared. Put a strip of matt electrical tape on it and measure the tape.

Page loads but stays black

Open Serial Monitor: if the sensor isn't found, check the I²C wiring and that VIN is on 3.3 V.

Take it further

  • Add an ET-S40 fuel gauge and show battery % on the page
  • Save snapshots to the ESP32's flash
  • Use the cheaper ET-S36 (8×8) for people counting instead of imaging

Turning this into a product?

We design custom boards that combine these sensors on one PCB, with firmware and production.