DHT22 library, based on Simon Cooksey's. Improved with some error checking and more rigid timing (interrupts off).

Fork of lib_dht22 by Jodie Perry

Revision:
3:40df3c72813f
Parent:
2:02cbaab7c6cd
Child:
4:30a98da09c59
--- a/dht22.cpp	Wed Nov 02 16:29:53 2016 +0000
+++ b/dht22.cpp	Thu Nov 03 11:08:45 2016 +0000
@@ -1,5 +1,6 @@
 /*
  * (C) The University of Kent and Simon Cooksey 2015.
+ * Proddled a bit by Fred
  */
 
 #include "mbed.h"
@@ -12,142 +13,222 @@
  * of the HIGH time on the signal pin.
  */
 
-/*
- * Wait for a rising or falling edge on the sense pin.
- *
- * Returns: the number of uS elapsed before the transition
- */
-int DHT22::wait_for_edge(edge_type_t type)
+/* delay for approx 2 microseconds */
+void DHT22::wait_2us (void)
 {
-    //__disable_irq ();
-    dht22_s.input();
+    int i;
+    
+    /* 16 * 14 nops, plus loopend */
+    for (i=0; i<16; i++) {
+        __asm__ __volatile__ ("         \n" \
+            "   nop                     \n" \
+            "   nop                     \n" \
+            "   nop                     \n" \
+            "   nop                     \n" \
+            "   nop                     \n" \
+            "   nop                     \n" \
+            "   nop                     \n" \
+            "   nop                     \n" \
+            "   nop                     \n" \
+            "   nop                     \n" \
+            "   nop                     \n" \
+            "   nop                     \n" \
+            "   nop                     \n" \
+            "   nop                     \n");
+
+    }
+    return;
+}
+
 
-    // Timing is done by increasing this number, as the Timer class appears to
-    // be super slow.
-    int time = 0;
-    do {
-        wait_us(2);
-        time+=2;
-    } while(dht22_s != (int)type);
+/* ensure the comms pin is set for input */
+void DHT22::setinput (void)
+{
+    if (!isinput) {
+        dht22_s.input ();
+        isinput = 1;
+    }
+}
+
+/* ensure the comms pin is set for output */
+void DHT22::setoutput (void)
+{
+    if (isinput) {
+        dht22_s.output ();
+        isinput = 0;
+    }
+}
+
 
-    // wait for the edge to transition properly
-    wait_us(2);
-    //__enable_irq ();
-    return time;
+int DHT22::wait_for_level (int lvl, const int max)
+{
+    int time;
+
+    setinput ();
+    
+    for (time=0; ((max == -1) || (time < max)) && (dht22_s != lvl); time += 2) {
+        wait_2us ();
+    }
+    if ((max > 0) && (time >= max)) {
+        /* timed out */
+        return -1;
+    }
+    
+    if (time) {
+        /* means we saw a transition, so let it settle */
+        wait_2us ();
+        time += 2;
+    }
+    
+    return time;            
 }
 
+
 /*
  * Send a start bit to the DHT22
  */
-void DHT22::send_start()
+void DHT22::send_start (void)
 {
+    int dly;
+    
     //__disable_irq ();
-    dht22_s.output();
+    setoutput ();
     dht22_s = 0;
-    wait_us(DHT22_START_BIT_TIME);
+    /* drag low for 1ms */
+    for (dly=0; dly<(DHT22_START_BIT_TIME >> 1); dly++) {
+        wait_2us ();
+    }
+    // wait_us (DHT22_START_BIT_TIME);
     dht22_s = 1;
-    dht22_s.input();
+    setinput ();
     //__enable_irq ();
 }
 
 /*
  * Wait for the DHT22 to send the start bit ACK, after this we can read data.
  */
-void DHT22::await_start_response()
+int DHT22::wait_start (void)
 {
-    dht22_s.input();
-    wait_for_edge(EDGE_TYPE_FALLING); // 20-40 uS
-    wait_for_edge(EDGE_TYPE_RISING);  // 80 uS
-    wait_for_edge(EDGE_TYPE_FALLING); // 80 uS
+    /* level should be 1 */
+    if (dht22_s == 0) {
+        return -1;
+    }
+    if (wait_for_level (0, 500) < 0) {
+        /* should respond in 20-200 us, it didn't in 500 */
+        return -2;
+    }
+    if (wait_for_level (1, 100) < 0) {
+        /* only for 80 us (max 85 in datasheet) */
+        return -3;
+    }
+    if (wait_for_level (0, 100) < 0) {
+        /* only for 80 us (max 85 in datasheet) */
+        return -4;
+    }
+    /* at this point we're about to start seeing the MSB of data [39-0] */
+    
+    return 0;    
 }
 
 /*
- * Reads 16 bits of data from the DHT22
- *
- * Returns: the signed value read. dht22_t.
- * NB. the DHT22 uses a sign bit to do -ve and positive, but this is
- * incompatible with signed numbers in C, so the conversion is done here.
+ * reads 8 bits of data, returns value [0-255] on success, -1 on error (timeout)
  */
-int16_t DHT22::read_word()
+int DHT22::read_byte (void)
 {
-    dht22_s.input();
-    int32_t duration;
-    int16_t word = 0x00;
-    for(char bit = 0; bit < 16; bit++)
-    {
-        /*       /-----------\
-         *      /   duration  \    50us
-         * ----/               \-------
-         */
-        wait_for_edge(EDGE_TYPE_RISING);
-        duration = wait_for_edge(EDGE_TYPE_FALLING);
+    int d, bit;
+    int v = 0;
+    
+    /* should be zero already */
+    if (dht22_s == 1) {
+        return -1;
+    }
+    
+    for (bit=7; bit>=0; bit--) {
+        /* expect it to stay low for 50us (max 55) */
+        if (wait_for_level (1, 100) < 0) {
+            /* timed out after 100us */
+            return -2;
+        }
+
 
-        if(duration > DHT22_SIGNAL_HIGH_LOW_BOUNDARY)
-        {
-            word |= (1 << 15-bit);
+        d = wait_for_level (0, 100);
+        if (d < 0) {
+            /* timed out after 100us */
+            return -3;
         }
-        else
-        {
-            word |= (0 << 15-bit);
+
+
+        if (d > DHT22_SIGNAL_HIGH_LOW_BOUNDARY) {
+            v |= (1 << bit);
+            debug = 1;
+    } else {
+            debug = 0;
         }
+
     }
-    if(word & 0x8000)
-        return 1 - (word ^ 0x8000);
-    return word;
+    return v;
 }
 
-/*
- * Reads 8 bits of data from the DHT22
- *
- * Returns: the unsigned checksum value read. dht22_t.
- */
-uint8_t DHT22::read_checksum()
-{
-    dht22_s.input();
-    uint32_t duration;
-    uint8_t word;
-    for(char bit = 0; bit < sizeof(uint8_t)*8; bit++)
-    {
-        /*       /-----------\
-         *      /   duration  \    50us
-         * ----/               \-------
-         */
-        wait_for_edge(EDGE_TYPE_RISING);
-        duration = wait_for_edge(EDGE_TYPE_FALLING);
-
-        if(duration > DHT22_SIGNAL_HIGH_LOW_BOUNDARY)
-        {
-            word |= (1 << 7-bit);
-        }
-        else
-        {
-            word |= (0 << 7-bit);
-        }
-    }
-    return word;
-}
 
 /*
  * Reads a packet of DHT22 data.
  *
- * Param data: the packet to fill.
+ * Param data: the packet to fill.  returns 0 on success, < 0 on error (timeout of some kind)
  */
-void DHT22::read(DHT22_data_t * data)
+int DHT22::read (DHT22_data_t *data)
 {
+    uint8_t buf[5];
+    uint16_t u_hum, u_tmp;
+    int i;
+    
+    debug = 0;
+    __disable_irq ();
     // Send start bits
     send_start();
 
-    // Wait for device to respond
-    await_start_response();
-
-    // Read data bits (16+16)
-    int16_t humidity = read_word();
-    int16_t temp = read_word();
+    if (wait_start () < 0) {
+        __enable_irq ();
+        return -1;              /* timed out waiting for start response */
+    }
+    /* read 40 bits worth -- MSB first */
+    for (i=4; i>=0; i--) {
+        int v = read_byte ();
+        
+        if (v < 0) {
+            /* timed out waiting for data */
+            __enable_irq ();
+            return -2;
+        }
+        buf[i] = (uint8_t)v;
+    }
 
-    // Read checksum  (8)
-    uint8_t checksum = read_checksum();
+    /* yay, got here! -- sensor should release the wire in 50us (max 55) */
+    if (wait_for_level (1, 100) < 0) {
+        /* timed out at this point, but still fill the buffer */
+        i = -3;
+    } else {
+        i = 0;
+    }
+    __enable_irq ();
+    
+    /* unscramble */
+    u_hum = ((uint16_t)buf[4] << 8) | buf[3];
+    u_tmp = ((uint16_t)buf[2] << 8) | buf[1];
+    
+    if (u_hum & 0x8000) {
+        data->humidity = 1 - (int)(u_hum ^ 0x8000);
+    } else {
+        data->humidity = (int)u_hum;
+    }
+    if (u_tmp & 0x8000) {
+        data->temp = 1 - (int)(u_tmp ^ 0x8000);
+    } else {
+        data->temp = (int)u_tmp;
+    }
 
-    data->humidity = (humidity);
-    data->temp = (temp);
-    data->checksum = checksum;
-}
\ No newline at end of file
+    data->checksum = buf[0];
+    debug = 0;
+    
+    return i;
+}
+