温度センサーとRTC(実時間)モジュールをI2Cで接続してデータをSDカードに保存しました

なにを学習するの?

  • I2Cバス上で動作する温度センサとRTC(実時間)モジュールを接続し、取得したデータをSDカードに保存します。
  • I2Cのアドレスを確認する方法と設定する方法を学習し、Arduinoに接続した同じI2Cバス上で複数のデバイスを操作します。

必要な部品は?

ADT7410 I2C (Adafruit社製 ADT7410搭載 高精度I2C温度センサモジュール)1個
Arduino UNO用 データロガーシールド (I2C RTC(実時間)モジュールを含む)1個
Arduino UNO R31個
関連する商品・製品の購入先は?
関連する記事は?

回路図 (配線図)は?

  • Fritzingで描いた回路図(配線図)と実際の配線です。

I2Cセンサのアドレスを確認する方法は?

  • I2Cのアドレスを調べる方法を検索すると、Arduino Playground [Arduino Playground – I2cScanner]にI2Cのアドレスをスキャンする記事がありました。
  • 掲載されているスケッチをArduino UNO R3に書き込んでシリアルモニタでI2Cのアドレスを確認しました。
// --------------------------------------
// i2c_scanner
//
// Version 1
//    This program (or code that looks like it)
//    can be found in many places.
//    For example on the Arduino.cc forum.
//    The original author is not know.
// Version 2, Juni 2012, Using Arduino 1.0.1
//     Adapted to be as simple as possible by Arduino.cc user Krodal
// Version 3, Feb 26  2013
//    V3 by louarnold
// Version 4, March 3, 2013, Using Arduino 1.0.3
//    by Arduino.cc user Krodal.
//    Changes by louarnold removed.
//    Scanning addresses changed from 0...127 to 1...119,
//    according to the i2c scanner by Nick Gammon
//    https://www.gammon.com.au/forum/?id=10896
// Version 5, March 28, 2013
//    As version 4, but address scans now to 127.
//    A sensor seems to use address 120.
// Version 6, November 27, 2015.
//    Added waiting for the Leonardo serial communication.
// 
//
// This sketch tests the standard 7-bit addresses
// Devices with higher bit address might not be seen properly.
//

#include <Wire.h>


void setup()
{
  Wire.begin();

  Serial.begin(9600);
  while (!Serial);             // Leonardo: wait for serial monitor
  Serial.println("\nI2C Scanner");
}


void loop()
{
  byte error, address;
  int nDevices;

  Serial.println("Scanning...");

  nDevices = 0;
  for(address = 1; address < 127; address++ ) 
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.print(address,HEX);
      Serial.println("  !");

      nDevices++;
    }
    else if (error==4) 
    {
      Serial.print("Unknown error at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");

  delay(5000);           // wait 5 seconds for next scan
}
  • ADT7410 I2CをArduino UNO R3に接続してアドレスを確認しました。アドレス、0x48が検出されています。
Scanning...
I2C device found at address 0x48  !
done
  • Arduino UNO用 データロガーシールドにはRTC(実時間)機能が搭載されており、拡大鏡で確認するとIC:DS1307が使われていました。このICは通信にI2Cを使っています。Arduino UNO R3に接続してアドレスを確認しました。アドレス、0x68が検出されています。
Scanning...
I2C device found at address 0x68  !
done
  • ADT7410 I2CとArduino UNO用 データロガーシールドをArduino UNO R3に接続してアドレスを確認しました。それぞれのアドレスである、0x48と0x68が検出されています。
Scanning...
I2C device found at address 0x48  !
I2C device found at address 0x68  !
done

I2Cのアドレスを変更するには?

  • ADT7410のアドレスを変更する方法を調べます。開発元のAnalog Devices社でデータシートが見つかりました。
  • 「アドレスビットを7ビット持っている、上位5ビットは固定で[10010]となっている、下位の2ビットをA1,A0に割り当てているので設定方法により、0x48(初期値)、0x49、0x4A,、0x4Bが選べる」となっています。
  • Arduino UNO用 データロガーシールドにはRTC(実時間)機能用のDS1307が使われています。このICは通信にI2Cを使っています。開発元のMaxim Integrated社でデータシートが見つかりました。
  • DS1307のアドレスは固定で、2進数で[1101000]、16進数では0x68です。
  • I2Cアドレスは、機器によって色々な値に変更が可能かな?と思っていたのですが、そんなに多くないようです。実際は多くの周辺機器を同じI2Cのバスに接続することは多くないからこのような仕様でも問題はないのかなと感じました。

I2Cセンサを2つ接続して通信するには?

  • ADT7410のアドレスの初期値は、0x48、データロガーシールドにはRTC(実時間)部のアドレスは、0x68です。各周辺機器用のライブラリのファイルを調べると、それぞれのアドレスが変数に割り振られていて、その数値を使って汎用のwireライブラリを呼ぶ動作になっています。今回の場合は2つの周辺機器を繋いでそれぞれのライブラリを使えば、それぞれの周辺機器からデータが読み出せました。
  • ADT7410のアドレスが設定されています
#define ADT7410_I2CADDR_DEFAULT 0x48 ///< I2C address
  • RTC(実時間)部のアドレスが設定されています
#define DS3231_ADDRESS              (0x68)
//----------------------------------------------
//https://arduinomakesiteasy.com/
//----------------------------------------------

#include <TimeLib.h>
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <DS3231.h>
#include "Adafruit_ADT7410.h"

//RTC(実時間) module
DS3231 clock;
RTCDateTime dt;
const int RTCI2C = 0x68;

//SD module
const int chipSelect = 10;  //SDカードCS
File logFile;
char fileName[16];
int fileNum = 0;

//温度センサー module
const int TEMPI2C = 0x48;
Adafruit_ADT7410 tempsensor = Adafruit_ADT7410();

//TimeStamp matrix (ファイル作成時のタイムスタンプ)
char timestamp[30];

//時間情報変数
String TIMEString;

//測定数
long measurecount;
float c;

//----------------------------------------------
//setup()
//----------------------------------------------
 
void setup(){
  Serial.begin(9600); //シリアルモニタの開始
  delay(500); //待ち時間 0.5秒 
  init_RTC(); //RTC初期化  
  init_TEMP(); //GPS初期化
  init_SD();  //SDカード初期化
  measurecount = 0;
}

//----------------------------------------------
//loop()
//----------------------------------------------
 
void loop(){

  measurecount += 1;

  // 実時間取得
  dt = clock.getDateTime();

  // 待ち時間 10秒
  delay(10000);     

  // 温度データ取得
  c = tempsensor.readTempC();

  // シリアルモニタへの出力
  TIMEString = measurecount; 
  TIMEString +=",";
  
  TIMEString += dt.year;
  TIMEString += "/";  
  TIMEString += dt.month;
  TIMEString += "/";    
  TIMEString += dt.day;
  TIMEString += "  ";    
  TIMEString += dt.hour;
  TIMEString += ":";    
  TIMEString += dt.minute;
  TIMEString += ".";    
  TIMEString += dt.second;  

  TIMEString +=",";
  TIMEString += c; 
  TIMEString += '\n'; 
  
  Serial.print(TIMEString);  

  // SDカードファイルへの出力
  logFile = SD.open(fileName, FILE_WRITE);
  logFile.write(TIMEString.c_str()); 
  logFile.close();    

}

//----------------------------------------------
//RTCの初期化
//----------------------------------------------

void init_RTC(void){

  Serial.print("RTC Start ---> ");

  // Initialize DS3231
  clock.begin();
 
  //スケッチのコンパイル時の時刻をRTCモジュールに書き込む
  //1回目はこのままコンパイル実行する、
  //2回目にコメントアウトして再度コンパイル実行する
  //clock.setDateTime(__DATE__, __TIME__);    

  Serial.println("Done");
 
}

//----------------------------------------------
//温度センサーの初期化
//----------------------------------------------

void init_TEMP(void){
  
  Serial.print("Temperature Sensor Start ---> ");
   
  if (!tempsensor.begin()) {
    Serial.println("Couldn't find ADT7410!");
    while (1);
  }
  
  Serial.println("Done");
}

//----------------------------------------------
//SDカード初期化とログファイル作成、タイムスタンプ設定
//----------------------------------------------

void init_SD(void){//SDカード初期化とログファイル作成、タイムスタンプ設定
  
  Serial.print("SD Start ---> ");
  
  while (!SD.begin(chipSelect)) {
    Serial.println("Failed during starting");
    //won't do anything later
    return;
  }
  Serial.println("Done");

  //ファイル作成・更新時のタイムスタンプを記録するスケッチ------------------------------
  SdFile::dateTimeCallback(dateTime);
  sprintf(timestamp, "%02d:%02d:%02d %2d/%2d/%2d \n",dt.hour,dt.minute,dt.second,dt.month,dt.day,dt.year-2000);
  //------------------------------------------------------------------------------

  boolean fileexist = false;
  String t;
  
  //既存のファイルのファイル名と重複しなければファイル名とする
  while(!fileexist){
    t = "TMP";
    if (fileNum < 10){
      t += "00";
    }
    else if(fileNum < 100){
      t += "0";
    }
    t += fileNum;
    t += ".LOG";
    t.toCharArray(fileName, 16);
    if(!SD.exists(fileName)) {
      fileexist = true;
    }
    fileNum++;
  }

  // 実時間取得
  dt = clock.getDateTime();
  
  // シリアルモニタへの出力
  TIMEString = "Count, Time, Temperature";

  Serial.println("");    
  Serial.println(TIMEString);  

  // ログファイルの1行目に測定開始時間を書き込む
  logFile=SD.open(fileName, FILE_WRITE);
  logFile.println(TIMEString.c_str());
  logFile.close();
}

//----------------------------------------------
//call back for file timestamps
//----------------------------------------------

void dateTime(uint16_t *date, uint16_t *time) {
 sprintf(timestamp, "%02d:%02d:%02d %2d/%2d/%2d \n",dt.hour,dt.minute,dt.second,dt.month,dt.day,dt.year-2000);
 *date = FAT_DATE(dt.year,dt.month,dt.day); // return date using FAT_DATE macro to format fields
 *time = FAT_TIME(dt.hour,dt.minute,dt.second); // return time using FAT_TIME macro to format fields
}

SDカードにどのようにデータを保存しているの?

  • SDカードに測定開始時間、10秒ごとの測定時間と測定温度を保存しています。
  • 測定回数、測定時刻、測定温度、をカンマ区切りで保存しているので、拡張子をcsvに変更すればエクセルで読み込めます。

温度データをエクセルでグラフ化しました

  • SDカードに保存したデータをEXCELに読み込んでグラフ化しました。朝7時前から翌日の8時頃までの居間の室温の変化測定しています。出勤後に室温は上昇を始め、帰宅してエアコンを入れる前は30.4度程度になっています。お風呂に入ったりして変化があります。寝る前にエアコンを止めると気温が上昇しています。1日でどの程度の変化があるかが解りました。
関連する記事は?

温度センサを搭載するシールドを作りました

  • 家の中で温度を測定していると、家族がArduinoボードから出ているワイヤを引っ掛けてしまいました。ワイヤが出ているのは何かと不便なので、シールドを作成しました。
  • 温度センサを載せるだけではスペースに余りがあるので、温度に応じて点灯するLEDと電流制限抵抗を追加しました。
  • LEDは3個配置して、低温(22度より低温)、適温(22度~28度)、高温(28度以上)で青LED、緑LED、赤LEDをそれぞれ光らせます。
  • KiCADで回路図を描いて、部品の配置を考えます。3Dで部品配置を確認できるので完成時のイメージが容易です。
  • 部品の配置が決まれば、図面に従って部品を基板上に配置して線材を半田付けします。
  • 温度センサを取り付けると、シールドの完成です。
関連する製品・商品は?
  • シールドをArduino UNO R3とSDデータロガーシールドの上に取り付けて動作を確認します。
  • LEDを動作させる機能をスケッチに追加しました。
  • Digital入出力の2,3,4PinをLEDの点灯に使用します。
  • LEDSet(void)で温度の測定データを調べ、測定データに従ってLEDを点灯します。
//----------------------------------------------
//https://arduinomakesiteasy.com/
//----------------------------------------------

#include <TimeLib.h>
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <DS3231.h>
#include "Adafruit_ADT7410.h"

#define BLUE 2
#define GREEN 3
#define RED 4

//RTC(実時間) module
DS3231 clock;
RTCDateTime dt;
const int RTCI2C = 0x68;

//SD module
const int chipSelect = 10;  //SDカードCS
File logFile;
char fileName[16];
int fileNum = 0;

//温度センサー module
const int TEMPI2C = 0x48;
Adafruit_ADT7410 tempsensor = Adafruit_ADT7410();

//TimeStamp matrix (ファイル作成時のタイムスタンプ)
char timestamp[30];

//時間情報変数
String TIMEString;

//測定変数
long measurecount;
float c;

//----------------------------------------------
//setup()
//----------------------------------------------
 
void setup(){
  Serial.begin(9600); //シリアルモニタの開始
  delay(500); //待ち時間 0.5秒 
  init_RTC(); //RTC初期化  
  init_TEMP(); //GPS初期化
  init_SD();  //SDカード初期化
  measurecount = 0;
  //LED設定
  pinMode(BLUE, OUTPUT);  
  pinMode(GREEN, OUTPUT);
  pinMode(RED, OUTPUT); 
  digitalWrite(BLUE, LOW);
  digitalWrite(GREEN, LOW);
  digitalWrite(RED, LOW);  
  
}

//----------------------------------------------
//loop()
//----------------------------------------------
 
void loop(){

  measurecount += 1;

  // 実時間取得
  dt = clock.getDateTime();

  // 待ち時間 10秒
  delay(10000);     

  // 温度データ取得
  c = tempsensor.readTempC();

  // シリアルモニタへの出力
  TIMEString = measurecount; 
  TIMEString +=",";
  
  TIMEString += dt.year;
  TIMEString += "/";  
  TIMEString += dt.month;
  TIMEString += "/";    
  TIMEString += dt.day;
  TIMEString += "  ";    
  TIMEString += dt.hour;
  TIMEString += ":";    
  TIMEString += dt.minute;
  TIMEString += ".";    
  TIMEString += dt.second;  

  TIMEString +=",";
  TIMEString += c; 
  TIMEString += '\n'; 
  
  Serial.print(TIMEString);  

  // SDカードファイルへの出力
  logFile = SD.open(fileName, FILE_WRITE);
  logFile.write(TIMEString.c_str()); 
  logFile.close();    

  LEDSet();

}

//----------------------------------------------
//RTCの初期化
//----------------------------------------------

void init_RTC(void){

  Serial.print("RTC Start ---> ");

  // Initialize DS3231
  clock.begin();
 
  //スケッチのコンパイル時の時刻をRTCモジュールに書き込む
  //1回目はこのままコンパイル実行する、
  //2回目にコメントアウトして再度コンパイル実行する
  //clock.setDateTime(__DATE__, __TIME__);    

  Serial.println("Done");
 
}

//----------------------------------------------
//温度センサーの初期化
//----------------------------------------------

void init_TEMP(void){
  
  Serial.print("Temperature Sensor Start ---> ");
   
  if (!tempsensor.begin()) {
    Serial.println("Couldn't find ADT7410!");
    while (1);
  }
  
  Serial.println("Done");
}

//----------------------------------------------
//SDカード初期化とログファイル作成、タイムスタンプ設定
//----------------------------------------------

void init_SD(void){//SDカード初期化とログファイル作成、タイムスタンプ設定
  
  Serial.print("SD Start ---> ");
  
  while (!SD.begin(chipSelect)) {
    Serial.println("Failed during starting");
    //won't do anything later
    return;
  }
  Serial.println("Done");

  //ファイル作成・更新時のタイムスタンプを記録するスケッチ------------------------------
  SdFile::dateTimeCallback(dateTime);
  sprintf(timestamp, "%02d:%02d:%02d %2d/%2d/%2d \n",dt.hour,dt.minute,dt.second,dt.month,dt.day,dt.year-2000);
  //------------------------------------------------------------------------------

  boolean fileexist = false;
  String t;
  
  //既存のファイルのファイル名と重複しなければファイル名とする
  while(!fileexist){
    t = "TMP";
    if (fileNum < 10){
      t += "00";
    }
    else if(fileNum < 100){
      t += "0";
    }
    t += fileNum;
    t += ".LOG";
    t.toCharArray(fileName, 16);
    if(!SD.exists(fileName)) {
      fileexist = true;
    }
    fileNum++;
  }

  // 実時間取得
  dt = clock.getDateTime();
  
  // シリアルモニタへの出力
  TIMEString = "Count, Time, Temperature";

  Serial.println("");    
  Serial.println(TIMEString);  

  // ログファイルの1行目に測定開始時間を書き込む
  logFile=SD.open(fileName, FILE_WRITE);
  logFile.println(TIMEString.c_str());
  logFile.close();
}

//----------------------------------------------
//call back for file timestamps
//----------------------------------------------

void dateTime(uint16_t *date, uint16_t *time) {
 sprintf(timestamp, "%02d:%02d:%02d %2d/%2d/%2d \n",dt.hour,dt.minute,dt.second,dt.month,dt.day,dt.year-2000);
 *date = FAT_DATE(dt.year,dt.month,dt.day); // return date using FAT_DATE macro to format fields
 *time = FAT_TIME(dt.hour,dt.minute,dt.second); // return time using FAT_TIME macro to format fields
}

//----------------------------------------------
//測定温度によってLEDの点灯を変更する
//----------------------------------------------

void LEDSet(void){
  //温度が22度より低い時は青のLEDを点灯する
  if (c < 22) {
  digitalWrite(BLUE, HIGH);
  digitalWrite(GREEN, LOW);
  digitalWrite(RED, LOW);  
  } 
 
  //温度が22度以上で28度より低い時は緑のLEDを点灯する
  if (c >= 22 & c < 28){
  digitalWrite(BLUE, LOW);
  digitalWrite(GREEN, HIGH);
  digitalWrite(RED, LOW);  
  } 
  //温度が28度以上の時は赤のLEDを点灯する  
  if (c >= 28){
  digitalWrite(BLUE, LOW);
  digitalWrite(GREEN, LOW);
  digitalWrite(RED, HIGH);  
  } 

}
KiCADを使って設計する方法の記事は?

ご質問、誤植の指摘などありましたら。「問い合わせ 」のページからお願いします。