BME280 Low Power

Arduino indoor climate monitor with dewpoint calculation


These are plans for a little indoor climate monitor with Nokia 5110 display. The Bosch BME280 sensor can output room temperature, relative humidity and absolute pressure.

The display shows Temperature T 25°C, Relative humidity 60%, Dewpoint temperature 17°C and sea-level pressure 1017 hPa (mbar).

The dew-point is calculated with the August-Roche-Magnus equation.

The sea-level pressure (that is given in weather forecasts) is calculated from the measured station pressure using an idealised atmospheric model with a lapse rate of -6.5 K/km. You have to put the correct station altitude in the code.

// ********CONSTANTS AND VARIABLES**********

// Station altitude in meters over sea-level

const float altitude = 40.0;


All components are 3.3V and the goal is to get the lowest possible current draw to have the AAA batteries last for more than a year.

I built two versions of this project. The first version was based on a 8 MHz 3.3V Arduino Pro Mini with 3 AAA batteries (800-1000 mAh). With the Arduino on, the power draw was approx. 4 mA (power LED deactivated). However, I used the rocketscream Low-Power library to let the Arduino sleep most of the time (8 seconds at a time). Disappointingly, the batteries were empty after only 2 months.

Bosch BME280 sensor on Sparkfun breakout-bord:

absolute accuracy ±1.0hPa, ±3%RH and ±0.5°C


On metal cover:

first line: 3-digit code

second line: UP


I installed a photo-resistor to switch off the display at night. With the display off, the current draw was 0.45 mA; with display on 0.55 mA. This was much more than expected. The Bosch sensor itself has a very low power consumption. I suspect therefore that this might have something to do with current draw through the pull-up resistors on the I2C-interface while the Arduino is sleeping.

I wonder what the low power commando does to the I2C-interface:

LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);

Several forum discussions I found, pointed out that the solution to the problem is to put the Bosch sensor to sleep as well. I tested several libraries and found out that the sparkfun library worked best for me. I rewrote the code based on github sparkfun_BME280_Arduino_Library Example 6 Low Power. This was a success. The current draw was reduced to 0.162 mA (display on) and 0.068 mA (display off). Most of the 0.068 mA is lost current on the ground pin of the Pro Mini voltage regulator. Now the batteries should last for almost a year.

Next, the low-power freaks usually pull out the onboard voltage regulator. I was curious how much more could be achieved. I decided to build a breadboard version with a Atmega328P-PU chip and a really good voltage regulator next: The Microchip MCP1700-3302E/TO (MCP1700-3302E/TO-ND on digikey.com).

The Atmega chip is programmed to run at 8 MHz without a crystal (Lilipad bootloader).

Now, the current draw was 0.11 mA at daytime and 7 µA at night. This should give more than a year battery lifetime.

I saw an interesting video by Kevin Darrah today (https://youtu.be/PlGycKwnsSw) about the fact that the sleeping current of many cheap Pro mini clones is far too high.

// ****************LIBRARIES****************

#include <Arduino.h>

#include <Math.h>

#include <LowPower.h>

#include <U8g2lib.h>

#include <SPI.h>

#include <Wire.h> // A4 SDA A5 SCL

#include <SparkFunBME280.h> // Bosch environmental sensor: pressure, temp, humidity


// ***************HARDWARE******************

// U8g2 Contructor List (Frame Buffer) for Nokia 5110 display

// Please UNCOMMENT one of the contructor lines below

// The complete list is available here: https://github.com/olikraus/u8g2/wiki/u8g2setupcpp

// Please update the pin numbers according to your setup. Use U8X8_PIN_NONE if the reset pin is not connected

//U8G2_PCD8544_84X48_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8); // Nokia 5110 Display

U8G2_PCD8544_84X48_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8); // Nokia 5110 Display

// End of constructor list


// Bosch BME280 sensor: absolute accuracy ±1.0hPa, ±3%RH and ±0.5°C

BME280 mySensor; // I2C environmental sensor


// ********CONSTANTS AND VARIABLES**********

// Station altitude in meters over sea-level

const float altitude = 40.0;


float humidity, temperature, dewpoint, AH, pressure, SeaLevelPressure, voltage;

char tempstring[6];


// Voltage divider with 10k resitor and Photo resistor on LDRPowerPin - measured voltage on LDRpin

#define LDRpin A0

#define LDRPowerPin 6

int lightIntensity = 0;



int readLightIntensity() {

int intensity = analogRead(LDRpin);

intensity = map(intensity, 700, 8, 0, 100);

return intensity;

}



void setup() {

// put your setup code here, to run once:

u8g2.begin();

u8g2.enableUTF8Print();

Serial.begin(9600);

Serial.println("Start up Bosch sensor and put it to sleep for now");


Wire.begin();

Wire.setClock(400000); //Increase to fast I2C speed!


mySensor.beginI2C();

mySensor.setMode(MODE_SLEEP); //Sleep for now

}


void loop() {

// put your main code here, to run repeatedly:

// switch on LDR, wait 10 ms, then measure light intensity

digitalWrite(LDRPowerPin, HIGH);

delay(10);

lightIntensity = readLightIntensity();

digitalWrite(LDRPowerPin, LOW);

Serial.print(" LDR: ");

Serial.println(lightIntensity);

// Turn On/Off Screen

if(lightIntensity<20)

{

u8g2.setPowerSave(1);

}else

{

mySensor.setMode(MODE_FORCED); //Wake up sensor and take reading

long startTime = millis();

while(mySensor.isMeasuring() == false) ; //Wait for sensor to start measurment

while(mySensor.isMeasuring() == true) ; //Hang out while sensor completes the reading

long endTime = millis();


//Sensor is now back asleep but we get the data


Serial.print(" Measure time(ms): ");

Serial.print(endTime - startTime);

humidity = mySensor.readFloatHumidity();

Serial.print(" Humidity: ");

Serial.print(humidity, 1);


pressure = mySensor.readFloatPressure()/100.0;

Serial.print(" Pressure: ");

Serial.print(pressure, 0);


temperature = mySensor.readTempC();

Serial.print(" Temp: ");

Serial.print(temperature, 2);


Serial.println();


// Sea-level pressure (calculated from station pressure)

// Idealized Standard Atmospheric Modell - dry air - use below 11 km altitude

// altitude = T0/L*((p/p0)^(-L*R/g)-1)

// Lapse rate L = -6.5 K/km / Sea-level standard temperature T0 = 288.15 K

// http://archive.psas.pdx.edu/RocketScience/PressureAltitude_Derived.pdf

SeaLevelPressure = pressure/pow((1-(altitude/44330.8)),5.25588);

// Serial.println(SeaLevelPressure);

// The August-Roche-Magnus equation, as described in Alduchov and Eskridge (1996)

// https://en.wikipedia.org/wiki/Vapour_pressure_of_water#Accuracy_of_different_formulations

// http://andrew.rsmas.miami.edu/bmcnoldy/Humidity.html

// Alternative formulas: http://www.decatur.de/javascript/dew/index.html

dewpoint = 243.04*(log(humidity/100)+((17.625*temperature)/(243.04+temperature)))/(17.625-log(humidity/100)-((17.625*temperature)/(243.04+temperature)));

// Serial.println(dewpoint);

// Calculation of the absolute humidity AH in g/m3 with August-Roche-Magnus

// p_H2O [Pa] = (%RH/100)*100*6.1094*exp(17.625*t/(t+243.04)) - t in Celsius

// p_H2O [Pa] = (AH[g/m3]/1000)*R*T ; Gas constant R for water 461.5 J/kg/K ; T[K] = t[C] + 273.15

AH = humidity*6.1094*exp(17.625*temperature/(temperature+243.04))*1000/461.5/(temperature + 273.15);


// create temperature string with one digit precision (rounded) after decimal sign

dtostrf((float)round(temperature*10)/10, 5, 1, tempstring);

// update display

u8g2.setPowerSave(0);

u8g2.clearBuffer(); // clear the internal memory

u8g2.setFont(u8g2_font_7x14B_tf); // choose a suitable font

u8g2.setCursor(0,13);

u8g2.print("T ");

u8g2.print(tempstring);

u8g2.print(" °C");

u8g2.setCursor(0,30);

u8g2.print("RH ");

u8g2.print(round(humidity));

u8g2.print("% ");

u8g2.print(round(dewpoint));

u8g2.print("°C");

u8g2.setCursor(0,47);

u8g2.print("P ");

u8g2.print(round(SeaLevelPressure));

u8g2.print(" hPa");

u8g2.sendBuffer(); // transfer internal memory to the display

}

// Wire.endTransmission();

// delay(1000);

LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);

}

© 2020 notthemarsian