Arduino で 温湿度を計測してみる

Arduinoで温度を測るには

Arduinoで温度を計測するには、何かしらのセンサーをつなぐことになります。が、そのセンサーはいろいろありすぎて、どれを使ってもいいのやら。そして、どうやってつなげばいいのやら、どうスケッチしてあげればいいのやら。

ということで、いくつかのセンサーの使い方をメモしてみる。

  1. DHT11 で計ってみる
  2. ADT7310 で計ってみる
  3. S-5851A で計ってみる
  4. LM35DZ で計ってみる

 

 

DHT11 で計ってみる

秋月電子で購入した[温湿度センサ モジュール DHT11(550円/秋月)]

買ったのちに、家でAmazonを見てみたらHiLetgo 3個セット DHT11温度センサーで、3個で750円(2020/03現在)。と、こっちの方が安かったというオチ。まぁ、出先でついでに買ったものなので良いのですが。

スペック的には…

◆主な仕様
・電源電圧:DC3.3V~5.5V
・消費電流:0.3mA(測定時)、60μA(スタンバイ時)
・サンプリング間隔:2秒以上
・内部ADコンバータ:各16bit
・サイズ:12×15.5×5.5mm(ピン含まず)
◆湿度センサ部
・センサ:有機ポリマー
・精度:±5% RH(@25℃)
・繰り返し精度:±1% RH
・レスポンス:6秒以内(1/e (63%)、@25℃、風速1m/s)
◆温度センサ部
・センサ:NTCサーミスタ
・精度:±2℃(@25℃)
・繰り返し精度:±0.2℃
・レスポンス:10秒以内(1/e (63%))
◆シリアル通信部
・形式:単線バス(双方向)、シリアル40bit構成
・出力データ:湿度8bit(分解能:1%RH)、温度8bit(分解能:1℃)
・出力:オープンドレイン
・通信距離:20m max(@5.1kΩプルアップ)

詳細はDHT11 PDFデータシート(2018/1/19)参照のこと。

秋月で買ったのはこんな子。

DHT11にプルアップ抵抗を付けただけの仕様です。抵抗1本ですのでDHT11単体(秋月価格300円/2020/03現在)で使ってもよかったのですが、Arduinoなんかで使うのであれば、こういう部分が簡略化された方が便利。はんだ付けの手間が省けます。

実際には、この様なケーブルでArduinoに線で接続するだけで完了です。

接続するピンは、モジュールの右のピンから順番に、Arduinoの「GND」「+5V」「DIGITAL 8pin」に接続してあげます。

下準備

このモジュールを使用するには、ライブラリの追加が必要です。追加作業は1度やっておけば次からは不要です。

1.Arduino IDEを起動する

2.[スケッチ]メニュー内にある[ライブラリをインクルード]を選択、中にある[ライブラリを管理…]をクリック

3.検索窓に”DHT”等と入れ絞り込み、[DHT sensor library]を探し、[インストール]をクリック

4.関連するモジュールのインストール確認が出るので、特に問題なければ[Install all]をクリック

ここまで出来たら実際にスケッチしてみます。

スケッチする

#include <DHT.h>

const int PIN_DHT = 8;
DHT dht(PIN_DHT,DHT11);

void setup() {
  Serial.begin(9600);
  dht.begin();
}

void loop() {

  delay(1500);
  float humidity = dht.readHumidity();
  float temperature = dht.readTemperature();

  Serial.print("Humidity:  ");
  Serial.print(humidity);
  Serial.print(" % \t");
  Serial.print("Temperature:  ");
  Serial.print(temperature);
  Serial.println(" Celsius");
}

コードはこちらから → DHT11

書き方としては割と簡単ですね。センサーの繋がっているピンを指定して、dht.begin();とすれば、dht.readHumidity();で湿度を。dht.readTemperature();で温度を取得できます。今回のスケッチを実行すると…

Humidity:  26.00 % 	Temperature:  24.60 Celsius
Humidity:  26.00 % 	Temperature:  24.60 Celsius
Humidity:  26.00 % 	Temperature:  24.60 Celsius
Humidity:  26.00 % 	Temperature:  24.60 Celsius
...

こんな感じで温度を取得することができます。

 

 

ADT7310 で計ってみる

次に試すのはADT7310。これもまた秋月電子で購入した[ADT7310使用 高精度・高分解能 SPI・16Bit 温度センサモジュール(500円/秋月)]

こっちも、実はAmazonの方が安いんじゃないかと心配になり検索。さすがに955円(2020/03)と秋月の方が若干安め。とはいえ、交通費を掛けてという状況ならこのくらいは誤差範囲という奴でしょうか。

主なスペックはこんな感じ。

◆主な仕様
・温度精度:±0.5℃@-40℃~+105℃(2.7V~3.6V)
±0.4℃@-40℃~+105℃(3.0V)
・温度分解能:0.0078℃(16ビット設定時)/0.0625℃(13ビット設定時)
・温度校正および温度補正、直線性補正等は不要
・動作/測定温度範囲: -55℃~+150℃
・電圧範囲:DC+2.7V~+5.5V
・SPI互換インターフェース
・消費電流(@VDD=3.3V、TA=+25℃)
ノーマル・モード:210μA(typ)
パワーセービング・モード(1サンプル/1秒):46μA(typ)
シャットダウン・モード:2μA(typ)
・基板サイズ:16×12.5ミリ
・基板上の入出力端子:6個(VDD,GND,SCLK,DOUT,DIN,CS)

詳細は秋月電子通商のマニュアル参照の事。

接続はピン数が多い分、ちょっと面倒。

ADT7310 = ARDUINO
VDD = +5V
SCL = 13pin
SDO = 12pin
SDI = 11pin
CS = 10pin
GND = GND

スケッチする

SPIで接続って話なんですけれど。いまいち使い方がわからない。そういう時はとググって探すに限ります。と探していると、>Arduino UnoでSPI通信(その2)温度センサADT7310 – 株式会社インデペンデンスシステムズ横浜を発見。ひとまずこれをそのまま行ってしまいましょう。出力結果とかは他と合わせる必要があるので、ちょっと改変。

#include <SPI.h>
/*
 * ADT7310での温度データ取得
 * ADT7310 - Arduino
 *  CS  - Pin10 SS(CS)
 *  SDI - Pin11 MOSI
 *  SDO - Pin12 MISO
 *  SCL - Pin13 SCK
 *  VDD - 5V
 *  GND - GND
 */

int SSPin = 10;
 
void setup(void) {
  pinMode(SSPin, OUTPUT);
  Serial.begin(9600);
   
  SPI.setBitOrder(MSBFIRST);  //最上位ビット(MSB)から送信
  SPI.setClockDivider(SPI_CLOCK_DIV128); //通信速度を遅く設定
  SPI.setDataMode(SPI_MODE0); //CPOL(クロック極性):0 CPHA(クロックフェーズ):0

  SPI.begin();
 
  SetSSPin(LOW);

  //ソフトウェアリセット
  SPI.transfer(0xFF);
  SPI.transfer(0xFF);
  SPI.transfer(0xFF);
  SPI.transfer(0xFF);
    
  SPI.transfer(0x54);
  delay(500);
}
 
// メインループ
void loop(void) {
  uint16_t uiVal;
  float fVal;
  int iVal;
 
  uiVal = (uint16_t)SPI.transfer(0) << 8;  // AD変換値 上位
  uiVal |= SPI.transfer(0);                // AD変換値 下位

  uiVal >>= 3;                          // シフトで13bit化   
 
  if(uiVal & 0x1000) {               // 13ビットで符号判定
    iVal = uiVal - 0x2000;         // マイナスの時 (10進数で8192)
  }
  else{
    iVal = uiVal;                 //プラスの時
  }
  
  fVal = (float)iVal / 16.0;           // 温度換算(摂氏)
  Serial.print("Temperature:  ");
  Serial.print(fVal, 4);
  Serial.println(" Celsius");

  delay(5000);
}

/*
 * SSピンの設定
 * Lowでマスタからの通信が有効(セレクト)
 * (Highではマスタから通信出来ない(無視される)セレクト解除の状態)
 */
void SetSSPin(int val)
{ 
    digitalWrite(SSPin, val); 
}

コードはこちらから → ADT7310.zip

人様が成功した書き方があれば、そのまま頂いて使ってしまうのが吉。だって、プログラム手法よりもプログラムによって得られる結果が欲しい工学人間ですから。

Temperature:  25.3125 Celsius
Temperature:  25.3125 Celsius
Temperature:  25.3125 Celsius
Temperature:  25.3125 Celsius
...

結果としてはこんな感じで結果が得られます。

 

 

S-5851A で計ってみる

これも毎度ながら秋月電子通商で購入。S-5851A使用2ワイヤデジタル温度センサモジュール(110円/2020年03月現在)

2ワイヤデジタル温度センサという割に…足が6本もあるという「一瞬」うそつき仕様。まぁ、足は6本あるけれど、実際には電源やらアドレス設定用だったりするので、通信の意味では2ワイヤってのは間違っちゃいないんだけれど。

■特長
・低電圧動作:VDD(min.)=2.7V
・低消費電流:
45μAtyp.(+25°C)
1μA typ.(+25°C、シャットダウン時)
・高精度:
±2.0°C(max.)-25°C~+85°C
±3.0°C(max.)-40°C~+125°C
・温度分解能:0.0625°C
・デジタル出力:2ワイヤシリアルインタフェース
・最大動作周波数:400kHz
・低電源電圧検出回路内蔵
・半導体のパッケージ:SOT-23-6

データシート

公式にも情報がないし。さてどうすっかな?とグーグル先生に確認。Arduinoそのものでの実装ではないものの、Sony Spresenseでの接続例を発見。SpresenseでLチカから始める (18) Wireライブラリ 温度S-5851A – クックブックというサイト。

このサイトによるとICのピンアサインは

PIN No. NAME Details
1 SCL SCLピン
2 Vss +5V
3 AD1 アドレス設定用
4 SDA SDAピン
5 AD0 アドレス設定用
6 Vdd GND

こんな感じになります。AD0とAD1をVss/VddにつなぐことでI2C的なアドレスを決めている様です。そのアドレス自体は、こんな感じ。

AD0 AD1 ADDRESS
Vss Vss 0x48
N/C Vss 0x49
Vdd Vss 0x4a
Vss Vdd 0x4b
N/C Vdd 0x4c
Vdd Vdd 0x4d
Vss N/C 0x4e
Vdd N/C 0x4f

接続は参考にしたサイトと同じ設定でまずは行います。SCL/SDAはARDUINOのSCL/SDAに接続。Vddは5Vに。それ以外は全てGNDに接続します。

スケッチしてみる

#include <Wire.h>
#define S5851address 0x48
#define TemperatureR 0x00
 
void setup() {
    Wire.begin();
    Serial.begin(9600);
}
 
int read_tempdata() {
    Wire.beginTransmission(S5851address); 
         Wire.write((byte)TemperatureR);
    Wire.endTransmission();
    Wire.requestFrom(S5851address, 2);
    //wait for response
    while(Wire.available() == 0);
    int T = Wire.read(); 
    T = ( T << 8 | Wire.read() ) >> 4  ; 
    return  ( -(T & 0b100000000000) | (T & 0b011111111111) );
}
 
void loop() {
    float Temp = read_tempdata() * 0.0625 ;
    Serial.print("Temperature:  ");
    Serial.print(Temp,2);
    Serial.println(" Celsius");
    delay(1000);
}

コードはこちらから → S-5851A.zipa

Temperature:  24.12 Celsius
Temperature:  24.12 Celsius
Temperature:  24.12 Celsius

 

 

LM35DZ

一番典型的な温度センサー。昔に秋月電子通商でまとめ買いした奴。当時で90円/1個。今は通販サイトに掲載がない所を見ると、在庫限りって設定なのかもしれません。

これ自身は接続はかなり容易で、チップを正面に見て、左側からVcc、Vout、GNDを接続できます。という事で、Vccに+5V、VoutをA0(Analog 0)、GNDをGNDに接続します。

繋いでスケッチすれば簡単に利用できます。

スケッチしてみる

int sensorPin = A0;
int sensorValue = 0;

void setup() {
    Serial.begin(9600);
}

void loop() {
    sensorValue = analogRead(sensorPin);
    float temp  = modTemp(sensorValue);
    Serial.print("Temperature:  ");
    Serial.print(temp,2);
    Serial.println(" Celsius");
    delay(1000);
}

float modTemp(int analog_val){
    float v  = 5;
    float tempC = ((v * analog_val) / 1024) * 100;
    return tempC;
}

ポイントは、このセンサーはアナログだという事。印加する電圧に対して温度により分圧された電圧がVoutに出てくるので、Analogポートから電圧を読んでいます。なので、スケッチ上も印加する電圧を明示的にして計算から求めています。

Temperature:  22.95 Celsius
Temperature:  22.95 Celsius
Temperature:  22.95 Celsius
...

こんな感じで簡単に結果が出てきます。

センサー別 精度ってどんな感じなの?

いろいろなセンサーをつないでみて、それぞれにどのような値が出るもんかな。と気になって、全部のセンサーをテストのときと同じ位置に接続。それにテストで作ったスケッチを全部くっつけてみる。

現在、つないでいる奴はというと、ADT7310、LM35DZ、DHT11、ADT7310、S5851A の5種類。値段的には500円以内くらいの安いセンサーばかり。

さて、どんな結果になるやら。まず、スケッチは下記のような感じにまとめました。

#include <DHT.h&gh;
#include <Wire.h&gh;
#include <SPI.h&gh;
#define S5851address 0x48
#define TemperatureR 0x00
const int PIN_DHT = 8;
DHT dht(PIN_DHT,DHT11);
int sensorPin = A0;
int sensorValue = 0;
int SSPin = 10;

void setup() {
    pinMode(SSPin, OUTPUT);
// S5851A
    Wire.begin();
    Serial.begin(9600);
// DHT11
    dht.begin();
// ADT7310
    SPI.setBitOrder(MSBFIRST);
    SPI.setClockDivider(SPI_CLOCK_DIV128);
    SPI.setDataMode(SPI_MODE0);
    SPI.begin();
    SetSSPin(LOW);
    SPI.transfer(0xFF);
    SPI.transfer(0xFF);
    SPI.transfer(0xFF);
    SPI.transfer(0xFF);
    SPI.transfer(0x54);
    delay(500);

}

void loop() {
// ADT7310
  uint16_t uiVal;
  float fVal;
  int iVal;

// LM35DZ
    sensorValue = analogRead(sensorPin);
    float temperature_lm35dz  = modTemp(sensorValue);
// DHT11
    float humidity_DHT11 = dht.readHumidity();
    float temperature_DHT11 = dht.readTemperature();
// ADT7310
    uiVal = (uint16_t)SPI.transfer(0) << 8;
    uiVal |= SPI.transfer(0);
    uiVal >>= 3;  
    if(uiVal & 0x1000) {
        iVal = uiVal - 0x2000;
    }
    else{
        iVal = uiVal;
    }
    float temperature_ADT7310 = (float)iVal / 16.0;
// S5851A
    float temperature_S5851A = read_tempdata() * 0.0625 ;
// Disp.
    Serial.print("LM35DZ:  ");
    Serial.print(temperature_lm35dz,2);
    Serial.print(" Celsius\t");
    Serial.print("DHT11:  ");
    Serial.print(temperature_DHT11,2);
    Serial.print(" Celsius\t");
    Serial.print("ADT7310:  ");
    Serial.print(temperature_ADT7310,2);
    Serial.print(" Celsius\t");
    Serial.print("S-5851A:  ");
    Serial.print(temperature_S5851A,2);
    Serial.println(" Celsius\t");    
    delay(1000);
}

float modTemp(int analog_val){
    float v  = 5;
    float tempC = ((v * analog_val) / 1024) * 100;
    return tempC;
}
void SetSSPin(int val)
{ 
    digitalWrite(SSPin, val); 
}
int read_tempdata() {
    Wire.beginTransmission(S5851address); 
         Wire.write((byte)TemperatureR);
    Wire.endTransmission();
    Wire.requestFrom(S5851address, 2);
    //wait for response
    while(Wire.available() == 0);
    int T = Wire.read(); 
    T = ( T << 8 | Wire.read() ) >> 4  ; 
    return  ( -(T & 0b100000000000) | (T & 0b011111111111) );
}

これをとりあえず流してみて、値を計数してみましょう。

 

LM35DZ:  22.46 Celsius	DHT11:  25.50 Celsius	ADT7310:  26.12 Celsius	S-5851A:  25.06 Celsius	
LM35DZ:  22.46 Celsius	DHT11:  25.50 Celsius	ADT7310:  26.12 Celsius	S-5851A:  25.06 Celsius	
LM35DZ:  22.46 Celsius	DHT11:  25.50 Celsius	ADT7310:  26.12 Celsius	S-5851A:  25.06 Celsius	
LM35DZ:  22.46 Celsius	DHT11:  25.50 Celsius	ADT7310:  26.12 Celsius	S-5851A:  25.06 Celsius	
LM35DZ:  22.46 Celsius	DHT11:  25.50 Celsius	ADT7310:  26.12 Celsius	S-5851A:  25.06 Celsius	
LM35DZ:  22.46 Celsius	DHT11:  25.50 Celsius	ADT7310:  26.12 Celsius	S-5851A:  25.06 Celsius	
...

アナログセンサーのLM35DZ、それ以外のDHT11、ADT7310、S-5851A はデジタルセンサー。 とりあえずつないだだけの結果としては、上記な感じ。

正直言って、デジタルは±1℃の範囲位の値で推移してます。DHT11は校正済みって話で、しかも常に3センサーの真ん中を指しているし、たぶん、このあたりが比較的正しい値なんでしょうね。 アナログはやっぱり大雑把な値は取れるけれど、温度計用途には無理っぽいなぁ。

LM35DZはアナログセンサー故、単につなぐという「デジタル的」な考え方だとだめだなぁ。と思うわけです。 LM35DZに印加する電圧。ArduinoのAnalog入力の精度、いろいろなものに引っ張られるのだから、そりゃそうなんだけれど。 ともかく、USB給電で動いているので5.0Vが本当に掛かっているかどうかも実際は怪しい。 正確に扱おうとすれば、別にアダプタ等で電源供給、レギュレーターかなにかで安定した5.0Vを供給すればだいぶ違うのかもしれない。そのうえで、アナログ入力側の校正も必要…そう考えると、温度計を作るのであればあまりよろしくないかもしれない。

結論

デジタルセンサーは案外にばらつきが少ない様だし、科学的な裏付けが必要な数字には使えないものの、環境温度の記録等の軽微な利用なら問題なさそうです。

対してアナログセンサーは、本当に大雑把な利用に限定すれば使いどころがあるという感じですね。室内温度が高温になったときにアラートを出すような「大雑把に温度を把握できれば事足りる」用途なら、センサーの値段も安いしいいかもしれませんね。

コメント