arduinoprojects

git clone https://git.tarina.org/arduinoprojects
Log | Files | Refs

SimpleDHT.cpp (10756B)


      1 /*
      2 The MIT License (MIT)
      3 
      4 Copyright (c) 2016-2017 winlin
      5 
      6 Permission is hereby granted, free of charge, to any person obtaining a copy
      7 of this software and associated documentation files (the "Software"), to deal
      8 in the Software without restriction, including without limitation the rights
      9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10 copies of the Software, and to permit persons to whom the Software is
     11 furnished to do so, subject to the following conditions:
     12 
     13 The above copyright notice and this permission notice shall be included in all
     14 copies or substantial portions of the Software.
     15 
     16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     22 SOFTWARE.
     23 */
     24 
     25 #include "SimpleDHT.h"
     26 
     27 SimpleDHT::SimpleDHT() {
     28 }
     29 
     30 SimpleDHT::SimpleDHT(int pin) {
     31     setPin(pin);
     32 }
     33 
     34 void SimpleDHT::setPin(int pin) {
     35     this->pin = pin;
     36 #ifdef __AVR
     37     // (only AVR) - set low level properties for configured pin
     38     bitmask = digitalPinToBitMask(pin);
     39     port = digitalPinToPort(pin);
     40 #endif
     41 }
     42 
     43 int SimpleDHT::setPinInputMode(uint8_t mode) {
     44     if (mode != INPUT && mode != INPUT_PULLUP) {
     45         return SimpleDHTErrPinMode;
     46     }
     47     this->pinInputMode = mode;
     48     return SimpleDHTErrSuccess;
     49 }
     50 
     51 int SimpleDHT::read(byte* ptemperature, byte* phumidity, byte pdata[5]) {
     52     int ret = SimpleDHTErrSuccess;
     53 
     54     if (pin == -1) {
     55         return SimpleDHTErrNoPin;
     56     }
     57 
     58     float temperature = 0;
     59     float humidity = 0;
     60     if ((ret = read2(&temperature, &humidity, pdata)) != SimpleDHTErrSuccess) {
     61         return ret;
     62     }
     63 
     64     if (ptemperature) {
     65         *ptemperature = static_cast<byte>(static_cast<int>(temperature));
     66     }
     67 
     68     if (phumidity) {
     69         *phumidity = static_cast<byte>(static_cast<int>(humidity));
     70     }
     71 
     72     return ret;
     73 }
     74 
     75 int SimpleDHT::read(int pin, byte* ptemperature, byte* phumidity, byte pdata[5]) {
     76     setPin(pin);
     77     return read(ptemperature, phumidity, pdata);
     78 }
     79 
     80 #ifdef __AVR
     81 int SimpleDHT::getBitmask() {
     82     return bitmask;
     83 }
     84 
     85 int SimpleDHT::getPort() {
     86     return port;
     87 }
     88 #endif
     89 
     90 long SimpleDHT::levelTime(byte level, int firstWait, int interval) {
     91     unsigned long time_start = micros();
     92     long time = 0;
     93 
     94 #ifdef __AVR
     95     uint8_t portState = level ? bitmask : 0;
     96 #endif
     97 
     98     bool loop = true;
     99     for (int i = 0 ; loop; i++) {
    100         if (time < 0 || time > levelTimeout) {
    101             return -1;
    102         }
    103 
    104         if (i == 0) {
    105             if (firstWait > 0) {
    106                 delayMicroseconds(firstWait);
    107             }
    108         } else if (interval > 0) {
    109             delayMicroseconds(interval);
    110         }
    111 
    112         // for an unsigned int type, the difference have a correct value
    113         // even if overflow, explanation here:
    114         //     https://arduino.stackexchange.com/questions/33572/arduino-countdown-without-using-delay
    115         time = micros() - time_start;
    116 
    117 #ifdef __AVR
    118         loop = ((*portInputRegister(port) & bitmask) == portState);
    119 #else
    120         loop = (digitalRead(pin) == level);
    121 #endif
    122     }
    123 
    124     return time;
    125 }
    126 
    127 //https://stackoverflow.com/a/2602885/4203189
    128 byte SimpleDHT::reverse(byte b) {
    129     b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
    130     b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
    131     b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
    132     return b;
    133 }
    134 
    135 int SimpleDHT::parse(byte data[5], short* ptemperature, short* phumidity) {
    136     short humidity = reverse(data[0]);
    137     short humidity2 = reverse(data[1]);
    138     short temperature = reverse(data[2]);
    139     short temperature2 = reverse(data[3]);
    140     byte check = reverse(data[4]);
    141     byte expect = static_cast<byte>(humidity) + static_cast<byte>(humidity2) + static_cast<byte>(temperature) + static_cast<byte>(temperature2);
    142     if (check != expect) {
    143         return SimpleDHTErrDataChecksum;
    144     }
    145     *ptemperature = temperature<<8 | temperature2;
    146     *phumidity = humidity<<8 | humidity2;
    147 
    148     return SimpleDHTErrSuccess;
    149 }
    150 
    151 SimpleDHT11::SimpleDHT11() {
    152 }
    153 
    154 SimpleDHT11::SimpleDHT11(int pin) : SimpleDHT (pin) {
    155 }
    156 
    157 int SimpleDHT11::read2(float* ptemperature, float* phumidity, byte pdata[5]) {
    158     int ret = SimpleDHTErrSuccess;
    159 
    160     if (pin == -1) {
    161         return SimpleDHTErrNoPin;
    162     }
    163 
    164     byte data[5] = {0};
    165     if ((ret = sample(data)) != SimpleDHTErrSuccess) {
    166         return ret;
    167     }
    168     short temperature = 0;
    169     short humidity = 0;
    170     if ((ret = parse(data, &temperature, &humidity)) != SimpleDHTErrSuccess) {
    171         return ret;
    172     }
    173 
    174     if (pdata) {
    175         memcpy(pdata, data, 5);
    176     }
    177     if (ptemperature) {
    178         *ptemperature = (int)(temperature>>8);
    179     }
    180     if (phumidity) {
    181         *phumidity = (int)(humidity>>8);
    182     }
    183 
    184     // For example, when remove the data line, it will be success with zero data.
    185     if (temperature == 0 && humidity == 0) {
    186         return SimpleDHTErrZeroSamples;
    187     }
    188 
    189     return ret;
    190 }
    191 
    192 int SimpleDHT11::read2(int pin, float* ptemperature, float* phumidity, byte pdata[5]) {
    193     setPin(pin);
    194     return read2(ptemperature, phumidity, pdata);
    195 }
    196 
    197 int SimpleDHT11::sample(byte data[5]) {
    198     // empty output data.
    199     memset(data, 0, 5);
    200 
    201     // According to protocol: [1] https://akizukidenshi.com/download/ds/aosong/DHT11.pdf
    202     // notify DHT11 to start:
    203     //    1. PULL LOW 20ms.
    204     //    2. PULL HIGH 20-40us.
    205     //    3. SET TO INPUT or INPUT_PULLUP.
    206     // Changes in timing done according to:
    207     //  [2] https://www.mouser.com/ds/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
    208     // - original values specified in code
    209     // - since they were not working (MCU-dependent timing?), replace in code with
    210     //   _working_ values based on measurements done with levelTimePrecise()
    211     pinMode(pin, OUTPUT);
    212     digitalWrite(pin, LOW);            // 1.
    213     delay(20);                         // specs [2]: 18us
    214 
    215     // Pull high and set to input, before wait 40us.
    216     // @see https://github.com/winlinvip/SimpleDHT/issues/4
    217     // @see https://github.com/winlinvip/SimpleDHT/pull/5
    218     digitalWrite(pin, HIGH);           // 2.
    219     pinMode(pin, this->pinInputMode);
    220     delayMicroseconds(25);             // specs [2]: 20-40us
    221 
    222     // DHT11 starting:
    223     //    1. PULL LOW 80us
    224     //    2. PULL HIGH 80us
    225     long t = levelTime(LOW);          // 1.
    226     if (t < 30) {                    // specs [2]: 80us
    227         return simpleDHTCombileError(t, SimpleDHTErrStartLow);
    228     }
    229 
    230     t = levelTime(HIGH);             // 2.
    231     if (t < 50) {                    // specs [2]: 80us
    232         return simpleDHTCombileError(t, SimpleDHTErrStartHigh);
    233     }
    234 
    235     // DHT11 data transmite:
    236     //    1. 1bit start, PULL LOW 50us
    237     //    2. PULL HIGH:
    238     //         - 26-28us, bit(0)
    239     //         - 70us, bit(1)
    240     for (int j = 0; j < 40; j++) {
    241           t = levelTime(LOW);          // 1.
    242           if (t < 24) {                    // specs says: 50us
    243               return simpleDHTCombileError(t, SimpleDHTErrDataLow);
    244           }
    245 
    246           // read a bit
    247           t = levelTime(HIGH);              // 2.
    248           if (t < 11) {                     // specs say: 20us
    249               return simpleDHTCombileError(t, SimpleDHTErrDataRead);
    250           }
    251           bitWrite(data[j / 8], j % 8, (t > 40 ? 1 : 0));     // specs: 26-28us -> 0, 70us -> 1
    252     }
    253 
    254     // DHT11 EOF:
    255     //    1. PULL LOW 50us.
    256     t = levelTime(LOW);                     // 1.
    257     if (t < 24) {                           // specs say: 50us
    258         return simpleDHTCombileError(t, SimpleDHTErrDataEOF);
    259     }
    260     return SimpleDHTErrSuccess;
    261 }
    262 
    263 SimpleDHT22::SimpleDHT22() {
    264 }
    265 
    266 SimpleDHT22::SimpleDHT22(int pin) : SimpleDHT (pin) {
    267 }
    268 
    269 int SimpleDHT22::read2(float* ptemperature, float* phumidity, byte pdata[5]) {
    270     int ret = SimpleDHTErrSuccess;
    271 
    272     if (pin == -1) {
    273         return SimpleDHTErrNoPin;
    274     }
    275 
    276     byte data[5] = {0};
    277     if ((ret = sample(data)) != SimpleDHTErrSuccess) {
    278         return ret;
    279     }
    280 
    281     short temperature = 0;
    282     short humidity = 0;
    283     if ((ret = parse(data, &temperature, &humidity)) != SimpleDHTErrSuccess) {
    284         return ret;
    285     }
    286 
    287     if (pdata) {
    288         memcpy(pdata, data, 5);
    289     }
    290     if (ptemperature) {
    291         *ptemperature = (float)((temperature & 0x8000 ? -1 : 1) * (temperature & 0x7FFF)) / 10.0;
    292     }
    293     if (phumidity) {
    294         *phumidity = (float)humidity / 10.0;
    295     }
    296 
    297     return ret;
    298 }
    299 
    300 int SimpleDHT22::read2(int pin, float* ptemperature, float* phumidity, byte pdata[5]) {
    301     setPin(pin);
    302     return read2(ptemperature, phumidity, pdata);
    303 }
    304 
    305 int SimpleDHT22::sample(byte data[5]) {
    306     // empty output data.
    307     memset(data, 0, 5);
    308 
    309     // According to protocol: http://akizukidenshi.com/download/ds/aosong/AM2302.pdf
    310     // notify DHT22 to start:
    311     //    1. T(be), PULL LOW 1ms(0.8-20ms).
    312     //    2. T(go), PULL HIGH 30us(20-200us), use 40us.
    313     //    3. SET TO INPUT or INPUT_PULLUP.
    314     pinMode(pin, OUTPUT);
    315     digitalWrite(pin, LOW);
    316     delayMicroseconds(1000);
    317     // Pull high and set to input, before wait 40us.
    318     // @see https://github.com/winlinvip/SimpleDHT/issues/4
    319     // @see https://github.com/winlinvip/SimpleDHT/pull/5
    320     digitalWrite(pin, HIGH);
    321     pinMode(pin, this->pinInputMode);
    322     delayMicroseconds(5);
    323 
    324     // DHT22 starting:
    325     //    1. T(rel), PULL LOW 80us(75-85us).
    326     //    2. T(reh), PULL HIGH 80us(75-85us).
    327     long t = 0;
    328     if ((t = levelTime(LOW)) < 30) {
    329         return simpleDHTCombileError(t, SimpleDHTErrStartLow);
    330     }
    331     if ((t = levelTime(HIGH)) < 50) {
    332         return simpleDHTCombileError(t, SimpleDHTErrStartHigh);
    333     }
    334 
    335     // DHT22 data transmite:
    336     //    1. T(LOW), 1bit start, PULL LOW 50us(48-55us).
    337     //    2. T(H0), PULL HIGH 26us(22-30us), bit(0)
    338     //    3. T(H1), PULL HIGH 70us(68-75us), bit(1)
    339     for (int j = 0; j < 40; j++) {
    340           t = levelTime(LOW);          // 1.
    341           if (t < 24) {                    // specs says: 50us
    342               return simpleDHTCombileError(t, SimpleDHTErrDataLow);
    343           }
    344 
    345           // read a bit
    346           t = levelTime(HIGH);              // 2.
    347           if (t < 11) {                     // specs say: 26us
    348               return simpleDHTCombileError(t, SimpleDHTErrDataRead);
    349           }
    350           bitWrite(data[j / 8], j % 8, (t > 40 ? 1 : 0));     // specs: 22-30us -> 0, 70us -> 1
    351     }
    352 
    353     // DHT22 EOF:
    354     //    1. T(en), PULL LOW 50us(45-55us).
    355     t = levelTime(LOW);
    356     if (t < 24) {
    357         return simpleDHTCombileError(t, SimpleDHTErrDataEOF);
    358     }
    359 
    360     return SimpleDHTErrSuccess;
    361 }