BME280 random incorrect readings

I'm using a BME280 to get Temperature and Humidity and most of the time it works. But as you can see on the graph, sometimes I get a zero degree C reading. while temperature is about 23 degrees.


Is there something wrong with the sensor or the code?

I guess I could make a function that tests if it gets 0 multiple times and only accept the reading of zero if it repeats, otherwise do a new reading. I would like it if this was not necessary though.

Such events happen with many sensors. There can be communication errors, timing errors, etc.

Your code could recognize and reject clear outliers. Some sensors respond with NAN (not a number) but if not, one approach is to reject a measurement that clearly deviates from the trend of the last few previous measurements. Or, also take the next measurement into consideration.

Noise. Just reject any reading that is that far out. Wild points happen. Could be a power blip or an ant walking across the sensor.

You wouldn't happen to have a code example for an average reading and that cancels out zero values if they don't repeat?

I'm using:
Adafruit_Sensor.h
Adafruit_BME280.h

And readings directly from:

bme.readTemperature()
bme.readHumidity()
(bme.readPressure() / 100.0F)

I typically use a low pass filter (an "exponential filter") to calculate a moving average, and some predetermined threshold value (e.g. three standard deviations from the mean) to reject outliers.

Example of decision making process:

float new_value = read_sensor();
if (fabs(new_value - moving_average) < threshold) update_moving_average();

example of low pass filter:

float alpha = 0.1; //adjustable 
... in update_moving_average()
moving_average = (1-alpha)*moving_average + alpha*new_value;

Note that the latter can be more efficiently calculated as
moving_average = moving_average + alpha*(new_value-moving_average);

For extreme outliers, a "median filter" is very useful. Look it up.

Would this work to some extent?

float alpha = 0.1; //adjustable 

void sensorData() {
float readTemp = bme.readTemperature();
if (fabs?(readTemp - movingAvg) < threshold?) updateMovingAvg();
}

void updateMovingAvg() {
movingAvg = movingAvg + alpha*(readTemp - movingAvg);
}

void loop() {
    if (true) {
        sensorData()
        updateMovingAvg() 
        Serial.println(movingAvg);
    }
}

I'm a newbie coder, lol.

No, not at all, and that code won't compile without many fatal errors. It looks like you are making code up with little to no idea what it does.

One serious problem is that variables must be defined such that they can either be seen by all functions, or values passed between the functions.

You need to start at the beginning and learn more about the coding language. A reasonable place to start would be to carefully study the simple examples that come with Arduino, and on line language tutorials. There are many of those, and some are really good.

I kinda can read code, just not very good at writing new stuff, lol
I find it challenging to learn too, so it's a bit annoying.
Keep forgetting simple things.

What should fabs and threshold be?

I did create the sensor post code by myself and compiled on first try without errors, but it's a while ago. Got to relearn everything, lol.

One thing to note is that it's always a zero value reading when it's not working, so I guess reading again if value is 0 is a good enough solution?

I don't have a code example but I would do something like:

  • average the last 5 samples ( could be more or less)
  • reject any reading that deviates by more than 10 degrees from the average.

If your application can have some fast rates of change, you could up the 10 degree differential to a larger number.

Is this better?

float alpha = 0.1; //adjustable
float readTemp = bme.readTemperature();
float movingAvg = 0;

void loop() {
    if (true) {
        sensorData();
        updateMovingAvg();
        Serial.println(movingAvg);
    }
}

void sensorData() {
    if (fabs(readTemp - movingAvg) < 40) updateMovingAvg();
}

void updateMovingAvg() {
    movingAvg = movingAvg + alpha*(readTemp - movingAvg);
}

He he. :slight_smile:

Better, but you have a long, long way to go.

float readTemp = bme.readTemperature();

You need to read the temperature in loop()

This does nothing useful:
` if (true) {'

You need a setup function, and define bme

Start by studying the program that works to read the sensor very carefully. Every line counts.

Ok! Yes I know it does not do anything useful, I'm just trying to think how to use that as an example. I have a code that is working and not using if (true), isn't that just for running the loop without conditions? I can post the full code tomorrow, but I've not added the stuff from this topic yet. I didn't feel it was needed. Just wanted some code examples.

Addittionally i suggest that you check the cables with a multimeter. Alot of times they look okay but in fact present the circuit with unresonable resistance due to poor connection.

Although you've not seen it yet but the "spike" could be positive as well.

Ok! I feel a bit silly posting this code, since I have it in another topic too, but here we go:

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <credentials.h>

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme; // I2C
WiFiClient wifiClient;

const char ssid[] = WIFI_SSID;
const char password[] = WIFI_PASSWD;

//Your Domain name with URL path or IP address with path
String serverName = "https://www.mysite.com/write_data.php";

// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastTime = 0;
unsigned long timerDelay = 60000;
unsigned short wifiFail = 0;

void setup() {
    unsigned short count = 0;
    Wire.begin();
    Serial.begin(115200);
    while(!Serial);    // time to get serial running
    Serial.println(F("BME280 test"));
    
    unsigned status;
    
    // default settings
    status = bme.begin(0x76);  
    // You can also pass in a Wire library object like &Wire2
    //status = bme.begin(0x76, &Wire2)
    if (!status) {
        Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
        Serial.print("SensorID was: 0x"); Serial.println(bme.sensorID(),16);
        Serial.print("        ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n");
        Serial.print("   ID of 0x56-0x58 represents a BMP 280,\n");
        Serial.print("        ID of 0x60 represents a BME 280.\n");
        Serial.print("        ID of 0x61 represents a BME 680.\n");
        while (1) delay(10);
    }
    
    Serial.println("BME280 Test Complete");
    
    WiFi.begin(ssid, password);
    Serial.println("Connecting");
    while(WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
        count++;
        
        if (count >= 30) {
            count = 0;
            wifiRestart();
        }
    }
    Serial.println("");
    Serial.print("Connected to WiFi network with IP Address: ");
    Serial.println(WiFi.localIP());
    
    Serial.println("Timer set to 1 minute (timerDelay variable), it will take 1 minute before publishing the first reading.");
}

void wifiRestart() {
    int tryCount = 0;
    while(WiFi.status() != WL_CONNECTED) {
        tryCount++;
        wifiFail++;
        WiFi.disconnect();
        WiFi.begin(ssid, password);
        vTaskDelay(5000);
        if(tryCount >= 10) {
            ESP.restart();
        }
    }
}

void runClear() {
    if(wifiFail >= 20000) {
        wifiFail = 0;
        Serial.println("Too many fails, counter reset");
    }
}

void loop() {
    //Send an HTTP POST request every 10 minutes
    if((millis() - lastTime) > timerDelay) {
        //Check WiFi connection status
        if((wifiClient.connected()) && (WiFi.status() == WL_CONNECTED)) {
        // if(WiFi.status() == WL_CONNECTED) {
            HTTPClient http;
            
            String serverPath = serverName + "?temp=" + bme.readTemperature() + "&humi=" + bme.readHumidity() + "&pres=" + (bme.readPressure() / 100.0F) + "&rssi=" + WiFi.RSSI();
            
            // Your Domain name with URL path or IP address with path
            http.begin(serverPath.c_str());
            
            // Send HTTP GET request
            int httpResponseCode = http.GET();
            
            if (httpResponseCode>0) {
                Serial.print("HTTP Response code: ");
                Serial.println(httpResponseCode);
            }
            else {
                Serial.print("Error code: ");
                Serial.println(httpResponseCode);
            }
            // Free resources
            http.end();
            printValues();
        }
        else {
            Serial.println("WiFi Disconnected");
            wifiRestart();
        }
        Serial.print("Send Time = ");
        Serial.println(lastTime);
        runClear();
        lastTime = millis();
    }
}

void printValues() {
    Serial.print("Test Time = ");
    Serial.println(lastTime);
    
    Serial.print("Temperature = ");
    Serial.print(bme.readTemperature());
    Serial.println(" *C");
    
    Serial.print("Pressure = ");
    Serial.print(bme.readPressure() / 100.0F);
    Serial.println(" hPa");
    
    Serial.print("Approx. Altitude = ");
    Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
    Serial.println(" m");
    
    Serial.print("Humidity = ");
    Serial.print(bme.readHumidity());
    Serial.println(" %");
    
    Serial.print("Signal strength (RSSI) = ");
    Serial.print(WiFi.RSSI());
    Serial.println(" dBm");
    
    Serial.print("Test Time = ");
    Serial.println(lastTime);
    
    Serial.print("WiFi failed ");
    Serial.print(wifiFail);
    Serial.println(" times.");
    
    Serial.println();
}

I'm looking for ways to improve the code, but for now the topic here is the sensor reading a zero value at rare occasions and I want to remove that, also an average of several readings in a short time-frame would be a more correct reading for the posting every minute. Any and all code examples are welcome. I'm not an experienced programmer. Though I did make most of this code myself as it was almost impossible to find something similar on the internet. Explaining https and GET request to post data. I made the website too, using PHP, SQL and Java Script.

Here is some WiFi art (removed all readings with 0 as a value manually in the database)!

Ok! I have to get this working.. lol

This is my new code so far:

float alpha = 0.1;
float new_value = 0;
float moving_average = 0;

void update_moving_average() {
    // moving_average = (1-alpha)*moving_average + alpha*new_value;
    moving_average = moving_average + alpha*(new_value-moving_average);
}

void loop() {
    // Send an HTTP POST request every 10 minutes
    if((millis() - lastTime) > timerDelay) {
        for(int repeat = 0; repeat <=5; ++repeat) {
            new_value = bme.readTemperature();
            if (fabs(new_value - moving_average) < 4) {
                update_moving_average();
                Serial.println(moving_average);
            }
        }
        **all the other loop code**

I guess the moving_average has to be set to the new_value before the abs function, or it will never go through, lol. And how will I know that the function don't start when the value is reading the wrong value, and using that as a reference?

I don't know if this will help but....

I have a different sensor and periodically receive readings with very large errors. In all cases I've logged only single readings had this issue. My setup is pretty simple with no noise generating circuits nearby.

My solution was to test for huge jumps and delete them. For an average I have (in another sensor installation) performed the following steps.

Read 9 readings into an array.
Sort the array
Average the middle 5 readings.
This effectively deletes the lowest and highest 2 readings.

You can do variations,
15 readings, average the middle n readings.

This is similar to a standard approach called the Median Filter (mentioned already above), which is very useful in certain circumstances.

Hello jremington!
Can you give me some insight if the code I wrote will work or not?
Or how to improve on it?

If the code works to your satisfaction, it is fine. If not, post the details of the problems you are having.