Тепловизор на HTPA-16x4 + UNO-R3+ TFTLCD_SPFD5408

RN6LJK
Offline
Зарегистрирован: 24.03.2013

Простой тепловизор на основе термопарной матрицы HTPA 16x4 и связки Arduino UNO-R3 + монитор TFTLCD_SPFD5408. Ничего нового в этом проекте нет. Все на готовых решениях. Пока используется как деловая игрушка, но планируется как измеритель для поиска тепловых утечек, обнаружение тепловыделяющих участков электропроводки и других применений для теплового контроля в домашнем и офисном хозяйстве. Решение вполне бюджетное. Термопарная матрица определяет температуры на матрице 16х4, что можно представить как такое графическое представление:

Результаты поступают в виде массива температур.

Подключается матрица к контроллеру по интерфейсу I2C:

Выполнил конструкцию как бутерброд:

 

Отображаются результаты на дисплее также в виде матрицы. 

Цветовая палитра пока также игрушечная. Опыта еще нет. В скетче видно как распределены цвета в зависимостри от температуры в градусах Цельсия.

Вот, например датчик направлен на монитор компа.

А вот в другую сторону.

Планируется подготовить несколько палитр и выполняь выбор кнопкой, которая уже запаяна, на снимке видно.

// Тепловизор
// Версия 1,  первая сырая
// Arduino IDE 1.6.7

#include <i2cmaster.h>
//i2cmaster comes from here: http://www.cheap-thermocam.bplaced.net/software/I2Cmaster.rar

#include "MLX90620_registers.h"


#include <SPFD5408_Adafruit_GFX.h>    // Core graphics library
#include <SPFD5408_Adafruit_TFTLCD.h> // Hardware-specific library
//#include <SPFD5408_TouchScreen.h>

#if defined(__SAM3X8E__)
    #undef __FlashStringHelper::F(string_literal)
    #define F(string_literal) string_literal
#endif


#define YP A1  // must be an analog pin, use "An" notation!
#define XM A2  // must be an analog pin, use "An" notation!
#define YM 7   // can be a digital pin
#define XP 6   // can be a digital pin



// Calibrate values
#define TS_MINX 125
#define TS_MINY 85
#define TS_MAXX 965
#define TS_MAXY 905



#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
// optional
#define LCD_RESET 1

// Assign human-readable names to some common 16-bit color values:
#define  BLACK   0x0000  // 0
#define BLUE    0x001F   // 1
#define RED     0xF800   // 2
#define GREEN   0x07E0   // 3
#define CYAN    0x07FF   // 4
#define MAGENTA 0xF81F   // 5
#define YELLOW  0xFFE0   // 6
#define WHITE   0xFFFF   // 7
//   | 0| 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|
uint16_t color_[8]={0x0000,0x001F,0xF800,0x07E0,0x07FF,0xF81F,0xFFE0,0xFFFF};
//uint16_t colr[8];
uint16_t matr[64];
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);



#define BOXSIZE_X 20 // не менять
#define BOXSIZE_Y 30 // 60 максимальное значение (20)

int oldcolor, currentcolor;
int SM_Y=60; // СМещение по Y (80)
int SM_X=0; // СМещение по X 

int refreshRate = 16; //Set this value to your desired refresh frequency


//Global variables
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
int irData[64]; //Contains the raw IR data from the sensor
float temperatures[64]; //Contains the calculated temperatures of each pixel in the array
float Tambient; //Tracks the changing ambient temperature of the sensor
byte eepromData[256]; //Contains the full EEPROM reading from the MLX (Slave 0x50)

//These are constants calculated from the calibration data stored in EEPROM
//See varInitialize and section 7.3 for more information
int v_th, a_cp, b_cp, tgc, b_i_scale;
float k_t1, k_t2, emissivity;
int a_ij[64], b_ij[64];

//These values are calculated using equation 7.3.3.2
//They are constants and can be calculated using the MLX90620_alphaCalculator sketch
float alpha_ij[64] = {
  1.79298E-8, 1.93850E-8, 1.87447E-8, 1.68820E-8, 2.01999E-8, 2.18297E-8, 2.12476E-8, 1.93268E-8, 
  2.20625E-8, 2.38670E-8, 2.38670E-8, 2.09566E-8, 2.36923E-8, 2.59042E-8, 2.53222E-8, 2.28192E-8, 
  2.49147E-8, 2.67191E-8, 2.67191E-8, 2.38670E-8, 2.59042E-8, 2.75340E-8, 2.71266E-8, 2.44490E-8, 
  2.61371E-8, 2.85818E-8, 2.79415E-8, 2.50893E-8, 2.65445E-8, 2.87564E-8, 2.83489E-8, 2.59042E-8, 
  2.61371E-8, 2.83489E-8, 2.83489E-8, 2.56714E-8, 2.63117E-8, 2.87564E-8, 2.81743E-8, 2.56714E-8, 
  2.59042E-8, 2.79415E-8, 2.77669E-8, 2.48565E-8, 2.52639E-8, 2.71266E-8, 2.69520E-8, 2.50893E-8, 
  2.42744E-8, 2.65445E-8, 2.63117E-8, 2.36341E-8, 2.34595E-8, 2.54968E-8, 2.50893E-8, 2.26446E-8, 
  2.14222E-8, 2.34595E-8, 2.36341E-8, 2.09566E-8, 1.97342E-8, 2.11894E-8, 2.14222E-8, 1.93268E-8, 
};

byte loopCount = 0; //Used in main loop
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


//Begin Program code

void setup()
{
  Serial.begin(115200);
  Serial.println("MLX90620");

  i2c_init(); //Init the I2C pins
  PORTC = (1 << PORTC4) | (1 << PORTC5); //Enable pull-ups

  delay(5); //Init procedure calls for a 5ms delay after power-on

  read_EEPROM_MLX90620(); //Read the entire EEPROM

  setConfiguration(refreshRate); //Configure the MLX sensor with the user's choice of refresh rate

  calculate_TA(); //Calculate the current Tambient

    tft.reset();

  tft.begin(0x9341); // SDFP5408
  tft.fillScreen(BLACK);
  
}
/*
colr[0]=color_[0];
colr[1]=color_[1];
colr[2]=color_[2];
colr[3]=color_[3];
colr[4]=color_[4];
colr[5]=color_[5];
colr[6]=color_[6];
colr[7]=color_[7];
*/
void loop()
{

  if(loopCount++ == 16) //Tambient changes more slowly than the pixel readings. Update TA only every 16 loops.
  { 
    calculate_TA(); //Calculate the new Tambient

    if(checkConfig_MLX90620()) //Every 16 readings check that the POR flag is not set
    {
      Serial.println("POR Detected!");
      setConfiguration(refreshRate); //Re-write the configuration bytes to the MLX
    }

    loopCount = 0; //Reset count
  }

  readIR_MLX90620(); //Get the 64 bytes of raw pixel data into the irData array

  calculate_TO(); //Run all the large calculations to get the temperature data for each pixel
ind_temperature();
//  prettyPrintTemperatures(); //Print the array in a 4 x 16 pattern
  //rawPrintTemperatures(); //Print the entire array so it can more easily be read by Processing app


}

//From the 256 bytes of EEPROM data, initialize 
void varInitialization(byte calibration_data[])
{
  v_th = 256 * calibration_data[VTH_H] + calibration_data[VTH_L];
  k_t1 = (256 * calibration_data[KT1_H] + calibration_data[KT1_L]) / 1024.0; //2^10 = 1024
  k_t2 = (256 * calibration_data[KT2_H] + calibration_data[KT2_L]) / 1048576.0; //2^20 = 1,048,576
  emissivity = ((unsigned int)256 * calibration_data[CAL_EMIS_H] + calibration_data[CAL_EMIS_L]) / 32768.0;
  
  a_cp = calibration_data[CAL_ACP];
  if(a_cp > 127) a_cp -= 256; //These values are stored as 2's compliment. This coverts it if necessary.

  b_cp = calibration_data[CAL_BCP];
  if(b_cp > 127) b_cp -= 256;

  tgc = calibration_data[CAL_TGC];
  if(tgc > 127) tgc -= 256;

  b_i_scale = calibration_data[CAL_BI_SCALE];

  for(int i = 0 ; i < 64 ; i++)
  {
    //Read the individual pixel offsets
    a_ij[i] = calibration_data[i]; 
    if(a_ij[i] > 127) a_ij[i] -= 256; //These values are stored as 2's compliment. This coverts it if necessary.

    //Read the individual pixel offset slope coefficients
    b_ij[i] = calibration_data[0x40 + i]; //Bi(i,j) begins 64 bytes into EEPROM at 0x40
    if(b_ij[i] > 127) b_ij[i] -= 256;
  }
  
}

//Receives the refresh rate for sensor scanning
//Sets the two byte configuration registers
//This function overwrites what is currently in the configuration registers
//The MLX doesn't seem to mind this (flags are read only)
void setConfiguration(int irRefreshRateHZ)
{
  byte Hz_LSB;

  switch(irRefreshRateHZ)
  {
  case 0:
    Hz_LSB = 0b00001111;
    break;
  case 1:
    Hz_LSB = 0b00001110;
    break;
  case 2:
    Hz_LSB = 0b00001101;
    break;
  case 4:
    Hz_LSB = 0b00001100;
    break;
  case 8:
    Hz_LSB = 0b00001011;
    break;
  case 16:
    Hz_LSB = 0b00001010;
    break;
  case 32:
    Hz_LSB = 0b00001001;
    break;
  default:
    Hz_LSB = 0b00001110;
  }

  byte defaultConfig_H = 0b01110100; // x111.01xx, Assumes NA = 0, ADC low reference enabled, Ta Refresh rate of 2Hz

  i2c_start_wait(MLX90620_WRITE);
  i2c_write(0x03); //Command = configuration value
  i2c_write((byte)Hz_LSB - 0x55);
  i2c_write(Hz_LSB);
  i2c_write(defaultConfig_H - 0x55); //Assumes NA = 0, ADC low reference enabled, Ta Refresh rate of 2Hz
  i2c_write(defaultConfig_H);
  i2c_stop();
}

//Read the 256 bytes from the MLX EEPROM and setup the various constants (*lots* of math)
//Note: The EEPROM on the MLX has a different I2C address from the MLX. I've never seen this before.
void read_EEPROM_MLX90620()
{
  i2c_start_wait(MLX90620_EEPROM_WRITE);
  i2c_write(0x00); //EEPROM info starts at location 0x00
  i2c_rep_start(MLX90620_EEPROM_READ);

  //Read all 256 bytes from the sensor's EEPROM
  for(int i = 0 ; i <= 255 ; i++)
    eepromData[i] = i2c_readAck();

  i2c_stop(); //We're done talking

  varInitialization(eepromData); //Calculate a bunch of constants from the EEPROM data

  writeTrimmingValue(eepromData[OSC_TRIM_VALUE]);
}

//Given a 8-bit number from EEPROM (Slave address 0x50), write value to MLX sensor (Slave address 0x60)
void writeTrimmingValue(byte val)
{
  i2c_start_wait(MLX90620_WRITE); //Write to the sensor
  i2c_write(0x04); //Command = write oscillator trimming value
  i2c_write((byte)val - 0xAA);
  i2c_write(val);
  i2c_write(0x56); //Always 0x56
  i2c_write(0x00); //Always 0x00
  i2c_stop();
}

//Gets the latest PTAT (package temperature ambient) reading from the MLX
//Then calculates a new Tambient
//Many of these values (k_t1, v_th, etc) come from varInitialization and EEPROM reading
//This has been tested to match example 7.3.2
void calculate_TA(void)
{
  unsigned int ptat = readPTAT_MLX90620();

  Tambient = (-k_t1 + sqrt(square(k_t1) - (4 * k_t2 * (v_th - (float)ptat)))) / (2*k_t2) + 25; //it's much more simple now, isn't it? :)
}

//Reads the PTAT data from the MLX
//Returns an unsigned int containing the PTAT
unsigned int readPTAT_MLX90620()
{
  i2c_start_wait(MLX90620_WRITE);
  i2c_write(CMD_READ_REGISTER); //Command = read PTAT
  i2c_write(0x90); //Start address is 0x90
  i2c_write(0x00); //Address step is 0
  i2c_write(0x01); //Number of reads is 1
  i2c_rep_start(MLX90620_READ);

  byte ptatLow = i2c_readAck(); //Grab the lower and higher PTAT bytes
  byte ptatHigh = i2c_readAck();

  i2c_stop();
  
  return( (unsigned int)(ptatHigh << 8) | ptatLow); //Combine bytes and return
}

//Calculate the temperatures seen for each pixel
//Relies on the raw irData array
//Returns an 64-int array called temperatures
void calculate_TO()
{
  float v_ir_off_comp;
  float v_ir_tgc_comp;
  float v_ir_comp;

  //Calculate the offset compensation for the one compensation pixel
  //This is a constant in the TO calculation, so calculate it here.
  int cpix = readCPIX_MLX90620(); //Go get the raw data of the compensation pixel
  float v_cp_off_comp = (float)cpix - (a_cp + (b_cp/pow(2, b_i_scale)) * (Tambient - 25)); 

  for (int i = 0 ; i < 64 ; i++)
  {
    v_ir_off_comp = irData[i] - (a_ij[i] + (float)(b_ij[i]/pow(2, b_i_scale)) * (Tambient - 25)); //#1: Calculate Offset Compensation 

    v_ir_tgc_comp = v_ir_off_comp - ( ((float)tgc/32) * v_cp_off_comp); //#2: Calculate Thermal Gradien Compensation (TGC)

    v_ir_comp = v_ir_tgc_comp / emissivity; //#3: Calculate Emissivity Compensation

    temperatures[i] = sqrt( sqrt( (v_ir_comp/alpha_ij[i]) + pow(Tambient + 273.15, 4) )) - 273.15;
  }
}

//Reads 64 bytes of pixel data from the MLX
//Loads the data into the irData array
void readIR_MLX90620()
{
  i2c_start_wait(MLX90620_WRITE);
  i2c_write(CMD_READ_REGISTER); //Command = read a register
  i2c_write(0x00); //Start address = 0x00
  i2c_write(0x01); //Address step = 1
  i2c_write(0x40); //Number of reads is 64
  i2c_rep_start(MLX90620_READ);

  for(int i = 0 ; i < 64 ; i++)
  {
    byte pixelDataLow = i2c_readAck();
    byte pixelDataHigh = i2c_readAck();
    irData[i] = (int)(pixelDataHigh << 8) | pixelDataLow;
  }

  i2c_stop();
}

//Read the compensation pixel 16 bit data
int readCPIX_MLX90620()
{
  i2c_start_wait(MLX90620_WRITE);
  i2c_write(CMD_READ_REGISTER); //Command = read register
  i2c_write(0x91);
  i2c_write(0x00);
  i2c_write(0x01);
  i2c_rep_start(MLX90620_READ);

  byte cpixLow = i2c_readAck(); //Grab the two bytes
  byte cpixHigh = i2c_readAck();
  i2c_stop();

  return ( (int)(cpixHigh << 8) | cpixLow);
}

//Reads the current configuration register (2 bytes) from the MLX
//Returns two bytes
unsigned int readConfig_MLX90620()
{
  i2c_start_wait(MLX90620_WRITE); //The MLX configuration is in the MLX, not EEPROM
  i2c_write(CMD_READ_REGISTER); //Command = read configuration register
  i2c_write(0x92); //Start address
  i2c_write(0x00); //Address step of zero
  i2c_write(0x01); //Number of reads is 1

    i2c_rep_start(MLX90620_READ);

  byte configLow = i2c_readAck(); //Grab the two bytes
  byte configHigh = i2c_readAck();

  i2c_stop();

  return( (unsigned int)(configHigh << 8) | configLow); //Combine the configuration bytes and return as one unsigned int
}

//Poll the MLX for its current status
//Returns true if the POR/Brown out bit is set
boolean checkConfig_MLX90620()
{
  if ( (readConfig_MLX90620() & (unsigned int)1<<POR_TEST) == 0)
    return true;
  else
    return false;
}

//Prints the temperatures in a way that's more easily viewable in the terminal window
void prettyPrintTemperatures()
{
  Serial.println();
  for(int i = 0 ; i < 64 ; i++)
  {
    if(i % 16 == 0) Serial.println();
    Serial.print(convertToFahrenheit(temperatures[i]));
    //Serial.print(irData[i]);
    Serial.print(", ");
  }
}

//Prints the temperatures in a way that's more easily parsed by a Processing app
//Each line starts with '$' and ends with '*'
void rawPrintTemperatures()
{
  Serial.print("$");
  for(int i = 0 ; i < 64 ; i++)
  {
    Serial.print(convertToFahrenheit(temperatures[i]));
    Serial.print(","); //Don't print comma on last temperature
  }
  Serial.println("*");
}

//Given a Celsius float, converts to Fahrenheit
float convertToFahrenheit (float Tc)
{
  float Tf = (9/5) * Tc + 32;

  return(Tf);
}

/*
#define  BLACK   0x0000  // 0
#define BLUE    0x001F   // 1
#define RED     0xF800   // 2
#define GREEN   0x07E0   // 3
#define CYAN    0x07FF   // 4
#define MAGENTA 0xF81F   // 5
#define YELLOW  0xFFE0   // 6
#define WHITE   0xFFFF   // 7

*/
void ind_temperature(){
int index;
for(int i=0;i<64;i++){
  int temp=temperatures[i];
  
  if(temp>-100&&temp<=1){ // -100 - +1                 WHITE
    index=7;
    matr[i]=color_[index];
    continue;
  }
  
  if(temp>1&&temp<=10){ // +1 - +10                 BLUE
    index=1;
    matr[i]=color_[index];
    continue;
  }

  if(temp>10&&temp<=19){ // +10 - +19                    CYAN
    index=4;
    matr[i]=color_[index];
    continue;
  }
  if(temp>19&&temp<=24){ // +19 - +24                 GREEN
    index=3;
    matr[i]=color_[index];
    continue;
  }  
  if(temp>24&&temp<=32){ // +24 - +32                YELLOW 
    index=6;
    matr[i]=color_[index];
    continue;
  }    
  if(temp>32&&temp<=36){ // +32 - +36                 RED 
    index=2;
    matr[i]=color_[index];
    continue;
  }      
  if(temp>36&&temp<=200){ // +36 - +200               MAGENTA
    index=5;
    matr[i]=color_[index];
    continue;
  }       
}
  
  ind_dispay();
}

void ind_dispay(){
 // 4-я СТРОКА  
  tft.fillRect(SM_Y, SM_X, BOXSIZE_Y, BOXSIZE_X, matr[48]); // 4-48
  tft.fillRect(SM_Y, SM_X+BOXSIZE_X, BOXSIZE_Y, BOXSIZE_X, matr[49]); // 4-49
  tft.fillRect(SM_Y, SM_X+BOXSIZE_X*2, BOXSIZE_Y, BOXSIZE_X, matr[50]); // 4-50
  tft.fillRect(SM_Y, SM_X+BOXSIZE_X*3, BOXSIZE_Y, BOXSIZE_X, matr[51]); // 4-51 
  tft.fillRect(SM_Y, SM_X+BOXSIZE_X*4, BOXSIZE_Y, BOXSIZE_X, matr[52]); // 4-52 
  tft.fillRect(SM_Y, SM_X+BOXSIZE_X*5, BOXSIZE_Y, BOXSIZE_X, matr[53]); // 4-53 
  tft.fillRect(SM_Y, SM_X+BOXSIZE_X*6, BOXSIZE_Y, BOXSIZE_X, matr[54]); // 4-54 
  tft.fillRect(SM_Y, SM_X+BOXSIZE_X*7, BOXSIZE_Y, BOXSIZE_X, matr[55]); // 4-55 
  tft.fillRect(SM_Y, SM_X+BOXSIZE_X*8, BOXSIZE_Y, BOXSIZE_X, matr[56]); // 4-56 
  tft.fillRect(SM_Y, SM_X+BOXSIZE_X*9, BOXSIZE_Y, BOXSIZE_X, matr[57]); // 4-57 
  tft.fillRect(SM_Y, SM_X+BOXSIZE_X*10, BOXSIZE_Y, BOXSIZE_X, matr[58]); // 4-58 
  tft.fillRect(SM_Y, SM_X+BOXSIZE_X*11, BOXSIZE_Y, BOXSIZE_X, matr[59]); // 4-59 
  tft.fillRect(SM_Y, SM_X+BOXSIZE_X*12, BOXSIZE_Y, BOXSIZE_X, matr[60]); // 4-60 
  tft.fillRect(SM_Y, SM_X+BOXSIZE_X*13, BOXSIZE_Y, BOXSIZE_X, matr[61]); // 4-61 
  tft.fillRect(SM_Y, SM_X+BOXSIZE_X*14, BOXSIZE_Y, BOXSIZE_X, matr[62]); // 4-62 
  tft.fillRect(SM_Y, SM_X+BOXSIZE_X*15, BOXSIZE_Y, BOXSIZE_X, matr[63]); // 4-63  
   
// 3-я СТРОКА  
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X, BOXSIZE_Y, BOXSIZE_X, matr[32]); // 3-32
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X, BOXSIZE_Y, BOXSIZE_X, matr[33]); // 3-33
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X*2, BOXSIZE_Y, BOXSIZE_X, matr[34]); // 3-34
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X*3, BOXSIZE_Y, BOXSIZE_X, matr[35]); // 3-35
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X*4, BOXSIZE_Y, BOXSIZE_X, matr[36]); // 3-36
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X*5, BOXSIZE_Y, BOXSIZE_X, matr[37]); // 3-37
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X*6, BOXSIZE_Y, BOXSIZE_X, matr[38]); // 3-38
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X*7, BOXSIZE_Y, BOXSIZE_X, matr[39]); // 3-39
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X*8, BOXSIZE_Y, BOXSIZE_X, matr[40]); // 3-40
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X*9, BOXSIZE_Y, BOXSIZE_X, matr[41]); // 3-41
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X*10, BOXSIZE_Y, BOXSIZE_X, matr[42]); // 3-42
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X*11, BOXSIZE_Y, BOXSIZE_X, matr[43]); // 3-43
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X*12, BOXSIZE_Y, BOXSIZE_X, matr[44]); // 3-44
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X*13, BOXSIZE_Y, BOXSIZE_X, matr[45]); // 3-45
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X*14, BOXSIZE_Y, BOXSIZE_X, matr[46]); // 3-46
  tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X*15, BOXSIZE_Y, BOXSIZE_X, matr[47]); // 3-47

  
// 2-я СТРОКА  
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X, BOXSIZE_Y, BOXSIZE_X, matr[16]); // 2-16
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X, BOXSIZE_Y, BOXSIZE_X, matr[17]); // 2-17
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X*2, BOXSIZE_Y, BOXSIZE_X, matr[18]); // 2-18
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X*3, BOXSIZE_Y, BOXSIZE_X, matr[19]); // 2-19
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X*4, BOXSIZE_Y, BOXSIZE_X, matr[20]); // 2-20
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X*5, BOXSIZE_Y, BOXSIZE_X, matr[21]); // 2-21
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X*6, BOXSIZE_Y, BOXSIZE_X, matr[22]); // 2-22
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X*7, BOXSIZE_Y, BOXSIZE_X, matr[23]); // 2-23
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X*8, BOXSIZE_Y, BOXSIZE_X, matr[24]); // 2-24
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X*9, BOXSIZE_Y, BOXSIZE_X, matr[25]); // 2-25
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X*10, BOXSIZE_Y, BOXSIZE_X, matr[26]); // 2-26
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X*11, BOXSIZE_Y, BOXSIZE_X, matr[27]); // 2-27
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X*12, BOXSIZE_Y, BOXSIZE_X, matr[28]); // 2-28
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X*13, BOXSIZE_Y, BOXSIZE_X, matr[29]); // 2-29
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X*14, BOXSIZE_Y, BOXSIZE_X, matr[30]); // 2-30
  tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X*15, BOXSIZE_Y, BOXSIZE_X, matr[31]); // 2-31
  
// 1-я СТРОКА  
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X, BOXSIZE_Y, BOXSIZE_X, matr[0]); //  1-0
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X, BOXSIZE_Y, BOXSIZE_X, matr[1]); //  1-1  
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X*2, BOXSIZE_Y, BOXSIZE_X, matr[2]); //  1-2  
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X*3, BOXSIZE_Y, BOXSIZE_X, matr[3]); //  1-3  
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X*4, BOXSIZE_Y, BOXSIZE_X, matr[4]); //  1-4  
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X*5, BOXSIZE_Y, BOXSIZE_X, matr[5]); //  1-5  
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X*6, BOXSIZE_Y, BOXSIZE_X, matr[6]); //  1-6  
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X*7, BOXSIZE_Y, BOXSIZE_X, matr[7]); //  1-7  
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X*8, BOXSIZE_Y, BOXSIZE_X, matr[8]); //  1-8  
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X*9, BOXSIZE_Y, BOXSIZE_X, matr[9]); //  1-9  
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X*10, BOXSIZE_Y, BOXSIZE_X, matr[10]); //  1-10  
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X*11, BOXSIZE_Y, BOXSIZE_X, matr[11]); //  1-11 
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X*12, BOXSIZE_Y, BOXSIZE_X, matr[12]); //  1-12 
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X*13, BOXSIZE_Y, BOXSIZE_X, matr[13]); //  1-13 
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X*14, BOXSIZE_Y, BOXSIZE_X, matr[14]); //  1-14 
  tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X*15, BOXSIZE_Y, BOXSIZE_X, matr[15]); //  1-15 
 
}

 

yul-i-an
yul-i-an аватар
Offline
Зарегистрирован: 10.12.2012

Занятный проект.

А вы не пробовали отрисовывать вложенными циклами?

//Чтонибудь в этом духе
//Код не рабочий - пища для размышлений
void ind_display(){
 for (int matr=0; i <= 62; i++){//перебераем пиксели
  for (int x=0; i <= 3; i++){//перебираем строки
    for (int y=0; i <= 16; i++){//перебираем столбцы
      //тут отрисовываем пиксель
    }
  }
 }
}

 

RN6LJK
Offline
Зарегистрирован: 24.03.2013

Сервис дополнен выбором из четырех палитр. Градации палитры индицируются в верхней части экрана. Выбор выполняется нажатием кнопки поключенной к нулевому порту через подтягивающий резистор 10К. Привожу измененный скетч.

// Тепловизор
// Версия 1.4,  
// Arduino IDE 1.6.7
// 29.07.2016
// кнопочный выбор палитры + индикация палитры
// причесан код ind_dispay()

#include <i2cmaster.h>
//i2cmaster comes from here: http://www.cheap-thermocam.bplaced.net/software/I2Cmaster.rar

#include "MLX90620_registers.h"


#include <SPFD5408_Adafruit_GFX.h>    // Core graphics library
#include <SPFD5408_Adafruit_TFTLCD.h> // Hardware-specific library
//#include <SPFD5408_TouchScreen.h>

#if defined(__SAM3X8E__)
    #undef __FlashStringHelper::F(string_literal)
    #define F(string_literal) string_literal
#endif


#define YP A1  // must be an analog pin, use "An" notation!
#define XM A2  // must be an analog pin, use "An" notation!
#define YM 7   // can be a digital pin
#define XP 6   // can be a digital pin



// Calibrate values
#define TS_MINX 125
#define TS_MINY 85
#define TS_MAXX 965
#define TS_MAXY 905



#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
// optional
#define LCD_RESET 1       // Замена  А4!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

// Assign human-readable names to some common 16-bit color values:
#define  BLACK   0x0000  // 0
#define BLUE    0x001F   // 1
#define RED     0xF800   // 2
#define GREEN   0x07E0   // 3
#define CYAN    0x07FF   // 4
#define MAGENTA 0xF81F   // 5
#define YELLOW  0xFFE0   // 6
#define WHITE   0xFFFF   // 7
//   | 0| 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|
uint16_t color_[8]={0x0000,0x001F,0xF800,0x07E0,0x07FF,0xF81F,0xFFE0,0xFFFF};
//uint16_t colr[8];
uint16_t matr[64];
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);



#define BOXSIZE_X 20 // не менять
#define BOXSIZE_Y 30 // 60 максимальное значение (20)
const int buttonPin_color = 0;     // порт выбора палитры
int rk=0; // 0,,,(dim-8)
int dim=32;
int kl=0;
// ЦВЕТОВЫЕ ПАЛИТРЫ - размер rang_  - dim
//                BLACK WHITE BLUE CYAN GREEN YELLOW RED MAGENTA
int rang_min[32]={   0, -100,    1, 10,   19,    24, 32,      36,     
                     0, -100,   -5,  5,   10,    15, 20,      25,
                     0, -100,   15, 20,   25,    30, 35,      40,
                     0, -100,   25, 28,   30,    31, 34,      36};
int rang_max[32]={   0,    1,   10, 19,   24,    32, 36,     200,
                     0,   -5,    5, 10,   15,    20, 25,     200,
                     0,   15,   20, 25,   30,    35, 40,     200,
                     0,   25,   28, 30,   31,    34, 36,     200};

int oldcolor, currentcolor;
int SM_Y=60; // СМещение по Y (80)
int SM_X=0; // СМещение по X 
  int X_=0;
  int Y_=0;
  int B_X=30;
  int B_Y=20;

int refreshRate = 16; //Set this value to your desired refresh frequency


//Global variables
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
int irData[64]; //Contains the raw IR data from the sensor
float temperatures[64]; //Contains the calculated temperatures of each pixel in the array
float Tambient; //Tracks the changing ambient temperature of the sensor
byte eepromData[256]; //Contains the full EEPROM reading from the MLX (Slave 0x50)

//These are constants calculated from the calibration data stored in EEPROM
//See varInitialize and section 7.3 for more information
int v_th, a_cp, b_cp, tgc, b_i_scale;
float k_t1, k_t2, emissivity;
int a_ij[64], b_ij[64];

//These values are calculated using equation 7.3.3.2
//They are constants and can be calculated using the MLX90620_alphaCalculator sketch
float alpha_ij[64] = {
  1.79298E-8, 1.93850E-8, 1.87447E-8, 1.68820E-8, 2.01999E-8, 2.18297E-8, 2.12476E-8, 1.93268E-8, 
  2.20625E-8, 2.38670E-8, 2.38670E-8, 2.09566E-8, 2.36923E-8, 2.59042E-8, 2.53222E-8, 2.28192E-8, 
  2.49147E-8, 2.67191E-8, 2.67191E-8, 2.38670E-8, 2.59042E-8, 2.75340E-8, 2.71266E-8, 2.44490E-8, 
  2.61371E-8, 2.85818E-8, 2.79415E-8, 2.50893E-8, 2.65445E-8, 2.87564E-8, 2.83489E-8, 2.59042E-8, 
  2.61371E-8, 2.83489E-8, 2.83489E-8, 2.56714E-8, 2.63117E-8, 2.87564E-8, 2.81743E-8, 2.56714E-8, 
  2.59042E-8, 2.79415E-8, 2.77669E-8, 2.48565E-8, 2.52639E-8, 2.71266E-8, 2.69520E-8, 2.50893E-8, 
  2.42744E-8, 2.65445E-8, 2.63117E-8, 2.36341E-8, 2.34595E-8, 2.54968E-8, 2.50893E-8, 2.26446E-8, 
  2.14222E-8, 2.34595E-8, 2.36341E-8, 2.09566E-8, 1.97342E-8, 2.11894E-8, 2.14222E-8, 1.93268E-8, 
};

byte loopCount = 0; //Used in main loop
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


//Begin Program code

void setup()
{
  Serial.begin(115200);
  Serial.println("MLX90620");

  pinMode(buttonPin_color, INPUT);
  digitalWrite(0, LOW); //включаем подтягивающий резистор 10К, включен между порт0 - +VCC
                        // кнопка между порт 0 - GND


  i2c_init(); //Init the I2C pins
  PORTC = (1 << PORTC4) | (1 << PORTC5); //Enable pull-ups

  delay(5); //Init procedure calls for a 5ms delay after power-on

  read_EEPROM_MLX90620(); //Read the entire EEPROM

  setConfiguration(refreshRate); //Configure the MLX sensor with the user's choice of refresh rate

  calculate_TA(); //Calculate the current Tambient

    tft.reset();

  tft.begin(0x9341); // SDFP5408  
  tft.fillScreen(BLACK);
  
  tft.setRotation(3);
  tft.setCursor (Y_, X_);
    tft.fillRect(Y_, X_, B_Y*15, B_X, color_[0]);

    tft.setCursor (Y_, X_);
  tft.setTextColor(WHITE);
  tft.setTextSize (3);
  tft.println(rang_max[1+rk]);
  
      tft.setCursor (B_Y*2, X_);
  tft.setTextColor(BLUE);
  tft.setTextSize (3);
  tft.println(rang_max[2+rk]);

  tft.setCursor (B_Y*4, X_);
  tft.setTextColor(CYAN);
  tft.setTextSize (3);
  tft.println(rang_max[3+rk]);

  tft.setCursor (B_Y*6, X_);
  tft.setTextColor(GREEN);
  tft.setTextSize (3);
  tft.println(rang_max[4+rk]);

  tft.setCursor (B_Y*8, X_);
  tft.setTextColor(YELLOW);
  tft.setTextSize (3);
  tft.println(rang_max[5+rk]);

  tft.setCursor (B_Y*10, X_);
  tft.setTextColor(RED);
  tft.setTextSize (3);
  tft.println(rang_max[6+rk]);

  tft.setCursor (B_Y*12, X_);
  tft.setTextColor(MAGENTA);
  tft.setTextSize (3);
  tft.println(rang_max[7+rk]);
  kl=0;
     tft.setRotation(2);

       
  
}

//------------------------------------------------------- LOOP ----------------------------------------------
void loop()
{
// Выбор палитры 
if (digitalRead(buttonPin_color) == LOW) { // Установка палитры
  kl=1;
  rk=rk+8;
  if (rk>(dim-8)) {
    rk=0;
  }
}
  if(loopCount++ == 16) //Tambient changes more slowly than the pixel readings. Update TA only every 16 loops.
  { 
    calculate_TA(); //Calculate the new Tambient

    if(checkConfig_MLX90620()) //Every 16 readings check that the POR flag is not set
    {
      Serial.println("POR Detected!");
      setConfiguration(refreshRate); //Re-write the configuration bytes to the MLX
    }

    loopCount = 0; //Reset count
  }

  readIR_MLX90620(); //Get the 64 bytes of raw pixel data into the irData array

  calculate_TO(); //Run all the large calculations to get the temperature data for each pixel
ind_temperature();
//  prettyPrintTemperatures(); //Print the array in a 4 x 16 pattern
  //rawPrintTemperatures(); //Print the entire array so it can more easily be read by Processing app

}

//-----------------------------------------------------------------------------------------------

//From the 256 bytes of EEPROM data, initialize 
void varInitialization(byte calibration_data[])
{
  v_th = 256 * calibration_data[VTH_H] + calibration_data[VTH_L];
  k_t1 = (256 * calibration_data[KT1_H] + calibration_data[KT1_L]) / 1024.0; //2^10 = 1024
  k_t2 = (256 * calibration_data[KT2_H] + calibration_data[KT2_L]) / 1048576.0; //2^20 = 1,048,576
  emissivity = ((unsigned int)256 * calibration_data[CAL_EMIS_H] + calibration_data[CAL_EMIS_L]) / 32768.0;
  
  a_cp = calibration_data[CAL_ACP];
  if(a_cp > 127) a_cp -= 256; //These values are stored as 2's compliment. This coverts it if necessary.

  b_cp = calibration_data[CAL_BCP];
  if(b_cp > 127) b_cp -= 256;

  tgc = calibration_data[CAL_TGC];
  if(tgc > 127) tgc -= 256;

  b_i_scale = calibration_data[CAL_BI_SCALE];

  for(int i = 0 ; i < 64 ; i++)
  {
    //Read the individual pixel offsets
    a_ij[i] = calibration_data[i]; 
    if(a_ij[i] > 127) a_ij[i] -= 256; //These values are stored as 2's compliment. This coverts it if necessary.

    //Read the individual pixel offset slope coefficients
    b_ij[i] = calibration_data[0x40 + i]; //Bi(i,j) begins 64 bytes into EEPROM at 0x40
    if(b_ij[i] > 127) b_ij[i] -= 256;
  }
  
}

//Receives the refresh rate for sensor scanning
//Sets the two byte configuration registers
//This function overwrites what is currently in the configuration registers
//The MLX doesn't seem to mind this (flags are read only)
void setConfiguration(int irRefreshRateHZ)
{
  byte Hz_LSB;

  switch(irRefreshRateHZ)
  {
  case 0:
    Hz_LSB = 0b00001111;
    break;
  case 1:
    Hz_LSB = 0b00001110;
    break;
  case 2:
    Hz_LSB = 0b00001101;
    break;
  case 4:
    Hz_LSB = 0b00001100;
    break;
  case 8:
    Hz_LSB = 0b00001011;
    break;
  case 16:
    Hz_LSB = 0b00001010;
    break;
  case 32:
    Hz_LSB = 0b00001001;
    break;
  default:
    Hz_LSB = 0b00001110;
  }

  byte defaultConfig_H = 0b01110100; // x111.01xx, Assumes NA = 0, ADC low reference enabled, Ta Refresh rate of 2Hz

  i2c_start_wait(MLX90620_WRITE);
  i2c_write(0x03); //Command = configuration value
  i2c_write((byte)Hz_LSB - 0x55);
  i2c_write(Hz_LSB);
  i2c_write(defaultConfig_H - 0x55); //Assumes NA = 0, ADC low reference enabled, Ta Refresh rate of 2Hz
  i2c_write(defaultConfig_H);
  i2c_stop();
}

//Read the 256 bytes from the MLX EEPROM and setup the various constants (*lots* of math)
//Note: The EEPROM on the MLX has a different I2C address from the MLX. I've never seen this before.
void read_EEPROM_MLX90620()
{
  i2c_start_wait(MLX90620_EEPROM_WRITE);
  i2c_write(0x00); //EEPROM info starts at location 0x00
  i2c_rep_start(MLX90620_EEPROM_READ);

  //Read all 256 bytes from the sensor's EEPROM
  for(int i = 0 ; i <= 255 ; i++)
    eepromData[i] = i2c_readAck();

  i2c_stop(); //We're done talking

  varInitialization(eepromData); //Calculate a bunch of constants from the EEPROM data

  writeTrimmingValue(eepromData[OSC_TRIM_VALUE]);
}

//Given a 8-bit number from EEPROM (Slave address 0x50), write value to MLX sensor (Slave address 0x60)
void writeTrimmingValue(byte val)
{
  i2c_start_wait(MLX90620_WRITE); //Write to the sensor
  i2c_write(0x04); //Command = write oscillator trimming value
  i2c_write((byte)val - 0xAA);
  i2c_write(val);
  i2c_write(0x56); //Always 0x56
  i2c_write(0x00); //Always 0x00
  i2c_stop();
}

//Gets the latest PTAT (package temperature ambient) reading from the MLX
//Then calculates a new Tambient
//Many of these values (k_t1, v_th, etc) come from varInitialization and EEPROM reading
//This has been tested to match example 7.3.2
void calculate_TA(void)
{
  unsigned int ptat = readPTAT_MLX90620();

  Tambient = (-k_t1 + sqrt(square(k_t1) - (4 * k_t2 * (v_th - (float)ptat)))) / (2*k_t2) + 25; //it's much more simple now, isn't it? :)
}

//Reads the PTAT data from the MLX
//Returns an unsigned int containing the PTAT
unsigned int readPTAT_MLX90620()
{
  i2c_start_wait(MLX90620_WRITE);
  i2c_write(CMD_READ_REGISTER); //Command = read PTAT
  i2c_write(0x90); //Start address is 0x90
  i2c_write(0x00); //Address step is 0
  i2c_write(0x01); //Number of reads is 1
  i2c_rep_start(MLX90620_READ);

  byte ptatLow = i2c_readAck(); //Grab the lower and higher PTAT bytes
  byte ptatHigh = i2c_readAck();

  i2c_stop();
  
  return( (unsigned int)(ptatHigh << 8) | ptatLow); //Combine bytes and return
}

//Calculate the temperatures seen for each pixel
//Relies on the raw irData array
//Returns an 64-int array called temperatures
void calculate_TO()
{
  float v_ir_off_comp;
  float v_ir_tgc_comp;
  float v_ir_comp;

  //Calculate the offset compensation for the one compensation pixel
  //This is a constant in the TO calculation, so calculate it here.
  int cpix = readCPIX_MLX90620(); //Go get the raw data of the compensation pixel
  float v_cp_off_comp = (float)cpix - (a_cp + (b_cp/pow(2, b_i_scale)) * (Tambient - 25)); 

  for (int i = 0 ; i < 64 ; i++)
  {
    v_ir_off_comp = irData[i] - (a_ij[i] + (float)(b_ij[i]/pow(2, b_i_scale)) * (Tambient - 25)); //#1: Calculate Offset Compensation 

    v_ir_tgc_comp = v_ir_off_comp - ( ((float)tgc/32) * v_cp_off_comp); //#2: Calculate Thermal Gradien Compensation (TGC)

    v_ir_comp = v_ir_tgc_comp / emissivity; //#3: Calculate Emissivity Compensation

    temperatures[i] = sqrt( sqrt( (v_ir_comp/alpha_ij[i]) + pow(Tambient + 273.15, 4) )) - 273.15;
  }
}

//Reads 64 bytes of pixel data from the MLX
//Loads the data into the irData array
void readIR_MLX90620()
{
  i2c_start_wait(MLX90620_WRITE);
  i2c_write(CMD_READ_REGISTER); //Command = read a register
  i2c_write(0x00); //Start address = 0x00
  i2c_write(0x01); //Address step = 1
  i2c_write(0x40); //Number of reads is 64
  i2c_rep_start(MLX90620_READ);

  for(int i = 0 ; i < 64 ; i++)
  {
    byte pixelDataLow = i2c_readAck();
    byte pixelDataHigh = i2c_readAck();
    irData[i] = (int)(pixelDataHigh << 8) | pixelDataLow;
  }

  i2c_stop();
}

//Read the compensation pixel 16 bit data
int readCPIX_MLX90620()
{
  i2c_start_wait(MLX90620_WRITE);
  i2c_write(CMD_READ_REGISTER); //Command = read register
  i2c_write(0x91);
  i2c_write(0x00);
  i2c_write(0x01);
  i2c_rep_start(MLX90620_READ);

  byte cpixLow = i2c_readAck(); //Grab the two bytes
  byte cpixHigh = i2c_readAck();
  i2c_stop();

  return ( (int)(cpixHigh << 8) | cpixLow);
}

//Reads the current configuration register (2 bytes) from the MLX
//Returns two bytes
unsigned int readConfig_MLX90620()
{
  i2c_start_wait(MLX90620_WRITE); //The MLX configuration is in the MLX, not EEPROM
  i2c_write(CMD_READ_REGISTER); //Command = read configuration register
  i2c_write(0x92); //Start address
  i2c_write(0x00); //Address step of zero
  i2c_write(0x01); //Number of reads is 1

    i2c_rep_start(MLX90620_READ);

  byte configLow = i2c_readAck(); //Grab the two bytes
  byte configHigh = i2c_readAck();

  i2c_stop();

  return( (unsigned int)(configHigh << 8) | configLow); //Combine the configuration bytes and return as one unsigned int
}

//Poll the MLX for its current status
//Returns true if the POR/Brown out bit is set
boolean checkConfig_MLX90620()
{
  if ( (readConfig_MLX90620() & (unsigned int)1<<POR_TEST) == 0)
    return true;
  else
    return false;
}

//Prints the temperatures in a way that's more easily viewable in the terminal window
void prettyPrintTemperatures()
{
  Serial.println();
  for(int i = 0 ; i < 64 ; i++)
  {
    if(i % 16 == 0) Serial.println();
    Serial.print(convertToFahrenheit(temperatures[i]));
    //Serial.print(irData[i]);
    Serial.print(", ");
  }
}

//Prints the temperatures in a way that's more easily parsed by a Processing app
//Each line starts with '$' and ends with '*'
void rawPrintTemperatures()
{
  Serial.print("$");
  for(int i = 0 ; i < 64 ; i++)
  {
    Serial.print(convertToFahrenheit(temperatures[i]));
    Serial.print(","); //Don't print comma on last temperature
  }
  Serial.println("*");
}

//Given a Celsius float, converts to Fahrenheit
float convertToFahrenheit (float Tc)
{
  float Tf = (9/5) * Tc + 32;

  return(Tf);
}

/* значение для index
#define  BLACK   0x0000  // 0
#define BLUE    0x001F   // 1
#define RED     0xF800   // 2
#define GREEN   0x07E0   // 3
#define CYAN    0x07FF   // 4
#define MAGENTA 0xF81F   // 5
#define YELLOW  0xFFE0   // 6
#define WHITE   0xFFFF   // 7

*/
// Вывод на монитор

void ind_temperature(){

  
int index;
for(int i=0;i<64;i++){
  int temp=temperatures[i];
  
  if(temp>rang_min[1+rk]&&temp<=rang_max[1+rk]){ // -100 - +1                WHITE
    index=7;
    matr[i]=color_[index];
    continue;
  }
  
  if(temp>rang_min[2+rk]&&temp<=rang_max[2+rk]){ // +1 - +10                   BLUE
    index=1;
    matr[i]=color_[index];
    continue;
  }

  if(temp>rang_min[3+rk]&&temp<=rang_max[3+rk]){ // +10 - +19                 CYAN
    index=4;
    matr[i]=color_[index];
    continue;
  }
  if(temp>rang_min[4+rk]&&temp<=rang_max[4+rk]){ // +19 - +24                 GREEN
    index=3;
    matr[i]=color_[index];
    continue;
  }  
  if(temp>rang_min[5+rk]&&temp<=rang_max[5+rk]){ // +24 - +32                 YELLOW 
    index=6;
    matr[i]=color_[index];
    continue;
  }    
  if(temp>rang_min[6+rk]&&temp<=rang_max[6+rk]){ // +32 - +36                 RED 
    index=2;
    matr[i]=color_[index];
    continue;
  }      
  if(temp>rang_min[7+rk]&&temp<=rang_max[7+rk]){ // +36 - +200               MAGENTA
    index=5;
    matr[i]=color_[index];
    continue;
  }       
}
  
  ind_dispay();
}

void ind_dispay(){
  if (kl==1) {  // устранение моргания индикатора палитры
  
  tft.setRotation(3);
  tft.setCursor (Y_, X_);
    tft.fillRect(Y_, X_, B_Y*15, B_X, color_[0]);

    tft.setCursor (Y_, X_);
  tft.setTextColor(WHITE);
  tft.setTextSize (3);
  tft.println(rang_max[1+rk]);
  
      tft.setCursor (B_Y*2, X_);
  tft.setTextColor(BLUE);
  tft.setTextSize (3);
  tft.println(rang_max[2+rk]);

  tft.setCursor (B_Y*4, X_);
  tft.setTextColor(CYAN);
  tft.setTextSize (3);
  tft.println(rang_max[3+rk]);

  tft.setCursor (B_Y*6, X_);
  tft.setTextColor(GREEN);
  tft.setTextSize (3);
  tft.println(rang_max[4+rk]);

  tft.setCursor (B_Y*8, X_);
  tft.setTextColor(YELLOW);
  tft.setTextSize (3);
  tft.println(rang_max[5+rk]);

  tft.setCursor (B_Y*10, X_);
  tft.setTextColor(RED);
  tft.setTextSize (3);
  tft.println(rang_max[6+rk]);

  tft.setCursor (B_Y*12, X_);
  tft.setTextColor(MAGENTA);
  tft.setTextSize (3);
  tft.println(rang_max[7+rk]);
  kl=0;
     tft.setRotation(2);
  }
    
 int ms=0;
 for (int mi=0;mi<=15;mi++){
   tft.fillRect(BOXSIZE_Y*3+SM_Y, SM_X+BOXSIZE_X*ms, BOXSIZE_Y, BOXSIZE_X, matr[mi]);    // 1
   tft.fillRect(BOXSIZE_Y*2+SM_Y, SM_X+BOXSIZE_X*ms, BOXSIZE_Y, BOXSIZE_X, matr[mi+16]); // 2
   tft.fillRect(BOXSIZE_Y+SM_Y, SM_X+BOXSIZE_X*ms, BOXSIZE_Y, BOXSIZE_X, matr[mi+32]);   // 3
   tft.fillRect(SM_Y, SM_X+BOXSIZE_X*ms, BOXSIZE_Y, BOXSIZE_X, matr[mi+48]);             // 4
   ms++;
 }
 
}

Кому интересно такую матрицу можно приобрести здесь:

http://www.azimp.ru/catalogue/IR-thermocouple-matrix1/127/

А здесь уже на готовых платах согласования:

http://electromicro.ru/market/datchiki_i_sensory/kit-teplovizor_infrakra...

https://amperkot.ru/products/teplovizor_kit_164_tochek/24125647.html

Все необходимые библиотеки у этих же продавцов прилагаются.

На фото горячая, холодная кружка и вид экрана.. 

Горячая кружка

Холодная кружка

Вывод градаций палитры