Cámara termográfica con amg8833

          El Sketch está basado en la demo de interpolación de la librería para el amg8833, pero  con bastantes modificaciones.
          He cambiado:
                    -El controlador de la pantalla al usar una diferente a la demo y su repercusión en el código
                    -Gran parte de la matriz de la paleta de colores
                    -Inversión del mapeo de color
                    -Añadido temperaturas en pantalla
                    -Seleccionar 6 rangos de medida. Uno de ajuste automático por defecto al encender el sistema. Se cambian con el botón izquierdo.

         
          Abajo el sketch para esp8266. Además, necesita la librería de interpolación. Todo completo bajar de aquí.
          Montaje de una cámara termográfica con el módulo amg8833. La resolución es modesta, 8x8 pixel pero el precio sube exponencialmente con la resolución. Como microprocesados he usado un Nodemcu D1 mini y una pantalla de 1,44 pulgadas.
          Dos micro pulsadores permiten seleccionar, uno la escala y el otro el encendido del sistema. La batería de 3,7v de litio es reciclada de una cámara fotográfica. 
          El montaje queda muy compacto dentro de un contenedor plástico para componentes y una tapa recortada de policarbonato. Dos trozos de varilla roscada de 3mm sujetan todo.
          Creo que unas fotos serán suficientemente explicativas.
         Las conexiones de amg8833, pantalla y esp8266 (Nodemcu D1 mini). También pongo las conexiones para esp32 por si alguien lo prefiere. 
         Disposición en placa de tiras. El display esta en un zócalo. La alimentación puede ser por la entrada usb del Nodemcu o externa con una batería de litio regulada a 3,3v con un integrado ams1117 soldado por el lado de las pistas, al igual que un puente de A0 a D3
         El zócalo negro para la pantalla. Se ven dos condensadores, uno de 10µF a la salida del ams1117 y otro de 100nF a la entrada.
         Los elementos usados, caja con tapa recortada de policarbonato, amg8833, Nodemcu, display 1,44, batería litio y dos micropulsadores (uno para encender y el otro para cambiar el rango de medida). Hay un módulo para cargar la batería de litio, pero aún no esta puesto en la caja.
//Sensor termico AMG8833
//OLED 1.44
//Nodemcu D1 Mini a 160Mhz
//Joaquin Paredes 2021 www.jopapa.me

#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <SPI.h>

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

//ESP826 Nodemcu D1 mini
#define TFT_DC D3 //A0
#define TFT_CS D8 //CS
#define TFT_MOSI D7 //SDA
#define TFT_CLK D5 //SCK
#define TFT_RST 0  //3,3v
//#define TFT_MISO 0

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST);


//Colores usados
const uint16_t camColors[] = {0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,                      //19
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0811,0x0011,0x0011,
0x0011,0x0011,0x0011,0x0031,0x0031,0x0051,0x0072,0x0072,0x0092,0x00B2,  //39
0x00B2,0x00D2,0x00F2,0x00F2,0x0112,0x0132,0x0152,0x0152,0x0172,0x0192,
0x0192,0x01B2,0x01D2,0x01F3,0x01F3,0x0213,0x0233,0x0253,0x0253,0x0273,  //59
0x0293,0x02B3,0x02D3,0x02D3,0x02F3,0x0313,0x0333,0x0333,0x0353,0x0373,
0x0394,0x03B4,0x03D4,0x03D4,0x03F4,0x0414,0x0434,0x0454,0x0474,0x0474,  //79
0x0494,0x04B4,0x04D4,0x04F4,0x0514,0x0534,0x0534,0x0554,0x0554,0x0574,
0x0574,0x0574,0x0574,0x0574,0x0574,0x0574,0x0574,0x0574,0x0574,0x0574,  //99
0x0574,0x0574,0x0574,0x0574,0x0574,0x0574,0x0574,0x0574,0x0574,0x0574,
0x05AD,0x05AC,0x05AC,0x05AB,0x05CB,0x05CB,0x05CA,0x05CA,0x05CA,0x05C9,  //119
0x05C9,0x05C8,0x05E8,0x05E8,0x05E7,0x05E7,0x05E6,0x05E6,0x05E6,0x05E5,
0x05E5,0x0604,0x0604,0x0604,0x0603,0x0603,0x0602,0x0602,0x0601,0x0621,  //139
0x0621,0x0620,0x0620,0x0620,0x0620,0x0E20,0x0E20,0x0E40,0x1640,0x1640,
0x1E40,0x1E40,0x2640,0x2640,0x2E40,0x2E60,0x3660,0x3660,0x3E60,0x3E60,  //159
0x3E60,0x4660,0x4660,0x4E60,0x4E80,0x5680,0x5680,0x5680,0x5680,0x5680,
0x5E80,0x5E80,0x5E80,0x5E80,0x5E80,0x5E80,0x5E80,0x5E80,0x5E80,0x5E80,  //179
0x6680,0x6680,0x6680,0x6680,0x6680,0x6680,0x6680,0x6680,0x6680,0x8EC0,
0x8EC0,0x8EC0,0x8EC0,0x8EC0,0x96C0,0x96C0,0x96C0,0x96C0,0x96C0,0x96C0,  //199
0x6680,0x6680,0x6680,0x6680,0x6680,0x6680,0x6680,0x6680,0x6680,0x6680,
0x6680,0x6680,0x6680,0x6680,0x6680,0x6680,0x6680,0x6680,0x6680,0x8EC0, //219
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;

#define AMG_COLS 8
#define AMG_ROWS 8
float pixels[AMG_COLS * AMG_ROWS];

#define INTERPOLATED_COLS 24
#define INTERPOLATED_ROWS 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);
int curMax=0;  //t leida actual
const int boton = D6; //Cambiar rango en gráfica
int Sboton = 0;
int MINTEMP = 20;  //Valores iniciales
int MAXTEMP = 39;
int matriz [6][6] ={  //Valores a seleccionar con boton
  {0,19},
  {20,39},
  {28 ,36},   //rango "humano en piel"
  {40,59},
  {60,79},
  {80,99},};
int vMatriz = 6; 
                   
void setup() {
  Serial.begin(115200); 
  pinMode(boton,INPUT_PULLUP);
  Serial.println("\n\nAMG88xx Interpolated Thermal Camera!");
  tft.initR(INITR_BLACKTAB);      // Init ST7735S chip, black tab
  tft.setRotation(3);
  tft.fillScreen(ST77XX_BLACK);
   
  // 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 --");
  limites();    //Inicia rango en gráfica
  tft.setTextSize(2);
  tft.setCursor(10, 100);
}

void loop() {
  //read all the pixels
  amg.readPixels(pixels);

  float dest_2d[INTERPOLATED_ROWS * INTERPOLATED_COLS];
  int32_t t = millis();
  interpolate_image(pixels, AMG_ROWS, AMG_COLS, dest_2d, INTERPOLATED_ROWS, INTERPOLATED_COLS);
  uint16_t boxsize = min(tft.width() / INTERPOLATED_COLS, tft.height() / INTERPOLATED_COLS);
  drawpixels(dest_2d, INTERPOLATED_ROWS, INTERPOLATED_COLS, boxsize, boxsize, false);

//Seleccion de valores en gráfica
  Sboton = digitalRead(boton);
  if(Sboton==LOW){
   delay(500);
   if(Sboton==LOW){
    vMatriz++;
    if(vMatriz > 6){ vMatriz=0;}
    }
   }
   if(vMatriz <=5 ){ 
     MINTEMP=matriz[vMatriz][0]; MAXTEMP=matriz[vMatriz][1];
     limites();  //nuevos limites en pantalla
   } 
   if(vMatriz == 6){
    automatico();  //Ajuste automático
   } 

  tmax(); //t maxima leida por el sensor
}

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;
     if(val >= curMax){   
      curMax = val;
     } 
      uint8_t colorIndex = map(colorTemp, MAXTEMP, MINTEMP, 0, 255);
      colorIndex = constrain(colorIndex, 0, 255);
      //draw the pixels!
      uint16_t color;
      color = val * 2;
      tft.fillRect(40+boxWidth * x, (boxHeight * y)+5, boxWidth, boxHeight, camColors[colorIndex]);
    }
  }
}

void automatico(){  //El max valor siempre aparece en rojo
  MAXTEMP = curMax;
  MINTEMP = MAXTEMP-5;
  limites();
}

void tmax(){   //Muestra en pantalla
  tft.setRotation(0);
  tft.fillRect(40, 95 , 80, 20, ST77XX_BLACK);
  tft.setCursor(6, 95);
  if (vMatriz == 6){
   tft.print("MaxA: ");tft.print(curMax);tft.print((char)247);
  }else{
  tft.print("Max: ");tft.print(curMax);tft.print((char)247);
  }
  tft.setRotation(3); 
  curMax=0;
}

void limites(){      //Muestra en pantalla
tft.setRotation(0);
   tft.fillRect(65, 95 , 80, 40, ST77XX_BLACK);
tft.setTextSize(1);
tft.setCursor(2, 118);
tft.print("Grafica ");tft.print("Min:");tft.print(MINTEMP);
tft.print(" Max:");tft.print(MAXTEMP);
tft.setRotation(3);
tft.setTextSize(2);
}

Menu