| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- /*
- * dht11_reader.c
- *
- * Standalone DHT11 reader using libgpiod (C).
- * Uses open-drain output for the host start signal and a tight
- * user-space polling loop to sample the line at ~1us intervals.
- *
- * Usage:
- * ./dht11_reader [chip] [line]
- *
- * Defaults:
- * chip = gpiochip0
- * line = 19 (RK3562 GPIO0_C3_d on the 20-pin header pin 2)
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdint.h>
- #include <string.h>
- #include <unistd.h>
- #include <time.h>
- #include <errno.h>
- #include <sched.h>
- #include <gpiod.h>
- #define DEFAULT_CHIP "gpiochip0"
- #define DEFAULT_LINE 19
- /* DHT11 timing (microseconds) */
- #define START_LOW_US 20000 /* 18-30 ms */
- #define RELEASE_MIN_US 20 /* host release must be >= 20us */
- #define RESPONSE_TIMEOUT_US 200
- #define BIT_TIMEOUT_US 120
- #define BIT_THRESHOLD_US 40 /* high > 40us -> 1 */
- #define MAX_SAMPLES 8192
- struct sample {
- uint64_t ts_us;
- int val;
- };
- static inline uint64_t now_us(void)
- {
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return (uint64_t)ts.tv_sec * 1000000ULL + (uint64_t)ts.tv_nsec / 1000ULL;
- }
- static inline void busy_wait_us(uint64_t us)
- {
- uint64_t deadline = now_us() + us;
- while (now_us() < deadline) {
- __asm__ volatile ("nop");
- }
- }
- static void usage(const char *prog)
- {
- fprintf(stderr, "Usage: %s [chip] [line]\n", prog);
- fprintf(stderr, " chip: gpio chip name (default: %s)\n", DEFAULT_CHIP);
- fprintf(stderr, " line: gpio line offset (default: %d, RK3562 GPIO0_C3_d / header pin 2)\n", DEFAULT_LINE);
- }
- static void rt_setup(void)
- {
- struct sched_param param = { .sched_priority = 50 };
- if (sched_setscheduler(0, SCHED_FIFO, ¶m) < 0) {
- /* non-fatal */
- }
- }
- int main(int argc, char *argv[])
- {
- const char *chip_name = DEFAULT_CHIP;
- unsigned int line_offset = DEFAULT_LINE;
- if (argc >= 2) chip_name = argv[1];
- if (argc >= 3) line_offset = (unsigned int)atoi(argv[2]);
- if (argc > 3) { usage(argv[0]); return 2; }
- rt_setup();
- struct gpiod_chip *chip = gpiod_chip_open_by_name(chip_name);
- if (!chip) {
- fprintf(stderr, "ERR: failed to open %s: %s\n", chip_name, strerror(errno));
- return 1;
- }
- struct gpiod_line *line = gpiod_chip_get_line(chip, line_offset);
- if (!line) {
- fprintf(stderr, "ERR: failed to get line %u: %s\n", line_offset, strerror(errno));
- gpiod_chip_close(chip);
- return 1;
- }
- gpiod_line_release(line);
- /* Open-drain output: 1 = Hi-Z (pull-up), 0 = drive low */
- if (gpiod_line_request_output_flags(line, "dht11_reader",
- GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN, 1) < 0) {
- fprintf(stderr, "ERR: failed to request open-drain output: %s\n", strerror(errno));
- gpiod_chip_close(chip);
- return 1;
- }
- /* Stabilize high */
- gpiod_line_set_value(line, 1);
- busy_wait_us(2000000);
- /* Start signal: low for 20ms */
- gpiod_line_set_value(line, 0);
- busy_wait_us(START_LOW_US);
- /* Release and immediately begin sampling */
- gpiod_line_set_value(line, 1);
- uint64_t release_ts = now_us();
- struct sample samples[MAX_SAMPLES];
- int n_samples = 0;
- uint64_t deadline = release_ts + RESPONSE_TIMEOUT_US + 40 * (80 + 80) + 1000;
- while (n_samples < MAX_SAMPLES && now_us() < deadline) {
- int v = gpiod_line_get_value(line);
- if (v < 0) continue;
- samples[n_samples].ts_us = now_us();
- samples[n_samples].val = v;
- n_samples++;
- }
- gpiod_line_release(line);
- gpiod_chip_close(chip);
- fprintf(stderr, "DEBUG: captured %d samples over %llu us\n",
- n_samples, (unsigned long long)(samples[n_samples - 1].ts_us - samples[0].ts_us));
- /* Find response: first sustained low (>=40us) then high */
- int resp_low_start = -1, resp_low_end = -1, resp_high_end = -1;
- for (int i = 0; i < n_samples - 1; i++) {
- if (resp_low_start < 0 && samples[i].val == 0) {
- resp_low_start = i;
- }
- if (resp_low_start >= 0 && resp_low_end < 0 && samples[i].val == 1) {
- resp_low_end = i;
- }
- if (resp_low_end >= 0 && resp_high_end < 0 && samples[i].val == 0) {
- resp_high_end = i;
- break;
- }
- }
- if (resp_low_start < 0 || resp_low_end < 0 || resp_high_end < 0) {
- fprintf(stderr, "ERR: could not find response pulse\n");
- return 1;
- }
- uint64_t resp_low_dur = samples[resp_low_end].ts_us - samples[resp_low_start].ts_us;
- uint64_t resp_high_dur = samples[resp_high_end].ts_us - samples[resp_low_end].ts_us;
- fprintf(stderr, "DEBUG: response low=%llu us high=%llu us\n",
- (unsigned long long)resp_low_dur,
- (unsigned long long)resp_high_dur);
- /* Decode 40 bits: each bit starts after a falling edge; high duration decides value */
- uint8_t data[5] = {0};
- int bit_idx = 0;
- int i = resp_high_end;
- while (bit_idx < 40 && i < n_samples - 1) {
- /* find rising edge (start of bit high pulse) */
- while (i < n_samples - 1 && !(samples[i].val == 0 && samples[i + 1].val == 1)) i++;
- if (i >= n_samples - 1) break;
- uint64_t rise_ts = samples[i + 1].ts_us;
- /* find next falling edge (end of bit high pulse) */
- i++;
- while (i < n_samples - 1 && !(samples[i].val == 1 && samples[i + 1].val == 0)) i++;
- if (i >= n_samples - 1) {
- /* last bit: measure until end of capture */
- uint64_t high_dur = samples[n_samples - 1].ts_us - rise_ts;
- int bit = (high_dur > BIT_THRESHOLD_US) ? 1 : 0;
- data[bit_idx / 8] = (data[bit_idx / 8] << 1) | bit;
- fprintf(stderr, "DEBUG: bit %02d high=%llu us -> %d (tail)\n",
- bit_idx, (unsigned long long)high_dur, bit);
- bit_idx++;
- break;
- }
- uint64_t fall_ts = samples[i + 1].ts_us;
- uint64_t high_dur = fall_ts - rise_ts;
- int bit = (high_dur > BIT_THRESHOLD_US) ? 1 : 0;
- data[bit_idx / 8] = (data[bit_idx / 8] << 1) | bit;
- fprintf(stderr, "DEBUG: bit %02d high=%llu us -> %d\n",
- bit_idx, (unsigned long long)high_dur, bit);
- bit_idx++;
- }
- if (bit_idx != 40) {
- fprintf(stderr, "ERR: only decoded %d bits\n", bit_idx);
- return 1;
- }
- uint8_t checksum = data[0] + data[1] + data[2] + data[3];
- int valid = (checksum == data[4]);
- int humidity = data[0];
- int temperature = data[2];
- printf("{");
- printf("\"valid\":%s,", valid ? "true" : "false");
- printf("\"humidity\":%d,", humidity);
- printf("\"temperature\":%d,", temperature);
- printf("\"raw\":[%u,%u,%u,%u,%u],", data[0], data[1], data[2], data[3], data[4]);
- printf("\"checksum\":%u", checksum);
- printf("}\n");
- return valid ? 0 : 1;
- }
|