DHT11s read fine from loop, but first one in list doesn't work when in timer...?

I've got a bit of a puzzler.

This is my first big project with an Arduino (Mega 2560,) and I've run into something odd. In the code pasted in below, things work perfectly as-is. However, if I comment out the readSensors() call inside the loop and uncomment it from inside the timer, the first sensor will always fail to read. I've tried different inputs, flopping the inputs, adding in a delay, and no matter what I've tried, whatever input is first will fail to read every time when I call the function from inside the timer. I also tried placing the read code directly inside the timer, and experienced the same results.

Any thoughts? :confused:

#include "DHT.h"

#define DHTPIN 9     // left sensor
#define DHTPIN2 8  //right sensor
#define gled 10 //green LED
#define rled 11 //red LED
int timer1_counter;
int timer_cycle;

#define DHTTYPE DHT11

DHT dht(DHTPIN, DHTTYPE);
DHT dht2(DHTPIN2, DHTTYPE);

void setup()
{
  Serial.begin(9600);
  dht.begin();
  dht2.begin();
  pinMode(gled, OUTPUT);
  pinMode(rled, OUTPUT);

  // initialize timer1 
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;

  // Set timer1_counter to the correct value for our interrupt interval
  timer1_counter = 3036;   // ends up with 1 second intervals
  
  TCNT1 = timer1_counter;   // preload timer
  TCCR1B |= (1 << CS12);    // 256 prescaler 
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
  interrupts();             // enable all interrupts
}

ISR(TIMER1_OVF_vect)        // interrupt service routine 
{
  TCNT1 = timer1_counter;   // preload timer
  timer_cycle++;
  digitalWrite(gled, digitalRead(gled) ^ 1); //blink green LED
  if (timer_cycle >= 10) { //execute every 10s
    timer_cycle = 0;
    digitalWrite(rled, digitalRead(rled) ^ 1); //toggle red LED
    //readSensors();

  }
}

void loop()
{
  readSensors();
  delay(5000);
}

void readSensors() {
     // 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();
    float t = dht.readTemperature();
    h = dht.readHumidity();
    t = dht.readTemperature();
    float h2 = dht2.readHumidity();
    float t2 = dht2.readTemperature();
  
    if (isnan(t)) { t = 0; }
    if (isnan(t2)) { t2 = 0; }
    if (isnan(h)) { h = 0; }
    if (isnan(h2)) { h2 = 0; }

    Serial.print("Humidity 1: "); 
    Serial.print(h);
    Serial.print(" %\t");
    Serial.print("Temperature 1: "); 
    Serial.print(t * 1.8 + 32);
    Serial.println(" *F");
    Serial.print("Humidity 2: "); 
    Serial.print(h2);
    Serial.print(" %\t");
    Serial.print("Temperature 2: "); 
    Serial.print(t2 * 1.8 + 32);
    Serial.println(" *F");
    Serial.println(""); 
}

Have you looked at this page? Arduino Playground - HomePage

In there, you'll find a reference to an interrupt-driven version of the library. Follow that, and then follow the link to the GitHub page for the library. It's at GitHub - niesteszeck/idDHT11: Interrupt driven DHT11 library

No idea if that's what you want, but it might be worth a look.

It is difficult to have this type of code in an ISR. Basically you get a deadlock as the code fills the serial buffer and waits for the UART to transmit but that does not happen as the UART ISR is locked out.

I recommend that you remove the ISR and use millis() for period checking in the loop().

Cheers!

The timing of DHT sensors is quite critical and easily affected if an ISR takes too long.
Without diving into the code you could disable the interrupts when reading the sensor.

Awesome, thanks so much everyone :slight_smile:

I've been doing a lot of reading and Googling this morning, and this is kind of a basic summary of what I've learned (that's related to this particular issue anyhow.)

The fact that I'm calling the function (or executing the code) from within the ISR is most likely the culprit. It appears that when you're in an ISR, all the timers are disabled, including the ones that are used by millis(), etc. I'm using the Adafruit DHT11 library currently, and it does depend on the millis() function (among others,) so that would explain things.

It looks like the reason that throwing the code out into a separate function doesn't work is because it's executing the function synchronously, so at that point, the timers are still disabled since I'm still technically in the ISR. Out of curiosity, is there a way to call a function asynchronously? (Sorry, my C is a bit rusty, it's only been, oh, 15 years since I've done any C programming at this point...)

Lar3ry, I'll definitely look into that interrupt-based DHT library, it might help out with what I'm trying to do.

If nothing else, I'll either switch over to using millis(), or just use the ISR to set a flag to have the function kick off as a part of my loop().

Thanks so much everyone! So much to learn :slight_smile: I'm looking forward to the day when I'll feel like I'm able to contribute instead of just asking a myriad of questions... :stuck_out_tongue:

I've had a little bit of time this evening to play around with things, and this is where I'm at currently:

I looked into the DHT11 library that relied on interrupts, and it would definitely work awesomely if I just needed to poll one or two DHT11's. However, it looks like it needs a dedicated interrupt pin on the Arduino for each sensor that gets polled, which means that you could realistically poll 3 or maybe 4 sensors on a Mega, since they have 6 timers total. However, eventually I plan on having up to 12 DHT11's attached to my board, so that won't work long-term unfortunately :confused:

So, instead, I just modified my code to use the interrupt to set a flag to have my loop() call the readSensors() function, which works as expected.

I would like to, if possible, have this function execute asynchronously though, because once I get my project fully implemented, it'll have around 30 sensors attached to it, which will all get polled during that function call, so it could potentially put the main loop() on hold for a little while.

I know that normally this probably wouldn't be a big deal, but my plans for this project are to do a home environmental monitor. The readSensors() function I'm planning to have dump the data to the serial line to be picked up by a computer to do logging to a mySQL database at regular intervals, while the main loop() will do stuff like check a water leak sensor, etc. To that end, I'd hate to have a water leak go undetected for 30-45 seconds so that I can record the temperatures upstairs into a database.

I found where you can do a std::async function call in regular C code, but it doesn't look that that's possible in the Arduino IDE? (It throws the error "'async' is not a member of 'std'".) Would this maybe be a job for TimedAction? Arduino Playground - TimedAction Library

Current code:

#include "DHT.h"

#define DHTPIN 9     // what pin we're connected to
#define DHTPIN2 8
#define gled 10
#define rled 11
#define yled 7
int timer1_counter;
volatile int timer_cycle;
volatile boolean pollSensors = false;

#define DHTTYPE DHT11   // DHT 11 

DHT dht(DHTPIN, DHTTYPE);
DHT dht2(DHTPIN2, DHTTYPE);

void setup()
{
  Serial.begin(115200);
  dht.begin();
  dht2.begin();
  pinMode(gled, OUTPUT);
  pinMode(rled, OUTPUT);
  pinMode(yled, OUTPUT);

  // initialize timer1 
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;

  // Set timer1_counter to the correct value for our interrupt interval
  timer1_counter = 3036;   // preload timer 65536-16000000/256/2
  
  TCNT1 = timer1_counter;   // preload timer
  TCCR1B |= (1 << CS12);    // 256 prescaler 
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
  interrupts();             // enable all interrupts
}

ISR(TIMER1_OVF_vect)        // interrupt service routine 
{
  TCNT1 = timer1_counter;   // preload timer
  timer_cycle++;
  digitalWrite(gled, digitalRead(gled) ^ 1);
  if (timer_cycle >= 10) {
    timer_cycle = 0;
    digitalWrite(rled, digitalRead(rled) ^ 1);
    pollSensors = true;
  }
}

void loop()
{
  if (pollSensors) {
    readSensors();
  }
}

void readSensors() {
    // Reading temperature or humidity takes about 250 milliseconds!
    // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
    analogWrite(yled,100);
    float h = dht.readHumidity();
    float t = dht.readTemperature();
    h = dht.readHumidity();
    t = dht.readTemperature();
    float h2 = dht2.readHumidity();
    float t2 = dht2.readTemperature();
  
    if (isnan(t)) { t = 0; }
    if (isnan(t2)) { t2 = 0; }
    if (isnan(h)) { h = 0; }
    if (isnan(h2)) { h2 = 0; }

    Serial.print("Humidity 1: "); 
    Serial.print(h);
    Serial.print(" %\t");
    Serial.print("Temperature 1: "); 
    Serial.print(t * 1.8 + 32);
    Serial.println(" *F");
    Serial.print("Humidity 2: "); 
    Serial.print(h2);
    Serial.print(" %\t");
    Serial.print("Temperature 2: "); 
    Serial.print(t2 * 1.8 + 32);
    Serial.println(" *F");
    Serial.println("");
    analogWrite(yled,0);
    pollSensors = false;
}

Not to muddy the waters, but... Have you considered using adressable sensors that talk
microwire or SPI? Probably a little more money (LM74 is around $2 at digikey) but would eliminate
running out of pins. AND you get to start over with your programming!!
Just a thought..

I'd definitely be up for it at some point, and that would definitely reduce the amount of low-voltage wiring I need to run between the basement and second floor :wink: But, at least for right now, I have to stick with the DHT11's.

Something I've noticed is - if I wrap my loop() with code that turns an LED on and off (beginning and end of the loop,) using the TimedAction code (which relies on millis()) causes a substantially more noticeable flicker on the LED than using the interrupts to set a flag to do the checks. So, even though both methods result in the main loop() being put on pause when the sensor reading function is running, doing the checks with interrupts seems to be much more efficient.