Création d’une Caméra Thermique DIY avec AMG8833 et ESP8266

Préparez-vous à découvrir comment fabriquer votre propre caméra thermique en utilisant un capteur AMG8833, un écran LCD et un serveur ESP8266. Non seulement vous pourrez voir le monde sous un angle totalement différent, mais vous pourrez aussi partager ces images incroyables sur votre serveur. Alors, êtes-vous prêts à transformer votre vision du monde ? C’est parti !🎥

Capteur AmG8833 schematic

#include <Wire.h>
#include <Adafruit_AMG88xx.h>

#include <Adafruit_GFX.h>      // include Adafruit graphics library
#include <Adafruit_ST7735.h>   // include Adafruit ST7735 TFT library

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#define SSID “NOM_DE_MON_WIFI”

#define PASSWORD “MotDePasse”

// creer un serveur sur le port 80R

ESP8266WebServer server(80);

// Ecran
// ST7735 TFT module connections
#define TFT_RST   D4     // TFT RST pin is connected to NodeMCU pin D4 (GPIO2)
#define TFT_CS    D3     // TFT CS  pin is connected to NodeMCU pin D4 (GPIO0)
#define TFT_DC    D8     // TFT DC  pin is connected to NodeMCU pin D4 (GPIO4)
// initialize ST7735 TFT library with hardware SPI module
// SCK (CLK) —> NodeMCU pin D5 (GPIO14)  SCL
// MOSI(DIN) —> NodeMCU pin D7 (GPIO13)   SDA
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
//fin ecran
//capteur thermique
Adafruit_AMG88xx amg;
// tableau de flotant avec la taile du capteur
float pixels[AMG88xx_PIXEL_ARRAY_SIZE];
// taille du capteur et coeficient conversion temperature
#define IMAGE_W 8
#define IMAGE_H 8
#define TEMP_CONV_FACTOR 0.25
// Temperature minimale 0 , maximale 80 pour le capteur AMG8833
//valeur minimale de reference (Couleur bleu a l’ecran)
int MINTEMP = 6;
//valeur maximale de reference (Couleur rouge a l’ecran)
int MAXTEMP = 40;
//Tableau de couleur utilisé pour l’affichage
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,};
String htmlTable;
//Convertisseur de couleur en couleur rgb
void convertColor(uint16_t color, uint8_t& r, uint8_t& g, uint8_t& b) {
    r = (color >> 8) & 0xFF; // Extrait la composante rouge
    g = (color >> 3) & 0xFF; // Extrait la composante verte
    b = (color << 3) & 0xFF; // Extrait la composante bleue
}
// Page web principale de l’esp qui contient l’appel a image
void handleRoot() {
  String html = “<html><body>”;

  html += “<h1>Thermal Imaging</h1>”;

  html += “<div id=’camera’ style=”></div>”;
  html += “<input type=’range’ id=’intervalSlider’ min=’50’ max=’1000′ value=’200′ step=’50’ oninput=’rangeValue.innerText = this.value’ onchange=’updateInterval(this.value)’>”;
  html += “<label for=’intervalSlider’>Intervalle (ms): <span id=’rangeValue’>200</span></label>”;

   html += “<div><a href=’https://retroetgeek.com’ target=’_blank’>Made by  Retroetgeek.com</a> </div>”;

  html += “<script>”;
  html += “function chargerImage() {“;
  html += “var xhttp = new XMLHttpRequest();”;
  html += “xhttp.onreadystatechange = function() {“;
  html += “if (this.readyState == 4 && this.status == 200) {“;
  html += “document.getElementById(‘camera’).innerHTML = this.responseText;”;
  html += “}};”;
  html += “xhttp.open(‘GET’, ‘image’, true);”;
  html += “xhttp.send();”;

  html += “}”;

  html += “function updateInterval(value) {“;
  html += “clearInterval(timer);”; // Arrêter l’intervalle actuel
  html += “chargerImage();”; // Charger immédiatement une image
  html += “timer = setInterval(chargerImage, value);”; // Définir le nouvel intervalle
  html += “}”;
html += “var timer = setInterval(chargerImage, 200);”; // Initialiser l’intervalle
  html += “</script>”;
  html += “</body></html>”;
  server.send(200, “text/html”, html);
}
// REquete de l’image qui affiche le contenu de htmlTable
void handleImage() {
  String html = “<style>table {table-layout: fixed;border-collapse: collapse;}td {width: 80px;aspect-ratio: 1/1;height: 80px;text-align:center;}</style>”;
  html += htmlTable;
  html += “”;
  html += “”;
  server.send(200, “text/html”, html);
}

void setup() {

  Serial.begin(115200);
  while (!Serial) {
    delay(10); // Attendre la connexion avec la console série
  }
  Serial.println(“Démarrage…”);
  if (!amg.begin()) {
    Serial.println(“Echec de l’initialisation du capteur AMG8833”);
    while (1);
  }
  Serial.println(“Initialisation du capteur AMG8833 réussie”);
  delay(1000);
  // initialisation de l’ecran avec fond noir
  tft.initR(INITR_BLACKTAB);
  tft.fillScreen(ST7735_BLACK);
  //
  //tft.fillRect(0, 0, 30, 30, ST7735_YELLOW);
  tft.println(” “);
  tft.print(” Made By”);
  tft.println(” “);
  tft.println(” “);

  tft.print(” Retroetgeek.com”);

  // connect to WiFi network
  WiFi.begin(SSID, PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
  }
  WiFi.hostname(“CamThermique_Retro”);
  // affichage sur serial port de l’adresse ip
  Serial.println(“WiFi connected”);
  Serial.println(“IP address: “);
  Serial.println(WiFi.localIP());
// affichage sur l’ecran de l’adresse ip
  tft.println(” “);
  tft.println(” “);
  tft.print( WiFi.localIP());
  delay(3000);
  // definition des url et des fonctions qui seront appelés lorsque l’on se connecte dessus
  server.on(“/”, handleRoot);
  server.on(“/image”, handleImage);
  // start server
  server.begin();
}

void loop() {

  amg.readPixels(pixels);
  // Taille d’un carré pour remplir l’écran
  int squareSize = 15;
// initialisation valeur minimale et maximale pour definir quelles seront les vraies valeurs
  float min_temp=+999;

  float max_temp=-999;

// calcul du min et max pour optimiser l’image
  for (int i = 0; i < IMAGE_W * IMAGE_H; i++) {
    float tempC = pixels[i] ;
    if(tempC<min_temp){
      min_temp=tempC;
    }
    if(tempC>max_temp){
      max_temp=tempC;
    }
  }
    if((max_temp-min_temp) < 10){
      float delta = 10-(max_temp-min_temp);
      MINTEMP = min_temp-(delta/2);
      MAXTEMP = max_temp+(delta/2);
    }
    else{
      MINTEMP = min_temp;
      MAXTEMP = max_temp;
    }
// boucle de remplissage de l’ecran tft
  for (int i = 0; i < IMAGE_W * IMAGE_H; i++) {
    //float tempC = pixels[i] * TEMP_CONV_FACTOR;
    // recuperation de la temperature sur le tableau du capteur
    float tempC = pixels[i] ;
//    if(tempC<min_temp){
//      min_temp=tempC;
 //   }
//    if(tempC>max_temp){
//      max_temp=tempC;
//    }
    // Calcul de la position du carré à dessiner
    int x = (i % IMAGE_W) * squareSize;
    int y = (i / IMAGE_W) * squareSize;
    //mapping de la couleur par rapport au min max
    uint8_t colorIndex = map(pixels[i], MINTEMP, MAXTEMP, 0, 255);
    colorIndex = (uint8_t)constrain((int16_t)colorIndex, (int16_t)0, (int16_t)255);
    // Dessin du carré
    tft.fillRect(x, y, squareSize, squareSize, camColors[colorIndex]);
  }
  //ajout minimum max pour l’ecran
  int pos_x_txt=0;
  int pos_y_txt=((64 / IMAGE_W) * squareSize )+ squareSize;;
  tft.fillRect(0, pos_y_txt, 160, 100, ST7735_BLACK);
 // min_temp
 // max_temp
  tft.setCursor(0, pos_y_txt);
  tft.setTextColor(ST7735_WHITE);
  tft.setTextSize(1);
  tft.print(“Min:”);
  tft.print(min_temp);
  tft.print(” Max:”);
  tft.println(max_temp);
  tft.print(” Retroetgeek.com”);
// generation html de l’image thermique
  htmlTable = “<table>”;
  for (int i = 0; i < IMAGE_H; i++) {
    htmlTable += “<tr>”;

    for (int j = 0; j < IMAGE_W; j++) {

      // recuperation de la temperature sur le tableau du capteur
      float tempC = pixels[i * IMAGE_W + j];
      //mapping de la couleur par rapport au min max
      uint8_t colorIndex = map(pixels[i * IMAGE_W + j], MINTEMP, MAXTEMP, 0, 255);

      colorIndex = (uint8_t)constrain((int16_t)colorIndex, (int16_t)0, (int16_t)255);

      // conversion de la couleur pour ressortir en rgb
      uint8_t r, g, b;
      convertColor(camColors[colorIndex], r, g, b);
            //debug
      // if(i==0 && j ==0 ){
      //   Serial.println(“Débug”);
      //   Serial.println(colorIndex);

      //   Serial.println(camColors[colorIndex]);

      //   Serial.print(r);
      //   Serial.print(“/”);
      //   Serial.print( g );
      //   Serial.print(“/”);
      //   Serial.println( b);
      // }
      // on rempli htmlTable avec la couleur associé ainsi que la temperature
      htmlTable += “<td style=\”background-color:rgb(” + String(r) + “,” + String(g) + “,” + String(b) + “)\”>”+ tempC+ “</td>”;
    }
    htmlTable += “</tr>”;
  }
  htmlTable += “</table>”;
  //on affiche le min max de la mesure actuelle
  htmlTable += “<div><label> Temperature / min : “+ String (min_temp) +” / max : “+ String (max_temp) +”  </label></div>”;
  // handle URL requests
  server.handleClient();
}

En matériel pour le capteur thermique il nous faut :

– 1 ESP8266 ou autre esp
– 1 capteur thermique AMG8833
– 1 écran lcd 128 x 160 (optionnel )

Pour les branchements de l’amg8833

On va devoir se référer aux i2c et spi de la carte que l’on a ici ( voir schéma )
Le pin D1 pour le SDA du capteur thermique.
Le pin D2 POUR LE SCL du capteur aussi.
Pour alimenter le capteur on peut soit le brancher a du 5v sur le VIN soit en 3.3V sur le 3V
Et on oublie pas le GND à la masse.

Et pour l’écran qui est optionnel .
Le pin D3 sur le pin CS
Le pin D4 connecté sur le RST
Le pin D5 connecté au SCL de l’écran.
Le pin D7 au SDA toujours de l’écran.
Et le pin D8 au pin DC.
L’alimenter en 3 ou 5V sur le Vcc et toujours la masse au Gnd.

Si vous n’avez pas encore effectué cette étape dans votre logiciel Arduino, suivez ces instructions : allez dans “Préférences”, puis “Gestionnaire de cartes supplémentaires” et ajoutez l’URL suivante : http://arduino.esp8266.com/stable/package_esp8266com_index.json

Ajouter la carte dans outils, type de carte, gestionnaire de carte et ajouter esp8266

Lorsque vous téléversez votre programme, assurez-vous de sélectionner la carte correcte pour un transfert de code réussi.

Maintenant, entrons dans la partie de programmation du microcontrôleur ESP8266 !

Voici ce que j’ai prévu :

  1. Établir une connexion à un réseau Wi-Fi.
  2. Récupérer les données du capteur thermique AMG8833.
  3. Remplit l’écran LCD.
  4. Générer la page avec les données thermique et en faire une sorte de webcam

Pour réaliser ces tâches, nous utilisons plusieurs bibliothèques indispensables dans notre programme :

  • Adafruit AMG88xx de Adafruit pour gérer le capteur thermique.
  • Adafruit ST7735 de Adafruit qui correspond a mon écran lcd, vous allez devoir trouver la librairie de votre écran si il est différent.
  • Pour les autres librairies nécessaires, elles sont incluses lorsque vous ajoutez le package ESP8266. Si par hasard vous ne les trouvez pas, une recherche sur google il devrait vous permettre de les localiser. 😄

Passons au code de la caméra thermique, voici un exemple d’utilisation

Appelons les différentes librairies pour i2c avec Wire et le capteur thermique AMG8833
Puis si vous avez le même écran lcd ( sachant que c’est optionnel ) les librairies adafruit.
Ensuite les librairies pour le wifi de l’esp …

Avec SSID on y entre le nom de son wifi et dans PASSWORD le mot de passe.

On crée le serveur avec l’esp pour qu’il réponde à nos demandes avec ESP8266WebServer.

Puis si vous possédez toujours un écran on doit définir les pin pour pouvoir lui afficher quelque chose avec les différents define. Attention le câblage est diffèrent suivant votre ESP et votre écran lcd , donc à vous de voir la documentation.
Ensuite on va associer la variable tft avec l’écran et la librairie ST7735.

Puis la variable amg a la librairie Adafruit_AMG88xx

On affecte une variable pixels en float avec AMG88xx_PIXEL_ARRAY_SIZE

Puis on affecte IMAGE_W et IMAGE_H avec leur taille respective ( capteur 8×8 ).

Je définit une valeur minimale pour le mapping des températures ainsi qu’une valeur maximale avec MINTEMP et MAXTEMP, mais je vais modifier les valeurs plus tard dans le programme.

Puis le tableau camColors qui lui va contenir un tableau de couleur.
C’est lui qui va me permettre d’avoir a l’image des couleurs allant du violet pour le plus froid au rouge pour le plus chaud.

Je crée une variable String appelée htmlTable qui contiendra l’image html de la camera thermique.

J’ai ensuite un convertisseur de couleur nommé convertColor qui transforme la couleur du tableau en couleur rgb, pour être interprété en html.

Pour gerer l’affichage de notre image thermique je vais avoir besoin d’une fonction , ici c’est handleRoot qui contient le code html et javascript.
Dans cette page on a une function qui va tous les intervale d’un temps donnée, appeler la page image de notre serveur esp et l’afficher.

Puis la fonction handleImage qui elle va gérer “l’image” générée.

Partie setup, j’active le serial avec le classique Serial.begin puis on essaye d’activer le capteur thermique avec !amg.begin et je boucle tant que le capteur ne répond pas.

Ensuite on est sur une partie qui gère l’écran, on lui fait un init avec tft.initR pour lui appliquer un fond noir et on appelle l’écran ST7735.

On initialise après notre wifi et on boucle tant que l’on est pas connecté.
Je donne un nom a mon esp pour l’identifier plus rapidement avec Wifi.hostname.

J’affiche l’IP sur le serial ainsi que sur l’écran si on en a un.

Puis on définit les url surs lesquelles l’esp va répondre avec server.on et on va appeler 2 fonctions différentes. La fonction handleRoot pour l’url de base et handleImage pour l’url /image qui nous sert à récupérer “l’image”.

Puis on démarre le serveur avec server.begin.

Dans la boucle je lance la récupération des valeurs du capteurs avec amg.readPixels(pixels) , les valeurs vont se retrouver dans le tableau de données pixels.

J’ai la taille de mes carrés de l’affichage écran lcd avec squareSize puis, je met des valeurs min_temp et max_temp super haute pour trouver ensuite le minimum et le maximum dans les relevés du capteur (tableau pixels ).

J’ai crée une boucle for pour récupérer le min et max du relevé de température actuel.
Ensuite petit calcul de delta entre la température maximale et minimale.
Si la température max-min est inférieure à 10 alors je vais rajouter le delta de la moitié de la température pour avoir un écart de 10 degré entre le max et le minimum.
Sinon je minimum et le maximum deviennent la référence pour le mapping de couleur que l’on va faire ensuite.

Occupons nous du remplissage de l’écran tft avec une boucle de parcours for.
On récupère la tempC ( température ) du pixel ou on se trouve.
X et Y sont ensuite définis  pour la position du carré a dessiner sur l’écran.
On va définir la couleur du carré avec un map de la température avec la map de la température du pixel sur lequel on on est avec comme mappeur MINTEMP et MAXTEMP.
On transforme le colorIndex pour récupérer une valeur
Et ensuite on va dessiner le “rectangle” avec tft.fillRect , des positions x et y, une taille de carré et la couleur qui va ressortir avec son index du tableau camColors.

Je me positionne ensuite en bas de l’écran pour écrire la température minimale et maximale relevée.

Passons sur la partie html, la génération du tableau qui va crée l’image thermique.
Je boucle de 0 à IMAGE_H et de 0 à IMAGE_W pour boucler sur toutes les valeurs de relevés de température.
On récupère les valeurs de pixels que l’on affecte dans tempC.

On est encore sur le système de récupération de couleur par rapport au mapping que l’on a vu dans la partie écran lcd.
Mais la ici la couleur récupérée on va la transformer en couleur rgb pour être interprétée en html avec le fonction convertColor; les valeurs de retour retrouveront dans r,g,b.
On rempli htmlTable avec un td du tableau avec la couleur dans background-color, ainsi que la valeur de température dans le td.
On termine avec un affichage de température min et max en html
Puis un server.handleClient pour que tout tourne bien pour l’esp.

Et voici le code : Télécharger Code caméra thermique AMG8833

Retrouve le matériel que j’ai utilisé pour la vidéo en bas de page

 

 

Esp8266 D1 Min Data

  • AMG8833 IR
    aliexpress 200
    amazon 200

    AMG8833 capteur thermique

  • MLX90640_CSB Camera Thermique 24 x 32
    aliexpress 200
    amazon 200

    Camera thermique 32×24 MLX90640 _ CSB

  • MLX90640 capteur thermique 32x24
    aliexpress 200
    amazon 200

    Capteur Thermique MLX90640

  • écran lcd 1.8 128x160 spi
    aliexpress 200

    Écran LCD TFT 1.8 pouces pour arduino et esp8266

N’hésitez pas à poser vos questions sur les réseaux sociaux de la chaîne instagramtwitter , facebook ,youtube , discord; si vous ne comprenez pas certaines parties du tutoriel n’hésitez pas , me dire ce que vous aimeriez que je crée pour en faire des vidéos tutoriel  et à partager les projets que vous aimeriez créer etc…

Comme toujours allez sur la page de C’est quoi Retro et Geek pour connaître tout ce que je recherche à faire sur la chaîne.

Merci les RetroGeeker et RetroGeekeuse