Here's a timer-based approach for a Mega2560 that you can adapt to a Nano by using pin D8 and Timer1 instead of Timer4.
/*
* Sketch: sketch_dec06a
* Target: Mega2560 (can be adapted to Nano)
*
*/
#define SNSR_READ_INT 1500ul //mS
#define SNSR_TIMEOUT 50ul //mS
//
#define MIN_ZERO_WIDTH 1040 //# uS = N*62.5nS 65uS nominal is 77uS
#define MAX_ZERO_WIDTH 1520 //# uS = N*62.5nS 95uS
//
#define MIN_ONE_WIDTH 1600 //# uS = N*62.5nS 100uS nominal is 120uS
#define MAX_ONE_WIDTH 2240 //# uS = N*62.5nS 140uS
//
#define PP_MIN_WIDTH 1120 //# uS = N*62.5nS 70uS
#define PP_MAX_WIDTH 1440 //# uS = N*62.5nS 90uS
enum eisrStates
{
ICP_PP_START = 0,
ICP_PP_TIMEPP,
ICP_DATA
};
const uint8_t
pinDbg = 7,
pinDHT11 = 49; //ICP4
uint32_t
tTimeout;
volatile uint8_t //5 bytes is 40 bits, enough to hold DHT11 data packet
grDHT11Data[5];
volatile bool
bDataFlag = false;
void setup()
{
Serial.begin(115200);
pinMode( pinDHT11, INPUT );
pinMode( pinDbg, OUTPUT );
digitalWrite( pinDbg, LOW );
//set-up input capture on pin 49 (ICP4), prescaler to /1
TCCR4A = 0;
//TCCR4B = _BV(ICNC4) | _BV(CS40);
TCCR4B = _BV(CS40);
}//setup
void loop()
{
static uint8_t
stateSensor = 0;
static uint32_t
tSensor,
tDHT = 0ul;
uint32_t
tNow = millis();
switch( stateSensor )
{
case 0:
//periodically read the sensor
if( (tNow - tDHT) >= SNSR_READ_INT )
{
//start by sending the start pulse
dht11Start();
//save the time now for the next reading
tDHT = tNow;
stateSensor++;
}//if
break;
case 1:
//either data finishes or we time out
if( (tNow - tTimeout) >= SNSR_TIMEOUT )
{
//timed out waiting for the sensor
Serial.println( "ERROR: Sensor time-out" );
stateSensor = 0;
}//if
else if( bDataFlag )
{
//data ready
showDHTData();
stateSensor = 0;
bDataFlag = false;
}//else
break;
}//switch
}//loop
void showDHTData( void )
{
char
szT[20];
#if 1
//human readable
sprintf( szT, "RH=%d.%d%% T=%d.%doC", grDHT11Data[0], grDHT11Data[1], grDHT11Data[2], grDHT11Data[3] );
Serial.println( szT );
#else
//raw data
for( uint8_t i=0; i<5; i++ )
{
if( grDHT11Data[i] < 0x10 )
Serial.write( '0' );
Serial.print( grDHT11Data[i], HEX );
Serial.write( ' ' );
}//for
uint8_t sum = 0;
for( uint8_t i=0; i<4; i++ )
sum = sum + grDHT11Data[i];
if( sum == grDHT11Data[4] )
Serial.print( " (CRC OK)" );
else
Serial.print( " (CRC BAD)" );
Serial.write( '\n' );
#endif
}//showDHTData
void dht11Start( void )
{
//set the data pin to output and drive it low
pinMode( pinDHT11, OUTPUT );
digitalWrite( pinDHT11, LOW );
//give it a start pulse >= 18mS
delay( 30 );
//set the pin high, then revert to pull-up to release the line
digitalWrite( pinDHT11, HIGH );
pinMode( pinDHT11, INPUT );
//set up the input capture 4
noInterrupts();
TCCR4B &= ~(_BV(ICES4)); //look for falling edge
TIFR4 |= _BV( ICF4 ); //clear any existing flags
TIMSK4 |= _BV( ICIE4 ); //enable the input capture
tTimeout = millis(); //so we can check for timeout in loop()
interrupts();
}//dht11IssueStart
ISR( TIMER4_CAPT_vect )
{
static bool
bEdge = false;
static uint16_t
tStart;
static uint8_t
stateDHT = ICP_PP_START,
mask,
index;
uint16_t
tDur,
tEdge = ICR4;
switch( stateDHT )
{
case ICP_PP_START:
//after the start pulse, we set ICP4 to falling edge to detect the beginning of the presence pulse
//save the time of this interrupt and set the edge to rising so we can measure its width
tStart = tEdge;
TCCR4B |= _BV(ICES4); //set rising edge interrupt
stateDHT = ICP_PP_TIMEPP; //move to time the PP width
break;
case ICP_PP_TIMEPP:
//if here, we got a rising edge
//is the duration of that pulse within spec?
tDur = tEdge - tStart;
if( (tDur >= PP_MIN_WIDTH) && (tDur <= PP_MAX_WIDTH) )
{
//PP was a good width; we are ready to start receiving data
//set up for falling edge interrupts
bEdge = false; //first falling edge is "special"
TCCR4B &= ~(_BV(ICES4)); //falling edges
mask = 0x80; //data us received msb first
index = 0; //index into our 5-byte array of data bytes
memset( grDHT11Data, 0, sizeof( grDHT11Data ) ); //clear the buffer
stateDHT = ICP_DATA; //and move to collect data
}//if
else
{
//invalid presence pulsewidth ;turn off ints and go back to start
//loop() will timeout as recovery
TIMSK4 &= ~_BV( ICIE4 );
stateDHT = ICP_PP_START;
}
break;
case ICP_DATA:
//collecting data here
//is this the first falling edge after the presence pulse?
switch( bEdge )
{
case false:
//yes; just save the time and set the flag true so we go to the next state
//on the following interrupts
//log time of first falling edge
tStart = tEdge;
bEdge = true;
break;
case true:
//get the time between falling edges
//for a 0:
//
// _____a ___b a-b time is nominally 77uS
// |__| |_
//
//for a 1:
//
// _____a ______b a-b time is nominally 120uS
// |__| |_
//
tDur = tEdge - tStart;
//we need to save the time of this falling edge because it's actually the start of the next bit
tStart = tEdge;
//for readability; if the duration between falling edges is within the realm of '0'
if( (tDur >= MIN_ZERO_WIDTH) && (tDur <= MAX_ZERO_WIDTH) )
{
grDHT11Data[index] |= 0; //OR nothing in
}//if
else if( (tDur >= MIN_ONE_WIDTH) && (tDur <= MAX_ONE_WIDTH) )
{
//duration was within the window for a '1' so we or the mask in here
grDHT11Data[index] |= mask;
}//else
//for each falling edge, we bump the mask one-bit to the right
mask >>= 1;
//when it hits zero, we're done this byte so...
if( mask == 0 )
{
//reset the mask...
mask = 0x80;
//and move to the next byte index
index++;
//when we've done all five bytes...
if( index == 5 )
{
//set a flag so loop() knows
bDataFlag = true;
//reset the state variable back to the start
stateDHT = ICP_PP_START;
//and disable further input capture interrupts
TIMSK4 &= ~_BV( ICIE4 );
}//if
}//if
break;
}//switch
break;
}//switch
}//ISR CAPT4



