Help using DO WHILE to make sure temperature reading is "normal"

Greetings!

I’m a relatively new Arduino user and I have managed to code one of my first projects, which is a simple temperature sensor that is continuously called and displayed to an OLED screen. In order to smooth out the readings, I have created a buffer that holds the last 10 readings and every 6 seconds, a new reading is added, the last one is dropped, and the screen updates with the average of the 10 readings. This helps to minimize the jumpy nature of the sensor.

However, I have a problem where my sensor occasionally throws a far off reading like 1600 degrees. I would like to handle this issue by reading the sensor and checking that the reading is “normal” before adding it to the buffer. If it’s not normal, then I want to wait something like 1 second and read the sensor again until the reading is normal. I think “normal” should be anywhere from -35 to 135 degrees.

I think the best way to do this would be to use the DO WHILE function, but I’m not sure of correct way to incorpoate this into my VOID LOOP. My first try resulted in the OLED sensor getting hung up on the “Calibrating Sensor” screen within the SETUP LOOP.

How can I modify my code to check if the sensor reading is between -35 and 135 before updating the buffer?

#include "U8glib.h"
#include <OneWire.h> 

int DS18S20_Pin = 4; //DS18S20 Signal pin on digital 
OneWire ds(DS18S20_Pin); // Temperature chip i/o on digital pin 2
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NO_ACK); // I2C / TWI

const int numReadings = 10;     // the number of samples to keep track of
int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average


void setup() {  
  // put your setup code here, to run once:

  // flip screen, if required
  //u8g.setRot180();

  // draw splash screen
  drawSplash();
 
  // show splash screen for a 5 seconds
  delay(5000);

  // take in initial reading of the sensor to throw out
  float temp = getTemp() * 9/5 + 32;

  // initialize all the readings to the current temp:
  temp = getTemp() * 9/5 + 32;
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
}


void loop(void) {
  // put your main code here, to run repeatedly:
 
  // subtract the last reading:
  total = total - readings[readIndex];
  
  // read from the sensor:
  readings[readIndex] = getTemp() * 9/5 + 32;
  
  // add the reading to the total:
  total = total + readings[readIndex];
  
  // advance to the next position in the array:
  readIndex = readIndex + 1;

  // if we're at the end of the array...
  if (readIndex >= numReadings) {
    // ...wrap around to the beginning:
    readIndex = 0;
  }

  // calculate the average:
  average = total / numReadings;
  
  // update display
  u8g.firstPage();
    do {  
        //u8g.setFont(u8g_font_unifont);
        //u8g.drawStr( 0, 15, "Temperature:");   
        u8g.setFont(u8g_font_fub30);
        u8g.setPrintPos(15,50);
        u8g.print(average);
        u8g.print("\xb0 F");
       } while( u8g.nextPage() );
    delay(6000); 

}

float getTemp(){
 //returns the temperature from one DS18S20 in DEG Celsius

 byte data[12];
 byte addr[8];

 if ( !ds.search(addr)) {
   //no more sensors on chain, reset search
   ds.reset_search();
   return -1000;
 }

 if ( OneWire::crc8( addr, 7) != addr[7]) {
   Serial.println("CRC is not valid!");
   return -1000;
 }

 if ( addr[0] != 0x10 && addr[0] != 0x28) {
   Serial.print("Device is not recognized");
   return -1000;
 }

 ds.reset();
 ds.select(addr);
 ds.write(0x44,1); // start conversion, with parasite power on at the end

 byte present = ds.reset();
 ds.select(addr);  
 ds.write(0xBE); // Read Scratchpad

 
 for (int i = 0; i < 9; i++) { // we need 9 bytes
  data[i] = ds.read();
 }
 
 ds.reset_search();
 
 byte MSB = data[1];
 byte LSB = data[0];

 float tempRead = ((MSB << 8) | LSB); //using two's compliment
 float TemperatureSum = tempRead / 16;
 
 return TemperatureSum;
 
}

void drawSplashScreen(){
    u8g.setFont(u8g_font_profont15); 
    u8g.drawStr(25,23, "Calibrating");
    u8g.drawStr(40,43, "Sensor");
    u8g.drawFrame(2,2,124,60);
    u8g.drawFrame(4,4,120,56);    
}

void drawSplash(){
      // picture loop for splash screen
    u8g.firstPage();  
    do {
      drawSplashScreen();
    } while( u8g.nextPage() );
}

I'm not sure I follow the logic of using a do while. Why not just an if. IF the temp is wrong, don't log it and try again on the next pass through loop.

Delta_G:
I’m not sure I follow the logic of using a do while. Why not just an if. IF the temp is wrong, don’t log it and try again on the next pass through loop.

Good point. My thought was that when a false reading was encountered, it would continue to retry until it got a valid reading. However, the solution you indicated is much more simple and easy to implement. Thank you.

In case anyone is searching for this same thing later, here is how I only captured valid sensor readings.

#include "U8glib.h"
#include <OneWire.h> 

int DS18S20_Pin = 4; //DS18S20 Signal pin on digital 
OneWire ds(DS18S20_Pin); // Temperature chip i/o on digital pin 2
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NO_ACK); // I2C / TWI

const int numReadings = 10;     // the number of samples to keep track of
int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average


void setup() {  
  // draw splash screen
  drawSplash();
 
  // show splash screen for a 5 seconds
  delay(5000);

  // take in initial reading of the sensor to throw out
  float temp = getTemp() * 9/5 + 32;

  // initialize all the readings to the current temp:
  temp = getTemp() * 9/5 + 32;
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
}


void loop(void) {
  // subtract the last reading:
  total = total - readings[readIndex];
  
  // read from the sensor:
  readings[readIndex] = getTemp() * 9/5 + 32;

  // check if reading is normal
  if((readings[readIndex] > -35) && (readings[readIndex] < 135)){
  
    // add the reading to the total:
    total = total + readings[readIndex];
  }
  
  // advance to the next position in the array:
  readIndex = readIndex + 1;

  // if we're at the end of the array...
  if (readIndex >= numReadings) {
    // ...wrap around to the beginning:
    readIndex = 0;
  }

  // calculate the average:
  average = total / numReadings;
  
  // update display
  u8g.firstPage();
    do {  
        //u8g.setFont(u8g_font_unifont);
        //u8g.drawStr( 0, 15, "Temperature:");   
        u8g.setFont(u8g_font_fub30);
        u8g.setPrintPos(15,50);
        u8g.print(average);
        u8g.print("\xb0 F");
       } while( u8g.nextPage() );
    delay(6000); 

}

float getTemp(){
 //returns the temperature from one DS18S20 in DEG Celsius

 byte data[12];
 byte addr[8];

 if ( !ds.search(addr)) {
   //no more sensors on chain, reset search
   ds.reset_search();
   return -1000;
 }

 if ( OneWire::crc8( addr, 7) != addr[7]) {
   Serial.println("CRC is not valid!");
   return -1000;
 }

 if ( addr[0] != 0x10 && addr[0] != 0x28) {
   Serial.print("Device is not recognized");
   return -1000;
 }

 ds.reset();
 ds.select(addr);
 ds.write(0x44,1); // start conversion, with parasite power on at the end

 byte present = ds.reset();
 ds.select(addr);  
 ds.write(0xBE); // Read Scratchpad

 
 for (int i = 0; i < 9; i++) { // we need 9 bytes
  data[i] = ds.read();
 }
 
 ds.reset_search();
 
 byte MSB = data[1];
 byte LSB = data[0];

 float tempRead = ((MSB << 8) | LSB); //using two's compliment
 float TemperatureSum = tempRead / 16;
 
 return TemperatureSum;
 
}

void drawSplashScreen(){
    u8g.setFont(u8g_font_profont15); 
    u8g.drawStr(25,23, "Calibrating");
    u8g.drawStr(40,43, "Sensor");
    u8g.drawFrame(2,2,124,60);
    u8g.drawFrame(4,4,120,56);    
}

void drawSplash(){
      // picture loop for splash screen
    u8g.firstPage();  
    do {
      drawSplashScreen();
    } while( u8g.nextPage() );
}

Rather than just throwing away bad data, you really need to investigate how the bad data values are being generated. I can't remember ever seeing a mention of a temperature as high as 1600F being read from a DS18S20 or DS18B20. One thing you definitely need to fix is that you aren't checking for error returns from getTemp(). There are three situations in which it will return -1000 (one of which shouldn't happen). The problem is that although you don't add this value to the total, you do store it in readings[readIndex]. Later on it will come time to subtract this value from the total which will end up adding 1000 to it which will blow the average out of the water :) If you don't add a value to total, you must not store it in readings[readIndex] and also you must not increment readIndex.

Pete

P.S. You haven't described how your project is wired. The bad readings could be due to a bad connection.

Pete

el_supremo:
Rather than just throwing away bad data, you really need to investigate how the bad data values are being generated. I can’t remember ever seeing a mention of a temperature as high as 1600F being read from a DS18S20 or DS18B20.
One thing you definitely need to fix is that you aren’t checking for error returns from getTemp(). There are three situations in which it will return -1000 (one of which shouldn’t happen). The problem is that although you don’t add this value to the total, you do store it in readings[readIndex]. Later on it will come time to subtract this value from the total which will end up adding 1000 to it which will blow the average out of the water :slight_smile:
If you don’t add a value to total, you must not store it in readings[readIndex] and also you must not increment readIndex.

Pete

Pete – Thank you very much for your response. I am running the code on an Arduino Nano in my vehicle. I am getting 12v from the cigarette lighter, and using a buck converted to decrease the voltage to ~4v to power the Arduino. The wires for the temperature probe snake though the interior of my car and poke out below the car.

Regarding your point on not storing the bad values and increment the counter…did I mention that I’m new to this Arduino programming?? Haha!!

I agree with you that I would like to figure out what is causing the bad readings, but I’m not sure where to start. The majority of the time, the sensor reads fine, but every one in awhile, I get a wacky reading. The problem might come from the fact that this is installed in my car, and the voltage is variable.

If you don’t mind, check out the updates that I made and let me know what you think.

void loop() {

  // read sensor and convert to F
  temp = getTemp() * 9/5 + 32;

  // check if reading is within normal range
  if ((temp > -35.0) && (temp < 135.0)){

    // subtract the last reading
    total = total - readings[readIndex];

    // add the new reading to the total
    readings[readIndex] = temp;

    // advance to the next position in the array:
    readIndex = readIndex + 1;

    // if we're at the end  of the array, wrap around to the beginning
    if (readIndex >= numReadings) {
      readIndex = 0;
    }

    // calculate the average:
    average = total / numReadings;
  
    // update display
    u8g.firstPage();
      do {  
          u8g.setFont(u8g_font_fub30);
          u8g.setPrintPos(15,50);
          u8g.print(average);
          u8g.print("\xb0 F");
         } while( u8g.nextPage() );
    
    // wait 6 seconds
    delay(6000);    
  }

  else
  {
    // wait 0.5 seconds and try again for a normal reading
    delay(500);
  }

}

The modified code looks good.

Do you have a 4.7k pullup resistor on the DS18S20 data pin?

It sounds like the wire to the DS18S20 snakes a fair distance from the NANO, in which case it might be advisable to reduce the pullup resistor. Maybe try 2.2k. A vehicle is a rather noisy environment to have long wires. Do you have a capacitor on the input and output of the buck converter?

Pete

I do have a 4.7k resistor. I agree about the noisy environment. After a lot of reading, it seemed like the buck converter was maybe not perfect for the situation, but the best way for me to handle it. I do not have any additional capacitors installed. Do you think that might help?

Hi,

I am getting 12v from the cigarette lighter, and using a buck converted to decrease the voltage to ~4v to power the Arduino. The wires for the temperature probe snake though the interior of my car and poke out below the car.

Why 4V?

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Thanks.. Tom... :)