Powerdown Question

Hi all!

Using ArduinoMiniPro 5V16MHz, SSD1306 OLED Display I2C Bus, DHT22

I tried out this short sketch below which reads a DHT22 and displays humidity and temperature
for testing purposes.

Because it consumes too much energy I implemented the powerdown lib from rocketscream.
First I used “LowPower.powerDown” (see lines with “###” above and below)
The sketch was running once without any trouble, but after the first loop data was not updated - nor came up the “failed to read sensor” - error message but the MCU kept running (internal LED kept on blinking - see end of sketch)
Then I used “LowPower.idle” - same effect
The sketch is only running as intended with continuous data update when I use “delay(2000)”
Any ideas why?

Flo

/*
 * Random Nerd Tutorials - Rui Santos 
 * Complete Project Details http://randomnerdtutorials.com
 */
 
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <DHT.h>
#include "LowPower.h"
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

#define DHTPIN 2
#define DHTTYPE DHT22
#define OLED_RESET 4
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Initialize DHT sensor
DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Wire.begin();
  dht.begin();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);// initialize with the I2C addr 0x3C
  pinMode(LED_BUILTIN, OUTPUT);
}

void displayTempHumid(){
// ###################################
  delay(2000); 
  // ##-->> OR USING:   LowPower.idle(SLEEP_2S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF);
  // ##-->> OR USING:   LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF);
// ###################################

  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius
  float t = dht.readTemperature();
  // Read temperature as Fahrenheit
  float f = dht.readTemperature(true);

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t) || isnan(f)) {
    display.clearDisplay(); // clearing the display
    display.setTextColor(WHITE); //setting the color
    display.setTextSize(1); //set the font size
    display.setCursor(5,0); //set the cursor coordinates
    display.print("Failed to read from DHT sensor!");
    return;
  }
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(2);
  display.setCursor(0,0);
  display.print("F:"); 
  display.print(h);
  display.print(" %");
  display.setCursor(0,40);
  display.print("T:"); 
  display.print(t);
  display.print(" C"); 
}
void loop() {
  displayTempHumid();
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on
  display.display();
  digitalWrite(LED_BUILTIN, LOW);   // turn the LED off
}

Do you understand that the OLED almost certainly consumes an order of magnitude MORE power than the Arduino??

Regards,
Ray L.

Hi Ray!

Did you read the question?
Have you ever used this type of display?

Guess not.

The display needs only a very small amount of energy (measured current WITH and WITHOUT the display)

With usage of Powerdown the current - using a UNO - drops from 50mA to 35mA which is not bad, but not enough, that's why I used the minipro.

So, what about the original question? Why is this sketch not working as it should with "powerdown".
One explanation might be, that the hardware needs a certain time to "wake up" again? Maybe the I2C circuitry needs some time before working properly?
Maybe I should do some investigation.

I just thought, that somebody would know right away...

Flo

PS: Just did a measurement for you - Ray: The display, displaying 2 lines of size 2 text is needing a little less than 4mA!!

So after one day of investigation:

Using the mentioned PowerDown library and the DHT library together won't work.

Inside the DHT-Lib there are some parts to catch unwanted behaviour - eg. if someone won't wait the 2seconds between DHT-reads.

So the DHT-Lib uses "millis()" to check the time between reads. If it is lower than 2s the last read value is returned - exactly the behaviour I noticed.

Obviously the "millis" function is disabled with the Powerdown functions, so that the 2s measured by millis will be reached definitely NOT in 2s, but in an even much longer time.

Flo

In power down mode, the processor is stopped, and cannot execute functions in the DHT library, so you have nothing to worry about.

When power up mode is resumed, you must restore all the appropriate power on states, and turn on interrupts.

See this excellent tutorial.

Hi again

Did some more investigation.

Firstly - no offense please - I have to complain to various coders for NOT DOCUMENTING their libraries which is very annoying because it cost much time to find out what the code is doing!

So if someone want to use “idle” or “sleep” functions in their code AND use libraries which depend on a correct function of “millis()” which you have to find out eventually they have to either change the libs code or use this:

extern volatile unsigned long timer0_millis;

where “timer0_millis” is the actual variable where “millis” are stored

this line goes in the global scope (where the #include" instructions are)

and then:

cli() //disable Interrupts so that the timer_0 interrupts won't interfere while adding something to "timer0_millis"
timer0_millis = timer0_millis + "constant"; // where "constant" is the idle/sleep time in milliseconds
sei()  //enable interrupts

in your code AFTER the “idle/sleep” instruction
You don’t need to save the status because this is no service routine…

Maybe this helps

Flo

Hi jremington

jremington:
In power down mode, the processor is stopped, and cannot execute functions in the DHT library, so you have nothing to worry about.

I know! It should not execute code in any library. Like I said: powerdown influences the DHT-lib so that it’s errorhandling won’t work!
Like I wrote in my post before:
The DHT-Lib depends on a correct functioning of “millis()”.
Because I haven’t found a description on the DHT lib and after having found out that it uses “millis()” I had to find out how millis are working. It took quite a long time to find out.
“millis” are a part of lib “wire” and millis are not a timer value but a value that is incremented by a timer! I assumed, that this timer keeps on running in idle/sleep mode, which it obviously does. But its interrupts are blocked because of “powerdown” state so the timer0_millis are NOT incremented, so that the DHT library thinks that I’m trying to read the sensor more often than every two seconds (which is the minimum time for reading).

jremington:
When power up mode is resumed, you must restore all the appropriate power on states, and turn on interrupts.

See this excellent tutorial.

No, I do not have to do anything, because the code inside the rocketscream powerdown lib is doing that for me…

Thanks

Flo