非公式Rock研究所

Radxaのシングルボード(主にRock4c+)について書いています

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

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言語+システムコールコード

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

C言語+システムコールコード実行結果

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

C言語+システムコールコード実行結果受信カウント

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

今度は値を読み込んだ時間を計測するコードを書いてみます。

C言語+システムコールコード(出力時間計測) C言語+システムコールコード(出力時間計測)実行結果(失敗1) C言語+システムコールコード(出力時間計測)実行結果(失敗2) C言語+システムコールコード(出力時間計測)実行結果(成功)

read"81"まで値が入れば成功です。実行結果の多くはそこまで到達せず途中で値が途切れてしまいますが、いくつかは、きれいに値が埋まり成功します。

試しに成功したケースの計測値を使って変換してみます。

DHT 11 Humidity & Temperature Sensor
https://osoyoo.com/driver/DHT11-datasheet.pdf

DHT11モジュールDatasheet(抜粋)
- 『DHT 11 Humidity & Temperature Sensor』 より

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;
}

(振り返ってみると、技術や理解が浅いところからのスタートにもかかわらず、よく書いたなと自画自賛。)