DIY pocket thermal imager

Very easy to repeat and inexpensive pocket-sized DIY thermal imager

Public Chat
Similar projects worth following
The thermal imager is based on MLX90640 sensor from Melexis. It does all the hard work, although thermal post-processing is also necessary.

Operating time on 1 battery charge - 8 hours 40 minutes.

Thermal imager weight (without case) - 72 grams 

Let's take a closer look at the details of the project…

MLX90640 sensor

There are 2 versions of the sensor:

With the letter A in the name has FOV 110 / 75

With the letter B in the name it has FOV 55 / 35 - I have one

Inside the sensor is a 32x24 matrix of elements sensitive to IR radiation. The manufacturer allows up to 4 defective pixels. They are stored in EEPROM and are considered to be interpolation from neighboring pixels. 

Inside the sensor there are 768 IR receivers (!!!) + VDD meter + built-in chip thermometer (it estimates the temperature of the sensor case). 

The sensor is factory calibrated. Calibration coefficients are stored internally in the sensor in EEPROM. 

Temperature range: -40…+300

Operating temperature range: -40 ... +85

The sensor is powered by 3.3 V (up to 3.6 V). Withstands 5V supply for a short time. 

Average current consumption 20 mA. 

The sensor has 4 pins. 2 of them for power and 2 more for the i2c interface. 

The i2c pins are 5V tolerant. The i2c frequency is up to 1MHz. The i2c address can be changed by software, the default is 0x33.

Software from Melexis

In addition to the sensor, I have an evaluation board from melexis (EVB90640-41).

The board allows you to connect the sensor to a computer.

The board has a socket where you need to insert the sensor, and a mini-USB port for connecting to a computer.

The evaluation board is based on the STM32F446 microcontroller.

The board has CAN and LIN interfaces. The purpose of these interfaces is unknown to me. Most likely Melexis implemented them for one of their customers.

VP230 - CAN transceiver

80020BA - LIN transceiver

In MlxCIRT 90640 software, you can view general information about the sensor, start data acquisition and configure parameters in EEPROM.

The frame rate is selectable from 0.5 Hz to 64 Hz. 

This is the frequency of reading half a frame from the matrix (the matrix is scanned in 2 stages using a checkerboard or interlaced pattern). 

The matrix scan template can also be selected in MlxCIRT 90640. The manufacturer recommends the chess template because the factory calibration is done for it.

You can update the firmware of the demo board in MlxCIRT 90640.

Reading thermograms is possible in a CSV file for further analysis.

Let's start reading thermograms.

Now the sensor is directed to the ceiling. There are no hot and cold objects on it and you see a rather noisy image. This is because the software has automatically chosen a very narrow temperature range for the color scale - only 2 degrees.

Now I brought my hand into the field of view of the sensor and this is what the thermogram looks like now:

During a fast movement (FPS = 4 Hz is selected), we see a characteristic “checkerboard defect” on the thermogram, associated with the order of scanning the matrix by the sensor:

Now let's try a frequency of 32 Hz. As you can see, the image has become noisier:

This effect is mentioned in the documentation. The higher the frequency of collecting thermograms, the higher the thermal noise of the pixel. The dependence is approximately the following: the noise increases by 0.1 degrees with each Hz of frequency (for the sensor version with FOV 55).

It turns out that with a data collection frequency of 64 Hz, we can get up to 5-6 degrees of noise.

Therefore, high acquisition rates of thermograms are not suitable for domestic use, but this allows the sensor to be used in systems where it is necessary to quickly determine the appearance and location of a very hot or cold object (if the temperature of the object is very different from the ambient temperature).

The noise also depends on the temperature of the measured object. The lower the temperature of the object, the...

Read more »

V1.1 case model

Zip Archive - 232.00 kB - 03/04/2023 at 19:21


  • 1 × LILYGO TTGO T4 v1.3 board
  • 1 × Melexis MLX90640 sensor
  • 1 × LP603060 Li-Po battery or equivalent

View all 3 project logs

Enjoy this project?



nigel wrote 04/02/2024 at 12:39 point

Are the capacitors connected in series or parallel?

  Are you sure? yes | no

Ruslan wrote 04/02/2024 at 14:04 point


  Are you sure? yes | no

nrwest wrote 10/31/2023 at 03:58 point

Thank you for sharing this, Ruslan. Much more elegant than my own attempt from 2021 :-)

Has anyone made a case for this, but using 18650 cell(s) for power?

  Are you sure? yes | no

jmfluxa wrote 10/17/2023 at 15:35 point

Hello and thank you for the wonderful project!

I am using a T4 v1.3 board and after flashing the build, I'm encountering a boot-up error. The program seems to crash during the boot process and gets stuck in a crash loop. Below are the logs I'm getting:

ELF file SHA256: c68fc8d6b9a99b5d


ets Jul 29 2019 12:21:46

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)

configsip: 0, SPIWP:0xee


mode:DIO, clock div:2


ho 0 tail 12 room 4



0x40080400: _init at ??:?

entry 0x40080680

I (29) boot: ESP-IDF v4.3.6-119-g3b34ed72dd 2nd stage bootloader

I (29) boot: compile time 10:49:04

I (29) boot: chip revision: v3.0

I (33) boot.esp32: SPI Speed      : 40MHz

I (38) boot.esp32: SPI Mode       : DIO

I (43) boot.esp32: SPI Flash Size : 4MB

I (47) boot: Enabling RNG early entropy source...

I (53) boot: Partition Table:

I (56) boot: ## Label            Usage          Type ST Offset   Length

I (63) boot:  0 nvs              WiFi data        01 02 00009000 00006000

I (71) boot:  1 phy_init         RF data          01 01 0000f000 00001000

I (78) boot:  2 factory          factory app      00 00 00010000 00100000

I (86) boot: End of partition table

I (90) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=13090h ( 77968) map

I (127) esp_image: segment 1: paddr=000230b8 vaddr=3ffb0000 size=024d4h (  9428) load

I (131) esp_image: segment 2: paddr=00025594 vaddr=40080000 size=0aa84h ( 43652) load

I (151) esp_image: segment 3: paddr=00030020 vaddr=400d0020 size=4e288h (320136) map

I (267) esp_image: segment 4: paddr=0007e2b0 vaddr=4008aa84 size=05044h ( 20548) load

I (283) boot: Loaded app from partition at offset 0x10000

I (283) boot: Disabling RNG early entropy source...

I (295) cpu_start: Pro cpu up.

I (295) cpu_start: Starting app cpu, entry point is 0x400813c4

0x400813c4: call_start_cpu1 at /Users/admin/esp/esp-idf/components/esp_system/port/cpu_start.c:153

I (281) cpu_start: App cpu up.

I (311) cpu_start: Pro cpu start user code

I (311) cpu_start: cpu freq: 160000000

I (311) cpu_start: Application information:

I (316) cpu_start: Project name:     mlx90640_thermoimager

I (322) cpu_start: App version:      efd656a-dirty

I (327) cpu_start: Compile time:     Oct 17 2023 10:48:54

I (334) cpu_start: ELF file SHA256:  c68fc8d6b9a99b5d...

I (340) cpu_start: ESP-IDF:          v4.3.6-119-g3b34ed72dd

I (346) cpu_start: Min chip rev:     v0.0

I (350) cpu_start: Max chip rev:     v3.99 

I (355) cpu_start: Chip rev:         v3.0

I (360) heap_init: Initializing. RAM available for dynamic allocation:

I (367) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM

I (373) heap_init: At 3FFDB698 len 00004968 (18 KiB): DRAM

I (379) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM

I (386) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM

I (392) heap_init: At 4008FAC8 len 00010538 (65 KiB): IRAM

I (400) spi_flash: detected chip: winbond

I (403) spi_flash: flash io: dio

I (408) cpu_start: Starting scheduler on PRO CPU.

assert failed: esp_dport_access_int_init dport_access.c:178 (res == pdTRUE)

Backtrace:0x4008174f:0x3ffe7bd0 0x400886cd:0x3ffe7bf0 0x4008e6a9:0x3ffe7c10 0x400d7256:0x3ffe7d30 0x4011da08:0x3ffe7d60 0x4008173b:0x3ffe7d80 0x4008146d:0x3ffe7da0 0x40007c15:0x3ffe7dc0 0x4000073d:0x3ffe7e30

0x4008174f: panic_abort at /Users/admin/esp/esp-idf/components/esp_system/panic.c:393

0x400886cd: esp_system_abort at /Users/admin/esp/esp-idf/components/esp_system/system_api.c:112

0x4008e6a9: __assert_func at /Users/admin/esp/esp-idf/components/newlib/assert.c:85

0x400d7256: esp_dport_access_int_init at /Users/admin/esp/esp-idf/components/esp32/dport_access.c:178 (discriminator 1)

0x4011da08: esp_startup_start_app_other_cores at /Users/admin/esp/esp-idf/components/freertos/port/xtensa/port.c:519

0x4008173b: start_cpu_other_cores_default at /Users/admin/esp/esp-idf/components/esp_system/startup.c:228

0x4008146d: call_start_cpu1 at /Users/admin/esp/esp-idf/components/esp_system/port/cpu_start.c:198

  Are you sure? yes | no

Ruslan wrote 10/18/2023 at 04:44 point


Let's try ESP-IDF v3.3 version of this project. Here's how to do it:

Remove the development environment from Espressif you have installed (VSCode, ESP-IDF). Install ESP-IDF version 3.3:

Installation instructions here:

Firmware sources for library 3.3 - here:

  Are you sure? yes | no

nigel wrote 07/14/2023 at 15:07 point

Federico, what happens when you select amd/x86 as the compiler kit? Does it work?

  Are you sure? yes | no

Federico Allegretti wrote 08/09/2023 at 13:49 point

got this:

[cms-driver] Error during CMake configure:     Error: Configuration failed.
    at CMakeServerClient.onMessage (c:\Users\Administrator\.vscode\extensions\ms-vscode.cmake-tools-1.14.34\dist\main.js:42823:33)
    at CMakeServerClient.onMoreData (c:\Users\Administrator\.vscode\extensions\ms-vscode.cmake-tools-1.14.34\dist\main.js:42796:18)
    at Socket.emit (node:events:513:28)
    at addChunk (node:internal/streams/readable:315:12)
    at readableAddChunk (node:internal/streams/readable:289:9)
    at Readable.push (node:internal/streams/readable:228:10)
    at Pipe.onStreamRead (node:internal/stream_base_commons:190:23)

in problems got:

CMake Error at CMakeLists.txt:5 (include):include could not find load file:


  Are you sure? yes | no

nigel wrote 08/10/2023 at 14:24 point

Hi Federico, please see your private messages.   You need to start again using version 3.3 of esp-idf which is the one Ruslan used when he built the thermal imager. I have copied some very basic instructions - you will have to experiment a bit.

  Are you sure? yes | no

Federico Allegretti wrote 06/27/2023 at 12:17 point

Hello. trying to compile but got this error:

Error: Bootloader binary size 0x72b0 bytes is too large for partition table offset 0x8000. Bootloader binary can be maximum 0x7000 (28672) bytes unless the partition table offset is increased in the Partition Table section of the project configuration menu.

  Are you sure? yes | no

Ruslan wrote 06/30/2023 at 14:11 point

This is strange because other people have not encountered this error. Please tell me which project you are compiling? v1.0 or v1.1? Try v1.1 if not:

What development environment are you using? VSCode + ESP-IDF? What version of the ESP-IDF?

  Are you sure? yes | no

Federico Allegretti wrote 07/13/2023 at 15:03 point


vscode 1.79.2 + idf 1.6.4

thermoimager v1.1

  Are you sure? yes | no

nigel wrote 07/05/2023 at 09:18 point

I got a similar error because I had not selected the correct esp32 device. Click on the esp32 icon in the status bar at the bottom of Vs Code and make sure that 1) your project is selected 2) ESP32 is selected and 3) ESP-WROVER-KIT-3.3v is selected. Then try to build and flash again.  I also reduced the flash baud rate from 460800 to 115200.

  Are you sure? yes | no

Federico Allegretti wrote 07/13/2023 at 15:06 point

done what you suggest .. now i was asked to set the "kit" to build the code.

"Scan for kits" will provide no result

"unspecified" fail the build.

All others are amd/x86 compilers

  Are you sure? yes | no

nigel wrote 06/14/2023 at 09:48 point

Does anyone have a schematic of the hardware connecting the sensor to the 20 pin connector as I cannot see how it is wired up and which RCs to use from just the photos? Regarding the user below, I got 2.4" which I believe the case is modelled on but I am not 3D printing the case.

  Are you sure? yes | no

Ruslan wrote 06/14/2023 at 10:41 point

2 resistors are needed to pull up I2C bus (SDA and SCL) to +3.3V. The value of the resistors is from 1 KΩ to 10 KΩ.

The other 2 resistors (at the top of the board) are a voltage divider to measure battery voltage. The first resistor is 5.1 kΩ between VBAT and IO35. The second resistor is 4.7 kΩ between GND and IO35.

Pin SCL is IO26 on 20pin header, pin SDA is IO33 on 20pin header

  Are you sure? yes | no

nigel wrote 06/14/2023 at 21:31 point

Thanks, I will try.

  Are you sure? yes | no

nigel wrote 04/09/2023 at 17:50 point

Hi, I am interested in building this camera for monitoring of body temperature of hedgehogs and cats.  I also have the Melixes evaluation board with sensors but I want to use the camera without the computer. I am not a software person so I am not sure how to make the hardware work -- no readMe file.  Do I have to import the files into vsCode?  Then what is SDK for?  Also, how do I then program the

LILYGO TTGO T4 v1.3 board?  Sorry for all the questions but if I want to build it I would need more step-by-step instructions.  Also, the Russian battery is not available in the EU and I cannot find an equivalent.

  Are you sure? yes | no

Ruslan wrote 04/09/2023 at 20:03 point

That's right, LILYGO TTGO T4 v1.3 board.

You can choose any Li-ion or Li-Po battery, suitable in size and with a voltage of 3.7V.
The battery I used has the following dimensions:
Length: 60±1mm
Width: 30±1mm
Thickness: 6±1mm

To install the development environment, I recommend using this instruction:

After that, simply open the project folder with VSCode and click the compile button in the VSCode status bar. The button next to it writes the firmware to the TTGO T4 board (you need to select the correct COM port).

  Are you sure? yes | no

nigel wrote 04/10/2023 at 08:44 point

Thank you so  much for answers to my questions. I am waiting for delivery of LILYGO TTGO board. When I get it I will follow the instructions.  Hopefully everything will go okay.

  Are you sure? yes | no

arborealfirecat wrote 03/09/2023 at 20:11 point

Great project! Thanks for the breakdown.

I'm currently trying to replicate your project but have been unable to compile the firmware.

I'm using the esp-idf framework as a VSCode extension & compiling on Arch Linux.

Are you able to provide me with some pointers such as your esp-idf framework version?

I'm relatively new to compiling C for Arduino, so I've included the full build log at

  Are you sure? yes | no

Ruslan wrote 03/10/2023 at 13:28 point

i was using a rather old version of ESP-IDF (v.3.3.1)
Today i updated the project to ESP-IDF v4.4.3, please try:

  Are you sure? yes | no

arborealfirecat wrote 03/10/2023 at 14:58 point

Thanks, I've managed to compile the project after one slight change.

As my OS is case sensitive the includes for MLX90640_API on main.c line 7 and task_mlx.c line 5 had to be changed to uppercase.

Now I just need to figure out why flashing the device loops the following:

  Are you sure? yes | no

Ruslan wrote 03/11/2023 at 19:54 point

Unfortunately, I do not have a thermal imager at hand right now to reproduce the problem on my own. The previous version for ESP-IDF 3.3.1 definitely worked without problems. I tried to create a clean project again and setup sdkconfig manually, please try it.

  Are you sure? yes | no

eBender wrote 02/28/2023 at 20:29 point

Hey great project and form factor! not sure if i missed it in the description but could you post your code? i'm really interested in your de-interlacing solution, doing something similar. thank you!

  Are you sure? yes | no

Ruslan wrote 02/28/2023 at 20:33 point

Thanks! I have not published the code yet, but I will definitely do it when I finish the description of this project. I have to prepare the code for publication and upload it to github

  Are you sure? yes | no

eBender wrote 02/28/2023 at 20:34 point

sounds great, will keep an eye out!

  Are you sure? yes | no

Federico Allegretti wrote 02/28/2023 at 20:13 point

Hello. Got a mlx thermal I plan to bind to a raspberry pi pico.  Going to follow your project ;-)

  Are you sure? yes | no

Ruslan wrote 02/28/2023 at 20:29 point

Thank you

  Are you sure? yes | no

karelv wrote 02/24/2023 at 18:29 point

De-interlace filter helps to smooth the image when the object is moving.

The IIR filter reduces the noise for the pixel that do not see a temperature change, for example the background.

  Are you sure? yes | no

Ruslan wrote 02/24/2023 at 19:38 point

Thank you

  Are you sure? yes | no


[this comment has been deleted]

Ruslan wrote 02/24/2023 at 04:46 point

Unfortunately, Melexis does not publish USB protocol description for EVB90640-41. I see solutions like this:

- use the library for Windows from Melexis.
- explore USB protocol and implement it yourself
- write your own firmware for EVB90640-41 or use another board instead

Maximum refresh rate of the full image is 32 Hz, but with this frequency the image will be quite noisy (noise is about 5 degrees)

  Are you sure? yes | no

Ruslan wrote 02/24/2023 at 04:54 point

Also i think EVB90640-41 is too expensive)

  Are you sure? yes | no

karelv wrote 02/24/2023 at 18:20 point

Agreed, but bare in mind it comes with a small field of view MLX90640 as well as MLX90641!

  Are you sure? yes | no

karelv wrote 02/24/2023 at 18:23 point

It has 64Hz refresh rate. So each 16ms there is a subframe available, and thus every 32ms there's a full frame available.

Remember that faster refresh rate yield in higher noise levels.

  Are you sure? yes | no

Ruslan wrote 02/24/2023 at 19:40 point

yes, I wrote about it in the text

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates