dht.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /* ****************************************************************************
  2. *
  3. * ESP32 platform interface for DHT temperature & humidity sensors
  4. *
  5. * Copyright (c) 2017, Arnim Laeuger
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to deal
  9. * in the Software without restriction, including without limitation the rights
  10. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. * copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in all
  15. * copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23. * SOFTWARE.
  24. * ****************************************************************************/
  25. #include "platform.h"
  26. #include "driver/rmt.h"
  27. #include "driver/gpio.h"
  28. #include "freertos/FreeRTOS.h"
  29. #include "freertos/task.h"
  30. #include "esp_log.h"
  31. #undef DHT_DEBUG
  32. // RX idle threshold [us]
  33. // needs to be larger than any duration occurring during bit slots
  34. // datasheet specs up to 200us for "Bus master has released time"
  35. #define DHT_DURATION_RX_IDLE (250)
  36. // zero bit duration threshold [us]
  37. // a high phase
  38. // * shorter than this is detected as a zero bit
  39. // * longer than this is detected as a one bit
  40. #define DHT_DURATION_ZERO (50)
  41. // grouped information for RMT management
  42. static struct {
  43. int channel;
  44. RingbufHandle_t rb;
  45. int gpio;
  46. } dht_rmt = {-1, NULL, -1};
  47. static void dht_deinit( void )
  48. {
  49. // drive idle level 1
  50. gpio_set_level( dht_rmt.gpio, 1 );
  51. rmt_rx_stop( dht_rmt.channel );
  52. rmt_driver_uninstall( dht_rmt.channel );
  53. platform_rmt_release( dht_rmt.channel );
  54. // invalidate channel and gpio assignments
  55. dht_rmt.channel = -1;
  56. dht_rmt.gpio = -1;
  57. }
  58. static int dht_init( uint8_t gpio_num )
  59. {
  60. // acquire an RMT module for RX
  61. if ((dht_rmt.channel = platform_rmt_allocate( 1 )) >= 0) {
  62. #ifdef DHT_DEBUG
  63. ESP_LOGI("dht", "RMT RX channel: %d", dht_rmt.channel);
  64. #endif
  65. rmt_config_t rmt_rx;
  66. rmt_rx.channel = dht_rmt.channel;
  67. rmt_rx.gpio_num = gpio_num;
  68. rmt_rx.clk_div = 80; // base period is 1us
  69. rmt_rx.mem_block_num = 1;
  70. rmt_rx.rmt_mode = RMT_MODE_RX;
  71. rmt_rx.rx_config.filter_en = true;
  72. rmt_rx.rx_config.filter_ticks_thresh = 30;
  73. rmt_rx.rx_config.idle_threshold = DHT_DURATION_RX_IDLE;
  74. if (rmt_config( &rmt_rx ) == ESP_OK) {
  75. if (rmt_driver_install( rmt_rx.channel, 512, 0 ) == ESP_OK) {
  76. rmt_get_ringbuf_handler( dht_rmt.channel, &dht_rmt.rb );
  77. dht_rmt.gpio = gpio_num;
  78. // use gpio for TX direction
  79. // drive idle level 1
  80. gpio_set_level( gpio_num, 1 );
  81. gpio_pullup_dis( gpio_num );
  82. gpio_pulldown_dis( gpio_num );
  83. gpio_set_direction( gpio_num, GPIO_MODE_INPUT_OUTPUT_OD );
  84. gpio_set_intr_type( gpio_num, GPIO_INTR_DISABLE );
  85. return PLATFORM_OK;
  86. }
  87. }
  88. platform_rmt_release( dht_rmt.channel );
  89. }
  90. return PLATFORM_ERR;
  91. }
  92. int platform_dht_read( uint8_t gpio_num, uint8_t wakeup_ms, uint8_t *data )
  93. {
  94. if (dht_init( gpio_num ) != PLATFORM_OK)
  95. return PLATFORM_ERR;
  96. // send start signal and arm RX channel
  97. TickType_t xDelay = pdMS_TO_TICKS( wakeup_ms ) > 0 ? pdMS_TO_TICKS( wakeup_ms ) : 1;
  98. gpio_set_level( gpio_num, 0 ); // pull wire low
  99. vTaskDelay( xDelay ); // time low phase
  100. rmt_rx_start( dht_rmt.channel, true ); // arm RX channel
  101. gpio_set_level( gpio_num, 1 ); // release wire
  102. // wait for incoming bit stream
  103. size_t rx_size;
  104. rmt_item32_t* rx_items = (rmt_item32_t *)xRingbufferReceive( dht_rmt.rb, &rx_size, pdMS_TO_TICKS( 100 ) );
  105. // default is "no error"
  106. // error conditions have to overwrite this with PLATFORM_ERR
  107. int res = PLATFORM_OK;
  108. if (rx_items) {
  109. #ifdef DHT_DEBUG
  110. ESP_LOGI("dht", "rx_items received: %d", rx_size);
  111. for (size_t i = 0; i < rx_size / 4; i++) {
  112. ESP_LOGI("dht", "level: %d, duration %d", rx_items[i].level0, rx_items[i].duration0);
  113. ESP_LOGI("dht", "level: %d, duration %d", rx_items[i].level1, rx_items[i].duration1);
  114. }
  115. #endif
  116. // we expect 40 bits of payload plus a response bit and two edges for start and stop signals
  117. // each bit on the wire consumes 2 rmt samples (and 2 rmt samples stretch over 4 bytes)
  118. if (rx_size >= (5*8 + 1+1) * 4) {
  119. // check framing
  120. if (rx_items[ 0].level0 == 1 && // rising edge of the start signal
  121. rx_items[ 0].level1 == 0 && rx_items[1].level0 == 1 && // response signal
  122. rx_items[41].level1 == 0) { // falling edge of stop signal
  123. // run through the bytes
  124. for (size_t byte = 0; byte < 5 && res == PLATFORM_OK; byte++) {
  125. size_t bit_pos = 1 + byte*8;
  126. data[byte] = 0;
  127. // decode the bits inside a byte
  128. for (size_t bit = 0; bit < 8; bit++, bit_pos++) {
  129. if (rx_items[bit_pos].level1 != 0) {
  130. // not a falling edge, terminate decoding
  131. res = PLATFORM_ERR;
  132. break;
  133. }
  134. // ignore duration of low level
  135. // data is sent MSB first
  136. data[byte] <<= 1;
  137. if (rx_items[bit_pos + 1].level0 == 1 && rx_items[bit_pos + 1].duration0 > DHT_DURATION_ZERO)
  138. data[byte] |= 1;
  139. }
  140. #ifdef DHT_DEBUG
  141. ESP_LOGI("dht", "data 0x%02x", data[byte]);
  142. #endif
  143. }
  144. // all done
  145. } else {
  146. // framing mismatch on start, response, or stop signals
  147. res = PLATFORM_ERR;
  148. }
  149. } else {
  150. // too few bits received
  151. res = PLATFORM_ERR;
  152. }
  153. vRingbufferReturnItem( dht_rmt.rb, (void *)rx_items );
  154. } else {
  155. // time out occurred, this indicates an unconnected / misconfigured bus
  156. res = PLATFORM_ERR;
  157. }
  158. dht_deinit();
  159. return res;
  160. }