Conflict: IR Sensor and File System

Hi. I have the sample code for the MLX90641 IR Sensor running fine. If I try to add the file system to that code, It stops working. Stops displaying the field. The data can still get pumped to the serial window, but the field doesnt draw or refresh. All I need to do is add #include <Seeed_FS.h> and it will fail. I don’t initialize anything or use anything.

I suspect there must be some variable conflict… I tried renaming ones that could have been defined globally elsewhere (like “status”) but that didnt seem to help. Any ideas?

Original code is from here: https://wiki.seeedstudio.com/Grove-Thermal-Imaging-Camera-IR-Array/

You also need to post your own code, otherwise we can’t help you

Thanks for writing. There is no other code. If I copy/paste the example from the seeedstudio wiki, as included in the original code link up top, and simply add #include <Seeed_FS.h> at the top, we get the problem. No original code or deviation from the sample at all.

Do you mean that after adding the file “#include <Seeed_FS.h>” to the code of the wiki page, the serial port will output data only once instead of continuously refreshing?

Once I include the “#include <Seeed_FS.h>” the part of the code that displays the temperature gradients mapped to colors no longer displays. The Serial Monitor still gets any of the Serial.println statements that I have in there for debug. There is just something in the display that doesn’t work. There is text that still writes to the display, and the legend, drawn to the right, is still there. I think there is something in the code where the variables are being overwritten or used for different things, but I can’t be sure.

I suppose I should have mentioned that it is running on a WioTerminal

Hi because I don’t have “Grove - thermal imaging camera” here, I need to help you look at the problem from the code. Can you tell me where your “Seeed_FS.h” file is downloaded from? Or provide it to me directly

Thank you … sure… everything in my sample comes from the Seeed Wiki…
FS page with instructions: https://wiki.seeedstudio.com/Wio-Terminal-FS-Overview/
FS repo: https://github.com/Seeed-Studio/Seeed_Arduino_SFUD
Base code is “Software Code 2” on the sensor page: h t t p s : / / wiki.seeedstudio.c o m /Grove-Thermal-Imaging-Camera-IR-Array/

The stuff in Setup works… it draws the legend, and the piece of text at the top of the screen that shows the temperature at the crosshairs works… but once you get into Loop… it is like all of the “Display.” calls are failing, and the “tft.” ones are working… tft is the TFT_espi object, and Display is the eSprite object. I would think if there were something in the naming clashes, there would be a compile error. Again, this is SeeedStudio code… I have added nothing to this example. Thanks again for the help.

Hi Ejog

Please take the sreenshot what error on the IDE, and make sure you just add the library on the top without other changed, that will be better to help you.

Best regards
F

Hi, thanks for the help. There is no error on compile. I’ll show the code with the include included and the compile results, and then the display before and after.

Display before file system include…
[ugh… I can only do one file per post… stay tune for display before post and display after post.

Display before file system include…

Display after file system include…

Bump. Has any else seen problems in TFT or DISPLAY objects from other libraries?

Bump. Can anyone suggest non Seeed libraries with which I might have better luck?

HI

image

I don’t get what you mean mate.
just paste all your code in there will get better help.

ok. In the Apr 21 post, I showed the code I used, which out of the box, with no modifications from me other than combining them, failed. Here is my code. The code that I have added is a red herring, don’t be distracted by that.

//#include <sfud_cfg.h>
//#include <sfud_def.h>
//#include <warpper_HAL.h>
//#include <sfud.h>
//#include <Arduino_QSPI_Hal.h>
//#include <sfud_flash_def.h>

#include <Seeed_FS.h>
// The simple act of including these files breaks the code.  The actice part of the screen no longer redraws.  It overwrites itself, and doesn't print out the graphics display. 
//  Seeed_FS.h is enough to kill the display. 


#include <Wire.h>
#include "MLX90641_API.h"
#include "MLX9064X_I2C_Driver.h"
#include <TFT_eSPI.h>                // Include the graphics library (this includes the sprite functions)  

// This code is based on the example provided by SEEEDStudio for use with the IR Array and their WioTerminal, Buzzer, etc... <<<<<<<<
//This IR thermal camera carries a 16x12 array of thermal sensors (MLX90641) and it can detect the temperature of objects from far away with a 
//center area accuracy of ±1℃ and average accuracy of ±1.5℃. In order to obtain the thermal images easily, the I2C protocol is used to get the 
//low-resolution images from the camera. The FOV (Field of View) of this camera is 110°x75°, and the temperature measurement range is -40℃ to 300℃. 
//In order to obtain the thermal image easily, I2C protocol is used to get the low-resolution image from the camera.
 
const byte MLX90641_address = 0x33; //Default 7-bit unshifted address of the MLX90641
#define TA_SHIFT 12 //Default shift for MLX90641 in open air
#define debug  Serial
uint16_t eeMLX90641[832];
float MLX90641To[192];
uint16_t MLX90641Frame[242];
paramsMLX90641 MLX90641;
int errorno = 0;
String carName ="Truck";  // // bring it over
String inputMode="IR";      // future, can also be EXHAUST and ???   
#define BUZZER_PIN WIO_BUZZER /* sig pin of the buzzer */


 
TFT_eSPI    tft = TFT_eSPI(); 
TFT_eSprite Display = TFT_eSprite(&tft);  // Create Sprite object "img" with pointer to "tft" object
// the pointer is used by pushSprite() to push it onto the TFT
 
unsigned long CurTime;
 
uint16_t TheColor;
// start with some initial colors
uint16_t MinTemp = 25;
uint16_t MaxTemp = 38;
 
// 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;
 
// array for the interpolated array
float HDTemp[6400];
float pointTemp,pointTempF;
//======================================= SETUP 
void setup() {
    Wire.begin();
    Wire.setClock(2000000); //Increase I2C clock speed to 2M
    debug.begin(115200); //Fast debug as possible

       
    Serial.println("Starting Setup... /n");
    
    // set the buttons
    pinMode(WIO_5S_UP, INPUT_PULLUP);
    pinMode(WIO_5S_DOWN, INPUT_PULLUP);
    pinMode(WIO_5S_LEFT, INPUT_PULLUP);
    pinMode(WIO_5S_RIGHT, INPUT_PULLUP);
    pinMode(WIO_5S_PRESS, INPUT_PULLUP);
    pinMode(WIO_KEY_A, INPUT_PULLUP);
    pinMode(WIO_KEY_B, INPUT_PULLUP);
    pinMode(WIO_KEY_C, INPUT_PULLUP);
    //set buzzer pin as output
    pinMode(BUZZER_PIN, OUTPUT);




  
    // start the display and set the background to black
 
    if (isConnected() == false) {
        debug.println("MLX90641 not detected at default I2C address. Please check wiring. Freezing.");
        while (1);
    }
    //Get device parameters - We only have to do this once
    int MLXstatus;
    MLXstatus = MLX90641_DumpEE(MLX90641_address, eeMLX90641);
    errorno = MLXstatus;//MLX90641_CheckEEPROMValid(eeMLX90641);//eeMLX90641[10] & 0x0040;//
 
    if (MLXstatus != 0) {
        debug.println("Failed to load system parameters");
       while(1);
    }
 
    MLXstatus = MLX90641_ExtractParameters(eeMLX90641, &MLX90641);
    //errorno = status;
    if (MLXstatus != 0) {
        debug.println("Parameter extraction failed");
        while(1);
    }
 
    //Once params are extracted, we can release eeMLX90641 array
 
    MLX90641_SetRefreshRate(MLX90641_address, 0x05); //Set rate to 16Hz
 
    tft.begin();
    tft.setRotation(3);
    tft.fillScreen(TFT_BLACK);
    Display.createSprite(TFT_HEIGHT, TFT_WIDTH);
    Display.fillSprite(TFT_BLACK); 
 
    // get the cutoff points for the color interpolation routines
    // note this function called when the temp scale is changed
    Getabcd();
 
    // draw a legend with the scale that matches the sensors max and min
    DrawLegend();    
}

void loop() {
    // draw a large white border for the temperature area
    Display.fillRect(10, 10, 220, 220, TFT_WHITE);
    for (byte x = 0 ; x < 2 ; x++) {
        int MLX90status = MLX90641_GetFrameData(MLX90641_address, MLX90641Frame);  // This status variable doesnt seem to ever be used.   maybe they just need to reset something using method??? 
 
        float vdd = MLX90641_GetVdd(MLX90641Frame, &MLX90641);
        float Ta = MLX90641_GetTa(MLX90641Frame, &MLX90641);
 
        float tr = Ta - TA_SHIFT; //Reflected temperature based on the sensor ambient temperature
        float emissivity = 0.95;
 
        MLX90641_CalculateTo(MLX90641Frame, &MLX90641, emissivity, tr, MLX90641To);
    }
 
    interpolate_image(MLX90641To,12,16,HDTemp,80,80);


    //display the 80 x 80 array
    DisplayGradient();
 
    //Crosshair in the middle of the screen
    Display.drawCircle(115, 115, 5, TFT_WHITE);
    Display.drawFastVLine(115, 105, 20, TFT_WHITE);
    Display.drawFastHLine(105, 115, 20, TFT_WHITE);
    //Displaying the temp at the middle of the Screen
 
    //Push the Sprite to the screen
    Display.pushSprite(0, 0);
 
    tft.setRotation(3);
    tft.setTextColor(TFT_WHITE);
    // This is writing the cross hair temperature 
    // to Farenheith 
    tft.drawFloat(HDTemp[35 * 80 + 35], 2, 90, 20);  
    pointTemp=HDTemp[35*80 + 35];
    pointTempF=(pointTemp*9/5)+32;
    tft.drawFloat(pointTempF, 2, 90, 40) ;   

   // arrow keys change the car for which this image is being saved. 
  // Start with 4, set by up down left right. 
  if (digitalRead(WIO_5S_UP) == LOW) {
    carName="Tiga";
      Serial.println("5 Way Up");
      // Beep to acknowledge key press 
      playNote('C', 300);
    }
    else if (digitalRead(WIO_5S_DOWN) == LOW) {
      carName="Truck";
      Serial.println("5 Way Down");
      // Beep to acknowledge key press 
      playNote('C', 300); 
    }
    else if (digitalRead(WIO_5S_LEFT) == LOW) {
      carName="Lotus";
      Serial.println("5 Way Left"); 
      playNote('C', 300);
    }
      // Beep to acknowledge key press
    else if (digitalRead(WIO_5S_RIGHT) == LOW) {
      carName="Caldwell";
      Serial.println("5 Way Right");
      // Beep to acknowledge key press 
      playNote('C', 300); 
    }
    else if (digitalRead(WIO_5S_PRESS) == LOW) {
      Serial.println("5 Way Press");
      //Indicate which vehicle is being recorded 
      Serial.println(carName);
      //If the joystick button has been pressed, dump the current data to Serial (eventually file)
      //If we are in IR Mode... inputMode == "IR"
      //Dump the IR array to serial 
      for (int x = 0 ; x < 192 ; x++) {
        debug.print(MLX90641To[x], 2);
        debug.print(",");
      }
     debug.println(""); 
     // If we are in Gas Mode 
     //else
     // 
     // Beep to acknowledge key press 
      playNote('a', 300);  
      playNote('b', 300);
    }


  tft.setTextSize(2);
  tft.setCursor(5, 210);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  // sprintf(buf, "%2d/%2d", MinTemp, (int) (MinTemp * 1.12) + 32);
  tft.print(carName);



  
  
  //Top of the Display... probably used to switch modes from IR to gas monitor 
  if (digitalRead(WIO_KEY_A) == LOW) {
      Serial.println("A Key pressed");
      // Beep to acknowledge key press 
      playNote('c', 300);
    }
    else if (digitalRead(WIO_KEY_B) == LOW) {
      Serial.println("B Key pressed");
      // Beep to acknowledge key press 
      playNote('g', 300);
    }
    else if (digitalRead(WIO_KEY_C) == LOW) {
      Serial.println("C Key pressed");
      // Beep to acknowledge key press 
      playNote('a', 300);  
    }
      
 
}

// end of loop 



//Returns true if the MLX90640 is detected on the I2C bus
boolean isConnected() {
    Wire.beginTransmission((uint8_t)MLX90641_address);
    if (Wire.endTransmission() != 0) {
        return (false);    //Sensor did not ACK
    }
    return (true);
}
// function to display the results
void DisplayGradient() {
 
  tft.setRotation(4);
 
  // rip through 70 rows
  for (row = 0; row < 70; row ++) {
 
    // fast way to draw a non-flicker grid--just make every 10 MLX90641To 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 MLX90641To 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 * 80 + col]));
    }
  }
 
}
// 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 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;
 
}
float get_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y)
{
    if (x < 0)
    {
        x = 0;
    }
    if (y < 0)
    {
        y = 0;
    }
    if (x >= cols)
    {
        x = cols - 1;
    }
    if (y >= rows)
    {
        y = rows - 1;
    }
    return p[y * cols + x];
}
 
void set_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y, float f)
{
    if ((x < 0) || (x >= cols))
    {
        return;
    }
    if ((y < 0) || (y >= rows))
    {
        return;
    }
    p[y * cols + x] = f;
}
 
// src is a grid src_rows * src_cols
// dest is a pre-allocated grid, dest_rows*dest_cols
void interpolate_image(float *src, uint8_t src_rows, uint8_t src_cols,
                       float *dest, uint8_t dest_rows, uint8_t dest_cols)
{
    float mu_x = (src_cols - 1.0) / (dest_cols - 1.0);
    float mu_y = (src_rows - 1.0) / (dest_rows - 1.0);
 
    float adj_2d[16]; // matrix for storing adjacents
 
    for (uint8_t y_idx = 0; y_idx < dest_rows; y_idx++)
    {
        for (uint8_t x_idx = 0; x_idx < dest_cols; x_idx++)
        {
            float x = x_idx * mu_x;
            float y = y_idx * mu_y;
            get_adjacents_2d(src, adj_2d, src_rows, src_cols, x, y);
 
            float frac_x = x - (int)x; // we only need the ~delta~ between the points
            float frac_y = y - (int)y; // we only need the ~delta~ between the points
            float out = bicubicInterpolate(adj_2d, frac_x, frac_y);
            set_point(dest, dest_rows, dest_cols, x_idx, y_idx, out);
        }
    }
}
 
// p is a list of 4 points, 2 to the left, 2 to the right
float cubicInterpolate(float p[], float x)
{
    float r = p[1] + (0.5 * x * (p[2] - p[0] + x * (2.0 * p[0] - 5.0 * p[1] + 4.0 * p[2] - p[3] + x * (3.0 * (p[1] - p[2]) + p[3] - p[0]))));
    return r;
}
 
// p is a 16-point 4x4 array of the 2 rows & columns left/right/above/below
float bicubicInterpolate(float p[], float x, float y)
{
    float arr[4] = {0, 0, 0, 0};
    arr[0] = cubicInterpolate(p + 0, x);
    arr[1] = cubicInterpolate(p + 4, x);
    arr[2] = cubicInterpolate(p + 8, x);
    arr[3] = cubicInterpolate(p + 12, x);
    return cubicInterpolate(arr, y);
}
 
// src is rows*cols and dest is a 4-point array passed in already allocated!
void get_adjacents_1d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y)
{
    // pick two items to the left
    dest[0] = get_point(src, rows, cols, x - 1, y);
    dest[1] = get_point(src, rows, cols, x, y);
    // pick two items to the right
    dest[2] = get_point(src, rows, cols, x + 1, y);
    dest[3] = get_point(src, rows, cols, x + 2, y);
}
 
// src is rows*cols and dest is a 16-point array passed in already allocated!
void get_adjacents_2d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y)
{
    float arr[4];
    for (int8_t delta_y = -1; delta_y < 3; delta_y++)
    {                                          // -1, 0, 1, 2
        float *row = dest + 4 * (delta_y + 1); // index into each chunk of 4
        for (int8_t delta_x = -1; delta_x < 3; delta_x++)
        { // -1, 0, 1, 2
            row[delta_x + 1] = get_point(src, rows, cols, x + delta_x, y + delta_y);
        }
    }
}

// function to play notes... from Seeedstudio examples 
//Play tone
void playTone(int tone, int duration) {
    for (long i = 0; i < duration * 1000L; i += tone * 2) {
        digitalWrite(BUZZER_PIN, HIGH);
        delayMicroseconds(tone);
        digitalWrite(BUZZER_PIN, LOW);
        delayMicroseconds(tone);
    }
}
 
void playNote(char note, int duration) {
    char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
    int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956 };
 
    // play the tone corresponding to the note name
    for (int i = 0; i < 8; i++) {
        if (names[i] == note) {
            playTone(tones[i], duration);
        }
    }
}


 
// function to draw a legend
void DrawLegend() {
 
  //color legend with max and min text
  j = 0;
 
  float inc = (MaxTemp - MinTemp ) / 160.0;
 
  for (ii = MinTemp; ii < MaxTemp; ii += inc) {
    tft.drawFastHLine(260, 200 - j++, 30, GetColor(ii));
  }
 
  tft.setTextSize(2);
  tft.setCursor(245, 20);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  sprintf(buf, "%2d/%2d", MaxTemp, (int) (MaxTemp * 1.12) + 32);
  tft.print(buf);
 
  tft.setTextSize(2);
  tft.setCursor(245, 210);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  sprintf(buf, "%2d/%2d", MinTemp, (int) (MinTemp * 1.12) + 32);
  tft.print(buf);
 
}

There is got some macro definition repeat, between MLX90641 library and seeed_FS.h libraray.

Yep. That was my theory. Any advice on what to localize and fix?

try this one