1//////////////////////////////////////////////////////////////////////////////// 2// // 3// This software is supplied under the terms of a license agreement or // 4// nondisclosure agreement with Mitov Software and may not be copied // 5// or disclosed except in accordance with the terms of that agreement. // 6// Copyright(c) 2002-2016 Mitov Software. All Rights Reserved. // 7// // 8//////////////////////////////////////////////////////////////////////////////// 9 10#ifndef _MITOV_DHT_SENSOR_h 11#define _MITOV_DHT_SENSOR_h 12 13#include <Mitov.h> 14 15namespace Mitov 16{ 17//--------------------------------------------------------------------------- 18template<int PIN_NUMBER>class BasicDHTSensor :publicOpenWire::Component,publicMitov::ClockingSupport 19{ 20typedefOpenWire::Component inherited; 21 22const uint32_t MIN_INTERVAL =2000; 23 24public: 25OpenWire::SourcePin TemperatureOutputPin; 26OpenWire::SourcePin HumidityOutputPin; 27 28public: 29bool InFahrenheit =false; 30 31protected: 32uint8_t data[5]; 33uint32_t _lastreadtime = -MIN_INTERVAL; 34uint32_t _maxcycles; 35bool _lastresult =false; 36 37#ifdef __AVR 38// Use direct GPIO access on an 8-bit AVR so keep track of the port and bitmask 39// for the digital pin connected to the DHT. Other platforms will use digitalRead. 40uint8_t _bit, _port; 41#endif 42 43// DHT *FSensor; 44 45protected: 46virtualvoidSystemInit() override 47{ 48pinMode( PIN_NUMBER, INPUT_PULLUP ); 49// FSensor = new DHT( PIN_NUMBER, SENSOR_TYPE ); 50// FSensor->begin(); 51inherited::SystemInit(); 52} 53 54virtualvoidSystemLoopBegin(unsigned long currentMicros ) override 55{ 56if( ! ClockInputPin.IsConnected() ) 57ReadSensor(); 58 59inherited::SystemLoopBegin( currentMicros ); 60} 61 62protected: 63virtualvoidReadSensor() =0; 64 65uint32_texpectPulse(bool level) 66{ 67uint32_t count =0; 68// On AVR platforms use direct GPIO port access as it's much faster and better 69// for catching pulses that are 10's of microseconds in length: 70#ifdef __AVR 71uint8_t portState = level ? _bit :0; 72while((*portInputRegister(_port) & _bit) == portState) 73// Otherwise fall back to using digitalRead (this seems to be necessary on ESP8266 74// right now, perhaps bugs in direct port access functions?). 75#else 76while(digitalRead(PIN_NUMBER) == level) 77#endif 78{ 79if(count++ >= _maxcycles) 80return0;// Exceeded timeout, fail. 81} 82 83return count; 84} 85 86boolTryRead() 87{ 88uint32_t currenttime =millis(); 89if( (currenttime - _lastreadtime) <2000) 90return _lastresult;// return last correct measurement 91 92 _lastreadtime = currenttime; 93 94// Reset 40 bits of received data to zero. 95 data[0] = data[1] = data[2] = data[3] = data[4] =0; 96 97// Send start signal. See DHT datasheet for full signal diagram: 98// http://www.adafruit.com/datasheets/Digital%20humidity%20and%20temperature%20sensor%20AM2302.pdf 99 100// Go into high impedence state to let pull-up raise data line level and 101// start the reading process. 102digitalWrite( PIN_NUMBER, HIGH); 103delay(250); 104 105// First set data line low for 20 milliseconds. 106pinMode( PIN_NUMBER, OUTPUT); 107digitalWrite( PIN_NUMBER, LOW); 108delay(20); 109 110uint32_t cycles[80]; 111{ 112// Turn off interrupts temporarily because the next sections are timing critical 113// and we don't want any interruptions. 114 InterruptLock lock; 115 116// End the start signal by setting data line high for 40 microseconds. 117digitalWrite( PIN_NUMBER, HIGH); 118delayMicroseconds(40); 119 120// Now start reading the data line to get the value from the DHT sensor. 121pinMode( PIN_NUMBER, INPUT_PULLUP ); 122delayMicroseconds(10);// Delay a bit to let sensor pull data line low. 123 124// First expect a low signal for ~80 microseconds followed by a high signal 125// for ~80 microseconds again. 126if(expectPulse(LOW) ==0) 127{ 128// DEBUG_PRINTLN(F("Timeout waiting for start signal low pulse.")); 129 _lastresult =false; 130return _lastresult; 131} 132if(expectPulse(HIGH) ==0) 133{ 134// DEBUG_PRINTLN(F("Timeout waiting for start signal high pulse.")); 135 _lastresult =false; 136return _lastresult; 137} 138 139// Now read the 40 bits sent by the sensor. Each bit is sent as a 50 140// microsecond low pulse followed by a variable length high pulse. If the 141// high pulse is ~28 microseconds then it's a 0 and if it's ~70 microseconds 142// then it's a 1. We measure the cycle count of the initial 50us low pulse 143// and use that to compare to the cycle count of the high pulse to determine 144// if the bit is a 0 (high state cycle count < low state cycle count), or a 145// 1 (high state cycle count > low state cycle count). Note that for speed all 146// the pulses are read into a array and then examined in a later step. 147for(int i=0; i<80; i+=2) 148{ 149 cycles[i] =expectPulse(LOW); 150 cycles[i+1] =expectPulse(HIGH); 151} 152 153}// Timing critical code is now complete. 154 155// Inspect pulses and determine which ones are 0 (high state cycle count < low 156// state cycle count), or 1 (high state cycle count > low state cycle count). 157for(int i=0; i<40; ++i) 158{ 159uint32_t lowCycles = cycles[2*i]; 160uint32_t highCycles = cycles[2*i+1]; 161if((lowCycles ==0) || (highCycles ==0)) 162{ 163// DEBUG_PRINTLN(F("Timeout waiting for pulse.")); 164 _lastresult =false; 165return _lastresult; 166} 167 168 data[i/8] <<=1; 169// Now compare the low and high cycle times to see if the bit is a 0 or 1. 170if(highCycles > lowCycles) 171// High cycles are greater than 50us low cycle count, must be a 1. 172 data[i/8] |=1; 173 174// Else high cycles are less than (or equal to, a weird case) the 50us low 175// cycle count so this must be a zero. Nothing needs to be changed in the 176// stored data. 177} 178 179/* 180 DEBUG_PRINTLN(F("Received:")); 181 DEBUG_PRINT(data[0], HEX); DEBUG_PRINT(F(", ")); 182 DEBUG_PRINT(data[1], HEX); DEBUG_PRINT(F(", ")); 183 DEBUG_PRINT(data[2], HEX); DEBUG_PRINT(F(", ")); 184 DEBUG_PRINT(data[3], HEX); DEBUG_PRINT(F(", ")); 185 DEBUG_PRINT(data[4], HEX); DEBUG_PRINT(F(" =? ")); 186 DEBUG_PRINTLN((data[0] + data[1] + data[2] + data[3]) & 0xFF, HEX); 187*/ 188// Check we read 40 bits and that the checksum matches. 189if(data[4] == ((data[0] + data[1] + data[2] + data[3]) &0xFF)) 190{ 191 _lastresult =true; 192return _lastresult; 193} 194 195else 196{ 197// DEBUG_PRINTLN(F("Checksum failure!")); 198 _lastresult =false; 199return _lastresult; 200} 201} 202 203protected: 204voidDoClockReceive(void*_Data ) override 205{ 206ReadSensor(); 207} 208 209public: 210BasicDHTSensor() 211{ 212#ifdef __AVR 213 _bit =digitalPinToBitMask( PIN_NUMBER ); 214 _port =digitalPinToPort( PIN_NUMBER ); 215#endif 216 _maxcycles =microsecondsToClockCycles(1000);// 1 millisecond timeout for 217// reading pulses from DHT sensor. 218} 219}; 220//--------------------------------------------------------------------------- 221template<int PIN_NUMBER>class DHT11Sensor :public BasicDHTSensor<PIN_NUMBER> 222{ 223typedef BasicDHTSensor<PIN_NUMBER> inherited; 224 225protected: 226virtualvoidReadSensor() override 227{ 228if( !inherited::TryRead()) 229return; 230 231if(inherited::TemperatureOutputPin.IsConnected() ) 232{ 233float AValue =inherited::data[2]; 234if(inherited::InFahrenheit ) 235 AValue =ConvertCtoF( AValue ); 236 237inherited::TemperatureOutputPin.Notify( &AValue ); 238} 239 240if(inherited::HumidityOutputPin.IsConnected() ) 241{ 242float AValue =inherited::data[0]; 243inherited::HumidityOutputPin.Notify( &AValue ); 244} 245} 246}; 247//--------------------------------------------------------------------------- 248template<int PIN_NUMBER>class DHT22Sensor :public BasicDHTSensor<PIN_NUMBER> 249{ 250typedef BasicDHTSensor<PIN_NUMBER> inherited; 251 252protected: 253virtualvoidReadSensor() override 254{ 255if( !inherited::TryRead()) 256return; 257 258if(inherited::TemperatureOutputPin.IsConnected() ) 259{ 260float AValue =inherited::data[2] &0x7F; 261 AValue *=256; 262 AValue +=inherited::data[3]; 263 AValue *=0.1; 264if(inherited::data[2] &0x80) 265 AValue *= -1; 266 267if(inherited::InFahrenheit ) 268 AValue =ConvertCtoF( AValue ); 269 270inherited::TemperatureOutputPin.Notify( &AValue ); 271} 272 273if(inherited::HumidityOutputPin.IsConnected() ) 274{ 275float AValue =inherited::data[0]; 276 AValue *=256; 277 AValue +=inherited::data[1]; 278 AValue *=0.1; 279inherited::HumidityOutputPin.Notify( &AValue ); 280} 281} 282}; 283//--------------------------------------------------------------------------- 284} 285 286#endif