Question regarding ssd1306 and sensor details (using u8g2)

Hey everyone, I am trying to use the u8g2 library with an ssd1306 oled to display the details from an adafruit IR sensor (mlx90614). Ive written a similar project before using a DHT temp/humidity sensor so just tried to modify existing code to suit the different library, however the method used in one differed from the other, so Im not entirely sure how correct any of this is. The program compiles and uploads okay, but nothing displays to the screen (which i have also tested using an example program). Any help is much appreciated, thanks!

#include <Arduino.h>
#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

#include <Adafruit_MLX90614.h>

Adafruit_MLX90614 mlx = Adafruit_MLX90614();

U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);   // All Boards without Reset of the Display

const long screenInterval = 1000;           // interval at which to buffer (milliseconds)
unsigned long sensorInterval;
unsigned long previousMillis;

void setup() {
  u8g2.begin();
  mlx.begin();
  u8g2.enableUTF8Print();
  u8g2.println("Adafruit MLX90614 Test");
}

void loop() {
  for(static unsigned long previousMillis=millis();
      millis()-previousMillis>=sensorInterval;
      previousMillis=millis()){
          u8g2.clearBuffer();
          // Get temperature and print its value.
          u8g2.setFont(u8g2_font_5x7_tf);
          u8g2.setCursor(0,15);
          u8g2.print("Ambient = "); u8g2.print(mlx.readAmbientTempC()); 
          u8g2.println("*C\tObject = "); u8g2.print(mlx.readObjectTempC()); u8g2.print("*C");
          u8g2.setCursor(0,25);
          u8g2.print("Ambient = "); u8g2.print(mlx.readAmbientTempF()); 
          u8g2.println("*F\tObject = "); u8g2.print(mlx.readObjectTempF()); u8g2.print("*F");
       
  }

  for(static unsigned long previousMillis=millis();
      millis()-previousMillis>=screenInterval/4;
      previousMillis=millis()){
           u8g2.sendBuffer();} 
}

Also, if this is just too much of a mess for anyone to want to open that can of worms... that's fine too, just please let me know and I'll start again from scratch. Thanks!

First off. Use the ctrl-T to format your code. It will indent everything nicely. And insert the spaces that are missing on your keyboard.

Then look at your logic. Take a piece of paper and a pencil. Hand trace the flow.

#include <Arduino.h>
#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

#include <Adafruit_MLX90614.h>

Adafruit_MLX90614 mlx = Adafruit_MLX90614();

U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);   // All Boards without Reset of the Display

const long screenInterval = 1000;           // interval at which to buffer (milliseconds)
unsigned long sensorInterval;
unsigned long previousMillis;

void setup() {
    u8g2.begin();
    mlx.begin();
    u8g2.enableUTF8Print();
    u8g2.println("Adafruit MLX90614 Test");
}

void loop() {
    for (static unsigned long previousMillis = millis();
            millis() - previousMillis >= sensorInterval;
            previousMillis = millis()) {
        u8g2.clearBuffer();
        // Get temperature and print its value.
        u8g2.setFont(u8g2_font_5x7_tf);
        u8g2.setCursor(0, 15);
        u8g2.print("Ambient = "); u8g2.print(mlx.readAmbientTempC());
        u8g2.println("*C\tObject = "); u8g2.print(mlx.readObjectTempC()); u8g2.print("*C");
        u8g2.setCursor(0, 25);
        u8g2.print("Ambient = "); u8g2.print(mlx.readAmbientTempF());
        u8g2.println("*F\tObject = "); u8g2.print(mlx.readObjectTempF()); u8g2.print("*F");

    }

    for (static unsigned long previousMillis = millis();
            millis() - previousMillis >= screenInterval / 4;
            previousMillis = millis()) {
        u8g2.sendBuffer();
    }
}

As a general rule, you run the loop() regularly. Then read sensors or update display when necessary.

U8glib required you to run the loop. Because it updated the screen one small section at a time.

U8g2 works the same with the “page buffer style” constructors.
The “full buffer style” constructors can draw the whole screen in one go.

A simple way to run the loop is to check millis().

David.

Oh awesome, thanks! I'll give all of it another review now, then.

I think there is one more issue in your code: For proper cooperation between multiple I2C devices on the same bus, you MUST use the HW I2C constructor. See also the U8g2 FAQ for this:

Oliver

Hmm, okay. So Ive formatted my code, but havent changed anything yet. But here it is, for better readability:

#include <Arduino.h>
#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
#include <Adafruit_MLX90614.h>

Adafruit_MLX90614 mlx = Adafruit_MLX90614();

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);  // All Boards without Reset of the Display

const long screenInterval = 1000;           // interval at which to buffer (milliseconds)
unsigned long sensorInterval;
unsigned long previousMillis;

void setup() {
  u8g2.begin();
  mlx.begin();
  u8g2.enableUTF8Print();
}

void loop() {
  for (static unsigned long previousMillis = millis();
       millis() - previousMillis >= sensorInterval;
       previousMillis = millis()) {
    u8g2.clearBuffer();

    // Get temperature and print its value.
    u8g2.setFont(u8g2_font_5x7_tf);
    u8g2.setCursor(0, 15);
    u8g2.print("Ambient = "); u8g2.print(mlx.readAmbientTempC());
    u8g2.println("*C\tObject = "); u8g2.print(mlx.readObjectTempC()); u8g2.print("*C");
    u8g2.setCursor(0, 25);
    u8g2.print("Ambient = "); u8g2.print(mlx.readAmbientTempF());
    u8g2.println("*F\tObject = "); u8g2.print(mlx.readObjectTempF()); u8g2.print("*F");
  }

  for (static unsigned long previousMillis = millis();
       millis() - previousMillis >= screenInterval / 4;
       previousMillis = millis()) {
    u8g2.sendBuffer();
  }
}

However, Im still a bit lost. Just to reiterate, I have successfully used the u8g2 library in the past, but it was my first project and was 6 or so months ago, so im sure i probably need a refresher on most or a lot of it as ive already forgotten. That being said, its structured differently than the program for the other sensor i am using, in that in my previous program I can see that it gathers and populates the data in setup, then calls it in loop to print to the screen, but in the IR sensor program it just calls it in main() with just the begin statement in setup. Here is the sensor program on its own:

/*************************************************** 
  This is a library example for the MLX90614 Temp Sensor

  Designed specifically to work with the MLX90614 sensors in the
  adafruit shop
  ----> https://www.adafruit.com/products/1748
  ----> https://www.adafruit.com/products/1749

  These sensors use I2C to communicate, 2 pins are required to  
  interface
  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.  
  BSD license, all text above must be included in any redistribution
 ****************************************************/

#include <Wire.h>
#include <Adafruit_MLX90614.h>

Adafruit_MLX90614 mlx = Adafruit_MLX90614();

void setup() {
  Serial.begin(9600);

  Serial.println("Adafruit MLX90614 test");  

  mlx.begin();  
}

void loop() {
  Serial.print("Ambient = "); Serial.print(mlx.readAmbientTempC()); 
  Serial.print("*C\tObject = "); Serial.print(mlx.readObjectTempC()); Serial.println("*C");
  Serial.print("Ambient = "); Serial.print(mlx.readAmbientTempF()); 
  Serial.print("*F\tObject = "); Serial.print(mlx.readObjectTempF()); Serial.println("*F");

  Serial.println();
  delay(500);
}

And please correct me if Im wrong, but is this difference in structure because the sensor program does the rest from the header file, or am I mistaken? Just having difficulty visualizing or properly tracing the flow of the program as Im trying to write/use it, sorry. Thanks!

And just a quick edit, I did change the constructors to hardware, though

It should work or at least you see something.
Ok, one problem is there: There should be only one for loop and the sendBuffer command should be the last command in the body of the for loop.

Oliver

Hmm, i had used two for loops in the other sensor program so I figured i could here as well, to test the conditional statement and either clear and fill the buffer or to send it depending on timing with mills(). What other conditional statement might be better? Ive looked into others but with my inexperience im not too sure.

Draw a flow diagram with pencil and paper.
Hand trace the program flow with your pencil.

This will show you where it might get stuck.
But most importantly it will show you how to organise the logic efficiently.

As a general rule you run through the main loop. Only read sensors at a sensible frequency. Only update a display when something has actually changed.

David.

Hmm, alright. Before I thought you had meant trace the flow in what I had already written. With that in mind, I'll start from scratch, and write out a preliminary flow diagram, then I'll try to rewrite from the ground up. Thanks!

An equally good exercise. This would explain your current program behaviour.

Drawing a fresh flow diagram is part of your design process in any NEW project. You do this before you touch the keyboard to write the code. It means that everything will work first time !!!

David.

Yeah, I can certainly understand that, and I know my practices are messy (to say the least), there are just other certain key things I was confused about beforehand.

Now I'm seeing a little more progress. I was mistaken in thinking the for statement could be used like multiple if statements (and I'm still a bit confused how I managed to do that in my other program) but now Ive simply restructured it using two if statements to compare the timing of the previousMillis to either a screen or sensor interval, and either clear and fill the buffer or send it accordingly. Now I'm actually seeing the correct printout to my screen, however the temperature is wildly inaccurate and the screen doesn't seem to be refreshing at all. I'm sure the problem just lies in my conditional statements, so I'm just going to give the timing a closer look and see if it can be fixed by adjusting that. But again, if I sit down and give it a proper flow diagram before starting as well I'm sure this will be more apparent. Thanks!

Okay, sorry, but im still stuck. Here is my updated code, rewritten from scratch. As of right now, the appropriate readouts are displaying on the screen (Ambient and object temperatures in both F and C), but the temperatures are in the thousands, and dont change regardless of whats in front of the sensor.

#include <Wire.h>
#include <Adafruit_MLX90614.h>

#include <Arduino.h>
#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

Adafruit_MLX90614 mlx = Adafruit_MLX90614();

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

const long screenInterval = 1000;           // interval at which to buffer (milliseconds)
unsigned long sensorInterval = 500;
unsigned long previousMillis;

void setup() {
  mlx.begin();
  u8g2.begin();
  u8g2.enableUTF8Print();
}

void loop() {
  if (millis() - previousMillis >= sensorInterval) {
    previousMillis = millis();
    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_5x7_tf);
    u8g2.setCursor(0, 15);
    u8g2.print("Ambient = "); u8g2.print(mlx.readAmbientTempC());
    u8g2.setCursor(0, 25);
    u8g2.print("*C\tObject = "); u8g2.print(mlx.readObjectTempC()); u8g2.println("*C");
    u8g2.setCursor(0, 35);
    u8g2.print("Ambient = "); u8g2.print(mlx.readAmbientTempF());
    u8g2.setCursor(0, 45);
    u8g2.print("*F\tObject = "); u8g2.print(mlx.readObjectTempF()); u8g2.println("*F");
    u8g2.println();
  }
  if (millis() - previousMillis >= screenInterval / 4) {
    u8g2.sendBuffer();
  }
}

As for my logic, I simply created three objects above my setup, one for the sensorInterval, one for screenInterval, and one for the previousMillis to use for comparison.

In my first if statement, I test if the previousMillis value subtracted from the current millis is greater than or equal to the interval, and if so to clear the screen then update the sensor values, as well as update the previous time to the current mills on the clock.

Then, in my second if statement, i use a similar conditional but divide the screenInterval value by 4 (as it is initially a 1000 ms timer, it makes that 250), which is a value smaller than that of the sensorInterval, ensuring this will happen before the previousMillis is updated.

Will keep trying to figure it out, but heres where I am now. Thanks!

Go on. Hand trace the flow.

That is what your pencil is for. Draw arrows to show how it works.

If you don't own a pencil, go out and BUY one. The ones with an eraser on the blunt end are most convenient.

Imaginary arrows do not work as well as real-life pencil strokes.
Pens are too permanent. You tend to draw imaginary arrows which misses the whole purpose.

David.

At this point I have tried hand tracing the flow, but unfortunately it hasn’t left me any less confused. I was thinking maybe it was getting stuck updating the previous time before the other if statement could be met, so I even tried placing them on their own separate timers for the sake of clarity but am still getting the same result. Im just not sure how the flow should look well enough for me to know where its going wrong. Here is my current code, with the two separate timers now:

#include <Wire.h>
#include <Adafruit_MLX90614.h>

#include <Arduino.h>
#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

Adafruit_MLX90614 mlx = Adafruit_MLX90614();

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

const long screenInterval = 1000;           // interval at which to buffer (milliseconds)
unsigned long sensorInterval = 500;
unsigned long screenPreviousMillis = 0;
unsigned long sensorPreviousMillis = 0;


void setup() {
  mlx.begin();
  u8g2.begin();
  u8g2.enableUTF8Print();
}

void loop() {
  unsigned long screenCurrentMillis = millis();
  unsigned long sensorCurrentMillis = millis();
  if (sensorCurrentMillis - sensorPreviousMillis >= sensorInterval) {
    sensorPreviousMillis = sensorCurrentMillis;
    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_5x7_tf);
    u8g2.setCursor(0, 15);
    u8g2.print("Ambient = "); u8g2.print(mlx.readAmbientTempC());
    u8g2.setCursor(0, 25);
    u8g2.print("*C\tObject = "); u8g2.print(mlx.readObjectTempC()); u8g2.println("*C");
    u8g2.setCursor(0, 35);
    u8g2.print("Ambient = "); u8g2.print(mlx.readAmbientTempF());
    u8g2.setCursor(0, 45);
    u8g2.print("*F\tObject = "); u8g2.print(mlx.readObjectTempF()); u8g2.println("*F");
    u8g2.println();
  }
  if (screenCurrentMillis - screenPreviousMillis >= screenInterval / 4) {
    screenPreviousMillis = screenCurrentMillis;
    u8g2.sendBuffer();
  }
}

The clearBuffer and the draw commands must always be exectuted together with the sendBuffer command. It does not make sense to put them into different if conditions.

The clearBuffer and the draw commands just render the picture in the uC memory. At this point of time the picture is not yet transfered and not visible on the display. The sendBuffer command will transfer the new picture to the display and only then the picture will be visible.

The sendBuffer command is the required final step to make your new picture visible.

If you do not execute the sendBuffer (because the first if is true and the second is false), then you wasted time in rendering the picture. If you only execute sendBuffer (because the first if is false and the second is true), then you just send an old picture again, which is also wasted time, because there will be no visual change on the display.

Oliver

I still reckon that it is YOUR job to hand-trace your flow diagram.

void loop()
{
    unsigned long sensorCurrentMillis = millis();
    static float oldAmbientC, oldObjectC;                       // retains values
    if (sensorCurrentMillis - sensorPreviousMillis >= sensorInterval) {
        sensorPreviousMillis = sensorCurrentMillis;             // for next read
        float AmbientC = mlx.readAmbientTempC();
        float ObjectC = mlx.readObjectTempC();
        float AmbientF = mlx.readAmbientTempF();
        float ObjectF = mlx.readObjectTempF();
        if (AmbientC != oldAmbientC || ObjectC != oldObjectC) { // ?changed
            oldAmbientC = AmbientC;                             // remember
            oldObjectC = ObjectC;
            u8g2.clearBuffer();                                 // update
            u8g2.setFont(u8g2_font_5x7_tf);
            u8g2.setCursor(0, 15);
            u8g2.print("Ambient = "); u8g2.print(AmbientC);
            u8g2.setCursor(0, 25);
            u8g2.print("*C\tObject = "); u8g2.print(ObjectC); u8g2.println("*C");
            u8g2.setCursor(0, 35);
            u8g2.print("Ambient = "); u8g2.print(AmbientF);
            u8g2.setCursor(0, 45);
            u8g2.print("*F\tObject = "); u8g2.print(ObjectF); u8g2.println("*F");
            u8g2.println();
            u8g2.sendBuffer();                                  // draw the screen
        }
    }
}

Hand-trace my code. e.g. for millis() = 499, 500, 501, 502, ..., 999, 1000, 1001, 1002, ...
Then hand-trace your code for millis() = 499, 500, 501, 502, ..., 999, 1000, 1001, 1002, ...

Untested. I am not very familiar with U8g2lib.

David.

Ah, okay, thanks for clarifying, Oliver.

Its more apparent to me now how I was also just getting my signals crossed in trying to update the sensor details in the same conditional as the clearBuffer, in an attempt to clear the buffer and fill it (as previously stated) then sendBuffer in the second conditional, when it would have made more sense for me to populate the data into custom objects using the mlx library functions using one conditional, then call that using both clear and sendBuffer in the second conditional.

I can also see this same structure pretty clearly in what David just posted above, though in his example he wrote the second conditional statement comparing the screens previous temperature and current temperature and updating, then executing screen commands if it has changed. That also makes much more sense to simplify the code as well, rather than adding a secondary timer for millis(). The next step in this project is also for me to add an analogRead() to measure a pin voltage, so I can see this being applicable for that in much the same way. Thanks!

I have yet to test David's code and will here shortly, but whether or not it works Im still going to try to write my own just to be sure I understand what im doing well enough, and so i can deconstruct/better understand parts of Davids code as well, such as choice of data types (though i will likewise try not to bother too much with more fundamental questions that can be asked elsewhere). Once I test David's code, Ill at least update with whether or not it works, though. Thanks so much for the help!

And as for tracing the logic flow by hand, ive already tried this a few times by now, just kept getting hung up because i was mistaken how it should look/work, but i will sit down with it again with this new code/info and im sure that will be made more apparent. Thanks!

Okay, so I’m back already. Going to try to make this as concise as possible because I tried a few things, so bear with me here if i am a little verbose, sorry in advance.

First, I tried David’s program, which unfortunately yielded the same results (sensor values in the thousands with no change or refresh).

Then, just for the sale of clarity and in order to better understand what I was doing step by step, i went back and revised my program over several steps as I transitioned it closer to David’s program, and also to troubleshoot/try different things along the way because David’s program didnt work.

For the first of my own attempts, i kept both set on their own separate timers and conditional statements, but changed it so first the sensor would populate an object I created, then the clearBuffer, print, and sendBuffer commands would be included in their own separate statement. This yielded different results, except gave me zeros in place of the thousands. Here is that code, just so you can look at what i tried:

void loop() {
  unsigned long sensorCurrentMillis = millis();
  unsigned long screenCurrentMillis = millis();
  if (sensorCurrentMillis - sensorPreviousMillis >= sensorInterval) {
    sensorPreviousMillis = sensorCurrentMillis;
    float AmbientC = mlx.readAmbientTempC();
    float ObjectC = mlx.readObjectTempC();
    float AmbientF = mlx.readAmbientTempF();
    float ObjectF = mlx.readObjectTempF();
  }
  if (screenCurrentMillis - screenPreviousMillis >= screenInterval) {
    screenPreviousMillis = screenCurrentMillis;
    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_5x7_tf);
    u8g2.setCursor(0, 15);
    u8g2.print("Ambient = "); u8g2.print(AmbientC);
    u8g2.setCursor(0, 25);
    u8g2.print("*C\tObject = "); u8g2.print(ObjectC); u8g2.println("*C");
    u8g2.setCursor(0, 35);
    u8g2.print("Ambient = "); u8g2.print(AmbientF);
    u8g2.setCursor(0, 45);
    u8g2.print("*F\tObject = "); u8g2.print(ObjectF); u8g2.println("*F");
    u8g2.println();
    u8g2.sendBuffer();
  }
}

Then, because i noticed in davids program that the second if statement was actually nested in the first (instead of after), i tried to separate the two just for the sake of argument, but that just left the screen blank. here is the code, with the second millis timer replaced by a change in sensor readings:

void loop() {
  if (sensorCurrentMillis - sensorPreviousMillis >= sensorInterval) {
    sensorPreviousMillis = sensorCurrentMillis;
    float AmbientC = mlx.readAmbientTempC();
    float ObjectC = mlx.readObjectTempC();
    float AmbientF = mlx.readAmbientTempF();
    float ObjectF = mlx.readObjectTempF();
  }

  if (AmbientC != oldAmbientC || ObjectC != oldObjectC) { // ?changed
    oldAmbientC = AmbientC;                             // remember
    oldObjectC = ObjectC;
    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_5x7_tf);
    u8g2.setCursor(0, 15);
    u8g2.print("Ambient = "); u8g2.print(AmbientC);
    u8g2.setCursor(0, 25);
    u8g2.print("*C\tObject = "); u8g2.print(ObjectC); u8g2.println("*C");
    u8g2.setCursor(0, 35);
    u8g2.print("Ambient = "); u8g2.print(AmbientF);
    u8g2.setCursor(0, 45);
    u8g2.print("*F\tObject = "); u8g2.print(ObjectF); u8g2.println("*F");
    u8g2.println();
    u8g2.sendBuffer();
  }
}

And lastly, i figured maybe i should still include the conditional checking for temperature change (in case it wasnt updating the sensor), like this:

void loop() {
  unsigned long sensorCurrentMillis = millis();
  unsigned long screenCurrentMillis = millis();
  if (sensorCurrentMillis - sensorPreviousMillis >= sensorInterval) {
    sensorPreviousMillis = sensorCurrentMillis;
    float AmbientC = mlx.readAmbientTempC();
    float ObjectC = mlx.readObjectTempC();
    float AmbientF = mlx.readAmbientTempF();
    float ObjectF = mlx.readObjectTempF();
    if (AmbientC != oldAmbientC || ObjectC != oldObjectC) { // ?changed
      oldAmbientC = AmbientC;                             // remember
      oldObjectC = ObjectC;
    }
  }
  if (screenCurrentMillis - screenPreviousMillis >= screenInterval) {
    screenPreviousMillis = screenCurrentMillis;
    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_5x7_tf);
    u8g2.setCursor(0, 15);
    u8g2.print("Ambient = "); u8g2.print(AmbientC);
    u8g2.setCursor(0, 25);
    u8g2.print("*C\tObject = "); u8g2.print(ObjectC); u8g2.println("*C");
    u8g2.setCursor(0, 35);
    u8g2.print("Ambient = "); u8g2.print(AmbientF);
    u8g2.setCursor(0, 45);
    u8g2.print("*F\tObject = "); u8g2.print(ObjectF); u8g2.println("*F");
    u8g2.println();
    u8g2.sendBuffer();
  }
}

And that gave me the display back, but again just with zeroes. And lastly, that leads me to having my program written just like david’s (which i also copy and pasted, just to be sure in testing it). I have also tried hand tracing the flow of each along the way, but at this point Im still just as confused as to where or why its hanging up. Even hand tracing, im simply too green at this to see whats likely right in front of me, sorry!

I have no idea what your MLX90614 library is or does. I just assumed it was something designed for primitive countries that measure in bushels and farenheit.

Yes, my example will fail if your Sensor always returns 0. Because oldAmbientC is initialised to 0, it will not register as a "changed" value that needs to update the screen.

In this situation you normally initialize a static to an impossible value. Then the conditional will succeed on the first pass.

It is your job to read your Adafruit_MLX90614 docs. Does it return a temperature in float or integer? Does it use Farenheit in tenths or hundredths?

The first job is to see what the class methods are actually returning. e.g. print to Serial

David.