Тепловизор на HTPA-16x4 + UNO-R3+ TFTLCD_SPFD5408
- Войдите на сайт для отправки комментариев
Простой тепловизор на основе термопарной матрицы 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 }
Занятный проект.
А вы не пробовали отрисовывать вложенными циклами?
Сервис дополнен выбором из четырех палитр. Градации палитры индицируются в верхней части экрана. Выбор выполняется нажатием кнопки поключенной к нулевому порту через подтягивающий резистор 10К. Привожу измененный скетч.
Кому интересно такую матрицу можно приобрести здесь:
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
Все необходимые библиотеки у этих же продавцов прилагаются.
На фото горячая, холодная кружка и вид экрана..
Горячая кружка
Холодная кружка
Вывод градаций палитры