温湿度を計ってみよう ~AM2320 by C~
はじめに
温湿度計DHT11より少し高性能なAM2320を使用して温湿度を測定していきます。
AM2320の測定値の取得方法は2種類あります。
・I2C
・One-Wire
今回はOne-Wire方式で取得していきます。
大まかなやり方はDHT11の時と変わりません。
用意するもの
Raspberry Pi | Raspberry Pi 4 |
AM2320 | AM2320 |
抵抗 | カーボン抵抗 1/2W 5.1kΩ(100本入) |
ジャンパーケーブル | ジャンパーケーブル |
ブレッドボード | ブレッドボード |
読み取り方法の確認
計測データの取得方法等をデータシートで確認します。。
言わずもがな英語です・・・・。
私も英語を読めるわけではないですが、ざっくりとまとめます。
ちゃんと読めてるわけではないので説明が間違っている箇所もあるかと思いますが、とりあえず動けばOKっということで・・・。
データを受信する流れは図の通りです。
スタートシグナルを受信するとレスポンスを返してデータを送信します。
各シグナルには送出時間が決まっています。
各シグナルの時間は表のとおりです。
読み取りの流れ
①スタートシグナル
Lowシグナルを1ms(Tbe) AM2320に送出し、30μs(Tgo)Highシグナルを送出するとスタートします。
②待機状態
スタートシグナルを送出した後、AM2320から待機シグナルを受信します。
Lowシグナルを80μs(Trel)を受信後、Highシグナルを80μs(Treh)を受信します。
③データの受信
- Lowシグナルを50μs(Tlow)を受信します。
- Highシグナルをデータビットが「0」の場合は26μs、データビットが「1」の場合は70μs受信します。
- 1に戻ります。
- 2~3を40ビット分(40回)繰り返します。
①~③の動作をプログラムに書き起こせば温湿度を測定することができます。
測定プログラム
データ受信処理をプログラム化していきます。
まず、定数部分。
#include <stdio.h>
#include <time.h>
#include <wiringPi.h>
#include <math.h>
//AM2320 parameter
#define AM2320_DATAPIN 18 //データ受信PIN番号
#define AM2320_START_SIG_LOW 1 //スタートLOWシグナル
#define AM2320_START_PullUp 20 //スタートHIGHシグナル
#define AM2320_WAITRES_LOW 75 //待機LOWシグナル
#define AM2320_WAITRES_HIGH 75 //待機HIGHシグナル
#define AM2320_DATAWAIT_LOW 48 //データ待機
#define AM2320_BITZERO 22 //受信データが「0」の時のシグナル長
#define AM2320_BITONE 68 //受信データが「1」の時のシグナル長
#define AM2320_END 45 //終わりシグナル
各パラメータ1回しか使わないのでわざわざ宣言する必要もないかもしれませんが、1μs増減させて試すとかあるとめんどくさいので。
続いてGPIOとかのセットアップ。
int AM2320_DATA[40]= {0}; //受信データの格納
wiringPiSetupGpio(); //受信ピンをGPIOで指定
pinMode(AM2320_DATAPIN , OUTPUT); //ピンをアウトプットモードに変更
digitalWrite(AM2320_DATAPIN , HIGH); //アウトプットをHIGHに
delay(2000); //2000ms(2s)待つ
LOWシグナルがデータ受信の契機となっているため、まずHIGHシグナルを送出して待機させておきます。
時間は2秒としてますがもっと短くてもいいと思います。ここはお好みです。
続いて上記①の部分
//START OUTPUT LOW >18ms
digitalWrite(AM2320_DATAPIN , LOW);
delay(AM2320_START_SIG_LOW);
//START PUll up
digitalWrite(AM2320_DATAPIN , HIGH);
delayMicroseconds(AM2320_START_PullUp);
ここでぼーっとしてると嵌ります。μsとmsを間違います。msは初めのLOWシグナルのみで、あとはμsです。
②の部分
pinMode(AM2320_DATAPIN , INPUT);
//Response LOW SIG
pinMode(AM2320_DATAPIN , INPUT);
for ( int i = 0 ; i < AM2320_WAITRES_LOW ; i++ ){
if (digitalRead(AM2320_DATAPIN) == HIGH){
break;
}
delayMicroseconds(1);
}
//Response HIGH SIG
delayMicroseconds(AM2320_WAITRES_HIGH);
for ( int i = 0 ; i < AM2320_WAITRES_HIGH ; i++ ){
if (digitalRead(AM2320_DATAPIN) == LOW){
break;
}
delayMicroseconds(1);
}
for分で1μs毎にピンの状態を確認していきます。
状態が変わったらfor分を抜けて次の処理にいきます。
③の部分
//DATA Recieve
for ( int i = 0 ; i < 40 ; i++){ //受信するデータは40ビット
//start bit wait
delayMicroseconds(AM2320_DATAWAIT_LOW);
for ( int j = 0 ; j < AM2320_DATAWAIT_LOW ; j++){
if(digitalRead(AM2320_DATAPIN) == HIGH){
break;
}
delayMicroseconds(1);
}
for ( int j = 0 ; j < 100 ; j++){
if (digitalRead(AM2320_DATAPIN) == LOW){
if ( j < AM2320_BITONE ){ //0 1 の判定
AM2320_DATA[i] = 0;
} else {
AM2320_DATA[i] = 1;
}
break;
}
delayMicroseconds(1);
}
}
受信するデータは40ビットなので40回繰り返します。
受信データは配列に格納していきます。(AM2320_DATA[40])
Highシグナルの長さで「0」「1」がきまるので、if分で長さに応じてデータを格納していきます。
//End process
delayMicroseconds(AM2320_END);
digitalWrite(AM2320_DATAPIN , HIGH);
//data check
int dec[4] = {1,2,4,8};
float dt[10] = {0};
int hex[4] = {1,16,256,1536};
for ( int i = 0 , j = 3 ,k = 0 , l = 3 ; i < 40 ; i++ , j--){
if (AM2320_DATA[i] == 1 ){
dt[k] += dec[j];
}
if ( j == 0 ){
dt[k] = dt[k] * hex[l];
l -= 1;
if ( l < 0){
l = 3 ;
}
j = 4;
k += 1;
}
}
float hum = (dt[0] + dt[1] + dt[2] + dt[3]) / 10 ;
float tem = (dt[5] + dt[6] + dt[7]) / 10 ;
if (dt[4] == 1536){
tem = tem * -1 ;
}
printf( "temperature %d.%d humdity %d.%d \n" , tem , hum );
終了プロセスはなくても大丈夫かと思いますが念のため。
受信したデータは、2進数を16進数に変換して10進数に変換します。
もっと簡単に変換できそうですが知らんので。こんな感じです。
素子の配線
GPIO18をデータピンにしています。
AM2320の3,4番ピンをGND、1番ピンをVDD(5V)、2番ピンをGPIO18に接続です。
プログラムの実行
コンパイルして実行します。
gcc -o am2320.c am2320 -lwiringPi
./am2320
temperature 25.600000 humdity 29.500000
それっぽい気温と湿度が表示されれば成功です。(室内の乾燥がやばいですね。)
受信データのビットずれなどで値がおかしな時がありますが、何回か実行すれば大丈夫です。
おわりに
DHT11よりも高性能なモジュールで簡単に温湿度を計測してみました。
氷点下も測れるらしいのでやってみたいですが、その環境がないので難しいです。
DHT11高性能ですがお手軽価格であることは変わらないので、正確性を増したい方はおすすめです。
AM2320はI2Cによる取得も可能であるため(こっちが主流?)、こちらもチャレンジしていきたいと思います。
LEDやセグと組み合わせれば自作の温湿度受けを制作できそうです。
プログラム全文
DHT11の時とほぼ同じです。
もっとスマートな書き方がありそうですが、動くのでOK。
#include <stdio.h>
#include <time.h>
#include <wiringPi.h>
#include <math.h>
//AM2320 parameter
#define AM2320_DATAPIN 18
#define AM2320_START_SIG_LOW 1
#define AM2320_START_PullUp 20
#define AM2320_WAITRES_LOW 75
#define AM2320_WAITRES_HIGH 75
#define AM2320_DATAWAIT_LOW 48
#define AM2320_BITZERO 22
#define AM2320_BITONE 68
#define AM2320_END 45
int main()
{
//START
//SetUp
int AM2320_DATA[40]= {0};
wiringPiSetupGpio();
pinMode(AM2320_DATAPIN , OUTPUT);
digitalWrite(AM2320_DATAPIN , HIGH);
delay(2000);
//START OUTPUT LOW >1ms
digitalWrite(AM2320_DATAPIN , LOW);
delay(AM2320_START_SIG_LOW);
//START PUll up
digitalWrite(AM2320_DATAPIN , HIGH);
delayMicroseconds(AM2320_START_PullUp);
//Response LOW SIG
pinMode(AM2320_DATAPIN , INPUT);
for ( int i = 0 ; i < AM2320_WAITRES_LOW ; i++ ){
if (digitalRead(AM2320_DATAPIN) == HIGH){
break;
}
delayMicroseconds(1);
}
//Response HIGH SIG
delayMicroseconds(AM2320_WAITRES_HIGH);
for ( int i = 0 ; i < AM2320_WAITRES_HIGH ; i++ ){
if (digitalRead(AM2320_DATAPIN) == LOW){
break;
}
delayMicroseconds(1);
}
//DATA Recieve
for ( int i = 0 ; i < 40 ; i++){
//start bit wait
delayMicroseconds(AM2320_DATAWAIT_LOW);
for ( int j = 0 ; j < AM2320_DATAWAIT_LOW ; j++){
if(digitalRead(AM2320_DATAPIN) == HIGH){
break;
}
delayMicroseconds(1);
}
for ( int j = 0 ; j < 255 ; j++){
if (digitalRead(AM2320_DATAPIN) == LOW){
if ( j < AM2320_BITONE ){
AM2320_DATA[i] = 0;
} else {
AM2320_DATA[i] = 1;
}
break;
}
delayMicroseconds(1);
}
}
//End process
delayMicroseconds(AM2320_END);
digitalWrite(AM2320_DATAPIN , HIGH);
//data check
int dec[4] = {1,2,4,8};
float dt[10] = {0};
int hex[4] = {1,16,256,1536};
for ( int i = 0 , j = 3 ,k = 0 , l = 3 ; i < 40 ; i++ , j--){
if (AM2320_DATA[i] == 1 ){
dt[k] += dec[j];
}
if ( j == 0 ){
dt[k] = dt[k] * hex[l];
l -= 1;
if ( l < 0){
l = 3 ;
}
j = 4;
k += 1;
}
}
float hum = (dt[0] + dt[1] + dt[2] + dt[3]) / 10 ;
float tem = (dt[5] + dt[6] + dt[7]) / 10 ;
if (dt[4] == 1536 ){
tem = tem * -1 ;
}
printf( "temperature %d.%d humdity %d.%d \n" , tem , hum );
}