AMG8833 Thermal Camera using ESP32 & TFT Display, Thermal Imaging Camera
Table of Contents
AMG8833 Thermal Camera using ESP32:
AMG8833 Thermal Camera using ESP32 & TFT Display, Thermal Imaging Camera–
In today’s article, you will learn how to make a simple thermal camera using the AMG8833 Grid-EYE Sensor by Panasonic, a 320×240 IPS TFT LCD Display, and the DFRobot FireBeetle 2 ESP32 S3 WROOM-1 controller board.
Amazon Links:
FireBeetle 2 Official Product page
*Disclosure: These are affiliate links. As an Amazon Associate I earn from qualifying purchases.
Instead of using the FireBeetle 2 ESP32 module you can also use Arduino and other variants of the ESP32 and ESP8266. But the advantage of using the FireBeetle 2 ESP32 Board is that, it has the GDI “Graphics Device interface”. So, using the GDI interface you can connect the TFT LCD with the ESP32 in just a few seconds
But if we connect the same 320×240 IPS TFT LCD Display to the Arduino Nano, Arduino Uno, and previous versions of the ESP32, and ESP8266, then we will need a lot of jumper wires.
The good thing about this TFT LCD is that it has a GDI interface and male headers. So, if your controller has a GDI interface, you can connect this display using an FPC cable; otherwise, you will have to use jumper wires.
Another advantage of using the FireBeetle 2 ESP32 board is that
It has the AXP313A Power Management chip and the ETA6003 Lithium-ion Battery Charging Management Chip, and a Lithium-Ion battery port. So, just connect a single cell lithium Ion battery and forget about its charging and management; the onboard chips will take care of it.
Let’s go ahead and connect the TFT LCD using the FPC Cable.
If you are using previous versions of the ESP32 boards then you can follow this circuit diagram.
AMG8833:
The AMG8833 Grid-EYE is a low cost infrared sensor array developed by Panasonic. There are also other variants of the same sensor like AMG8834, AMG8853, and AMG8854. These numbers have specific meanings.
The 1st and 2nd numbers represent vertical and horizontal pixels. So, 8 8 means that this temperature sensor is an 8×8 array of IR thermal sensors. When connected to a microcontroller or raspberry Pi it will return an array of 64 individual infrared temperature readings over I2C. This sensor has only 64 (8×8) pixels.
3rd number represents the Applied voltage, 3 means 3.3VDC and 5 means 5VDC.
If the 4th number is 3 then it’s a high performance type High gain and if its 4 then it’s a high performance Low gain.
So, the AMG8833 is a 64 pixels 3.3VDC High gain sensor.
The Horizontal and vertical viewing angle is 60o .
The operating temperature range is 0 to 80 0C.
Temperature accuracy is ±2.5 0C.
Human Detection distance is around 7 meters.
Current consumption in the normal mode is 4.5mA, 0.2mA in sleep mode, and 0.8mA in stand-by mode.
Altium Designer, Altium 365, & Octopart:
Altium Designer is the world’s most trusted PCB design system. Altium Designer enables engineers to effortlessly connect with every facet of the electronics design process. Over 35 years of innovation and development focused on a truly unified design environment makes it the most widely used PCB design solution. With Altium Designer you can create PCB designs with an intuitive and powerful interface that connects you to every aspect of the electronics design process. Route it your way through any angle, tune for the delay, Push, Slide, and Walkaround faster than ever.
Easily work together with your mechanical team and forget about the days of swapping design files. Every design change stays in sync between Altium Designer and SOLIDWORKS, PTC Creo, Autodesk Inventor, Autodesk Fusion 360, or Siemens NX*.
Interact and collaborate with mechanical designers like never before in a photo-realistic, 3D design environment.
One of the best things about Altium Designer is that you can share your designs with your team members using Altium 365. They can check your design, leave comments, and if there are any issues, they can fix them from anywhere in the world. Altium Designer also uses the world’s fastest components search engine, Octopart, so you won’t have any difficulty in searching for components.
Altium Designer, Altium 365, and Octopart—unleashes the full potential of electronics design by seamlessly integrating design tools, collaboration platforms, and component databases. Together, they offer engineers a comprehensive and synchronized experience, leading to improved productivity, reduced errors, and accelerated innovation in the world of electronics design.
AMG8833 Interfacing with ESP32:
Connect the VIN and GND pins of the AMG8833 sensor to the ESP32 3.3V and GND pins. Connect the SCL and SDA pins to the FireBeetle 2 ESP32 board SCL and SDA pins. So, if you are using the Firebeetle 2 ESP32 board then you will only need 4 wires.
Now, before we start the programming, first of all, we will need to add the ESP32 FireBeetle 2 in the Arduino IDE. Because, by default no ESP32 board is installed in the Arduino IDE and you can confirm this by going to the Tools Menu, then to Board, and you can see there is no ESP32 board.
So, first we will need to add it in the boards list, for this copy this Board Manager URL link;
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
Then go back to the Arduino IDE, go to the File Menu, then preferences, and paste this link in the Additional Boards Manager URLs and click the Ok button.
Next, go to the Tools Menu, then Board and click on the Boards Manager. Search for the ESP32. You can see we have Arduino ESP32 Boards and ESP32 by Espressif Systems.
So, make sure you install this one and don’t forget to select the latest version. Finally, the board installation has been completed and now we can confirm this by going to the boards list.
You can see all the different variants of the ESP32 Boards have been added, so, scroll down and you will see the DFrobot FireBeetle ESP32 S3 board.
Next, we are going to add library for this TFT LCD module.
So, while your Arduino IDE is open go to the Sketch Menu, then to Include Library, and click on the Add .ZIP Library. Browse to the location and select this library,
Now, we will install library for the AMG8833 sensor.
So, while your Arduino IDE is open go to the Sketch Menu, then to Include Library, and click on the manage libraries. Search for the AMG8833.
You can see I have already installed the Adafruit AMG88xx Library.
AMG8833 Programming:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 |
#include "DFRobot_GDL.h" #include <TouchScreen.h> #include <Wire.h> #include <Adafruit_AMG88xx.h> #if defined ARDUINO_SAM_ZERO #define TFT_DC 7 #define TFT_CS 5 #define TFT_RST 6 /*ESP32 and ESP8266*/ #elif defined(ESP32) || defined(ESP8266) #define TFT_DC D2 #define TFT_CS D6 #define TFT_RST D3 /*AVR series mainboard*/ #else #define TFT_DC 2 #define TFT_CS 3 #define TFT_RST 4 #endif /** * @brief Constructor Constructors for hardware SPI communication * @param dc Command pin or data line pin of SPI communication * @param cs Chip select pin for SPI communication * @param rst Reset pin of the screen * @param bl Screen backlight pin */ //DFRobot_ST7789_240x240_HW_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST); DFRobot_ST7789_240x320_HW_SPI tft(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST); //DFRobot_ILI9341_240x320_HW_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST); //DFRobot_ILI9488_320x480_HW_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST); /* M0 mainboard DMA transfer */ //DFRobot_ST7789_240x240_DMA_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST); //DFRobot_ST7789_240x320_DMA_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST); //DFRobot_ILI9341_240x320_DMA_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST); //DFRobot_ILI9488_320x480_DMA_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST); /*______Assign names to colors and pressure_______*/ #define BLACK 0x0000 //Black->White #define YELLOW 0x001F //Blue->Yellow #define RED 0xF800 //Red->Cyan #define PINK 0x07E0 //Green-> Pink #define CYAN 0x07FF //Cyan -> Red #define GREEN 0xF81F //Pink -> Green #define BLUE 0xFFE0 //Yellow->Blue #define WHITE 0xFFFF //White-> Black #define MINPRESSURE 10 #define MAXPRESSURE 1000 //#define DEBUG //Comment this out to remove the text overlay //#define SHOW_TEMP_TEXT //low range of the sensor (this will be blue on the screen) #define MINTEMP 10 // minimum temperature value can be 0 degrees Celsius //high range of the sensor (this will be red on the screen) #define MAXTEMP 39 // maximum temperature value can be 80 degrees Celsius //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,}; Adafruit_AMG88xx amg; unsigned long delayTime; #define AMG_COLS 8 #define AMG_ROWS 8 float pixels[AMG_COLS * AMG_ROWS]; float pixels2[AMG_COLS * AMG_ROWS]; #define INTERPOLATED_COLS 30 //24 #define INTERPOLATED_ROWS 30 //24 float get_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y); void set_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y, float f); void get_adjacents_1d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y); void get_adjacents_2d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y); float cubicInterpolate(float p[], float x); float bicubicInterpolate(float p[], float x, float y); void interpolate_image(float *src, uint8_t src_rows, uint8_t src_cols, float *dest, uint8_t dest_rows, uint8_t dest_cols); void setup() { delay(500); Serial.begin(115200); Serial.println("\n\nAMG88xx Interpolated Thermal Camera!"); tft.begin(); tft.setRotation(0); //PORTRAIT tft.fillScreen(COLOR_RGB565_BLACK); colorbar(); // default settings if (!amg.begin()) { Serial.println("Could not find a valid AMG88xx sensor, check wiring!"); while (1) { delay(1); } } Serial.println("-- Thermal Camera Test --"); } float pix_max,pos_x,pos_y; void loop() { //read all the pixels // digitalWrite(ledPin, HIGH); amg.readPixels(pixels2); for (int i=0;i<64;i++) pixels[i]=(pixels2[((((int)(i/8)*8)+7)-(i%8))])+2.5; //error +2.5c #ifdef DEBUG Serial.print("["); for(int i=1; i<=AMG88xx_PIXEL_ARRAY_SIZE; i++){ Serial.print(pixels[(i-1)]); Serial.print(", "); if( i%8 == 0 ) Serial.println(); } Serial.println("]"); Serial.println(); #endif float dest_2d[INTERPOLATED_ROWS * INTERPOLATED_COLS]; int32_t t = millis(); interpolate_image(pixels, AMG_ROWS, AMG_COLS, dest_2d, INTERPOLATED_ROWS, INTERPOLATED_COLS); #ifdef DEBUG Serial.print("Interpolation took "); Serial.print(millis()-t); Serial.println(" ms"); #endif uint16_t boxsize = min(tft.width() / INTERPOLATED_COLS, tft.height() / INTERPOLATED_COLS); drawpixels(dest_2d, INTERPOLATED_ROWS, INTERPOLATED_COLS, boxsize, boxsize, false); tft.setTextColor(COLOR_RGB565_RED); tft.setCursor(0,250); tft.setTextSize(3); tft.print("MAX:"); tft.fillRect(70, 250, 120,30,COLOR_RGB565_BLACK ); tft.print(pix_max); tft.print(" C"); tft.drawCircle(pos_x,pos_y,6,0); tft.drawCircle(pos_x,pos_y,5,0); tft.drawLine(pos_x,pos_y-3, pos_x, pos_y+3, 0); tft.drawLine(pos_x-3,pos_y, pos_x+3, pos_y, 0); pix_max=0; } void drawpixels(float *p, uint8_t rows, uint8_t cols, uint8_t boxWidth, uint8_t boxHeight, boolean showVal) { int colorTemp; for (int y=0; y<rows; y++) { for (int x=0; x<cols; x++) { float val = get_point(p, rows, cols, x, y); if(val >= MAXTEMP) colorTemp = MAXTEMP; else if(val <= MINTEMP) colorTemp = MINTEMP; else colorTemp = val; uint8_t colorIndex = map(colorTemp, MINTEMP, MAXTEMP, 0, 255); colorIndex = constrain(colorIndex, 0, 255); //draw the pixels! uint16_t color; color = val * 2; tft.fillRect(boxWidth * x, boxHeight * y, boxWidth, boxHeight, camColors[colorIndex]); if (pix_max<val){ pix_max =val; pos_x=boxWidth * x; pos_y=boxHeight * y; } if (showVal) { tft.setCursor(boxWidth * y + boxWidth/2 - 12, 40 + boxHeight * x + boxHeight/2 - 4); tft.setTextColor(WHITE); tft.setTextSize(1); tft.print(val,1); } } } } #include <Arduino.h> float get_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y); void set_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y,float f); void get_adjacents_1d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y); void get_adjacents_2d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y); float cubicInterpolate(float p[], float x); float bicubicInterpolate(float p[], float x, float y); void interpolate_image(float *src, uint8_t src_rows, uint8_t src_cols, float *dest, uint8_t dest_rows, uint8_t dest_cols); float get_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y) { if (x < 0) x = 0; if (y < 0) y = 0; if (x >= cols) x = cols - 1; if (y >= rows) y = rows - 1; return p[y * cols + x]; } void set_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y, float f) { if ((x < 0) || (x >= cols)) return; if ((y < 0) || (y >= rows)) return; p[y * cols + x] = f; } // src is a grid src_rows * src_cols // dest is a pre-allocated grid, dest_rows*dest_cols void interpolate_image(float *src, uint8_t src_rows, uint8_t src_cols, float *dest, uint8_t dest_rows, uint8_t dest_cols) { float mu_x = (src_cols - 1.0) / (dest_cols - 1.0); float mu_y = (src_rows - 1.0) / (dest_rows - 1.0); float adj_2d[16]; // matrix for storing adjacents for (uint8_t y_idx = 0; y_idx < dest_rows; y_idx++) { for (uint8_t x_idx = 0; x_idx < dest_cols; x_idx++) { float x = x_idx * mu_x; float y = y_idx * mu_y; // Serial.print("("); Serial.print(y_idx); Serial.print(", "); // Serial.print(x_idx); Serial.print(") = "); Serial.print("("); // Serial.print(y); Serial.print(", "); Serial.print(x); Serial.print(") = // "); get_adjacents_2d(src, adj_2d, src_rows, src_cols, x, y); /* Serial.print("["); for (uint8_t i=0; i<16; i++) { Serial.print(adj_2d[i]); Serial.print(", "); } Serial.println("]"); */ float frac_x = x - (int)x; // we only need the ~delta~ between the points float frac_y = y - (int)y; // we only need the ~delta~ between the points float out = bicubicInterpolate(adj_2d, frac_x, frac_y); // Serial.print("\tInterp: "); Serial.println(out); set_point(dest, dest_rows, dest_cols, x_idx, y_idx, out); } } } // p is a list of 4 points, 2 to the left, 2 to the right float cubicInterpolate(float p[], float x) { float r = p[1] + (0.5 * x * (p[2] - p[0] + x * (2.0 * p[0] - 5.0 * p[1] + 4.0 * p[2] - p[3] + x * (3.0 * (p[1] - p[2]) + p[3] - p[0])))); return r; } // p is a 16-point 4x4 array of the 2 rows & columns left/right/above/below float bicubicInterpolate(float p[], float x, float y) { float arr[4] = {0, 0, 0, 0}; arr[0] = cubicInterpolate(p + 0, x); arr[1] = cubicInterpolate(p + 4, x); arr[2] = cubicInterpolate(p + 8, x); arr[3] = cubicInterpolate(p + 12, x); return cubicInterpolate(arr, y); } // src is rows*cols and dest is a 4-point array passed in already allocated! void get_adjacents_1d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y) { // Serial.print("("); Serial.print(x); Serial.print(", "); Serial.print(y); // Serial.println(")"); // pick two items to the left dest[0] = get_point(src, rows, cols, x - 1, y); dest[1] = get_point(src, rows, cols, x, y); // pick two items to the right dest[2] = get_point(src, rows, cols, x + 1, y); dest[3] = get_point(src, rows, cols, x + 2, y); } // src is rows*cols and dest is a 16-point array passed in already allocated! void get_adjacents_2d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y) { // Serial.print("("); Serial.print(x); Serial.print(", "); Serial.print(y); // Serial.println(")"); float arr[4]; for (int8_t delta_y = -1; delta_y < 3; delta_y++) { // -1, 0, 1, 2 float *row = dest + 4 * (delta_y + 1); // index into each chunk of 4 for (int8_t delta_x = -1; delta_x < 3; delta_x++) { // -1, 0, 1, 2 row[delta_x + 1] = get_point(src, rows, cols, x + delta_x, y + delta_y); } } } void colorbar(){ for (int i=0;i<240;i++){ tft.fillRect(i, 280, 1,24, camColors[(int)(i*1.05)]); } tft.setTextColor(COLOR_RGB565_WHITE); tft.setCursor(0,285); tft.setTextSize(2); tft.print(MINTEMP); tft.print("C"); tft.setCursor(202,285); tft.print(MAXTEMP); tft.print("C"); } |
Anyway, to upload the program, make sure your ESP32 FireBeetle 2 board is connected to your Laptop or PC.
Go to the Tools menu and select the DFRobot FireBeetle 2 ESP32-S3 Board. Make sure you enable the USB CDC On Boot.
Set the Flash size to 16MB (128Mb).
Set the Partition Scheme to 16M Flash (3MB).
Set the PSRAM to OPI PSRAM.
Finally, select the port and then you can upload the program.
Now, let’s go ahead and watch the AMG8833 based Thermal imaging camera in action.
Watch video tutorial for the full testing and step-by-step explanation.
Watch Video Tutorial:
Hey, you have been a guide for me for the microcontroller related projects so thanks a lot. I wanted guidance from you for the PCB of this very project. Can you please provide the above said.