Arduino Mega 2560 LESSON:12 DHT11 温度湿度センサー dht nonblocking.cpp
Arduino Mega 2560 LESSON:12 DHT11 温度湿度センサーに戻る。
/*
* DHT11, DHT21, and DHT22 non-blocking library.
* Based on Adafruit Industries' DHT driver library.
*
* (C) 2015 Ole Wolf <wolf@blazingangles.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dht_nonblocking.h"
#define DHT_IDLE 0
#define DHT_BEGIN_MEASUREMENT 1
#define DHT_BEGIN_MEASUREMENT_2 2
#define DHT_DO_READING 3
#define DHT_COOLDOWN 4
/* Number of milliseconds before a new sensor read may be initiated. */
#define COOLDOWN_TIME 2000
/*
* Constructor for the sensor. It remembers the pin number and the
* type of sensor, and initializes internal variables.
*/
DHT_nonblocking::DHT_nonblocking( uint8_t pin, uint8_t type )
: _pin( pin ),
_type( type ),
_bit( digitalPinToBitMask( pin ) ),
_port( digitalPinToPort( pin ) ),
_maxcycles( microsecondsToClockCycles( 1000 ) )
{
dht_state = DHT_IDLE;
pinMode( _pin, INPUT );
digitalWrite( _pin, HIGH );
}
/*
* Instruct the DHT to begin sampling. Keep polling until it returns true.
* The tempearture is in degrees Celsius, and the humidity is in %.
*/
bool DHT_nonblocking::measure( float *temperature, float *humidity )
{
if( read_nonblocking( ) == true )
{
*temperature = read_temperature( );
*humidity = read_humidity( );
return( true );
}
else
{
return( false );
}
}
float DHT_nonblocking::read_temperature( ) const
{
int16_t value;
float to_return;
switch( _type )
{
case DHT_TYPE_11:
value = data[ 2 ];
to_return = (float) value;
break;
case DHT_TYPE_21:
case DHT_TYPE_22:
value = ( data[ 2 ] & 0x7f ) << 8;
value |= data[ 3 ];
if( ( data[ 2 ] & 0x80 ) != 0 )
{
value = -value;
}
to_return = ( (float) value ) / 10.0;
break;
default:
to_return = NAN;
break;
}
return( to_return );
}
float DHT_nonblocking::read_humidity( ) const
{
uint16_t value;
float to_return;
switch( _type )
{
case DHT_TYPE_11:
value = data[ 0 ];
to_return = (float) value;
break;
case DHT_TYPE_21:
case DHT_TYPE_22:
value = data[ 0 ] << 8;
value |= data[ 1 ];
to_return = (float)value / 10.0;
break;
default:
to_return = NAN;
break;
}
return( to_return );
}
/*
* Expect the input to be at the specified level and return the number
* of loop cycles spent there. This is identical to Adafruit's blocking
* driver.
*/
uint32_t DHT_nonblocking::expect_pulse(bool level) const
{
uint32_t count = 0;
// On AVR platforms use direct GPIO port access as it's much faster and better
// for catching pulses that are 10's of microseconds in length:
#ifdef __AVR
uint8_t portState = level ? _bit : 0;
while ((*portInputRegister(_port) & _bit) == portState) {
if (count++ >= _maxcycles) {
return 0; // Exceeded timeout, fail.
}
}
// Otherwise fall back to using digitalRead (this seems to be necessary on ESP8266
// right now, perhaps bugs in direct port access functions?).
#else
while (digitalRead(_pin) == level) {
if (count++ >= _maxcycles) {
return 0; // Exceeded timeout, fail.
}
}
#endif
return count;
}
/*
* State machine of the non-blocking read.
*/
bool DHT_nonblocking::read_nonblocking( )
{
bool status = false;
switch( dht_state )
{
/* We may begin measuring any time. */
case DHT_IDLE:
dht_state = DHT_BEGIN_MEASUREMENT;
break;
/* Initiate a sensor read. The read begins by going to high impedance
state for 250 ms. */
case DHT_BEGIN_MEASUREMENT:
digitalWrite( _pin, HIGH );
/* Reset 40 bits of received data to zero. */
data[ 0 ] = data[ 1 ] = data[ 2 ] = data[ 3 ] = data[ 4 ] = 0;
dht_timestamp = millis( );
dht_state = DHT_BEGIN_MEASUREMENT_2;
break;
/* After the high impedance state, pull the pin low for 20 ms. */
case DHT_BEGIN_MEASUREMENT_2:
/* Wait for 250 ms. */
if( millis( ) - dht_timestamp > 250 )
{
pinMode( _pin, OUTPUT );
digitalWrite( _pin, LOW );
dht_timestamp = millis( );
dht_state = DHT_DO_READING;
}
break;
case DHT_DO_READING:
/* Wait for 20 ms. */
if( millis( ) - dht_timestamp > 20 )
{
dht_timestamp = millis( );
dht_state = DHT_COOLDOWN;
status = read_data( );
// if( status != true )
// {
// Serial.println( "Reading failed" );
// }
}
break;
/* If it has been less than two seconds since the last time we read
the sensor, then let the sensor cool down.. */
case DHT_COOLDOWN:
if( millis( ) - dht_timestamp > COOLDOWN_TIME )
{
dht_state = DHT_IDLE;
}
break;
default:
break;
}
return( status );
}
/* Read sensor data. This is identical to Adafruit's blocking driver. */
bool DHT_nonblocking::read_data( )
{
uint32_t cycles[ 80 ];
/* Turn off interrupts temporarily because the next sections are timing critical
and we don't want any interruptions. */
{
volatile DHT_interrupt interrupt;
// End the start signal by setting data line high for 40 microseconds.
digitalWrite( _pin, HIGH );
delayMicroseconds( 40 );
// Now start reading the data line to get the value from the DHT sensor.
pinMode( _pin, INPUT );
// Delay a bit to let sensor pull data line low.
delayMicroseconds( 10 );
// First expect a low signal for ~80 microseconds followed by a high signal
// for ~80 microseconds again.
if( expect_pulse( LOW ) == 0 )
{
return( false );
}
if( expect_pulse( HIGH ) == 0 )
{
return( false );
}
// Now read the 40 bits sent by the sensor. Each bit is sent as a 50
// microsecond low pulse followed by a variable length high pulse. If the
// high pulse is ~28 microseconds then it's a 0 and if it's ~70 microseconds
// then it's a 1. We measure the cycle count of the initial 50us low pulse
// and use that to compare to the cycle count of the high pulse to determine
// if the bit is a 0 (high state cycle count < low state cycle count), or a
// 1 (high state cycle count > low state cycle count). Note that for speed all
// the pulses are read into a array and then examined in a later step.
for( int i = 0; i < 80; i += 2 )
{
cycles[ i ] = expect_pulse( LOW );
cycles[ i + 1 ] = expect_pulse( HIGH );
}
/* Timing critical code is now complete. */
}
// Inspect pulses and determine which ones are 0 (high state cycle count < low
// state cycle count), or 1 (high state cycle count > low state cycle count).
for( int i = 0; i < 40; ++i )
{
uint32_t low_cycles = cycles[ 2 * i ];
uint32_t high_cycles = cycles[ 2 * i + 1 ];
if( ( low_cycles == 0 ) || ( high_cycles == 0 ) )
{
return( false );
}
data[ i / 8 ] <<= 1;
// Now compare the low and high cycle times to see if the bit is a 0 or 1.
if( high_cycles > low_cycles )
{
// High cycles are greater than 50us low cycle count, must be a 1.
data[ i / 8 ] |= 1;
}
// Else high cycles are less than (or equal to, a weird case) the 50us low
// cycle count so this must be a zero. Nothing needs to be changed in the
// stored data.
}
// Check we read 40 bits and that the checksum matches.
if( data[ 4 ] == ( ( data[ 0 ] + data[ 1 ] + data[ 2 ] + data[ 3 ]) & 0xFF ) )
{
return( true );
}
else
{
return( false );
}
}
void DHT_nonblocking::print_raw_data()
{
Serial.print("Sample RAW Bits: ");
for (int i = 0; i < 5; i++)
{
for (int bit = 7; bit >= 0; bit--)
{
Serial.print((data[i] >> bit) & 1);
}
if (i < 4)
{
Serial.print(" ");
}
}
Serial.println();
}