DHT11モジュールとの格闘(解決への糸口)

まだまだ継続中の温湿度センサー、DHT11モジュールです。
【Article】「DHT11モジュールとの格闘」 - 非公式Rock研究所
https://informal-r-labo.net/posts/article013
【Article】DHT11モジュールとの格闘(続編) - 非公式Rock研究所
https://informal-r-labo.net/posts/article014
【Article】DHT11モジュールとの格闘(いろいろ試してみた編) - 非公式Rock研究所
https://informal-r-labo.net/posts/article015
前回の記事ではArduinoを使ってDHT11モジュールが壊れていないことは確認できました。
ただ、この後どうすればいいのかわからず、あれこれネットを検索していると、システムコールでGPIOを制御しているページを見つけました。
システムコールでラズパイを操作してみよう! #Linux - Qiita
https://qiita.com/kohey0701/items/1d1ff90817c2ca2cdb1d
掲載されているページはRaspberry Piを対象としていますが、Rock4c+でもGPIOは"/sys/class/gpio"からファイルとしてアクセスできることはRadxaのwikiにも書いてあります。
Rock4/hardware/gpio - Radxa Wiki
https://wiki.radxa.com/Rock4/hardware/gpio
うまくいくかは分かりませんが、試してみることにしました。
C言語+システムコール
しかし、私はシステムコールに関する知識がほぼ皆無なので、c言語やシステムコールの学習からスタートしました。
C言語の入門書や、システムコールに関する書籍を読んだりしていて、手を動かせるようになるまで時間がかかりました。
そのあとは、見よう見まね。コンパイルエラーを出しまくりながらもコードを書いてみました。

エラーを吐かずに実行できるところまで到達したので、コードを実行してみます。

C言語+mraaのときより細かく読み込めているようです。

そして何度か実行していると、上のイメージのようにたまに成功します。(bitのペアが40個ある。)
成功率は高くありませんが、今までまったく成功しなかった読み込み処理が成功したのは私の中では大きな進歩です。
今度は値を読み込んだ時間を計測するコードを書いてみます。




read"81"まで値が入れば成功です。実行結果の多くはそこまで到達せず途中で値が途切れてしまいますが、いくつかは、きれいに値が埋まり成功します。
試しに成功したケースの計測値を使って変換してみます。
DHT 11 Humidity & Temperature Sensor
https://osoyoo.com/driver/DHT11-datasheet.pdf

osoyooのDHT11モジュールのdatasheetから、
'1'(High)の出力時間が短い(26μ秒~28μ秒)時は0、出力時間が長い(70μ秒)時は1を示しているので、50μ秒を基準に変換し、スプレッドシートで作った変換表に当てはめてみます。

おぉ!チェックデジットが合ってる!
湿度53%、温度21.8℃も部屋の状態として妥当な値です。
少し、光が見えてきました。あとは失敗時の再実行や、計測値の数字変換処理を考えます。
参考
今回の試作品プログラムです。
注意:動作は保証できません!自己責任でお願いします。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
char gpioNo[] = "154";
char sysPathDirection[100];
char sysPathValue[100];
int fd2;
const int TIME_OUT = 100000000;
struct timespec start_time;
struct timespec change_time[90];
int err_exit(char* p){
perror(p);
exit(1);
}
void gpio_write(char* p, char* s)
{
int fd;
fd = open(p, O_WRONLY);
if(fd < 0){
err_exit(p);
}
if(write(fd, s, sizeof(s)) < 0){
close(fd);
err_exit(p);
}
close(fd);
}
void read_Signal(char v, struct timespec* t)
{
char buf;
for(int i = 0; i < 100000; i++){
lseek(fd2, 0, SEEK_SET);
read(fd2, &buf, 1);
if(buf != v){
clock_gettime(CLOCK_MONOTONIC, t);
break;
}
// clock_gettime(CLOCK_MONOTONIC, t);
}
}
void sendStartSignal(void)
{
gpio_write("/sys/class/gpio/export", gpioNo);
sprintf(sysPathDirection, "/sys/class/gpio/gpio%s/direction", gpioNo);
gpio_write(sysPathDirection, "out");
sprintf(sysPathValue, "/sys/class/gpio/gpio%s/value", gpioNo);
fd2 = open(sysPathValue, O_RDWR);
if(fd2 < 0){
err_exit(sysPathValue);
}
struct timespec req;
req.tv_sec = 1;
req.tv_nsec = 0;
write(fd2, "1", 1);
nanosleep(&req, NULL);
req.tv_sec = 0;
req.tv_nsec = 18 * 1000000;
write(fd2, "0", 1);
nanosleep(&req, NULL);
close(fd2);
}
int main(void)
{
printf("start DHT11 Read!\n");
sendStartSignal();
gpio_write(sysPathDirection, "in");
fd2 = open(sysPathValue, O_RDONLY);
if(fd2 < 0){
err_exit(sysPathValue);
}
// send start signal form DTH11
clock_gettime(CLOCK_MONOTONIC, &change_time[0]);
read_Signal('0', &change_time[1]);
read_Signal('1', &change_time[2]);
for(int i = 0; i < 40; i++){
read_Signal('0', &change_time[2 * i + 3]);
read_Signal('1', &change_time[2 * i + 4]);
clock_gettime(CLOCK_MONOTONIC, &change_time[89]);
if((change_time[89].tv_nsec - change_time[0].tv_nsec) > TIME_OUT){
printf("TIME_OUT\n");
break;
}
}
for(int j = 0; j < 90; j++){
printf("read\"%d\" start_time: %ld, end_time: %ld, span: %ld\n", j, change_time[j].tv_nsec, change_time[j+1].tv_nsec, change_time[j+1].tv_nsec - change_time[j].tv_nsec);
}
close(fd2);
printf("\n");
gpio_write("/sys/class/gpio/unexport", gpioNo);
return 0;
}
(振り返ってみると、技術や理解が浅いところからのスタートにもかかわらず、よく書いたなと自画自賛。)