dht11_reader.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /*
  2. * dht11_reader.c
  3. *
  4. * Standalone DHT11 reader using libgpiod (C).
  5. * Uses open-drain output for the host start signal and a tight
  6. * user-space polling loop to sample the line at ~1us intervals.
  7. *
  8. * Usage:
  9. * ./dht11_reader [chip] [line]
  10. *
  11. * Defaults:
  12. * chip = gpiochip0
  13. * line = 19 (RK3562 GPIO0_C3_d on the 20-pin header pin 2)
  14. */
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <stdint.h>
  18. #include <string.h>
  19. #include <unistd.h>
  20. #include <time.h>
  21. #include <errno.h>
  22. #include <sched.h>
  23. #include <gpiod.h>
  24. #define DEFAULT_CHIP "gpiochip0"
  25. #define DEFAULT_LINE 19
  26. /* DHT11 timing (microseconds) */
  27. #define START_LOW_US 20000 /* 18-30 ms */
  28. #define RELEASE_MIN_US 20 /* host release must be >= 20us */
  29. #define RESPONSE_TIMEOUT_US 200
  30. #define BIT_TIMEOUT_US 120
  31. #define BIT_THRESHOLD_US 40 /* high > 40us -> 1 */
  32. #define MAX_SAMPLES 8192
  33. struct sample {
  34. uint64_t ts_us;
  35. int val;
  36. };
  37. static inline uint64_t now_us(void)
  38. {
  39. struct timespec ts;
  40. clock_gettime(CLOCK_MONOTONIC, &ts);
  41. return (uint64_t)ts.tv_sec * 1000000ULL + (uint64_t)ts.tv_nsec / 1000ULL;
  42. }
  43. static inline void busy_wait_us(uint64_t us)
  44. {
  45. uint64_t deadline = now_us() + us;
  46. while (now_us() < deadline) {
  47. __asm__ volatile ("nop");
  48. }
  49. }
  50. static void usage(const char *prog)
  51. {
  52. fprintf(stderr, "Usage: %s [chip] [line]\n", prog);
  53. fprintf(stderr, " chip: gpio chip name (default: %s)\n", DEFAULT_CHIP);
  54. fprintf(stderr, " line: gpio line offset (default: %d, RK3562 GPIO0_C3_d / header pin 2)\n", DEFAULT_LINE);
  55. }
  56. static void rt_setup(void)
  57. {
  58. struct sched_param param = { .sched_priority = 50 };
  59. if (sched_setscheduler(0, SCHED_FIFO, &param) < 0) {
  60. /* non-fatal */
  61. }
  62. }
  63. int main(int argc, char *argv[])
  64. {
  65. const char *chip_name = DEFAULT_CHIP;
  66. unsigned int line_offset = DEFAULT_LINE;
  67. if (argc >= 2) chip_name = argv[1];
  68. if (argc >= 3) line_offset = (unsigned int)atoi(argv[2]);
  69. if (argc > 3) { usage(argv[0]); return 2; }
  70. rt_setup();
  71. struct gpiod_chip *chip = gpiod_chip_open_by_name(chip_name);
  72. if (!chip) {
  73. fprintf(stderr, "ERR: failed to open %s: %s\n", chip_name, strerror(errno));
  74. return 1;
  75. }
  76. struct gpiod_line *line = gpiod_chip_get_line(chip, line_offset);
  77. if (!line) {
  78. fprintf(stderr, "ERR: failed to get line %u: %s\n", line_offset, strerror(errno));
  79. gpiod_chip_close(chip);
  80. return 1;
  81. }
  82. gpiod_line_release(line);
  83. /* Open-drain output: 1 = Hi-Z (pull-up), 0 = drive low */
  84. if (gpiod_line_request_output_flags(line, "dht11_reader",
  85. GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN, 1) < 0) {
  86. fprintf(stderr, "ERR: failed to request open-drain output: %s\n", strerror(errno));
  87. gpiod_chip_close(chip);
  88. return 1;
  89. }
  90. /* Stabilize high */
  91. gpiod_line_set_value(line, 1);
  92. busy_wait_us(2000000);
  93. /* Start signal: low for 20ms */
  94. gpiod_line_set_value(line, 0);
  95. busy_wait_us(START_LOW_US);
  96. /* Release and immediately begin sampling */
  97. gpiod_line_set_value(line, 1);
  98. uint64_t release_ts = now_us();
  99. struct sample samples[MAX_SAMPLES];
  100. int n_samples = 0;
  101. uint64_t deadline = release_ts + RESPONSE_TIMEOUT_US + 40 * (80 + 80) + 1000;
  102. while (n_samples < MAX_SAMPLES && now_us() < deadline) {
  103. int v = gpiod_line_get_value(line);
  104. if (v < 0) continue;
  105. samples[n_samples].ts_us = now_us();
  106. samples[n_samples].val = v;
  107. n_samples++;
  108. }
  109. gpiod_line_release(line);
  110. gpiod_chip_close(chip);
  111. fprintf(stderr, "DEBUG: captured %d samples over %llu us\n",
  112. n_samples, (unsigned long long)(samples[n_samples - 1].ts_us - samples[0].ts_us));
  113. /* Find response: first sustained low (>=40us) then high */
  114. int resp_low_start = -1, resp_low_end = -1, resp_high_end = -1;
  115. for (int i = 0; i < n_samples - 1; i++) {
  116. if (resp_low_start < 0 && samples[i].val == 0) {
  117. resp_low_start = i;
  118. }
  119. if (resp_low_start >= 0 && resp_low_end < 0 && samples[i].val == 1) {
  120. resp_low_end = i;
  121. }
  122. if (resp_low_end >= 0 && resp_high_end < 0 && samples[i].val == 0) {
  123. resp_high_end = i;
  124. break;
  125. }
  126. }
  127. if (resp_low_start < 0 || resp_low_end < 0 || resp_high_end < 0) {
  128. fprintf(stderr, "ERR: could not find response pulse\n");
  129. return 1;
  130. }
  131. uint64_t resp_low_dur = samples[resp_low_end].ts_us - samples[resp_low_start].ts_us;
  132. uint64_t resp_high_dur = samples[resp_high_end].ts_us - samples[resp_low_end].ts_us;
  133. fprintf(stderr, "DEBUG: response low=%llu us high=%llu us\n",
  134. (unsigned long long)resp_low_dur,
  135. (unsigned long long)resp_high_dur);
  136. /* Decode 40 bits: each bit starts after a falling edge; high duration decides value */
  137. uint8_t data[5] = {0};
  138. int bit_idx = 0;
  139. int i = resp_high_end;
  140. while (bit_idx < 40 && i < n_samples - 1) {
  141. /* find rising edge (start of bit high pulse) */
  142. while (i < n_samples - 1 && !(samples[i].val == 0 && samples[i + 1].val == 1)) i++;
  143. if (i >= n_samples - 1) break;
  144. uint64_t rise_ts = samples[i + 1].ts_us;
  145. /* find next falling edge (end of bit high pulse) */
  146. i++;
  147. while (i < n_samples - 1 && !(samples[i].val == 1 && samples[i + 1].val == 0)) i++;
  148. if (i >= n_samples - 1) {
  149. /* last bit: measure until end of capture */
  150. uint64_t high_dur = samples[n_samples - 1].ts_us - rise_ts;
  151. int bit = (high_dur > BIT_THRESHOLD_US) ? 1 : 0;
  152. data[bit_idx / 8] = (data[bit_idx / 8] << 1) | bit;
  153. fprintf(stderr, "DEBUG: bit %02d high=%llu us -> %d (tail)\n",
  154. bit_idx, (unsigned long long)high_dur, bit);
  155. bit_idx++;
  156. break;
  157. }
  158. uint64_t fall_ts = samples[i + 1].ts_us;
  159. uint64_t high_dur = fall_ts - rise_ts;
  160. int bit = (high_dur > BIT_THRESHOLD_US) ? 1 : 0;
  161. data[bit_idx / 8] = (data[bit_idx / 8] << 1) | bit;
  162. fprintf(stderr, "DEBUG: bit %02d high=%llu us -> %d\n",
  163. bit_idx, (unsigned long long)high_dur, bit);
  164. bit_idx++;
  165. }
  166. if (bit_idx != 40) {
  167. fprintf(stderr, "ERR: only decoded %d bits\n", bit_idx);
  168. return 1;
  169. }
  170. uint8_t checksum = data[0] + data[1] + data[2] + data[3];
  171. int valid = (checksum == data[4]);
  172. int humidity = data[0];
  173. int temperature = data[2];
  174. printf("{");
  175. printf("\"valid\":%s,", valid ? "true" : "false");
  176. printf("\"humidity\":%d,", humidity);
  177. printf("\"temperature\":%d,", temperature);
  178. printf("\"raw\":[%u,%u,%u,%u,%u],", data[0], data[1], data[2], data[3], data[4]);
  179. printf("\"checksum\":%u", checksum);
  180. printf("}\n");
  181. return valid ? 0 : 1;
  182. }