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/
Revision 2:e52d76ef24b3, committed 2016-11-03
- 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_ +