A simple library for reading data from a DHT22 sensor

Dependents:   frdm_dht22_example DISCO-L053C8_ePD_demo

Simple library for doing DHT22 sensor readings. I've tested this on an FRDM K64F, but I see no reason it wouldn't work on other platforms.

Note that the data in `dht22_data_t` is fixed point, to convert to a normal number, divide by 10.

There is an example application here: https://developer.mbed.org/users/co657_sjc80/code/frdm_dht22_example/

Files at this revision

API Documentation at this revision

Comitter:
co657_sjc80
Date:
Thu Nov 03 14:06:39 2016 +0000
Parent:
1:10ec58346011
Commit message:
Patches by Fred. Apparently the built in wait_us is a bit delicate, so Fred has added his own. Much nicer!

Changed in this revision

dht22.cpp Show annotated file Show diff for this revision Revisions of this file
dht22.h Show annotated file Show diff for this revision Revisions of this file
diff -r 10ec58346011 -r e52d76ef24b3 dht22.cpp
--- a/dht22.cpp	Wed Jan 06 15:36:07 2016 +0000
+++ b/dht22.cpp	Thu Nov 03 14:06:39 2016 +0000
@@ -1,149 +1,241 @@
 /*
  * (C) The University of Kent and Simon Cooksey 2015.
+ * Proddled a lot by Fred
  */
-
+ 
 #include "mbed.h"
 #include <inttypes.h>
-
+ 
 #include "dht22.h"
-
+ 
 /*
  * The DHT22 uses a 1 wire interface, sending 1's and 0s by varying the length
  * 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)
+{
+    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;
+}
+ 
+ 
+/* ensure the comms pin is set for input */
+void DHT22::setinput (void)
 {
-    dht22_s.input();
-
-    // 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);
-
-    // wait for the edge to transition properly
-    wait_us(2);
-    return time;
+    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;
+    }
 }
-
+ 
+ 
+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)
 {
-    dht22_s.output();
+    int dly;
+    
+    //__disable_irq ();
+    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()
-{
-    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
-}
-
-/*
- * 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.
- */
-int16_t DHT22::read_word()
+int DHT22::wait_start (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);
-
-        if(duration > DHT22_SIGNAL_HIGH_LOW_BOUNDARY)
-        {
-            word |= (1 << 15-bit);
-        }
-        else
-        {
-            word |= (0 << 15-bit);
+    /* 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 8 bits of data, returns value [0-255] on success, -1 on error (timeout)
+ */
+int DHT22::read_byte (void)
+{
+    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;
         }
+ 
+ 
+        d = wait_for_level (0, 100);
+        if (d < 0) {
+            /* timed out after 100us */
+            return -3;
+        }
+ 
+ 
+        if (d > DHT22_SIGNAL_HIGH_LOW_BOUNDARY) {
+            v |= (1 << bit);
+#ifdef DEBUG_DHT22
+            debug = 1;
+        } else {
+            debug = 0;
+#endif
+        }
+ 
     }
-    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;
+ 
+#ifdef DEBUG_DHT22
+    debug = 0;
+#endif
+    __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();
-
-    // Read checksum  (8)
-    uint8_t checksum = read_checksum();
-
-    data->humidity = (humidity);
-    data->temp = (temp);
-    data->checksum = checksum;
-}
\ No newline at end of file
+ 
+    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;
+    }
+ 
+    /* 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->checksum = buf[0];
+ 
+#ifdef DEBUG_DHT22
+    debug = 0;
+#endif
+    
+    return i;
+}
+ 
diff -r 10ec58346011 -r e52d76ef24b3 dht22.h
--- a/dht22.h	Wed Jan 06 15:36:07 2016 +0000
+++ b/dht22.h	Thu Nov 03 14:06:39 2016 +0000
@@ -1,38 +1,52 @@
 /*
  * (C) The University of Kent and Simon Cooksey 2015.
  */
-
+ 
 #ifndef __DHT22_h_
 #define __DHT22_h_
-
+ 
 // We'll pick a point to decide if a signal is 1 or 0 from. 
-#define DHT22_SIGNAL_HIGH_LOW_BOUNDARY      50   // uS
-#define DHT22_START_BIT_TIME                500  // uS
+#define DHT22_SIGNAL_HIGH_LOW_BOUNDARY      40   // uS
+#define DHT22_START_BIT_TIME                1000  // uS
 #define DHT22_START_BIT_RESPONSE            80   // uS
-
-typedef enum {
-    EDGE_TYPE_FALLING,
-    EDGE_TYPE_RISING,
-} edge_type_t;
-
+ 
+#undef DEBUG_DHT22
+ 
 typedef struct {
     int temp;
     int humidity;
     uint8_t checksum;
+    char dummy[3];
 } DHT22_data_t;
-
+ 
 class DHT22 {
 public:
-    DHT22(PinName pin)  : dht22_s(pin) { }
-    void read(DHT22_data_t * data);
+    DHT22 (PinName pin) : dht22_s (pin)
+#ifdef DEBUG_DHT22
+                            , debug (PTB19)     /* GROT! -- hardwired for K64F */
+#endif
+    {
+        dht22_s.input ();
+        isinput = 1;
+    }
+    
+    int read (DHT22_data_t *ptr);
 private:
     DigitalInOut dht22_s;
-
-    int wait_for_edge(edge_type_t type);
-    void send_start();
-    void await_start_response();
-    int16_t read_word();
-    uint8_t read_checksum();
+    int isinput;
+#ifdef DEBUG_DHT22
+    DigitalOut debug;
+#endif
+ 
+    void wait_2us (void);
+    void setinput (void);
+    void setoutput (void);
+    
+    int wait_for_level (int lvl, const int max);
+    void send_start (void);
+    int wait_start (void);
+    int read_byte (void);
 };
-
+ 
 #endif // __DHT22_h_
+