ゆるく楽しむプログラミング

温湿度センサー AM2320 (ONE-Wire / I2C 両用)

3.3~5V / ONE-Wire と I2Cのどちらでも使える汎用性の高い温湿度センサーです。

 Amazonだと高い & 中国発送(時間がかかる&偽物の可能性大)ですので
 秋月電子マルツオンラインなどの国内小売店での購入をお勧めします。


ONE-Wire(単線通信)での使い方

以下の手順で通信を行います

■通信の開始
 ホスト側から:18m秒間LOW信号を出力
  

■通信開始の応答
 デバイス側から:80μ秒LOW - 80μ秒HIGH
  

■データ:'0'
 デバイス側から;50μ秒LOW-28μ秒HIGH
  
■データ'1'
 デバイス側から:50μ秒LOW-70μ秒HIGH
  
■ 通信終了
 デバイス側から:50μ秒LOW の後HIGH
  

■ デバイスから送信されるデータ
 下記表の順番に40Bit(5Byte)が送信される
内容  範囲  長さ 
 湿度 HIGH  0x00 ~ 0x0F  8Bit(1Byte)
 湿度 LOW  0x00 ~ 0xFF  8Bit(1Byte)
 温度 HIGH  0x80 ~ 0x8F  8Bit(1Byte)
 温度 LOW  0x00 ~ 0xFF  8Bit(1Byte)
 Check SUM  0x00 ~ 0xFF   8Bit(1Byte)
※温度HIGHのMSBはマイナスフラグ(2の補数ではない)
 Ex. 0x00 0x65 = 10.1℃ / 0x80 0x65 = -10.1℃

ONE-Wire(単線通信)でデータの受信方法

■ 内蔵のPULL-UP抵抗を有効にする
 pinMode(DATAPIN, INPUT_PULLUP);

■ データ送信要求 を送信して 入力がLOWになるのを待つ
 pinMode(DATAPIN, OUTPUT); //開始信号
 delay(2); // ※通常1m秒なので、念のため(?)2m秒待つ
 pinMode(DATAPIN, INPUT); // 入力に切り替え
 while(digitalRead(DATAPIN) == LOW) { // 入力値がLOWになるまで待つ
   delayMicroseconds(5) ;
 }

■ 入力値が変化するまでdigitalRead()を繰り返す
 ※DHT11と同じ割り込み利用方法も可能。メリットがないと思われるので割愛
 ※AM2320の信号ピンは何処につないでも良い
    AM2320-Wire.zip

 

※ <>を全角<>に置き換えています。コピペの際は注意してください。
// 温湿度センサー AM2320 ONE-WIRE アクセステスト
#define BAUDRATE 9600
#define DATAPIN 2 // コレは何番ピンでも良い

char str[120] ;

// ----- Senser Data
struct AM2320_s {
  uint8_t humidityH ;   // 湿度の HIGH Byte
  uint8_t humidityL ;   // 湿度の LOW BYTE
  uint8_t temperatureH; // 温度の HIGH Byte
  uint8_t temperatureL; // 温度の LOW Byte
} am2320 ;

bool readAM2320( ) {
  uint8_t readData[5] ;
  uint8_t checkSum = 0 ;
  int setPos = 0 ;
  int bitLen = 0 ;
  int stat = HIGH ;
  uint8_t bitData ;
  unsigned long beginTime ;
  unsigned long passTime ;
  unsigned long zeroTime = 35 ; // 35μ秒以下なら0 以上なら1と判定 ※仕様では0は26~28μ秒
  bool flagStart = false;

  for (int i=0;i<5;i++) {
    readData[i] = 0 ;
  }

  // ----- データ送信要求 -----
  pinMode(DATAPIN, OUTPUT); //開始信号
  delay(2); // 0.8~20mS Typ:1mS
  pinMode(DATAPIN, INPUT);
  while(digitalRead(DATAPIN) == LOW) {
    delayMicroseconds(5) ;
  }

  // ----- ACSEPT -----
  flagStart = false;
  passTime = 0 ;
  beginTime = micros();
  while (passTime < 1000) {
    passTime = micros() - beginTime ;
    if (digitalRead(DATAPIN) !=  stat) {
      if (stat == HIGH) {
        stat = LOW ;
        if (flagStart) {
           break ; 
        }
      } else {
        beginTime = micros();
        flagStart = true ;
        stat = HIGH ;
      }
    }
  }
  
  // ----- データ受信 -----
  bitData = 0x80 ;
  flagStart = false;
  passTime = 0 ;
  beginTime = micros();
  while (bitLen < 40 && passTime < 1000) {
    passTime = micros() - beginTime ;
    if (digitalRead(DATAPIN) !=  stat) {
      if (stat == HIGH) {
        stat = LOW ;
        if (flagStart) {
          // ----- 1Bit 受信 -----
          if (passTime > zeroTime) {
            readData[setPos] |= bitData ;
          }
          if (bitData != 0x01) {
            bitData >>= 1 ;
          } else {
            bitData = 0x80 ;
            setPos ++ ;
          }

          // ----- ステート変数リセット -----
          beginTime = micros();
          flagStart = false;
          bitLen ++ ;
        }
      } else {
        beginTime = micros();
        flagStart = true ;
        stat = HIGH ;
      }
    }
  }

  // ----- 取得データ格納 -----
  am2320.humidityH = readData[0] ;   // 湿度の HIGH Byte
  am2320.humidityL = readData[1] ;   // 湿度の LOW Byte
  am2320.temperatureH = readData[2]; // 温度の HIGH Byte
  am2320.temperatureL = readData[3]; // 温度の LOW Byte
  checkSum = readData[0] + readData[1] + readData[2] + readData[3] ;
  return (bitLen == 40 && checkSum == readData[4]) ;
}

void setup() {
  Serial.begin(BAUDRATE);
  Serial.println("\nAM2320 - Read") ;

  pinMode(DATAPIN, INPUT_PULLUP);
}

void loop() {
  if (readAM2320( )) {
    int hum = (am2320.humidityH << 8) + am2320.humidityL ;
    int tem = ((am2320.temperatureH & 0x7F) << 8) + am2320.temperatureL ;
    if ((am2320.temperatureH & 0x80) != 0) tem *= -1 ;
    sprintf(str,"湿度:%2d.%d%%  気温:%2d.%d℃", hum / 10 , hum % 10 , tem / 10 , tem % 10) ;
    Serial.println(str) ;
  }
  delay(3000) ;
}

I2C接続でのデータ受信方法

I2Cアドレスは0x5C
I2Cコマンドは Wakeupと読み出し、書き込み ※ユーザー領域がありますが、使う意義が分からないので割愛
センサー値の読みだ債は3秒以上の間隔を開ける必要があります。
各コマンドの間隔もそれぞれの待ち時間を入れる必要があります。



I2Cでセンサー値を取得した場合は、チェックサムではなくCRC-16になっています。
※リトルインディアンの2バイト値なので上位バイトと下位バイトが反転しています。
  CRC-16の算出式はデータシートに記載されているリストをそのまま使用しています。
レジスタアドレス レジスタ値
 0x00  湿度 上位バイト 
 0x01  湿度 下位バイト
 0x02  温度 上位バイト
 0x03  温度 下位バイト
※ アドレス0x04移行にモデル番号、デバイスID、バージョン、ユーザーレジスタと続きますが、今回は使用しないので割愛

湿度・気温の値は12ビットの整数です。
気温は最上位ビットがマイナスフラグになっています。
気温の値が 0x00 0x65 の場合は 10.1℃ 、 0x80 0x65の場合は-10.1℃になります。


AM2320-I2C.zip

 

※ <>を全角<>に置き換えています。コピペの際は注意してください。
// 温湿度センサー AM2320 I2C アクセステスト
#include <Wire.h>
#define BAUDRATE 9600
#define AM2320ADDR 0x5C

// ----- Senser Data
struct AM2320_s {
  uint8_t humidityH ;   // 湿度の HIGH Byte
  uint8_t humidityL ;   // 湿度の LOW BYTE
  uint8_t temperatureH; // 温度の HIGH Byte
  uint8_t temperatureL; // 温度の LOW Byte
} am2320 ;

char str[120] ;

bool readAM2320( ) {
  uint8_t readData[8] ;
  // wake up
  Wire.beginTransmission(AM2320ADDR);
  Wire.write(0x00);
  Wire.endTransmission();
  delay(10); // wait 10 ms

  Wire.beginTransmission(AM2320ADDR);
  Wire.write(0x03);
  Wire.write(0x00);
  Wire.write(0x04);
  Wire.endTransmission();
  delay(2); // wait 2 ms

  // 4Byte + CRC Read
  Wire.requestFrom(AM2320ADDR, 8);
  int len = 0 ;
  while( len < 8) {
    if (Wire.available() > 0) {
      readData[len] = Wire.read();
      len ++ ;
    }
  }

  // ----- CRC16 -----
  int readLen = 6 ;
  uint16_t rCrc = readData[7] << 8 | readData[6] ; 
  uint16_t crc = 0xffff;
  for (int i = 0; i < readLen ; i++) {
    uint8_t b = readData[i];
    crc ^= b;
    for (int x = 0; x < 8; x++) {
      if (crc & 0x0001) {
        crc >>= 1;
        crc ^= 0xA001;
      } else {
        crc >>= 1;
      }
    }
  }

  // ----- 取得データ格納 -----
  am2320.humidityH = readData[2] ;   // 湿度の HIGH Byte
  am2320.humidityL = readData[3] ;   // 湿度の LOW Byte
  am2320.temperatureH = readData[4]; // 温度の HIGH Byte
  am2320.temperatureL = readData[5]; // 温度の LOW Byte
  return (rCrc == crc) ;
}

void setup() {
  Serial.begin(BAUDRATE);
  Serial.println("\nAM2320 - Read") ;

  Wire.begin();
}

void loop() {
  if (readAM2320( )) {
    int hum = (am2320.humidityH << 8) + am2320.humidityL ;
    int tem = ((am2320.temperatureH & 0x7F) << 8) + am2320.temperatureL ;
    if ((am2320.temperatureH & 0x80) != 0) tem *= -1 ;
    sprintf(str,"湿度:%2d.%d%%  気温:%2d.%d℃", hum / 10 , hum % 10 , tem / 10 , tem % 10) ;
    Serial.println(str) ;
  }
  delay(3000) ;
}

温湿度計デモ

温湿度計デモ:ディスプレイにはSSD1306 または SSD1315 を使用。
ボタンは数値表示/グラフ表示切替用 なのでなくても良い。

AM2320-DEMO.zip Arduino用
AM2320-DEMO-Pico.zip RaspberryPi Pico用:mbedベースのボードライブラリを想定
 ※メモリ容量の都合でフォントの定数定義(cont と static)が異なるだけでロジックは同じものです。)

 

解説動画