ATMega sleep and millis timer

Hi all

I’m building some low power RF sensor nodes and putting the ATmega328P in sleep (actually power down) mode between transmissions.

An issue that I’ve encountered is that whilst the ATmega is sleeping the millis timer stops causing the DHT library to return the same temperature until the ATmega has been awake for over 2 seconds.

In order to get around this I’ve been incrementing the millis timer manually with the duration of the sleep which initially seemed fine.

Having used this setup for a while I’ve noticed that the sensor reading is slow to adjust to big changes in temperature (e.g. blowing/breathing hot air just before the reading is taken) compared to taking readings with a delay of the same duration.

Has anyone been in this situation before or have any alternative solutions which still keep the low power requirement (looking at <1mA draw in sleep mode)?

Thanks

Yes, when the DTH is busy, keep the timers and interrupts running by sleeping less deep. For a battery powered Arduino I sometimes replace the delay() with my own function that delays by sleeping.

Peter_n: Yes, when the DTH is busy, keep the timers and interrupts running by sleeping less deep. For a battery powered Arduino I sometimes replace the delay() with my own function that delays by sleeping.

What do define as "busy"?

The current cycle is: read temp > sleep 15s > read temp > sleep 15s > back to start etc

I was thinking about the short delay while communicating with the DHT sensor. It is of course possible to sleep with power-down-mode in between. Now I understand your question ;) So when the Arduino awakes after 15 seconds, the DHT library is confused.

It should be no problem, and I don't know what could be wrong. Perhaps the DHT library is too much over the top with the timing. Can you show your sketch and tell which DHT library you use ?

I’m using the adafruit dht library at the moment on a 8MHz/3.3v Pro Mini clone.

Here’s a cut down version of the usage in my sketch:

#include <DHT.h>
#include <LowPower.h>

const byte DHT_PIN = 3;
const byte DHT_TYPE = 22;

DHT dht(DHT_PIN, DHT22, 1);

void setup() {
  Serial.begin(115200);
  dht.begin();
  delay(2000);
}

void loop() {
  float t = dht.readTemperature();
  Serial.println(t);
  Serial.flush();
  LowPower.powerDown(SLEEP_4S, ADC_OFF, BOD_OFF);
  extern volatile unsigned long timer0_millis;
  noInterrupts();
  timer0_millis += 4000;
  interrupts();
}

As you can see there’s a 4 second sleep between each reading which gives me time to blew hot air on the sensor.

Here’s the results:

21.00

21.00
(Blew hot air here)
21.00

21.70

21.40

21.20
(Blew more hot air here)
21.00

21.10

22.20

21.90

21.70

The rise in temperature isn’t being picked up until two, sometimes three, readings later.

The only meaningful state kept by the DHT class is that last update time. So, you can just create / destroy an instance when you need to take a reading. Something like this…

#include <DHT.h>
#include <LowPower.h>

const byte DHT_PIN = 3;
const byte DHT_TYPE = 22;

void setup() {
  Serial.begin(115200);
  delay(2000);
}

static float readTemperature()
{
  DHT dht(DHT_PIN, DHT22, 1);
  dht.begin();
  return( dht.readTemperature() );
}

void loop() {
  float t = readTemperature();
  Serial.println(t);
  Serial.flush();
  LowPower.powerDown(SLEEP_4S, ADC_OFF, BOD_OFF);
}

I've just tried your suggestion and its still the same.

I've come to the conclusion that it's probably just the way the DHT sensor works. I've tried another library and even replaced the powerDown with a delay and on the longer times it doesn't register the change in temperature until a reading or two down the line.

This is the Adafruit DHT library : https://github.com/adafruit/DHT-sensor-library

The LowPower library can be this one : https://github.com/rocketscream/Low-Power Or this one : https://github.com/rocketscream/Low-Power

I think the way the Adafruit DHT library use millis() might indeed conflict with your use of the sleep mode. Beside that, I think they use millis() in the wrong way. I think it is not an easy fix, a part needs to be rewritten.

Perhaps you better use this library : http://playground.arduino.cc/Main/DHTLib https://github.com/RobTillaart/Arduino/tree/master/libraries/DHTlib

Hi Peter

That's the low power library I use, and I tried Rob's DHT library earlier too and the outcome was the same.

It's as if when you call read Temp it pulls the value out of the sensor's registers and the sensor populates the register with the value from that point in time. So when you call read again it sends the temperature value from the previous time the read was called.

The “collecting period” is 2 seconds. I think that means that only once in two seconds the data can be requested. Or perhaps you are right and only after 2 seconds the data is put into the registers ?

The DHT22 is in some kind of low-power-mode. The start pulse of 18ms starts the sensor I always assumed that after that the sensor measures the temperature and humidity which are transferred directly after that.

I did a search in my projects, and I have used both the Adafruit and the robtillaart version, but I don’t have a DHTxx in a battery powered project.

This is the delay that I use for short delay, while the Arduino is waiting for a response of a sensor and so on.
It keeps all the timers and interrupts running. The TIMER0 is running, so millis() is updated. After the TIMER0 interrupt (and update of millis()), the function enters sleep mode again until the time is done.

// A delay of 0 could go wrong.
void delaySleep( unsigned long t)
{
  unsigned long startMillis = millis();
  unsigned long currentMillis;

  set_sleep_mode (SLEEP_MODE_IDLE);  
  
  do
  { 
    sleep_mode();
    currentMillis = millis();
  }
  while( currentMillis - startMillis <= t);
}

Peter,

Are you able to see if you can replicate my issue on your hardware? e.g. if your blow/breathe on your sensor does the temperature rise on the next reading?

Thanks

How fast do you expect the sensor to respond to changes in ambient temperature? Even if it is small, it will have some thermal resistance.

Test With Arduino 1.6.1, Arduino Uno, DTH22, and using an old test sketch I found. No sleep mode, and the DHT22 is always powered and the signal line is always pulled up. Delay between reading the DHT22 is 30 seconds, and only the temperature is used with a single read from the sensor. After stabilizing it reads 23.20 degrees. I start blowing at it after 10 seconds for 10 seconds long. Next read : 23.20 degrees :o Next read : 27.20 degrees (that is me blowing more than 40 seconds ago) :o :o :o

New test One minute delay in between, and using a hairdryer (let's melt the little thing). After reading the temperature, I start heating up the DTH22 and keep doing that until I see a change in temperature. Next temperature sample is still 23 degrees, and only the sample after that is 40 degrees. That means I can see the temperature change after two minutes !

"CONFIRMED" tavalin, I can replicate the buffering. I didn't know that.

Would it be possible to send a start pulse and read 2 seconds later the data of 2 seconds ago ? With a sleep mode during the 2 seconds.... I tried but it fails. The complete data transfer has to be finished.

Reading data from the DHT22 takes 20ms (the start delay) plus 0.8ms data transfer. During the 20ms it is possible to sleep, so the Arduino is awake during 0.8ms. If that is done every 2 seconds, that isn't so bad ? is it ?

Peter_n: "CONFIRMED" tavalin, I can replicate the buffering. I didn't know that.

Thanks Peter, glad to know it's reproducible! Also glad to know it's not attributed to putting the ATmega to sleep like I initially thought!

aarg: How fast do you expect the sensor to respond to changes in ambient temperature? Even if it is small, it will have some thermal resistance.

aarg, the problem as you can see from Peter's second test is that even though he was reading the sensor every minute, the change in temperature wasn't reported until 2 minutes later. Depending on your use case, knowing the temperature 2 minutes ago, or longer, isn't that useful.

From datasheet - see attachment (AM2302 == DHT22)

Note: the temperature and humidity data read by the host from the AM2302 is always the last measured value, such as the two measurement interval is very long, continuous read twice to the second value of real-time temperature and humidity values, while two readtake minimum time interval be 2S.

AM2302.pdf (570 KB)