[posted] Thermal imaging camera

KrisKasprzak

Well-known member
I have a leak in my A/C duct system and to help isolate the problem, I built a thermal imaging camera. It’s based on an AMG8833 sensor that has an 8x8 sensor grid. The unit was packaged in a breakout board from Adafruit, who also published a library for fast startup use. Connection is via I2C, and the examples made getting the unit working a snap.

Out of the box the results were not very good, namely because of a small 8x8 sensor and the few colors that were assigned to for each pixel to represent temperature. The library is just to get started, so no complaints—I’ll have to thank any library writer for doing the heavy lifting. My code improves the results by taking advantage of all colors on my 2.8” TFT screen (65K), and turned up the resolution to 70x70 with some basic interpolation.

My algorithm for color assignment based on temperature is linear (namely due to the need to keep code running as fast as possible), and 65K colors will not be perfectly smooth anyway. I found a neat website that had RGB color definition per temp, so i just had to plot the RGB values and develop some equations, using y=mx + b.

To generate data at the interior points I had to interpolate each row, expanding 8 points to 70 for each row, and when complete I ran interpolation against each of the 70 columns. My interpolation routine to generate 10 points between each measured point is also linear. Again, in an attempt to display sensor readings at the maximum of 10 fps (as governed by the sensor), I wanted to keep the math as fast as possible. Also the 65K color limit prevents the need for supper accurate log-based color interpolation.

I also added some touch screen capability so the user can set the max/min temperature for converting the temp to color before drawing the results to the screen, and a grid that uses an interesting concept to avoid flickering due to repeatedly drawing grid lines.

I’m using a Teensy 3.2 running at 120 mhz and the screen refreshes around 3 fps—good enough for an $80 project. I still can't find my leak though...

To help others get started, I created a YouTube video of this project and have a link to the source code. Have a look.

https://www.youtube.com/watch?v=A9F1ezGgaC4

Happy measuring

Kris 20171129_155425.jpg
 
Last edited:
@KrisKasprzak. Nice job on the imager and especially the interpolation. Had a project to update based on the mlx90620 chip. Have to put it on my list of things to do this winter :) Thanks for sharing.
 
That's awesome! Nicely done. That looks really nice from an 8x8 grid.

I wonder if there is a clever way to create a heat map of an area by repeatedly panning over a certain area. Basically, use the motion to gather info on the gaps.
Also would be kinda cool to do some stitching so you could create a detailed heat map of your entire wall. Just dumping all the values to an sdcard might be useful, and then doing all the processing on the PC.
 
@linuzgeek and @@KrisKasprzak. A lot of years ago some one had posted on how to do that with a mlx90614. The site is now defunct but he had the Arduino code and processing sketch to get some detailed images. I tried doing that with the 90620 but ran into problem with the interpolation to get a good image. If there is any interest by anyone for this I can post a copy of the page and code someplace.

UPDATE: Heres a link for a couple of the original 90614 project. https://drive.google.com/open?id=1Y4gssowNb4v-ce3pKQO85AvB0EnxSXI3
 
Last edited:
Have one of these in the mail, so interested in your results. With tiling images together do you have any idea what the actual FOV for each pixel is? If you have a point IR source some distance out does it always light up one pixel (pixels touch) sometimes vanish (pixels have dead space) or sometimes show up twice (pixels actually overlap).

If there are gaps wondering if you could use a mirror or prisim assembly to scan the FOV across the sensor to trade time for more pixels?

Probably winds up cheaper to just buy a real camera but many moons ago worked with an 8 sensor IR system that used a very complex scanning mirror system to make up a TV image from those 8 pixels.
 
Thanks for the compliments. The FOV is about 60 degrees so maybe if you get close enough to the heat source you could scan and build a larger array to avoid interpolation. At around 5 inches from the sensor i can "see" my fingers holding up a peace sign, at about 10 inches, the peace sign becomes a single blob. I really think there's not much you can do with an 8x8 array. Lately i've gotten to printing my own PCB boards, paper transfer from a laser printer, and 20 min in Muriatic+H2O2 acid. Works perfectly for a garage DYI. I forgot to reverse the pins on my 3v3 regulator as it's mounted on the copper side so some jumpers need to be added and I'm designing an enclosure to be 3D printed. I've become quite proficient at this process and warrants another YouTube video.

20171201_231653.jpg

20171201_231705.jpg
 
@@KrisKasprzak. Nice job on the circuit board. Never tried that will have to give it a shot at some point. Anyway, you guys got me going on this again, haven't touched for a few year, thanks for the inspiration. This was one of my first Arduino projects (it was before I discovered Teensies :) ). Anyway here is some more info that I thought might be of interest:

If there are gaps wondering if you could use a mirror or prisim assembly to scan the FOV across the sensor to trade time for more pixels?
Had in my notes that somebody did that with a lepton sensor, you can check this out Poor Mans Thermograph

I found this last night on someone that did some testing using several sensors that I thought was interesting, Low-cost IR Array Sensors performance characterization (Thermal Imaging)


Cheers
 
@KrisKasprzak Well done, i build your Thermo camera a few days ago together and install your source code.
But i cannot use the touchscreen. Can you send me your missing code to me please?
 
@KrisKasprzak, nice project. i enjoyed the video, good job on that and thanks for the code.
@mjs513, thank you for the code.
 
For those of you who downloaded my code, my first post did non include the touchscreen stuff. This was brought to my attention from a YouTube comment. A few weeks back i updated the code to the latest--perhaps josh911 downloaded before my update? At any rate feel free to download again, but attached here (hopefully i'm allowed to post code in this part of the forum).

Code:
/*

  This program is for upsizing an 8 x 8 array of thermal camera readings
  it will size up by 10x and display to a 240 x 320
  interpolation is linear and "good enough" given the display is a 5-6-5 color palet
  Total final array is an array of 70 x 70 of internal points only

  Revisions
  1.0     Kasprzak      Initial code

  MCU                       https://www.amazon.com/Teensy-3-2-with-pins/dp/B015QUPO5Y/ref=sr_1_2?s=industrial&ie=UTF8&qid=1510373806&sr=1-2&keywords=teensy+3.2
  Display                   https://www.amazon.com/Wrisky-240x320-Serial-Module-ILI9341/dp/B01KX26JJU/ref=sr_1_10?ie=UTF8&qid=1510373771&sr=8-10&keywords=240+x+320+tft
  Thermal sensor            https://learn.adafruit.com/adafruit-amg8833-8x8-thermal-camera-sensor/overview
  display library           https://github.com/PaulStoffregen/ILI9341_t3
  font library              https://github.com/PaulStoffregen/ILI9341_fonts
  sensor library            https://github.com/adafruit/Adafruit_AMG88xx
  touchscreen lib           https://github.com/dgolda/UTouch
  equations generated from  http://web-tech.ga-usa.com/2012/05/creating-a-custom-hot-to-cold-temperature-color-gradient-for-use-with-rrdtool/index.html

  Pinouts
  MCU         Device
  A4          AMG SDA
  A5          AMG SCL
  Gnd         Dispaly GND, AMG Gnd
  3v3         Dispaly Vcc,Display LED,Display RST, AMG Vcc
  2           Dispaly T_CLK
  3           Dispaly T_CS
  4           Dispaly T_DIN
  5           Dispaly T_DO
  6           Dispaly T_IRQ
  9           Display D/C
  10          Display CS
  11          Display MOSI
  12          Dispaly MISO
  13          Display SCK

*/

#include "ILI9341_t3.h"             // very fast library
#include "font_ArialBoldItalic.h"   // fonts for the startup screen
#include "font_Arial.h"             // fonts for the legend
#include "font_DroidSans.h"         // fonts for the startup screen
#include <Adafruit_AMG88xx.h>       // thermal camera lib
#include <UTouch.h>                 // touchscreen lib

#define PIN_CS 10                   // chip select for the display
#define PIN_DC 9                    // d/c pin for the display

// constants for the cute little keypad
#define KEYPAD_TOP 15
#define KEYPAD_LEFT 50
#define BUTTON_W 60
#define BUTTON_H 30
#define BUTTON_SPACING_X 10
#define BUTTON_SPACING_Y 10
#define BUTTON_TEXTSIZE 2

// fire up the display using a very fast driver
// this next line is for my modified library where I pass the screen dimensions in--that way i can use the same lib for my 3.5", 2.8" and other sizes
// ILI9341_t3 Display = ILI9341_t3(PIN_CS, PIN_DC, 240, 320); 

// you will need to use this line
ILI9341_t3 Display = ILI9341_t3(PIN_CS, PIN_DC);


// create some colors for the keypad buttons
#define C_BLUE Display.color565(0,0,255)
#define C_RED Display.color565(255,0,0)
#define C_GREEN Display.color565(0,255,0)
#define C_WHITE Display.color565(255,255,255)
#define C_BLACK Display.color565(0,0,0)
#define C_LTGREY Display.color565(200,200,200)
#define C_DKGREY Display.color565(80,80,80)
#define C_GREY Display.color565(127,127,127)

// create some text for the keypad butons
char KeyPadBtnText[12][5] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "Done", "0", "Clr" };

// define some colors for the keypad buttons
uint16_t KeyPadBtnColor[12] = {C_BLUE, C_BLUE, C_BLUE, C_BLUE, C_BLUE, C_BLUE, C_BLUE, C_BLUE, C_BLUE, C_GREEN, C_BLUE, C_RED };

// start with some initial colors
uint16_t MinTemp = 25;
uint16_t MaxTemp = 35;

// variables for interpolated colors
byte red, green, blue;

// variables for row/column interpolation
byte i, j, k, row, col, incr;
float intPoint, val, a, b, c, d, ii;
byte aLow, aHigh;

// size of a display "pixel"
byte BoxWidth = 3;
byte BoxHeight = 3;

int x, y;
char buf[20];

// variable to toggle the display grid
int ShowGrid = -1;
int DefaultTemp = -1;

// array for the 8 x 8 measured pixels
float pixels[64];

// array for the interpolated array
float HDTemp[80][80];

// create the keypad buttons
// note the ILI9438_3t library makes use of the Adafruit_GFX library (which makes use of the Adafruit_button library)
Adafruit_GFX_Button KeyPadBtn[12];

// create the camara object
Adafruit_AMG88xx ThermalSensor;

// create the touch screen object
UTouch  Touch( 2, 3, 4, 5, 6);

void setup() {

  // Serial.begin(115200);

  // start the display and set the background to black
  Display.begin();
  Display.fillScreen(C_BLACK);

  // initialize the touch screen and set location precision
  Touch.InitTouch();
  Touch.setPrecision(PREC_EXTREME);

  // create the keypad buttons
  for (row = 0; row < 4; row++) {
    for (col = 0; col < 3; col++) {
      KeyPadBtn[col + row * 3].initButton(&Display, BUTTON_H + BUTTON_SPACING_X + KEYPAD_LEFT + col * (BUTTON_W + BUTTON_SPACING_X ),
                                          KEYPAD_TOP + 2 * BUTTON_H + row * (BUTTON_H + BUTTON_SPACING_Y),
                                          BUTTON_W, BUTTON_H, C_WHITE, KeyPadBtnColor[col + row * 3], C_WHITE,
                                          KeyPadBtnText[col + row * 3], BUTTON_TEXTSIZE);
    }
  }

  // set display rotation (you may need to change to 0 depending on your display
  Display.setRotation(3);

  // show a cute splash screen (paint text twice to show a little shadow
  Display.setFont(DroidSans_40);
  Display.setCursor(22, 21);
  Display.setTextColor(C_WHITE, C_BLACK);
  Display.print("Thermal");

  Display.setFont(DroidSans_40);
  Display.setCursor(20, 20);
  Display.setTextColor(C_BLUE, C_BLACK);
  Display.print("Thermal");

  Display.setFont(Arial_48_Bold_Italic);
  Display.setCursor(52, 71);
  Display.setTextColor(C_WHITE, C_BLACK);
  Display.print("Camera");

  Display.setFont(Arial_48_Bold_Italic);
  Display.setCursor(50, 70);
  Display.setTextColor(C_RED, C_BLACK);
  Display.print("Camera");


  // let sensor boot up
  bool status = ThermalSensor.begin();
  delay(100);

  // check status and display results
  if (!status) {
    while (1) {
      Display.setFont(DroidSans_20);
      Display.setCursor(20, 150);
      Display.setTextColor(C_RED, C_BLACK);
      Display.print("Sensor: FAIL");
      delay(500);
      Display.setFont(DroidSans_20);
      Display.setCursor(20, 150);
      Display.setTextColor(C_BLACK, C_BLACK);
      Display.print("Sensor: FAIL");
      delay(500);
    }
  }
  else {
    Display.setFont(DroidSans_20);
    Display.setCursor(20, 150);
    Display.setTextColor(C_GREEN, C_BLACK);
    Display.print("Sensor: FOUND");
  }

  // read the camera for initial testing
  ThermalSensor.readPixels(pixels);

  // check status and display results
  if (pixels[0] < 0) {
    while (1) {
      Display.setFont(DroidSans_20);
      Display.setCursor(20, 180);
      Display.setTextColor(C_RED, C_BLACK);
      Display.print("Readings: FAIL");
      delay(500);
      Display.setFont(DroidSans_20);
      Display.setCursor(20, 180);
      Display.setTextColor(C_BLACK, C_BLACK);
      Display.print("Readings: FAIL");
      delay(500);
    }
  }
  else {
    Display.setFont(DroidSans_20);
    Display.setCursor(20, 180);
    Display.setTextColor(C_GREEN, C_BLACK);
    Display.print("Readings: OK");
    delay(2000);
  }

  // set display rotation and clear the fonts..the rotation of this display is a bit weird

  Display.setFontAdafruit();
  Display.fillScreen(C_BLACK);

  // get the cutoff points for the color interpolation routines
  // note this function called when the temp scale is changed
  Getabcd();

  // draw a cute legend with the scale that matches the sensors max and min
  DrawLegend();

  // draw a large white border for the temperature area
  Display.fillRect(10, 10, 220, 220, C_WHITE);


}

void loop() {

  // if someone touched the screen do something with it
  if (Touch.dataAvailable()) {
    ProcessTouch();
  }

  // read the sensor
  ThermalSensor.readPixels(pixels);

  // now that we have an 8 x 8 sensor array
  // interpolate to get a bigger screen
  InterpolateRows();

  // now that we have row data with 70 columns
  // interpolate each of the 70 columns
  // forget Arduino..no where near fast enough..Teensy at > 72 mhz is the starting point
  InterpolateCols();

  // display the 70 x 70 array
  DisplayGradient();

}


// interplation function to create 70 columns for 8 rows
void InterpolateRows() {

  // interpolate the 8 rows (interpolate the 70 column points between the 8 sensor pixels first)
  for (row = 0; row < 8; row ++) {
    for (col = 0; col < 70; col ++) {
      // get the first array point, then the next
      // also need to bump by 8 for the subsequent rows
      aLow =  col / 10 + (row * 8);
      aHigh = (col / 10) + 1 + (row * 8);
      // get the amount to interpolate for each of the 10 columns
      // here were doing simple linear interpolation mainly to keep performace high and
      // display is 5-6-5 color palet so fancy interpolation will get lost in low color depth
      intPoint =   (( pixels[aHigh] - pixels[aLow] ) / 10.0 );
      // determine how much to bump each column (basically 0-9)
      incr = col % 10;
      // find the interpolated value
      val = (intPoint * incr ) +  pixels[aLow];
      // store in the 70 x 70 array
      // since display is pointing away, reverse row to transpose row data
      HDTemp[ (7 - row) * 10][col] = val;

    }
  }
}

// interplation function to interpolate 70 columns from the interpolated rows
void InterpolateCols() {

  // then interpolate the 70 rows between the 8 sensor points
  for (col = 0; col < 70; col ++) {
    for (row = 0; row < 70; row ++) {
      // get the first array point, then the next
      // also need to bump by 8 for the subsequent cols
      aLow =  (row / 10 ) * 10;
      aHigh = aLow + 10;
      // get the amount to interpolate for each of the 10 columns
      // here were doing simple linear interpolation mainly to keep performace high and
      // display is 5-6-5 color palet so fancy interpolation will get lost in low color depth
      intPoint =   (( HDTemp[aHigh][col] - HDTemp[aLow][col] ) / 10.0 );
      // determine how much to bump each column (basically 0-9)
      incr = row % 10;
      // find the interpolated value
      val = (intPoint * incr ) +  HDTemp[aLow][col];
      // store in the 70 x 70 array
      HDTemp[ row ][col] = val;
    }
  }
}

// function to display the results
void DisplayGradient() {

  Display.setRotation(2);

  // rip through 70 rows
  for (row = 0; row < 70; row ++) {

    // fast way to draw a non-flicker grid--just make every 10 pixels 2x2 as opposed to 3x3
    // drawing lines after the grid will just flicker too much
    if (ShowGrid < 0) {
      BoxWidth = 3;
    }
    else {
      if ((row % 10 == 9) ) {
        BoxWidth = 2;
      }
      else {
        BoxWidth = 3;
      }
    }
    // then rip through each 70 cols
    for (col = 0; col < 70; col++) {

      // fast way to draw a non-flicker grid--just make every 10 pixels 2x2 as opposed to 3x3
      if (ShowGrid < 0) {
        BoxHeight = 3;
      }
      else {
        if ( (col % 10 == 9)) {
          BoxHeight = 2;
        }
        else {
          BoxHeight = 3;
        }
      }
      // finally we can draw each the 70 x 70 points, note the call to get interpolated color
      Display.fillRect((row * 3) + 15, (col * 3) + 15, BoxWidth, BoxHeight, GetColor(HDTemp[row][col]));
    }
  }

  Display.setRotation(3);

}

// my fast yet effective color interpolation routine
uint16_t GetColor(float val) {

  /*
    pass in value and figure out R G B
    several published ways to do this I basically graphed R G B and developed simple linear equations
    again a 5-6-5 color display will not need accurate temp to R G B color calculation

    equations based on
    http://web-tech.ga-usa.com/2012/05/creating-a-custom-hot-to-cold-temperature-color-gradient-for-use-with-rrdtool/index.html

  */

  red = constrain(255.0 / (c - b) * val - ((b * 255.0) / (c - b)), 0, 255);

  if ((val > MinTemp) & (val < a)) {
    green = constrain(255.0 / (a - MinTemp) * val - (255.0 * MinTemp) / (a - MinTemp), 0, 255);
  }
  else if ((val >= a) & (val <= c)) {
    green = 255;
  }
  else if (val > c) {
    green = constrain(255.0 / (c - d) * val - (d * 255.0) / (c - d), 0, 255);
  }
  else if ((val > d) | (val < a)) {
    green = 0;
  }

  if (val <= b) {
    blue = constrain(255.0 / (a - b) * val - (255.0 * b) / (a - b), 0, 255);
  }
  else if ((val > b) & (val <= d)) {
    blue = 0;
  }
  else if (val > d) {
    blue = constrain(240.0 / (MaxTemp - d) * val - (d * 240.0) / (MaxTemp - d), 0, 240);
  }

  // use the displays color mapping function to get 5-6-5 color palet (R=5 bits, G=6 bits, B-5 bits)
  return Display.color565(red, green, blue);

}

// function to automatically set the max / min scale based on adding an offset to the average temp from the 8 x 8 array
// you could also try setting max and min based on the actual max min
void SetTempScale() {

  if (DefaultTemp < 0) {
    MinTemp = 25;
    MaxTemp = 35;
    Getabcd();
    DrawLegend();
  }
  else {

    val = 0.0;
    for (i = 0; i < 64; i++) {
      val = val + pixels[i];
    }
    val = val / 64.0;

    MaxTemp = val + 2.0;
    MinTemp = val - 2.0;
    Getabcd();
    DrawLegend();
  }

}

// function to get the cutoff points in the temp vs RGB graph
void Getabcd() {

  a = MinTemp + (MaxTemp - MinTemp) * 0.2121;
  b = MinTemp + (MaxTemp - MinTemp) * 0.3182;
  c = MinTemp + (MaxTemp - MinTemp) * 0.4242;
  d = MinTemp + (MaxTemp - MinTemp) * 0.8182;

}

// function to handle screen touches
void ProcessTouch() {

  Touch.read();

  x = Touch.getX();
  y = Touch.getY();

  // yea i know better to have buttons
  if (x > 200) {
    if (y < 80) {
      KeyPad(MaxTemp);
    }
    else if (y > 160) {
      KeyPad(MinTemp);
    }
    else {
      DefaultTemp = DefaultTemp * -1;
      SetTempScale();
    }
  }

  else if (x <= 200) {
    // toggle grid
    ShowGrid = ShowGrid * -1;
    if (ShowGrid > 0) {
      Display.fillRect(15, 15, 210, 210, C_BLACK);
    }
  }
}

// function to draw a cute little legend
void DrawLegend() {

  // my cute little color legend with max and min text
  j = 0;

  float inc = (MaxTemp - MinTemp ) / 160.0;

  for (ii = MinTemp; ii < MaxTemp; ii += inc) {
    Display.drawFastHLine(260, 200 - j++, 30, GetColor(ii));
  }

  Display.setTextSize(2);
  Display.setCursor(245, 20);
  Display.setTextColor(C_WHITE, C_BLACK);
  sprintf(buf, "%2d/%2d", MaxTemp, (int) (MaxTemp * 1.8) + 32);
  Display.fillRect(233, 15, 94, 22, C_BLACK);
  Display.setFont(Arial_14);
  Display.print(buf);

  Display.setTextSize(2);
  // Display.setFont(Arial_24_Bold);
  Display.setCursor(245, 220);
  Display.setTextColor(C_WHITE, C_BLACK);
  sprintf(buf, "%2d/%2d", MinTemp, (int) (MinTemp * 1.8) + 32);
  Display.fillRect(233, 215, 94, 55, C_BLACK);
  Display.setFont(Arial_14);
  Display.print(buf);


}

// function to draw a numeric keypad
void KeyPad(uint16_t &TheNumber) {

  int left = KEYPAD_LEFT;
  int top = KEYPAD_TOP;
  int wide = (3 * BUTTON_W ) + (4 * BUTTON_SPACING_X);
  int high = (5 * BUTTON_H) +  (6 * BUTTON_SPACING_Y);
  int TempNum = TheNumber;
  bool KeepIn = true;

  Display.fillRect(left, top, wide , high, C_DKGREY);
  Display.drawRect(left, top, wide , high, C_LTGREY);

  Display.fillRect(left + 10, top + 10, wide - 20 , 30, C_WHITE);
  Display.drawRect(left + 10, top + 10, wide - 20 , 30, C_DKGREY);

  Display.setCursor(left  + 20 , top + 20);
  Display.setTextColor(C_BLACK, C_WHITE);

  Display.print(TheNumber);

  for (row = 0; row < 4; row++) {
    for (col = 0; col < 3; col++) {
      KeyPadBtn[col + row * 3].drawButton();
    }
  }
  delay(300); // debounce
  while (KeepIn) {
    // get the touch point

    Touch.read();
    x = Touch.getX();
    y = Touch.getY();

    // if nothing or user didn't press hard enough, don't do anything
    if (Touch.dataAvailable()) {

      // go thru all the KeyPadBtn, checking if they were pressed
      for (byte b = 0; b < 12; b++) {
        if (PressIt(KeyPadBtn[b], x, y) == true) {

          if ((b < 9) | (b == 10)) {

            TempNum *= 10;
            if (TempNum == 0) {
              Display.fillRect(left + 10, top + 10, wide - 20 , 30, C_WHITE);
            }

            if (b != 10) {
              TempNum += (b + 1);
            }

            if (TempNum > 80) {
              Display.fillRect(left + 10, top + 10, wide - 20 , 30, C_WHITE);
              //Display.setCursor(left  + 100 , top + 20);
              //Display.setTextColor(C_RED, C_WHITE);
              //Display.print("Max 100");
              TempNum = 0;
              TempNum += (b + 1);
            }
          }
          // clr button
          if (b == 11) {
            Display.fillRect(left + 10, top + 10, wide - 20 , 30, C_WHITE);
            Display.drawRect(left + 10, top + 10, wide - 20 , 30, C_DKGREY);
            TempNum = 0;
          }
          if (b == 9) {
            KeepIn = false;
          }

          Display.setCursor(left  + 20 , top + 20);
          Display.setTextColor(C_BLACK, C_WHITE);

          Display.print(TempNum);

        }
      }
    }
  }

  // clear screen redraw previous screen
  // update the Current Channel
  TheNumber = TempNum;

  Display.fillRect(0, 0, 319, 239, C_BLACK);
  Display.fillRect(10, 10, 220, 220, C_WHITE);
  Display.fillRect(15, 15, 210, 210, C_BLACK);
  Getabcd();
  DrawLegend();

}

// function to handle color inversion of a pressed button
bool PressIt(Adafruit_GFX_Button TheButton, int x, int y) {


  if (TheButton.contains(x, y)) {
    TheButton.press(true);  // tell the button it is pressed
  } else {
    TheButton.press(false);  // tell the button it is NOT pressed
  }
  if (TheButton.justReleased()) {
    TheButton.drawButton();  // draw normal
  }
  if (TheButton.justPressed()) {
    TheButton.drawButton(true);  // draw invert!
    delay(200); // UI debouncing
    TheButton.drawButton(false);  // draw invert!
    return true;
  }
  return false;
}


// end of code
 
@KrisKasprzak, your code here is the same as in the youtube video, but there is no update for the touchscreen inside.
 
@KrisKasprzak, in your video you did change the value of the upper and lower temperature with the keypad. Can you give me your solution please?
 
josh911,

I'ts in my code that i posted on YouTube and above. There is a function called processTouch that allows the user to change the upper and lower. There is also a KeyPad function and some other supporting functions.

Not sure why you think the keypad stuff is not there.


Code:
// function to handle screen touches
void ProcessTouch() {

  Touch.read();

  x = Touch.getX();
  y = Touch.getY();

  // yea i know better to have buttons
  if (x > 200) {
    if (y < 80) {
      KeyPad(MaxTemp);
    }
    else if (y > 160) {
      KeyPad(MinTemp);
    }
    else {
      DefaultTemp = DefaultTemp * -1;
      SetTempScale();
    }
  }

  else if (x <= 200) {
    // toggle grid
    ShowGrid = ShowGrid * -1;
    if (ShowGrid > 0) {
      Display.fillRect(15, 15, 210, 210, C_BLACK);
    }
  }
}
 
Sorry my mistake. I am a beginner from programming of Teensy. In your code is the Keypad function present.
I can switch the grid off and on, but if i want to change the value from the upper an lower temperture it is impossible.
Sorry for my bad english.
 
Last edited:
@KrisKasprzak, nice work on this project. Very cool!

Wondering if you (or someone else) could point me in the right direction. I'm trying to leverage what you've built for a different purpose. With that said, I have a 16 x 16 sensor grid vs. the 8 x 8 in your project. Same size screen (240 x 320)
I’ve tried increasing the array size, and the row counts from 8 to 9 with no luck. 8x8 it works perfectly. 16x16 hangs the Teensy 3.6. No output on LCD or Serial.

Any ideas on how to expand the grid to 16x16 ?

I’m using the following array to stage the data:

Code:
float pixels[81] = {
  1, 0, 0, 0, 0, 0, 0, 0, 2,
  0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0,
  3, 0, 0, 0, 0, 0, 0, 0, 4
};

Thank you in advance.

Curt
 
Which Teensy?

I'm not sure the 3.2 will have enough memory, and I'll guess performance will be around 2-3 seconds per frame. However, you will need several changes

Also, I'm not sure I see how you are using an array of 9 x 9 to store the sensor array of 16 x 16. See bel

1. line 116, float pixels[64]; -> float pixels[256]; // 1024K ram needed
2. line 119, float HDTemp[80][80]; -> float HDTemp[160][160]; 102,400 bytes needed (i think float HDTemp[150][150]; will work as well)
3. line 280 or so...
for (row = 0; row < 8; row ++) {
for (col = 0; col < 70; col ++) {
change to

for (row = 0; row < 16; row ++) {
for (col = 0; col < 150; col ++) {

4. 304 or so
for (col = 0; col < 70; col ++) {
for (row = 0; row < 70; row ++) {

change to
for (col = 0; col < 150; col ++) {
for (row = 0; row < 150; row ++) {


5. line 325 or so
for (row = 0; row < 70; row ++) {
change to
for (row = 0; row < 150; row ++) {

5. line 341 or so
for (col = 0; col < 70; col++) {
change to
for (col = 0; col < 150; col++) {
 
ahh I see a teensy 3.6. Performance may be 1-2 frames per sec. and you should have enough memory.

Put a Serial.println("it landed here"), at various points in your code to see where it hangs, if it sill does.
 
Thank you for helping

Sketch uses 22392 bytes (2%) of program storage space. Maximum is 1048576 bytes.
Global variables use 95620 bytes (36%) of dynamic memory, leaving 166524 bytes for local variables. Maximum is 262144 bytes.

Sorry, I meant 16, not 9. And the array I'm testing with is:

Code:
float pixels[256] = {
  1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4
};

I made the suggested changes. Do I also need to change the 8 here to 16?

Code:
      aLow =  col / 10 + (row * 8);
      aHigh = (col / 10) + 1 + (row * 8);

It's still hanging somewhere. I'm debugging now to see where it stops. Stay tuned.
 
It's hanging on the following line within InterpolateRows() after reaching row 8 and col 0:
Code:
HDTemp[ (7 - row) * 10][col] = val;


Code:
void InterpolateRows() {

  // interpolate the 8 rows (interpolate the 70 column points between the 8 sensor pixels first)
  for (row = 0; row < 16; row ++) {

    //test = test + 1;
    
    for (col = 0; col < 160; col ++) {
      Serial.print("row: ");Serial.print(row);Serial.print(" col: ");Serial.println(col);
      Serial.println("Test-1");

      
      // get the first array point, then the next
      // also need to bump by 8 for the subsequent rows
      aLow =  col / 10 + (row * 8);
      Serial.println("Test-1");
      aHigh = (col / 10) + 1 + (row * 8);
      Serial.println("Test-2");
      //Serial.print("aLow: "); Serial.print(aLow);Serial.print(" aHigh: "); Serial.println(aHigh);delay(50);
      // get the amount to interpolate for each of the 10 columns
      // here were doing simple linear interpolation mainly to keep performace high and
      // display is 5-6-5 color palet so fancy interpolation will get lost in low color depth
      intPoint =   (( pixels[aHigh] - pixels[aLow] ) / 10.0 );
      Serial.println("Test-3");
      // determine how much to bump each column (basically 0-9)
      incr = col % 10;
      Serial.println("Test-4");
      // find the interpolated value
      val = (intPoint * incr ) +  pixels[aLow];
      Serial.println("Test-5");
      // store in the 70 x 70 array
      // since display is pointing away, reverse row to transpose row data
      HDTemp[ (7 - row) * 10][col] = val;
      Serial.println("Test-6");
 
I was able to unblock the code by changing:

FROM:
HDTemp[ (7 - row) * 10][col] = val;

TO:
HDTemp[ (31 - row) * 10][col] = val;

The HDTemp may be overflowing. When outputting HDTemp[row][col] to serial, I get: "HDTemp: nan" on some of the rows/cols.

All of the code is being executed now; however, the output to the LCD unusable. Most of the "lit" pixels are not being displayed as they are outside of 240 x 320. I'm assuming the scaling needs to be adjusted somehow.

Any words of wisdom?
 
Back
Top