Photon MCP9808 periodically wrong temperature meassuring

Dear Forum,

in my setup based on a Photon, OLED and MCP9808 (from Adafruit) I’m measuring temperature to show on the OLED and also send a http GET to my own database. Everythings works good. But periodically and irregular it measures 1,8C. Before it was room temperature by 22C. I need to rest the Photo to get correct values again. I’m using the MCP9808 lib out of the Web IDE.

Does anyone had the same issue? What can be the root cause?

Thank you in advance for any response!

@r-morgenstern, the MCP9808 library on the IDE is a heavily modified version of the Adafruit library. Posting your code might help us better advise you.

@peekay123 Thank you for offering you help. Following my program. Looking forward to your advise.

// OLED
  #include "Adafruit_mfGFX.h"
  #include "Adafruit_SSD1306_mfGFX.h"
  #define OLED_RESET D4
  Adafruit_SSD1306 display(OLED_RESET);

// MCP9808
  #include "MCP9808.h"
  MCP9808 mcp = MCP9808();
  #define temp_iterations               12
  float   temp_avarage                  = 0;
  float   temp_values[temp_iterations]  = {0};

// TCP client
  #include "HttpClient.h"
  HttpClient http;

  // Headers currently need to be set at init, useful for API keys etc.
  http_header_t headers[] = {
    { "Content-Type" , "application/x-www-form-urlencoded" },
    { "Accept" , "*/*"},
    { NULL, NULL } // NOTE: Always terminate headers will NULL
  };
  http_request_t request;
  http_response_t response;

// Data Server Configuration
  #define data_server       "data....."
  #define data_port         80
  #define data_path         "/v1/"
  #define data_feedID       "temp_office"
  #define transmit_interval 150000UL        // 150000UL = 2,5min

  // send_data notification (position on screen)
  #define y_offset          3  // 3 for top
  unsigned int x_offset     = 62; // 124 for right

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

  Time.zone(+1);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)

  display.clearDisplay();   // clears the screen and buffer
  display.display();
  display.setTextColor(WHITE);

  while(! mcp.begin()){
    display.println("MCP9808 not found");
    display.display();
    delay(500);
  }

  mcp.setResolution(MCP9808_SLOWEST);
  display.println("MCP9808 OK");

  // initialize temp value array with current Temperature
  float temp = round(mcp.getTemperature()*10)/10;
  for (byte i=0; i < temp_iterations; i++) {
    temp_values[i] = temp;
  }

  display.display();
  delay(500);

  display.clearDisplay();   // clears the screen and buffer
  //display.drawFastHLine( 0, 10, 127, WHITE);
  display.display();

}

// LOOP
void loop() {

  update_wifi();
  update_time();
  update_temp();
  send_data();

}

void update_wifi() {

  static unsigned long previousMillis_update_wifi;

  if ((unsigned long) (millis() - previousMillis_update_wifi) > 5000UL) {
    previousMillis_update_wifi = millis();

    display.fillRect(0,0,100,10,BLACK);                    // clear area of signal streght and network

    // checking if connected
    if (WiFi.ready()){
      // display singal strength
      int wifi_rssi = WiFi.RSSI();
      int wifi_quality;

      wifi_quality = map(wifi_rssi, -1, -127, 100, 0);

      if (wifi_quality > 0) {
        display.drawFastVLine( 0, 6, 2, WHITE);
        display.drawFastVLine( 1, 6, 2, WHITE);
        display.setCursor(5,0);
      }
      if (wifi_quality >= 20) {
        display.drawFastVLine( 3, 5, 3, WHITE);
        display.drawFastVLine( 4, 5, 3, WHITE);
        display.setCursor(8,0);
      }
      if (wifi_quality >= 40) {
        display.drawFastVLine( 6, 4, 4, WHITE);
        display.drawFastVLine( 7, 4, 4, WHITE);
        display.setCursor(11,0);
      }
      if (wifi_quality >= 60) {
        display.drawFastVLine( 9, 3, 5, WHITE);
        display.drawFastVLine(10, 3, 5, WHITE);
        display.setCursor(14,0);
      }
      if (wifi_quality >= 80) {
        display.drawFastVLine(12, 2, 6, WHITE);
        display.drawFastVLine(13, 2, 6, WHITE);
        display.setCursor(17,0);
      }

      display.setFont(SansSerif_6);
      display.print(WiFi.SSID());
    }
    else {
      display.setCursor(0,0);
      display.setFont(SansSerif_6);
      display.print("no WiFi");
    }

    display.display();

  }
}

void update_time() {

  static unsigned long previousMillis_update_time;
  static boolean second_blink;

  if ((unsigned long) (millis() - previousMillis_update_time) > 1000UL) {
    previousMillis_update_time = millis();

    display.setFont(SansSerif_6);

    String time_out   = String(Time.hour());
    String minute_out = "";
    String hour_out   = time_out;

    time_out += ":";

    int min= Time.minute();
    if (min<10) {
      time_out  += "0" + String(min);
      minute_out = "0" + String(min);
    }
    else {
      time_out  += String(min);
      minute_out = String(min);
    }

    // elaborate the width of the time_out
    int time_out_width = 0;
    for(int i = 0; time_out[i] != '\0'; i++) {
      time_out_width += display.charWidth(time_out[i]);
    }

    // set data send notification before time
    x_offset = 120 - time_out_width;

    display.fillRect(100,0,128,10,BLACK);           // clear area of time
    display.setCursor(127 - time_out_width,0);
    display.print(hour_out);
    if (second_blink) {
      second_blink = false;
      display.print(":");
    } else {
      second_blink = true;
      display.setTextColor(BLACK);
      display.print(":");
      display.setTextColor(WHITE);
    }
    display.print(minute_out);
    display.display();
  }

}

void update_temp(){

  static unsigned long previousMillis_update_temp;
  if ((unsigned long) (millis() - previousMillis_update_temp) > 5000UL) {
    previousMillis_update_temp = millis();

    // temp values shiften
    for (byte x = 0; x < temp_iterations-1; x++) {
      temp_values[x] = temp_values[x+1];
    }
    temp_values[temp_iterations-1] = round(mcp.getTemperature()*10)/10;

    // calculate avarage
    temp_avarage = 0;
    for (byte x=0; x < temp_iterations; x++){
      temp_avarage = temp_avarage + temp_values[x];
    }
    temp_avarage = round((temp_avarage / temp_iterations)*10)/10;

    display.fillRect(0,10,128,55,BLACK);
    display.setFont(SansSerif_24);

    // elaborate the width of the Temp. output for horizontal centering
    String temp_out(temp_avarage, 1);
    temp_out = temp_out.replace(".", ",");

    int text_width = 0;
    for(int i = 0; temp_out[i] != '\0'; i++) {
      text_width += display.charWidth(temp_out[i]);
    }

    String temp_out2 = "~C";
    int text_width2 = 0;
    for(int i = 0; temp_out[i] != '\0'; i++) {
      text_width2 = text_width2 + display.charWidth(temp_out2[i]);
    }
    text_width2 = text_width2 - 7; // fix for wrong width of °
    int text_width_complete = text_width + text_width2 + 4; // // add a extra space between value and °C

    display.setCursor( (128 - text_width_complete)/2 ,24);
    display.print(temp_out);
    display.setCursor( ((128 - text_width_complete)/2)+text_width+4 ,24);
    display.print(temp_out2);

    display.display();

  }
}


void send_data() {

  static unsigned long previousMillis_send_time;

  if ((unsigned long) (millis() - previousMillis_send_time) > transmit_interval) {
    previousMillis_send_time = millis();

    //display.drawPixel(124, 3, WHITE); display.display();
    display.drawCircle(x_offset, y_offset, 3, WHITE); display.display();

    request.hostname = data_server;
    request.port = data_port;
    request.path = data_path;
    request.body = "feedID=";
    request.body += data_feedID;
    request.body += "&value1=";
    request.body += String(temp_avarage,1);
	  request.body += "\r\n";

    http.post(request, response, headers); // POST request

    // show HTTP response on display
    /*display.clearDisplay();
    display.setCursor(0,10);
    display.setFont(SansSerif_6);
    display.print("Response status: ");
    display.println(response.status);
    display.display();

    display.println("HTTP Response Body: ");
    display.println(response.body);
    display.display();
    delay(5000);*/

    if (response.status == 200){
      display.fillCircle(x_offset, y_offset, 3, WHITE); display.display();  // draw dot
      delay(500);
      display.fillCircle(x_offset, y_offset, 3, BLACK); display.display();  // clear dot
    }
    else {
      display.fillCircle(x_offset, y_offset, 3, BLACK);  // clear dot
      display.drawLine(x_offset -3, y_offset +3, x_offset +3, y_offset, WHITE); //x1=121 x2=127
      display.drawLine(x_offset -3, y_offset, x_offset +3, y_offset+6, WHITE);
      display.display();
      delay(500);
      display.fillRect(x_offset -3, 0, x_offset +3, 9,BLACK); //x1=110 x2=128
      display.display();
    }

  }
}

@r-morgenstern, I’ll have some suggestions later. In the meantime, can you post a picture of what the display looks like with data on it?

Here it is…

@r-morgenstern, your update time is overly complex. Take a look at the Particle Time section of the docs including Time.format() to simplify your code considerably.

You may want to consider writing spaces to erase your text instead of writing a black rectangle. Your setup() seems to have a LOT of display.display() calls that won’t do anything.

I’m looking into other ways to speed things up.

Wait... Doesn't iteration normally mean how many different types or times to repeat? Does this mean after the temperature changes for the 13th time something goes whack?
This is a total shot in the dark btw... I'm am a complete NOOB offering my 2 cents.

@Kurticus, in this case, the software takes the last 12 readings in a FIFO and averages them out for the displayed value. As written, instead of using a circular buffer, the author chose to shift the values in a fixed array, taking more time than it should. A circular buffer simply has pointers to the “start” and “end” values of the buffer and just changing the pointers is necessary.

1 Like

@peekay123 Thank you for the explanation. :smile:

1 Like

@peekay123 Thank you for reviewing my code and you suggestions.

I updated the program by slim fasten setup routine, implementation of the circular buffer and the optimized update_time function. You will find the program following. Do you think I will solve the problem? What is the relationship to the wrong values coming from MCP9808?

// OLED
  #include "Adafruit_mfGFX.h"
  #include "Adafruit_SSD1306_mfGFX.h"
  #define OLED_RESET D4
  Adafruit_SSD1306 display(OLED_RESET);

// MCP9808
  #include "MCP9808.h"
  MCP9808 mcp = MCP9808();
  #define temp_iterations               12
  float   temp_avarage                  = 0;
  float   temp_values[temp_iterations]  = {0};
  int     temp_value_pointer            = 0;

// TCP client
  #include "HttpClient.h"
  HttpClient http;

  // Headers currently need to be set at init, useful for API keys etc.
  http_header_t headers[] = {
    { "Content-Type" , "application/x-www-form-urlencoded" },
    { "Accept" , "*/*"},
    { NULL, NULL } // NOTE: Always terminate headers will NULL
  };
  http_request_t request;
  http_response_t response;

// Data Server Configuration
  #define data_server       "data.xxx"
  #define data_port         80
  #define data_path         "/v1/"
  #define data_feedID       "temp_office"
  #define transmit_interval 150000UL // 150000UL = 2,5min

  // send_data notification (position on screen)
  #define y_offset          3  // 3 for top
  unsigned int x_offset     = 62; // 124 for right

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

  Time.zone(+1);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)

  display.clearDisplay();   // clears the screen and buffer
  display.setTextColor(WHITE);

  while(! mcp.begin()){
    display.println("MCP9808 not found");
    display.display();
    delay(500);
  }

  mcp.setResolution(MCP9808_SLOWEST);
  display.println("MCP9808 OK");
  display.display();

  // initialize temp value array with current Temperature
  float temp = round(mcp.getTemperature()*10)/10;
  for (byte i=0; i < temp_iterations; i++) {
    temp_values[i] = temp;
  }

  delay(500);

  display.clearDisplay();   // clears the screen and buffer
  display.display();

}

// LOOP
void loop() {

  update_wifi();
  update_time();
  update_temp();
  send_data();

}

void update_wifi() {

  static unsigned long previousMillis_update_wifi;

  if ((unsigned long) (millis() - previousMillis_update_wifi) > 5000UL) {
    previousMillis_update_wifi = millis();

    display.fillRect(0,0,100,10,BLACK);                    // clear area of signal streght and network

    // checking if connected
    if (WiFi.ready()){
      // display singal strength
      int wifi_rssi = WiFi.RSSI();
      int wifi_quality;

      wifi_quality = map(wifi_rssi, -1, -127, 100, 0);

      if (wifi_quality > 0) {
        display.drawFastVLine( 0, 6, 2, WHITE);
        display.drawFastVLine( 1, 6, 2, WHITE);
        display.setCursor(5,0);
      }
      if (wifi_quality >= 20) {
        display.drawFastVLine( 3, 5, 3, WHITE);
        display.drawFastVLine( 4, 5, 3, WHITE);
        display.setCursor(8,0);
      }
      if (wifi_quality >= 40) {
        display.drawFastVLine( 6, 4, 4, WHITE);
        display.drawFastVLine( 7, 4, 4, WHITE);
        display.setCursor(11,0);
      }
      if (wifi_quality >= 60) {
        display.drawFastVLine( 9, 3, 5, WHITE);
        display.drawFastVLine(10, 3, 5, WHITE);
        display.setCursor(14,0);
      }
      if (wifi_quality >= 80) {
        display.drawFastVLine(12, 2, 6, WHITE);
        display.drawFastVLine(13, 2, 6, WHITE);
        display.setCursor(17,0);
      }

      display.setFont(SansSerif_6);
      display.print(WiFi.SSID());
    }
    else {
      display.setCursor(0,0);
      display.setFont(SansSerif_6);
      display.print("no WiFi");
    }

    display.display();
  }
}

void update_time() {

  static unsigned long previousMillis_update_time;
  static boolean second_blink;

  if ((unsigned long) (millis() - previousMillis_update_time) > 1000UL) {
    previousMillis_update_time = millis();

    display.setFont(SansSerif_6);

    String time_out = Time.format(Time.now(), "%H:%M");

    // elaborate the width of the time_out
    int time_out_width = 0;
    for(int i = 0; time_out[i] != '\0'; i++) {
      time_out_width += display.charWidth(time_out[i]);
    }

    // set data send notification before time
    x_offset = 120 - time_out_width;

    display.fillRect(100,0,128,10,BLACK);           // clear area of time
    display.setCursor(127 - time_out_width,0);
    display.print(time_out);
    display.display();  
  }
}

void update_temp(){

  static unsigned long previousMillis_update_temp;
  if ((unsigned long) (millis() - previousMillis_update_temp) > 5000UL) {
    previousMillis_update_temp = millis();

    // store current temperature in circular buffer for averaging
    temp_value_pointer ++;
    if (temp_value_pointer == temp_iterations-1) {
      temp_value_pointer = 0;
    }
    temp_values[temp_value_pointer] = round(mcp.getTemperature()*10)/10;

    // calculate avarage
    temp_avarage = 0;
    for (byte x=0; x < temp_iterations; x++){
      temp_avarage = temp_avarage + temp_values[x];
    }
    temp_avarage = round((temp_avarage / temp_iterations)*10)/10;

    display.fillRect(0,10,128,55,BLACK);
    display.setFont(SansSerif_24);

    // elaborate the width of the Temp. output for horizontal centering
    String temp_out(temp_avarage, 1);
    temp_out = temp_out.replace(".", ",");

    int text_width = 0;
    for(int i = 0; temp_out[i] != '\0'; i++) {
      text_width += display.charWidth(temp_out[i]);
    }

    String temp_out2 = "~C";
    int text_width2 = 0;
    for(int i = 0; temp_out[i] != '\0'; i++) {
      text_width2 = text_width2 + display.charWidth(temp_out2[i]);
    }
    text_width2 = text_width2 - 7; // fix for wrong width of °
    int text_width_complete = text_width + text_width2 + 4; // // add a extra space between value and °C

    display.setCursor( (128 - text_width_complete)/2 ,24);
    display.print(temp_out);
    display.setCursor( ((128 - text_width_complete)/2)+text_width+4 ,24);
    display.print(temp_out2);

    display.display();
   }
}


void send_data() {

  static unsigned long previousMillis_send_time;

  if ((unsigned long) (millis() - previousMillis_send_time) > transmit_interval) {
    previousMillis_send_time = millis();

    display.drawCircle(x_offset, y_offset, 3, WHITE); display.display();

    request.hostname = data_server;
    request.port = data_port;
    request.path = data_path;
    request.body = "feedID=";
    request.body += data_feedID;
    request.body += "&value1=";
    request.body += String(temp_avarage,1);
	request.body += "\r\n";

    http.post(request, response, headers); // POST request


    if (response.status == 200){
      display.fillCircle(x_offset, y_offset, 3, WHITE); display.display();  // draw dot
      delay(500);
      display.fillCircle(x_offset, y_offset, 3, BLACK); display.display();  // clear dot
    }
    else {
      display.fillCircle(x_offset, y_offset, 3, BLACK);  // clear dot
      display.drawLine(x_offset -3, y_offset +3, x_offset +3, y_offset, WHITE); //x1=121 x2=127
      display.drawLine(x_offset -3, y_offset, x_offset +3, y_offset+6, WHITE);
      display.display();
      delay(500);
      display.fillRect(x_offset -3, 0, x_offset +3, 9,BLACK); //x1=110 x2=128
      display.display();
    }

  }
}

@r-morgenstern, besides the optimization, I believe the issue with getting the occasional 1.8 degrees value may have nothing to do with the MCP9808 but with the calculation instead. Have you confirmed the non-average values of the temperature are actually 1.8 degrees? If zero or near-zero temperatures are getting placed in the circular buffer, the calculated average will be low. You need to display the “on sample” temperature values (or print your array to Serial) to see what the sensor is actually providing to the circular buffer.

That is something I thought as well @peekay123 . I already checked the “raw” values coming from MCP9808 ( mcp.getTemperature() ) and when the issue occurs they are 1.8. Also I was able see the behavior of the averaged temperature on the OLED in that case - every 5 sec. (update interval) die temperature become lower till it is 1.8 (60sec - 12 intervals each 5sec).

@r-morgenstern, ok so now it comes down to issues with the MPC9808. One quick way to solve the issue is to add a filter which check to see if a new reading is different from the previous or average reading by more than a certain amount. If it is, you simply toss that reading.

Yes, that can be a workaround if the 1.8 occurs once. But in my case it stay by 1.8 and need to restart the Photon. Is there is a away to reset by software?

@r-morgenstern, yup…System.reset()

It seems the MCP9808 is locking up for some reason. This is what needs to be looked into.

Do you have more than one MCP9808 to try? I just got one yesterday, and am testing it along with a BME280, and haven’t seen any problems. I wonder if you have a defective unit.

1 Like

@r-morgenstern, here is an interesting bit regarding a “fixed” output on the MCP9808 from the Arduino world:

https://forums.adafruit.com/viewtopic.php?f=22&t=72294

Might be worth investigating.