BME280 on batt. sleep question



  • Good day,
    As newbee in Arduino land.
    I wants to read the outside temp, hum and air-press by means of a nRF24L01 a BME280 with a arduino pro-mini and a RTC DS3231 a solarcell aprox. 6 to 8 Volt and 250mA a battt. LiPo 2400mA/3,7V and a batt. charger ??? with the following number 03962A
    it has a USB connector a batt. connector and out + and - it works with a bleu LED if batt. is chatrged and a red LED if not and power is connected by means of the solarcell or batt.

    My question is the following;
    I would like to run the outside unit as described above on the solacel and batt.
    This means the whole set needs only send the data lets say ones a ten minutes.
    This can be done by the use of sleep modes for the arduino it looks clear to me.
    But the sensor and ther RTC howe about these?
    The Nick Gammon and the article to remove the LED and voltregulator on the Arduino is known.
    For me not clear is the sleep for the RTC and the sensor. This I would like to have clear before I will swap to the MySensor lib. and scripts.
    Now my set-up is working.

    Like to hear.
    ilioSS


  • Mod

    Welcome to the MySensors community @ilioss !

    If you don't need exact sleep times, you can skip the RTC and just use MySensor's internal sleep function. It uses the internal oscillator, so it can be a few seconds off per 10 minutes. For most sensors being a few seconds off is not a problem.

    If you do need/want exact sleep times, MySensors can sleep until awaken by external interrupt. You would then configure the RTC to trigger an interrupt on the arduino after 10 minutes. I guess that's what you do with your existing solution?

    Documentation:
    https://www.mysensors.org/download/sensor_api_20#sleeping
    https://www.mysensors.org/apidocs/group__MySensorsCoregrp.html#ga57a5217aff45276f40018165d2fb10a1



  • Hello mfalkvidd,

    Thanks for your swift reply.
    IDD i do not need exact timer for sending the sensor data So I will change to yours script.
    Still interresting howe to have the arduino, RTC , sensor and nRF24 sleeping?
    External signal means one of a divice must be on all the time and consume current.
    I check your script and come with feedback.
    Thanks,
    ilioSS



  • I have basically the same setup.
    BME280 powered by solar and saves for the night into a 3.7V cell.
    I use a proMini 3V
    In addition, I have a second temp sensor (dallas) and a rain sensor attached.
    The pressure sensor is doing some calculation for forecast. Also battery monitoring to know if there is still enough energy. But the solar cells are usually enough. during winter when there is some snow it might get drained.
    It is sending the values from all sensors every 180000ms (3 minutes) and then going back to sleep. Also if it starts raining, the sensor has an interrupt and sends immediately.
    For charing, I use this board:
    https://www.aliexpress.com/item/32230225249.html?spm=a2g0s.9042311.0.0.27424c4dbHQ48S

    Running flawless since quite some time.

    Here is my code - it needs cleanup but I am too lazy

    // 2.4. add sun sensor and shorten to 2 min intervall
    
    #define MY_RADIO_NRF24
    #define MY_DEBUG
    //fix node id = 101 Wetter Station Dach
    #include <SPI.h>
    #include <MySensors.h>
    //#include <DHT.h>
    // #include <Bounce2.h>
    // #include <BH1750.h>
    #include <Wire.h>
    #include <DallasTemperature.h>
    #include <OneWire.h>
    #include <Vcc.h>
    #include <Adafruit_Sensor.h>
    #include <Adafruit_BME280.h> // I had to change I2C address in library for 0x76 (line 32)
    //   #include <Adafruit_BMP085.h>
    
    Adafruit_BME280 bme; // I2C
    
    #define CHILD_ID_HUM 0
    #define CHILD_ID_TEMP 1
    #define CHILD_ID_PRESS 2
    #define CHILD_ID_FORECAST 3
    //#define CHILD_ID_LIGHT 2
    // #define CHILD_ID_BARO 3
    #define CHILD_ID_BTEMP 4
    #define CHILD_ID_STEMP 5
    #define CHILD_ID_RAIN 6
    #define CHILD_ID_VOLT 7
    #define SENSOR A0
    #define P0 1010.6
    
    #define DIGITAL_INPUT_RAIN_SENSOR 3
    //#define HUMIDITY_SENSOR_DIGITAL_PIN 4
    //#define INTERRUPT DIGITAL_INPUT_RAIN_SENSOR-2
    #define COMPARE_TEMP 0 // Send temperature only if changed? 1 = Yes 0 = No
    
    #define ONE_WIRE_BUS 8 // Pin where dallase sensor is connected 
    #define MAX_ATTACHED_DS18B20 16
    
    
    const char *weather[] = { "stable", "sunny", "cloudy", "unstable", "thunderstorm", "unknown" };
    enum FORECAST
    {
      STABLE = 0,      // "Stable Weather Pattern"
      SUNNY = 1,      // "Slowly rising Good Weather", "Clear/Sunny "
      CLOUDY = 2,     // "Slowly falling L-Pressure ", "Cloudy/Rain "
      UNSTABLE = 3,   // "Quickly rising H-Press",     "Not Stable"
      THUNDERSTORM = 4, // "Quickly falling L-Press",    "Thunderstorm"
      UNKNOWN = 5     // "Unknown (More Time needed)
    };
    
    
    // for forecast - untouched by me :)
    
      float lastPressure = -1;
      float lastTemp = -1;
      int lastForecast = -1;
      const int LAST_SAMPLES_COUNT = 5;
      unsigned long  mySleep = 180000; //180000 are 3 minutes
      float lastPressureSamples[LAST_SAMPLES_COUNT];
      // this CONVERSION_FACTOR is used to convert from Pa to kPa in forecast algorithm
      // get kPa/h be dividing hPa by 10
      #define CONVERSION_FACTOR (1.0/10.0)
      int minuteCount = 0;
      bool firstRound = true;
      // average value is used in forecast algorithm.
      float pressureAvg;
      // average after 2 hours is used as reference value for the next iteration.
      float pressureAvg2;
      float dP_dt;
    
    
    
    
    //Bounce debouncer = Bounce();
    boolean metric = true;
    int altitude = 1500; // 741 feet above sealevel
    //float lastBmpTemp = -1;
    //float lastPressure = -1;
    float lastHum = -1;
    //float lastTemp = -1;
    //int BATTERY_SENSE_PIN = A1;
    int lastRainValue = -1;
    int lastBatteryPcnt = 0;
    int updateAll = 60;
    int updateCount = 0;
    uint16_t lastLux;
    unsigned long SLEEP_TIME = 180000; //3 minutes
    
    int rainCounter = 0;
    
    float lastTemperature[MAX_ATTACHED_DS18B20];
    int numSensors = 1;
    bool receivedConfig = false;
    
    float oldVolts = 0;
    const float VccExpected   = 3.1;
    const float VccCorrection = 3.1 / 3.1; // Measured Vcc by multimeter divided by reported Vcc
    Vcc vcc(VccCorrection);
    static int oldBatteryPcnt = 0;
    
    //Adafruit_BMP085 bmp = Adafruit_BMP085();
    //  BH1750 lightSensor;
    //DHT dht;
    //  MySensor gw;
    
    OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
    DallasTemperature sensors(&oneWire); // Pass the oneWire reference to Dallas Temperature.
    
    
    
    
    MyMessage msgHum(CHILD_ID_HUM, V_HUM);
    MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
    //MyMessage msgLux(CHILD_ID_LIGHT, V_LIGHT_LEVEL);
    // MyMessage msgBtemp(CHILD_ID_BTEMP, V_TEMP);
    MyMessage msgPress(CHILD_ID_PRESS, V_PRESSURE);
    MyMessage msgForecast(CHILD_ID_FORECAST, V_FORECAST);
    MyMessage msgRain(CHILD_ID_RAIN, V_RAIN);
    MyMessage msgSunTemp(CHILD_ID_STEMP, V_TEMP);
    MyMessage msgVolt(CHILD_ID_VOLT, V_VOLTAGE);
    
    void before()
    {
      // Startup up the OneWire library
      sensors.begin();
    }
    
    void setup()
    {
      // requestTemperatures() will not block current thread
      sensors.setWaitForConversion(false);
      //dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN);
       if (!bme.begin())
       {
        Serial.println("BME init failed!");
        while (1);
       }
      else Serial.println("BME init success!");
      
      ServerUpdate(); // for first data reading and sending to controler
      wait(2000);
      ServerUpdate();
    
      pinMode(DIGITAL_INPUT_RAIN_SENSOR, INPUT);
      // Activate internal pull-up
      digitalWrite(DIGITAL_INPUT_RAIN_SENSOR, HIGH);
    
      // use the 1.1 V internal reference
    #if defined(__AVR_ATmega2560__)
      analogReference(INTERNAL1V1);
    #else
      analogReference(INTERNAL);
    #endif
    }
    
    void presentation()
    {
      analogReference(INTERNAL);
    
    
      //     bmp.begin();
      sendSketchInfo("Weather Sensor", "2.01");
      present(CHILD_ID_HUM, S_HUM);
      present(CHILD_ID_TEMP, S_TEMP);
      present(CHILD_ID_PRESS, S_BARO);
      //  present(CHILD_ID_LIGHT, S_LIGHT_LEVEL);
      //   present(CHILD_ID_BARO, S_BARO);
      //  present(CHILD_ID_BTEMP, S_TEMP);
      present(CHILD_ID_RAIN, S_MOTION);
    
      present(CHILD_ID_STEMP, S_TEMP);
      present(CHILD_ID_VOLT, S_MULTIMETER);
      //pinMode(DIGITAL_INPUT_RAIN_SENSOR, INPUT);
      //      lightSensor.begin();
      metric = true;
    }
    
    
    
    void loop()
    {
    
    ServerUpdate();
      updateCount += 1;
      Serial.println("aufgewacht...");
    
      if (updateCount == updateAll) {
        lastTemp = -1;
        lastHum = -1;
        // lastLux = -1;
        //  lastBmpTemp = -1;
        //    lastPressure = -1;
        lastRainValue = -1;
        //   lastBatteryPcnt = -1;
        updateCount = 0;
      }
    
    /*
      
      delay(dht.getMinimumSamplingPeriod());
      float temperature = dht.getTemperature();
      if (isnan(temperature)) {
        lastTemp = -1;
      } else if (temperature != lastTemp) {
        lastTemp = temperature;
        if (!metric) {
          temperature = temperature * 1.8 + 32.0;
        }
        send(msgTemp.set(temperature, 1));
      }
      float humidity = dht.getHumidity();
      if (isnan(humidity)) {
        lastHum = -1;
      } else if (humidity != lastHum) {
        lastHum = humidity;
        send(msgHum.set(humidity, 1));
      }
    
      
    */
      /*  uint16_t lux = analogRead(SENSOR);
        // Serial.println("LUX: "+ String(lux));
        if (lux != lastLux) {
            send(msgLux.set(lux));
            lastLux = lux;
        }
    
      */
    
      /*
        float pressure = bmp.readSealevelPressure(altitude) * 0.01;
    
    
        float bmptemp = bmp.readTemperature();
        if (!metric) {
        bmptemp = bmptemp * 1.8 + 32.0;
        }
        if (bmptemp != lastBmpTemp) {
        gw.send(msgBtemp.set(bmptemp,1));
        lastBmpTemp = bmptemp;
        }
        if (pressure != lastPressure) {
        gw.send(msgPressure.set(pressure, 0));
        lastPressure = pressure;
        }
    
      */
      int rainValue = digitalRead(DIGITAL_INPUT_RAIN_SENSOR);
      wait(100);
    
      if (rainValue != lastRainValue) {
        send(msgRain.set(rainValue == 0 ? 1 : 0));
        lastRainValue = rainValue;
        Serial.println("RegenWert geaendert");
      }
      else if (rainCounter >= 5) //update rain every 10 minutes
      {
        send(msgRain.set(rainValue == 0 ? 1 : 0));
        rainCounter = 1;
        //Serial.println("rainCounter > als Wert");
      }
      else
      {
        rainCounter++;
        //Serial.println("rainCounter ++" + String(rainCounter));
    
      }
    
      /*
        int sensorValue = analogRead(BATTERY_SENSE_PIN);
        float voltage= sensorValue * (5.0 / 1023.0);
        Serial.println("sens:" +String (sensorValue));
        Serial.println("V:" +String (voltage));
        int batteryPcnt = (sensorValue - batteryBasement) * batteryConstant;
    
        if (lastBatteryPcnt != batteryPcnt) {
        gw.sendBatteryLevel(batteryPcnt);
        lastBatteryPcnt = batteryPcnt;
        }
      */
    
      // Fetch temperatures from Dallas sensors
      sensors.requestTemperatures();
      // query conversion time and sleep until conversion completed
      int16_t conversionTime = sensors.millisToWaitForConversion(sensors.getResolution());
      // sleep() call can be replaced by wait() call if node need to process incoming messages (or if node is repeater)
      wait(conversionTime);
    
      // Read temperatures and send them to controller
      for (int i = 0; i < numSensors && i < MAX_ATTACHED_DS18B20; i++) {
    
        // Fetch and round temperature to one decimal
        float sunTemperature = static_cast<float>(static_cast<int>((getControllerConfig().isMetric ? sensors.getTempCByIndex(i) : sensors.getTempFByIndex(i)) * 10.)) / 10.;
    
        // Only send data if temperature has changed and no error
    #if COMPARE_TEMP == 1
        if (lastTemperature[i] != sunTemperature && sunTemperature != -127.00 && sunTemperature != 85.00) {
    #else
        if (sunTemperature != -127.00 && sunTemperature != 85.00) {
    #endif
    
          // Send in the new temperature
          send(msgSunTemp.setSensor(5).set(sunTemperature, 1));
          // Save new temperatures for next compare
          lastTemperature[i] = sunTemperature;
        }
      }
    
      //Serial.println("wait...");
      //wait(5000);
    
      //BAtterie
      int batteryPcnt = (int)vcc.Read_Perc(VccExpected);
      float volts = (float)vcc.Read_Volts();
      int myPerc = volts * 100 / VccExpected;
      Serial.print("Battery percent: ");
      Serial.print(myPerc);
      Serial.println(" %");
    
      Serial.print("Volts: ");
      Serial.print(volts);
      send(msgVolt.set(volts,1));
      Serial.println(" V");
      if (oldBatteryPcnt != myPerc)
      {
        sendBatteryLevel(myPerc);
        oldBatteryPcnt = myPerc;
      }
    
    
      Serial.println("sleep now...");
      sleep(1, CHANGE, SLEEP_TIME);
      //sleep(SLEEP_TIME);
    
    
      //delay(5000);
    }
    
    
    void ServerUpdate() // used to read sensor data and send it to controller
    {
      double T, P, H;
      T=bme.readTemperature();
      P=bme.readPressure()/100.0+52.7;
      H=bme.readHumidity();
      
        int forecast = sample(P);
          send(msgTemp.set(T, 1));
          send(msgPress.set(P, 1));
          send(msgHum.set(H,1));
          send(msgForecast.set(weather[forecast]));
          
       // unmark for debuging
          Serial.print("T = \t"); Serial.print(T, 1); Serial.print(" degC\t");
          Serial.print("P = \t"); Serial.print(P, 1); Serial.print(" mBar\t");
          Serial.print("P = \t"); Serial.print(H, 1); Serial.print(" relH\t");
          Serial.print("F = \t"); Serial.print(weather[forecast]); Serial.println(" ?");
    }
    
    float getLastPressureSamplesAverage()
    {
      float lastPressureSamplesAverage = 0;
      for (int i = 0; i < LAST_SAMPLES_COUNT; i++)
      {
        lastPressureSamplesAverage += lastPressureSamples[i];
      }
      lastPressureSamplesAverage /= LAST_SAMPLES_COUNT;
    
      return lastPressureSamplesAverage;
    }
    
    // Algorithm found here
    // http://www.freescale.com/files/sensors/doc/app_note/AN3914.pdf
    // Pressure in hPa -->  forecast done by calculating kPa/h
    int sample(float pressure)
    {
      // Calculate the average of the last n minutes.
      int index = minuteCount % LAST_SAMPLES_COUNT;
      lastPressureSamples[index] = pressure;
    
      minuteCount++;
      if (minuteCount > 185)
      {
        minuteCount = 6;
      }
    
      if (minuteCount == 5)
      {
        pressureAvg = getLastPressureSamplesAverage();
      }
      else if (minuteCount == 35)
      {
        float lastPressureAvg = getLastPressureSamplesAverage();
        float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
        if (firstRound) // first time initial 3 hour
        {
          dP_dt = change * 2; // note this is for t = 0.5hour
        }
        else
        {
          dP_dt = change / 1.5; // divide by 1.5 as this is the difference in time from 0 value.
        }
      }
      else if (minuteCount == 65)
      {
        float lastPressureAvg = getLastPressureSamplesAverage();
        float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
        if (firstRound) //first time initial 3 hour
        {
          dP_dt = change; //note this is for t = 1 hour
        }
        else
        {
          dP_dt = change / 2; //divide by 2 as this is the difference in time from 0 value
        }
      }
      else if (minuteCount == 95)
      {
        float lastPressureAvg = getLastPressureSamplesAverage();
        float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
        if (firstRound) // first time initial 3 hour
        {
          dP_dt = change / 1.5; // note this is for t = 1.5 hour
        }
        else
        {
          dP_dt = change / 2.5; // divide by 2.5 as this is the difference in time from 0 value
        }
      }
      else if (minuteCount == 125)
      {
        float lastPressureAvg = getLastPressureSamplesAverage();
        pressureAvg2 = lastPressureAvg; // store for later use.
        float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
        if (firstRound) // first time initial 3 hour
        {
          dP_dt = change / 2; // note this is for t = 2 hour
        }
        else
        {
          dP_dt = change / 3; // divide by 3 as this is the difference in time from 0 value
        }
      }
      else if (minuteCount == 155)
      {
        float lastPressureAvg = getLastPressureSamplesAverage();
        float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
        if (firstRound) // first time initial 3 hour
        {
          dP_dt = change / 2.5; // note this is for t = 2.5 hour
        }
        else
        {
          dP_dt = change / 3.5; // divide by 3.5 as this is the difference in time from 0 value
        }
      }
      else if (minuteCount == 185)
      {
        float lastPressureAvg = getLastPressureSamplesAverage();
        float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
        if (firstRound) // first time initial 3 hour
        {
          dP_dt = change / 3; // note this is for t = 3 hour
        }
        else
        {
          dP_dt = change / 4; // divide by 4 as this is the difference in time from 0 value
        }
        pressureAvg = pressureAvg2; // Equating the pressure at 0 to the pressure at 2 hour after 3 hours have past.
        firstRound = false; // flag to let you know that this is on the past 3 hour mark. Initialized to 0 outside main loop.
      }
    
      int forecast = UNKNOWN;
      if (minuteCount < 35 && firstRound) //if time is less than 35 min on the first 3 hour interval.
      {
        forecast = UNKNOWN;
      }
      else if (dP_dt < (-0.25))
      {
        forecast = THUNDERSTORM;
      }
      else if (dP_dt > 0.25)
      {
        forecast = UNSTABLE;
      }
      else if ((dP_dt > (-0.25)) && (dP_dt < (-0.05)))
      {
        forecast = CLOUDY;
      }
      else if ((dP_dt > 0.05) && (dP_dt < 0.25))
      {
        forecast = SUNNY;
      }
      else if ((dP_dt > (-0.05)) && (dP_dt < 0.05))
      {
        forecast = STABLE;
      }
      else
      {
        forecast = UNKNOWN;
      }
    
      // uncomment when debugging
      //Serial.print(F("Forecast at minute "));
      //Serial.print(minuteCount);
      //Serial.print(F(" dP/dt = "));
      //Serial.print(dP_dt);
      //Serial.print(F("kPa/h --> "));
      //Serial.println(weather[forecast]);
    
      return forecast;
    }
    


  • For the BME280, if you use the standard library and setup it uses "forced mode" with a 1sec sleep between samples. This uses very low power, a few mico amps.


 

247
Online

9.0k
Users

9.7k
Topics

101.3k
Posts