letscontrolit / ESPEasy

Easy MultiSensor device based on ESP8266/ESP32
http://www.espeasy.com
Other
3.23k stars 2.2k forks source link

BME280 #2133

Closed bugChas3r closed 11 months ago

bugChas3r commented 5 years ago

BME-280 need to change mode]

Steps to reproduce

  1. connect BME-280 as I2C, upon powerup get readings
  2. wait some time (within non sleep period of ESP) read again note readings are same. Data acquisition has only occurred once, power reboot results in new data acquisition and new readings.
TD-er commented 5 years ago

Continuous reading will heat up the sensor. That's why I made it reading and sleep again.

TD-er commented 5 years ago

What reading interval do you need? And what sensor value do you need to be updated so fast? (e.g. the pressure?) N.B. Faster reading may also require to adjust the set filters in the sensor.

bugChas3r commented 5 years ago

Looking to use the sensor values in both a local LCD (I2C) display, also periodically send value by MQTT in hassio. Sleeping ESP-3286 results in the LCD to power down (no view) not user satisfactory. If overheating is a concern, Would "Forced mode" be a better power setting? Perhaps using a resume from sensor delay sleep event to ctrl_meas mode (0,1) to 01) operation; is similar to BMP180 operation. This would allow low sampling rates without a resetting of the device and subsequent LCD issue.

TD-er commented 5 years ago

I think we are not talking about the same thing here.

What I meant was that running the sensor in "continuous mode" (setting registers inside the sensor to take samples continuously) will heat up the sensor. But with the current settings, it is possible to let the BMx280 plugin take a sample every N seconds, with N about 20 seconds as minimum if I'm correct.

Reading the sensor more often may need adjustment of the internal (set in the sensor) filters also. N.B. these filters may also affect the first reading.

bugChas3r commented 5 years ago

Thank you for the clarification. Am I to understand the plugin as currently deployed, I can have readings measured every N seconds, is this a default or a setting ?

TD-er commented 5 years ago

Any plugin has as the last parameter the "Interval" in seconds. That's the interval for the plugin's read function to be called. The BME280 is a bit special in this, since it takes a while to collect a stable reading. So it will call this read function at the "interval" moment and about 1.5 seconds later (as soon as the measurement is complete) it will call the read function again.

So just experiment with this "interval" setting to see what suits your purpose best.

bugChas3r commented 5 years ago

I've set the interval to 20 seconds, however the values never change until I reboot. The log file indicates only once is the device read and reported via MQTT. while other devices, like light sensor (I2C), sysinfo each are read and reported via MQTT using the each respective plugin's delay setting. Perhaps my device is defective. Here is log file (set with debug level on web, info elsewhere) log10Dec18.txt

bugChas3r commented 5 years ago

When setting unit using defaults, as per prior comment above, my results were not available as indicated from post of TD-er. I looked at both the Bosch BME-280 data sheet and the code on Git hub for plugin p_28.

The data sheet specifies the 'Normal' mode operation works one time measuring the device values and store the results into memory then goes to sleep allowing only the data to be read as many times as called but never updated until a power reset (or software reset). It seems even a mode change ('Sleep' {00} to 'Normal' {11}) will not re-run the measurements. Whereas 'Force' mode operates once each time it is called to take a measurement and store the results then return to sleep awaiting the 'Force' mode call again.

For example, from Bosch data sheet, (Sect 3.5.1) Weather Sensor: Only a very low data rate is needed. Power consumption is minimal. Noise of pressure values is of no concern. Humidity, pressure and temperature are monitored. Table 7: Settings and performance for weather monitoring Suggested settings for weather monitoring Sensor mode forced mode, 1 sample / minute ....

In the code, _case PLUGIN_READ on line 380, code is called for a configuration read which is stored in switch statement (line 136) get_control_settings() const {_ resulting in a value of 0x93 (line 141) (Binary 10010011) this results in 'Normal' mode setting, attempting to have the unit set values then go to sleep until next power cycle, (but BME-280 does not wake up only reads stored values.

May I suggest the value here (line 141) be changed to either 0x91 or 0x92, (binary 10010001 and 10010010) which has the unit operating in 'Forced' mode allowing the measurements to be taken, stored and then returning back to sleep until code in plugin is called again, Thus the unit is not overheating and gives correct values with each call of 'case PLUGIN_READ'. Also may need to checked to be sure command: I2C_write8_reg(i2cAddress, BMx280_REGISTER_CONTROL, 0x00) (line 375) ;placing unit into to "Sleep' mode; does not effect the ability of waking from sleep to operate in 'Forced' mode. when needed.

Thank you.

stas-sl commented 1 year ago

Hi! I'm also studying how BME280 works, reading datasheet and comparing different implementations (e.g. Adafruit BME280 Library). From that I have a few comments.

In ESPEasy source code I found this comment:

// It takes at least 1.587 sec for valid measurements to complete.
// The datasheet names this the "T63" moment.
// 1 second = 63% of the time needed to perform a measurement.
# define P028_MEASUREMENT_TIMEOUT 1.587f

That corresponds to this Response time parameter from the datasheet:

image

I'm quite new to reading sensor specifications, but based on some explanations:

Responsiveness of any sensor is usually given as a Time Constant and represented by the Greek letter τ “tau”. It is defined as the time required for the sensor reading/output to reach to 63.2% of its total step change in measurand.

EXAMPLE: For a temperature sensor taken out of an ice bath at 0 °C into a room at 10 °C, it will take exactly one time constant (usually given in seconds) to reach 6.32 °C, which is exactly 63.2% of the 10 °C step change in temperature.

I understand it not that you have to wait 1 or 1.5 seconds after waking up from sleep mode to get accurate readings, but if a sudden change happened, then it will take some time before sensor could react to it. The issue is that you can't predict those sudden changes in environment, and they are not synchronized in any way to when ESPEasy starts measurements. So I don't think that waiting 1.5 or 5 or 10 seconds makes much sense.

Another issue is that Response time is not linear. If you look at the chart from the link I mentioned:

image

Then you can see, that after 1.5 seconds it will not take "full step", but only somewhere 75-80% of it. It takes almost 5x to reach >99% of full change.

There are also formulas to estimate measurement time in the datasheet:

image

So if we'd use max oversampling 16x for all 3 variables (temp, humidity, pressure), then according to this formulas it will take only about 100ms for one complete measurement.

Also, I agree with @bugChas3r that it is better to use Forced mode, which seems to be designed exactly for such scenarios in mind, when you don't need continuous stream of values (unlike Normal mode). You don't have to put sensor to sleep explicitly in this mode, it will switch to it automatically:

image

Considering IIR filters, I'm not sure if they make much sense when you take measurements once in a while. I believe they are more suited when running in continous/normal mode:

image

Here is Adafruit's implementation:

bool Adafruit_BME280::takeForcedMeasurement(void) {
  bool return_value = false;
  // If we are in forced mode, the BME sensor goes back to sleep after each
  // measurement and we need to set it to forced mode once at this point, so
  // it will take the next measurement and then return to sleep again.
  // In normal mode simply does new measurements periodically.
  if (_measReg.mode == MODE_FORCED) {
    return_value = true;
    // set to forced mode, i.e. "take next measurement"
    write8(BME280_REGISTER_CONTROL, _measReg.get());
    // Store current time to measure the timeout
    uint32_t timeout_start = millis();
    // wait until measurement has been completed, otherwise we would read the
    // the values from the last measurement or the timeout occurred after 2 sec.
    while (read8(BME280_REGISTER_STATUS) & 0x08) {
      // In case of a timeout, stop the while loop
      if ((millis() - timeout_start) > 2000) {
        return_value = false;
        break;
      }
      delay(1);
    }
  }
  return return_value;
}

From it you can see, that they do not put sensor to sleep mode after taking measurement and the while loop inside should not take more than 100ms according to the formulas above.

Here is how it could be used:

Adafruit_BME280 bme;

void setup()
{
  Serial.begin(115200);
  bme.begin(0x76);
  bme.setSampling(Adafruit_BME280::MODE_FORCED,
                  Adafruit_BME280::SAMPLING_X16,
                  Adafruit_BME280::SAMPLING_X16,
                  Adafruit_BME280::SAMPLING_X16,
                  Adafruit_BME280::FILTER_OFF,
                  Adafruit_BME280::STANDBY_MS_1000);
}

void loop()
{
  bme.takeForcedMeasurement();
  Serial.printf("Temperature = %.2f °C\r\n", bme.readTemperature());
  Serial.printf("Pressure = %.2f hPa\r\n", bme.readPressure() / 100.0f);
  Serial.printf("Approx. Altitude = %.2f m\r\n", bme.readAltitude(SEALEVELPRESSURE_HPA));
  Serial.printf("Humidity = %.2f\r\n\n", bme.readHumidity());

  delay(10000);
}

Here config is set only once in setup, unlike ESPEasy, which seems to make more actions than needed (explicitly setting sleep mode, config on each measurement):

void P028_data_struct::startMeasurement() {
  if (measurementInProgress()) { return; }

  if (!initialized()) {
    if (begin()) {
      state            = BMx_Initialized;
      last_measurement = 0;
    }
  }
  check(); // Check id device is present

  if (state != BMx_Error) {
    // Set the Sensor in sleep to be make sure that the following configs will be stored
    I2C_write8_reg(i2cAddress, BMx280_REGISTER_CONTROL, 0x00);

    if (hasHumidity()) {
      I2C_write8_reg(i2cAddress, BMx280_REGISTER_CONTROLHUMID, BME280_CONTROL_SETTING_HUMIDITY);
    }
    I2C_write8_reg(i2cAddress, BMx280_REGISTER_CONFIG,  get_config_settings());
    I2C_write8_reg(i2cAddress, BMx280_REGISTER_CONTROL, get_control_settings());
    state            = BMx_Wait_for_samples;
    last_measurement = millis();
  } else {
    lastMeasurementError = true;
  }
}
TD-er commented 1 year ago

The reason I decided to use an IIR filter instead of just taking the latest reading is mainly because of the air pressure. You can't rely on a single sample for air pressure as it would also "react" on opening/closing a door, some loud bang, etc.

The parameters for this IIR filter can of course be changed to take less samples. This will make it faster to respond to changes, and it will also be "closer" to the actual value.

Not turning the sensor to sleep is for sure an option. However I do have the impression the sensor may "heat itself". On the other hand, this is a constant offset (to be determined per setup) as the amount of energy consumed in the sensor is then constant. However, when changing values, you must put the sensor to sleep to be sure these changes will be active on the next reading.

Another thing you really MUST do with this sensor... You have to read all needed registers in 1 single I2C call. (!!!!) If you don't then you will end up with values belonging to 2 measurement periods. Typically the reported temperature is then a few degrees too high. This was confirmed by a support engineer from Bosch via email.

stas-sl commented 1 year ago

Not turning the sensor to sleep is for sure an option. However I do have the impression the sensor may "heat itself".

If you reread the extract from the datasheet I provided above, you'll see that it actually sleeps, you just don't need to put it there explicitly after each measurement:

image

However, when changing values, you must put the sensor to sleep to be sure these changes will be active on the next reading.

I understand it, but I don't think those could be changed between measurements. So it could be called just once on plugin initialization.

Another thing you really MUST do with this sensor... You have to read all needed registers in 1 single I2C call. (!!!!)

Yes, this is also mentioned in the datasheet.

image

However, it seems to me more applicable to Normal mode, when standby interval could be as low as 5ms, then indeed there could be high risk of reading values from different periods - which I'm not sure how bad is. I mean, if you take measurements so frequently, then they should be pretty close to each other. But if you are taking a single measurement in Forced mode and then it automatically goes to sleep for some time, then I don't think this should be an issue at all.

Of course there could be different use cases, but here are recommended settings in datasheet for conventional weather monitoring:

image
TD-er commented 1 year ago

Hmm I may have to read the apparently updated datasheet as this section on Data Readout was absolutely not present when I implemented support for it.

stas-sl commented 1 year ago

Well, anyway, just wanted to say thanks for your work :) I'm still learning ESPEasy and all that stuff, so everything I wrote should be taken with a grain of salt :) Sometimes my style of writing might seem aggressive (if not adding smiles), but I really thankful, and wish ESPEasy to only become better!

TD-er commented 1 year ago

Sure and getting to really understand what you're measuring is probably more important than you'd expect.

As I always say: "Meten is weten als je weet wat je meet" (Measuring is knowing, only if you know what you're measuring)

tonhuisman commented 11 months ago

This issue seems to be resolved, though there may be pending improvements. I suggest to close this as resolved.