DHT 11/22 Sensor using input capture interrupt

Hi everyone,

I’ve been working on a project lately that requires me to use a DHT22 or DHT11 sensor (AM2302 and many other names).

My project requires me to use several different types of interrupts. I have an output compare interrupt which causes a flag to be set every 2.5s. In the void loop(void) function, I check for this flag, and try to read the data from the DHT11/DHT22 sensor. Additionally, I also need a serialEvent interrupt.

However, the libraries provided by many websites (including the adafruit library) dont work with my other interrupts. This may/may not have something to do with those libraries using commands such as Interrupts() and noInterrupts().

I wrote an code using an input capture interrupt to read the data from the sensor. Every time the Arduino pin 8 (input capture pin) detected a change in signal, the value of the TCNT1 register is copied to some array, who’s index is incremented before the next transition occurs. After each input capture, the TCNT1 was set back to zero.

Below is a code of showing my problem. Please let me know if I am not being clear enough. The code below does not seem to always have the right data sent from the sensor. I have tried with many sensors, all with the same result. Can anyone try and play with my code and see where the error(s) are?

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>


volatile unsigned int logic[85];                  // holds the count values for input capture timing--> 82 since there are 2 edges for every high pulse, plus the handshake 
volatile unsigned int sensor_data[85];
volatile unsigned char index = 0;                 // array index for logic[]
volatile unsigned char index2 = 0;
unsigned char capture_reg_high;
unsigned char capture_reg_low;
unsigned char a = 0;
unsigned char flag1 =0;
unsigned char b = 0;


volatile unsigned char flag = 0x00;





void setup()
{
    pinMode(8, OUTPUT);
   

    Serial.begin(9600);    
    delay(2000);  
    
    Timer_OutputCompare_open();
    Timer_InputCapture_open();
   
    interrupts();               

 
}

void loop()
{
    // READING THE INPUTS    
    if (flag)           // if its time to wakeup the DHT22 sensor (every 2.5 s)
    {
        flag = 0;             // clear the flag
        wakeup_DHT();    // retrieve TCNT1 values from the DHT22 sensor and save into the logic array
    }
    
   
    if (flag1)           // if the sensor is finished sending us the data  
    {
        flag1 = 0; //&= 0xFD;
        index2 = 0;
        
        for(b=1; b<100; b++)
        {
            if (logic[b] < 500)
            {
                sensor_data[index2] = 0;
                index2++;
            }
            
            else if (logic[b] > 1000)
            {
                sensor_data[index2] = 1;
                index2++; 
            }
        }
        
        for(b=0; b<100; b++)
        {
            Serial.print(index, DEC);
            Serial.println(" = ");
            Serial.print(logic[b], DEC);
            Serial.println("\n\r");
        }
        
        for(index2=0; index2<85; index2++)
        {
            Serial.print(index2, DEC);
            Serial.println(" ok= ");
            Serial.print(sensor_data[index2], DEC);
            Serial.println("\n\r");
        }   
    }
}
    


// For sending data to the PC every 10ms
// output comparing every 1ms, which will be counted 10 times in the ISR before sending the value
void Timer_OutputCompare_open(void)
{
 TCCR0A = 0x02; // TCCR0A = (1 << WGM01) Set CTC Bit for output compare mode
 TCCR0B = 0x03; // TCCR0B = prescaler 64...gives a clock at 4us
 OCR0A = 250; // holds the count value to output compare at 1ms for the 2.5 second interval of waking of the DHT sensor
 OCR0B = 250;            // holds the count value to output compare at 1ms for the 1ms custom delay function
 TIMSK0 = 0x02; // enable interrupt on both channel A, which is used to wakeup the DHT timer
}


// For measuring the pulse width of the DHT22 signal, to decode the readings
void Timer_InputCapture_open(void)
{
 TCCR1A = 0x00; // Normal timer operating mode
 TCCR1B = 0x01; // prescaler to 8, detect rising edge (which should be the rising edge of the sensors handshake response)
 TCCR1C = 0x00;
 TIMSK1 = 0x00; // initially disable interrupts until the sensor has been woken up
}




/*
    functions below are for reading and analyzing values from the DHT22 sensor
*/




char wakeup_DHT(void)
{
        pinMode(8, OUTPUT);
        digitalWrite(8,HIGH);
        delay(5);
        digitalWrite(8,LOW); // Host need to pull down the pulse for 2 ms
        delay(2);
        digitalWrite(8,HIGH);  // Host needs to pull up the pulse for 30 us
        delayMicroseconds(28);          // We want the logic high for 30 us and the digitalwrite command takes about 2 us to compute
        digitalWrite(8,LOW);
        pinMode(8, INPUT);
        TIMSK1 = 0x20;        // enable input capture interrupt
}
    


// output compare interrupt service routine for sending data to the PC
// output compares every 1ms, but counted 10 times for 10ms
ISR(TIMER0_COMPA_vect)
{
    counter_interrupt++;

    if (counter_interrupt >= 2500)
    {
        counter_interrupt = 0;
        Serial.println("Waking up DHT22\n");
        flag = 1;
    }
}



// prescaler set to 1 to have the highest resolution
// counter is reset to 0 after every compare match
// although this makes the detection of pulses less accurate, it prevents the TCNT1 from reaching 0xFFFF and rolling over
ISR(TIMER1_CAPT_vect)
{
    capture_reg_low = ICR1L;
    capture_reg_high = ICR1H;
    
    TCNT1 = 0x0000;
    logic[index] = ((((int)capture_reg_high << 8) & 0xFF00) | (((int)capture_reg_low) & 0x00FF));
   
    index++;
   
    // toggle the edge triggering 
    if (TCCR1B & 0x40)
        TCCR1B &= 0xBF;
    else
        TCCR1B |= 0x40;
    
    if (index > 80)    
   {
        flag1=1; 
        TIMSK1 = 0x00;
   }
}

*Note: the first array logic contains the values of TCNT1.
**Note: the second array sensor_data is supposed to contain the actual 40 bit sequence sent by the sensor

Thank you kindly,

Robert

Hi, welcom to the forum.

Have you been using AVR programming, and you started with Arduino ? Which Arduino board do you use ? I can't compile it.

Do you use Arduino IDE 1.6.1 ? Where is your DHT library ? If that is in the sketch, do you check the checksum ?

Why do you use the DHT11/DTH22 anyway ? There are very inaccurate for humidity.

I don't understand why you need a timer and interrupt every 2.5s to get data from the DTH11. In the Arduino environment, we use millis().

Do you need a fast interrupt routine ? which one is it ? what does it do ?

I needed a fast interrupt myself. I removed every interrupt() and noInterrupt from the DHTxx library, and let it run in the background. Only when a valid checksum is received, I accept the data. Sometimes only 1 in 10 is valid data.

Peter_n, Thanks for your reply.

I have much experience programming Freescale microcontrollers. For the particular project I am working on, I am required to use an Arduino UNO R3. I think the Arduino IDE version I am using is 1.0.6.

The reason why there is an output compare interrupt, is to ensure that the sensor is read only every 2.5s. According to the datasheet of the DHT 11/22, the sensor can only be read every 2 seconds.

I cannot use a delay() command in my code. It will screw up the timing of my serial Receive interrupt, and some other time-based interrupts I am using. This is why I am using the output compare.

The reason you couldn’t compile the code is because when I removed alot of the code not related to the post, I accidentally deleted the variable declaration for the “counter_interrupt”. I have added it and reposted the code below.

I have not yet calculated the checksum. Just receiving the 40 bit sequence is the first priority. After I am sure that the input capture interrupt is working, I will create a function to analyze the 40 bits sent, and calculate the checksum.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>


volatile unsigned int logic[85];                  // holds the count values for input capture timing--> 82 since there are 2 edges for every high pulse, plus the handshake 
volatile unsigned int sensor_data[85];
volatile unsigned char index = 0;                 // array index for logic[]
volatile unsigned char index2 = 0;
unsigned char capture_reg_high;
unsigned char capture_reg_low;
unsigned char a = 0;
unsigned char flag1 =0;
unsigned char b = 0;
volatile unsigned counter_interrupt;


volatile unsigned char flag = 0x00;





void setup()
{
    pinMode(8, OUTPUT);
   

    Serial.begin(9600);    
    delay(2000);  
    
    Timer_OutputCompare_open();
    Timer_InputCapture_open();
   
    interrupts();               

 
}

void loop()
{
    // READING THE INPUTS    
    if (flag)           // if its time to wakeup the DHT22 sensor (every 2.5 s)
    {
        flag = 0;             // clear the flag
        wakeup_DHT();    // retrieve TCNT1 values from the DHT22 sensor and save into the logic array
    }
    
   
    if (flag1)           // if the sensor is finished sending us the data  
    {
        flag1 = 0; //&= 0xFD;
        index2 = 0;
        
        for(b=1; b<100; b++)
        {
            if (logic[b] < 500)
            {
                sensor_data[index2] = 0;
                index2++;
            }
            
            else if (logic[b] > 1000)
            {
                sensor_data[index2] = 1;
                index2++; 
            }
        }
        
        for(b=0; b<100; b++)
        {
            Serial.print(index, DEC);
            Serial.println(" = ");
            Serial.print(logic[b], DEC);
            Serial.println("\n\r");
        }
        
        for(index2=0; index2<85; index2++)
        {
            Serial.print(index2, DEC);
            Serial.println(" ok= ");
            Serial.print(sensor_data[index2], DEC);
            Serial.println("\n\r");
        }   
    }
}
    


// For sending data to the PC every 10ms
// output comparing every 1ms, which will be counted 10 times in the ISR before sending the value
void Timer_OutputCompare_open(void)
{
 TCCR0A = 0x02; // TCCR0A = (1 << WGM01) Set CTC Bit for output compare mode
 TCCR0B = 0x03; // TCCR0B = prescaler 64...gives a clock at 4us
 OCR0A = 250; // holds the count value to output compare at 1ms for the 2.5 second interval of waking of the DHT sensor
 OCR0B = 250;            // holds the count value to output compare at 1ms for the 1ms custom delay function
 TIMSK0 = 0x02; // enable interrupt on both channel A, which is used to wakeup the DHT timer
}


// For measuring the pulse width of the DHT22 signal, to decode the readings
void Timer_InputCapture_open(void)
{
 TCCR1A = 0x00; // Normal timer operating mode
 TCCR1B = 0x01; // prescaler to 8, detect rising edge (which should be the rising edge of the sensors handshake response)
 TCCR1C = 0x00;
 TIMSK1 = 0x00; // initially disable interrupts until the sensor has been woken up
}




/*
    functions below are for reading and analyzing values from the DHT22 sensor
*/




char wakeup_DHT(void)
{
        pinMode(8, OUTPUT);
        digitalWrite(8,HIGH);
        delay(5);
        digitalWrite(8,LOW); // Host need to pull down the pulse for 2 ms
        delay(2);
        digitalWrite(8,HIGH);  // Host needs to pull up the pulse for 30 us
        delayMicroseconds(28);          // We want the logic high for 30 us and the digitalwrite command takes about 2 us to compute
        digitalWrite(8,LOW);
        pinMode(8, INPUT);
        TIMSK1 = 0x20;        // enable input capture interrupt
}
    


// output compare interrupt service routine for sending data to the PC
// output compares every 1ms, but counted 10 times for 10ms
ISR(TIMER0_COMPA_vect)
{
    counter_interrupt++;

    if (counter_interrupt >= 2500)
    {
        counter_interrupt = 0;
        Serial.println("Waking up DHT22\n");
        flag = 1;
    }
}



// prescaler set to 1 to have the highest resolution
// counter is reset to 0 after every compare match
// although this makes the detection of pulses less accurate, it prevents the TCNT1 from reaching 0xFFFF and rolling over
ISR(TIMER1_CAPT_vect)
{
    capture_reg_low = ICR1L;
    capture_reg_high = ICR1H;
    
    TCNT1 = 0x0000;
    logic[index] = ((((int)capture_reg_high << 8) & 0xFF00) | (((int)capture_reg_low) & 0x00FF));
   
    index++;
   
    // toggle the edge triggering 
    if (TCCR1B & 0x40)
        TCCR1B &= 0xBF;
    else
        TCCR1B |= 0x40;
    
    if (index > 80)    
   {
        flag1=1; 
        TIMSK1 = 0x00;
   }
}

I will try your method of removing every instance of Interrupts() and noInterrupts().

Thanks so much for your help,

Robert

You have to say the magic word : millis() O well, you don't have to say it, but you should use it.

millis() returns the number of milliseconds in an unsigned long. It can be used for timing, for running code every 2.5 seconds, for delays, for doing multiple things at once. It's pure magic.

Start with the example: Blink Without Delay

So what about using the input capture interrupt? This is what I am looking for the solution to. As far as I know, the millis() command just returns an unsigned long of the number of milliseconds since the program has started.

I am interested in the time of a pulse width. Excuse me if I sound ignorant, but would you point me to an example that solves this particular solution?

Currently as we speak, I am making the modifications to the DHT library that you suggested in your earlier post.

I'll let you know if it works for me.

Thanks,

Robert

Would you be able to send me your modified library? If i gave you my email?

I think we don't understand each other yet (English is not my native language).

The serialEvent() is not an interrupt. The callback function is called after each loop().

If the normal DHTxx libraries are a problem for other interrupts, then I don't understand what is gained by adding more interrupts for the DHTxx sensors.

Are those other interrupts not in the sketch ? And every interrupt in the sketch is for the DTHxx sensors ? Measuring the pulse width is not for those other important interrupts but for the DTHxx ? Perhaps you can drop every interrupt routine for the DTHxx and use millis() for the 2.5s and timing specific code with short delay() (with or without interrupts).

I think that I used a modified Adafruit library. But that was for an ATmega8, and I don't have that here. This uses less disabling of interrupts : http://playground.arduino.cc/Main/DHTLib As you can see here : https://github.com/RobTillaart/Arduino/blob/master/libraries/DHTlib/dht.cpp it uses a delay (18ms I think), and it waits for the sensor signal.

During a delay, interrupts are handled normally.

Are those small delays a problem ?