温度センサーの利用(I2C接続)

I2C接続の高精度温度センサーを紹介します。先にアナログ型の温度センサを 紹介しましたが アナログ接続では電気的なノイズが乗りやすく高精度な測定は困難です。I2C 接続の場合、ディジタル出力のため、接続部分での電気的なノイズはほとんどありません。
  1. 温度センサーADT7410

     多くのアナログ出力の温度センサーは精度が±2度程度ですが、ここで紹介する温度センサーは絶対精度0.4度でかなり高精度です。チップ本体はピン間隔が狭い IC なので、これを小さな基板でモジュールとした部品を利用します。
    以下はADT7410の仕様です。
     動作電圧:3.0V〜5.0V
     消費電流:0.2mA
     測定範囲:−20〜+105℃
     AD変換:13/16bit、240mS
     精度:0.4度
     接続:I2C

  2. ADT7410の接続

     ADT7410 は I2C 接続で、接続端子は下図のようになります。VDD,GND に 3.3[V ]の電源を接続し、SCL,SDA を MINI のI2C 端子(A5,A4))に接続します。J3,J4 の端子は I2C のプルアップ用、J1,J2 ではモジューアドレスを変更できます。I2CアドレスはJ1,J2をブリッジしないとき、0x48(16進で48)となります。
     

       ADT7410 モジュール

     I2C 全体の接続回路です。右側のボードに、温度、加速度、RTC のモジュールが配置されています。加速度、RTC モジュールは続く節を参照してください。緑色が SCL、黄色がSDA、赤が電源+、黒が電源-(GND)です。
    電源は単4電池2本の電池ボックス(ボード上の黒いケース)を利用しています。


  3. I2Cの読み出し

     I2c の標準ライブラリ wire.h を利用して ADT7410 から読み込むとき注意が必要です。読み出す時には読み出しを開始する Wire.write() レジスタ番号を指定します。ここで、送信は終了するので、endTransmission(false); をしますが、このとき、false を指定して I2C バスの連続使用を指定する必要があります。false なしでも 動作する モジュールもありますが ADT7410 は対応していません。
     Wire.requestFrom(AT_ADRS, num); で num バイトの読み取り要求を行い、Wire.read() で読み込みます。最後に Wire.endTransmission(); で I2C の利用を終了します。
       Wire.beginTransmission(AT_ADRS) ;
       //読み出すレジスタを指定
       Wire.write((byte)0x0);
       //終了するがバスは解放しない
       Wire.endTransmission(false);
       //2バイト読み出す
       ans=Wire.requestFrom(AT_ADRS, 2);
       xH=Wire.read();
       xL=Wire.read();
       //読み出し終了
       Wire.endTransmission();

  4. ADT7410のデータ取得と小数の扱い

     ADT7410の温度データは 0,1 番地のレジスタに上位、下位の2バイトが入ります。データは上位バイトから13ビットで単位は 1/16度 です。上位、下位バイトを読み出し、16ビットに合成するには以下のようにします。

      ans=Wire.requestFrom(AT_ADRS, 2);
      xH=Wire.read();
       xL=Wire.read();
     tmp01=word(xH,xL); //上位と下位バイトを結合し、16ビット整数にする

     結果は上位13ビットですから tmp01を8で割れば1/16 度単位の温度となります。度の単位にするには16で割りますが、このままでは値は小数の扱いが必要になります。結果を整数で表示するには あらかじめ10をかけてから16で割ります。結果が 245 の場合 24.5 度になりますから、表示するときに 24 と 5 の間に 小数点を挿入します。

  5. ADTライブラリ

     纏めますと、ADT7410での温度読み取りは以下のようになります。まず、adt7410_init() で初期設定をします。ついで、adt_read( buffer) で、buffer に 0.1度 単位の温度を文字列で返します。
    #define ADT 0x48
    //初期化
    void adt_init()
    {
      Wire.begin();
      Wire.beginTransmission(AT_ADRS) ;
      Wire.write((byte)0x03);
      Wire.write((byte)0xC0);
      Wire.endTransmission(false);
    }
    
    void adt_read(char buff[]){
      byte xH,xL;
      int tmp;
       //I2c.read(ADT,0x0,2);
       Wire.beginTransmission(AT_ADRS) ;
       Wire.write((byte)0x0);
       Wire.endTransmission(false);
       ans=Wire.requestFrom(AT_ADRS, 2);
       xH=Wire.read();
       xL=Wire.read();
       Wire.endTransmission();
       tmp=(int)((word(xH,xL))*10.0/128.0);
       //Serial.println(tmp);
       sprintf(buff,"%4d",tmp);
       //小数点を挿入する +235 >> +23.5
       buff[5]='\0';buff[4]=buff[3];
       buff[3]='.';
    }
    

  6. 温度読み取りプログラム

     setup() では、PCとのシリアルポート、温度センサー、lcd の初期設定をします。loop() では、温度を読み出し、それを シリアルモニターと I2C接続のLCD(2行目)に表示します。
    #include <Wire.h>
    int ans;
    char msg[]="temp";
    
    int AT_ADRS=0x48;
     void setup(){
       Serial.begin(9600);
       adt_init();
       lcd_begin();
      lcd_setCursor(0,0);
      lcd_print(msg);
    
     }
     void loop(){
      char buff[6];
      adt_read(buff);
      Serial.println(buff);
      lcd_setCursor(0,1);
      lcd_print(buff);
      delay(500);   
     }

    プログラムを入力後Arduinoボードに送りこみます。結果は シリアルモニターと液晶で確認します。

      液晶の温度表示

  7. タブによるモジュール分割(タブ機能)

     上記のプログラムのあとで、ADT710 と LCD のメソッドを追加すれば良いのですが、すべてを一つのファイルに入れると、プログラム全体が長くなり編集しにくくなります。そこで、Arduino のタブ機能を利用します。
     プログラムのヘッダー部分の右に ▼ があります。これをクリックすると、「新規タブ」のメニューが出ます。これをクリックして、「新規ファイルの名前」に ADT7410 を入力し、「ok」をクリックします。


     すると、現在編集中のプログラムの横に、新規のタブ:ADT7490 が生成され、編集が可能になります。ここに、ADT7410 関連のメソッドを入力します。 さらに、新規タブ:LCD を作成し、ここに LCD 関連のメソッドを入力します。プログラムの 「検証」や「書き込み」を実行すると、すべてのタブを纏めて実行用のプログラムを作成してくれます。また、保存をかけると、各タブ毎に別のファイルで保存し、読み取るときはすべてのファイルを読み取りタブ形式で構成してくれます。
     できあがったモジュールを機能単位にファイルに分割して管理することで、モジュールの再利用が容易になります。
     以下は LCD のライブラリーです。
    #include <Wire.h>
    #define LCDA 0x3E //lcd address
    #define CONTRAST 0x20 //for 3.3V AQM0802
    //#define CONTRAST 0x30 //for 3.3V LCD1602A
    //byte val = 0;
    
    char lcdbuf[10]="";
    
    // 液晶へ1コマンド出力
    void lcd_cmd(byte cmd)
    {
      Wire.beginTransmission(LCDA);
      Wire.write((byte)0x00); // コマンド指定
      Wire.write(cmd); // コマンド出力
      Wire.endTransmission(); // ストップ
     //I2c.write((byte)LCDA,(byte)0,cmd);
    /* ClearかHomeか */
      if((cmd == 0x01)||(cmd == 0x02))
        delay(2); // 2msec待ち
      else
        delayMicroseconds(30); // 30μsec待ち
    }
    
    //データを送る
    void lcd_data(byte data)
    {
      Wire.beginTransmission(LCDA); // スタート
      Wire.write(0x40); // 表示データ指定
      Wire.write(data); // 表示データ出力
      Wire.endTransmission(); // ストップ
      delayMicroseconds(30); // 遅延
    }
    
    void lcd_setCursor(byte clm,byte row){
      //row=0 or 1
      if(row==0) lcd_cmd(0x80+clm);
      if(row==1) lcd_cmd(0xc0+clm);
    }
    
    // 初期化
    void lcd_begin(void)
    {
      Serial.println("lcd_begin");
      //I2c.begin(); // begin I2C master
      delay(10);
      lcd_cmd(0x38); // 8bit 2line Normal mode
      lcd_cmd(0x39); // 8bit 2line Extend mode
      lcd_cmd(0x14); // OSC 183Hz BIAS 1/5
    
      /* コントラスト設定 */
      lcd_cmd((byte)(0x70 + (CONTRAST & 0x0F)));//下位4bit
      lcd_cmd((byte)(0x5C + (CONTRAST >> 4)));//上位2bit
      lcd_cmd((byte)0x6B); // Follwer for 3.3V
      delay(1);
    
      lcd_cmd((byte)0x38); // Set Normal mode
      lcd_cmd((byte)0x0C); // Display On
      lcd_cmd((byte)0x01); // Clear Display
      delay(1);
      Serial.println("end of lcd_begin");
    }
    
    //全消去関数
    void lcd_clear(void)
    {
      lcd_cmd(0x01); //初期化コマンド出力
      delay(2);
    }
    
    //文字列表示関数
    void lcd_print(char* ptr)
    {
      while(*ptr != 0) //文字取り出し
        lcd_data(*ptr++); //文字表示
    }
    
    void lcd_print(int num){
       sprintf(lcdbuf,"%d",num);
       lcd_print(lcdbuf);
    }

  8. 発展

    1. 日付・時刻を合わせて記録するには?

      「時計モジュール」の節を参照してください

    2. 従来型の温度センサーと比較して高精度な理由は?

       温度の測定には、温度に依存しない基準電圧の生成が必要です。ADT7410 は Band gap Referrence 回路で高精度な絶対電圧の生成をしています。また、13/16 ビット ΔΣ型 AD 変換器 を内蔵しています。これは、AD変換は変換は遅いですが、高精度な変換が可能です。