libraries / Mitov / Mitov_DHT_Sensor.hon commit Added link to project report (97a3ba0)
   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//---------------------------------------------------------------------------
  18        template<int PIN_NUMBER> class BasicDHTSensor : public OpenWire::Component, public Mitov::ClockingSupport
  19        {
  20                typedef OpenWire::Component inherited;
  21
  22                const uint32_t MIN_INTERVAL = 2000;
  23
  24        public:
  25                OpenWire::SourcePin     TemperatureOutputPin;
  26                OpenWire::SourcePin     HumidityOutputPin;
  27
  28        public:
  29                bool    InFahrenheit = false;
  30
  31        protected:
  32                uint8_t data[5];
  33                uint32_t _lastreadtime = -MIN_INTERVAL;
  34                uint32_t _maxcycles;
  35                bool _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.
  40                uint8_t _bit, _port;
  41#endif
  42
  43//              DHT     *FSensor;
  44
  45        protected:
  46                virtual void SystemInit() override
  47                {
  48                        pinMode( PIN_NUMBER, INPUT_PULLUP );
  49//                      FSensor = new DHT( PIN_NUMBER, SENSOR_TYPE );
  50//                      FSensor->begin();
  51                        inherited::SystemInit();
  52                }
  53
  54                virtual void SystemLoopBegin( unsigned long currentMicros ) override
  55                {
  56                        if( ! ClockInputPin.IsConnected() )
  57                                ReadSensor();
  58
  59                        inherited::SystemLoopBegin( currentMicros );
  60                }
  61
  62        protected:
  63                virtual void ReadSensor() = 0;
  64
  65                uint32_t expectPulse(bool level) 
  66                {
  67                        uint32_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
  71                        uint8_t portState = level ? _bit : 0;
  72                        while ((*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
  76                        while (digitalRead(PIN_NUMBER) == level) 
  77#endif
  78                        {
  79                                if (count++ >= _maxcycles) 
  80                                        return 0; // Exceeded timeout, fail.
  81                        }
  82
  83                        return count;
  84                }
  85
  86                bool TryRead()
  87                {
  88                        uint32_t currenttime = millis();
  89                        if ( (currenttime - _lastreadtime) < 2000 ) 
  90                                return _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.
 102                        digitalWrite( PIN_NUMBER, HIGH);
 103                        delay(250);
 104
 105                        // First set data line low for 20 milliseconds.
 106                        pinMode( PIN_NUMBER, OUTPUT);
 107                        digitalWrite( PIN_NUMBER, LOW);
 108                        delay(20);
 109
 110                        uint32_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.
 117                                digitalWrite( PIN_NUMBER, HIGH);
 118                                delayMicroseconds(40);
 119
 120                                // Now start reading the data line to get the value from the DHT sensor.
 121                                pinMode( PIN_NUMBER, INPUT_PULLUP );
 122                                delayMicroseconds(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.
 126                                if (expectPulse(LOW) == 0) 
 127                                {
 128//                                      DEBUG_PRINTLN(F("Timeout waiting for start signal low pulse."));
 129                                        _lastresult = false;
 130                                        return _lastresult;
 131                                }
 132                                if (expectPulse(HIGH) == 0) 
 133                                {
 134//                                      DEBUG_PRINTLN(F("Timeout waiting for start signal high pulse."));
 135                                        _lastresult = false;
 136                                        return _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.
 147                                for (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).
 157                        for (int i=0; i<40; ++i) 
 158                        {
 159                                uint32_t lowCycles  = cycles[2*i];
 160                                uint32_t highCycles = cycles[2*i+1];
 161                                if ((lowCycles == 0) || (highCycles == 0)) 
 162                                {
 163//                                      DEBUG_PRINTLN(F("Timeout waiting for pulse."));
 164                                        _lastresult = false;
 165                                        return _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.
 170                                if (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.
 189                        if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) 
 190                        {
 191                                _lastresult = true;
 192                                return _lastresult;
 193                        }
 194
 195                        else 
 196                        {
 197        //                      DEBUG_PRINTLN(F("Checksum failure!"));
 198                                _lastresult = false;
 199                                return _lastresult;
 200                        }
 201                }
 202
 203        protected:
 204                void DoClockReceive( void *_Data ) override
 205                {
 206                        ReadSensor();
 207                }
 208
 209        public:
 210                BasicDHTSensor()
 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//---------------------------------------------------------------------------
 221        template<int PIN_NUMBER> class DHT11Sensor : public BasicDHTSensor<PIN_NUMBER>
 222        {
 223                typedef BasicDHTSensor<PIN_NUMBER> inherited;
 224
 225        protected:
 226                virtual void ReadSensor() override
 227                {
 228                        if( ! inherited::TryRead())
 229                                return;
 230
 231                        if( inherited::TemperatureOutputPin.IsConnected() )
 232                        {
 233                                float   AValue = inherited::data[2];
 234                                if( inherited::InFahrenheit )
 235                                        AValue = ConvertCtoF( AValue );
 236
 237                                inherited::TemperatureOutputPin.Notify( &AValue );
 238                        }
 239
 240                        if( inherited::HumidityOutputPin.IsConnected() )
 241                        {
 242                                float   AValue = inherited::data[0];
 243                                inherited::HumidityOutputPin.Notify( &AValue );
 244                        }
 245                }
 246        };
 247//---------------------------------------------------------------------------
 248        template<int PIN_NUMBER> class DHT22Sensor : public BasicDHTSensor<PIN_NUMBER>
 249        {
 250                typedef BasicDHTSensor<PIN_NUMBER> inherited;
 251
 252        protected:
 253                virtual void ReadSensor() override
 254                {
 255                        if( ! inherited::TryRead())
 256                                return;
 257
 258                        if( inherited::TemperatureOutputPin.IsConnected() )
 259                        {
 260                                float   AValue = inherited::data[2] & 0x7F;
 261                                AValue *= 256;
 262                                AValue += inherited::data[3];
 263                                AValue *= 0.1;
 264                                if( inherited::data[2] & 0x80 ) 
 265                                        AValue *= -1;
 266                                
 267                                if( inherited::InFahrenheit )
 268                                        AValue = ConvertCtoF( AValue );
 269
 270                                inherited::TemperatureOutputPin.Notify( &AValue );
 271                        }
 272
 273                        if( inherited::HumidityOutputPin.IsConnected() )
 274                        {
 275                                float   AValue = inherited::data[0];
 276                                AValue *= 256;
 277                                AValue += inherited::data[1];
 278                                AValue *= 0.1;
 279                                inherited::HumidityOutputPin.Notify( &AValue );
 280                        }
 281                }
 282        };
 283//---------------------------------------------------------------------------
 284}
 285
 286#endif