どうも、ktktです。ご無沙汰です。
試験とかゼミレポートとかいっぱいあって死んじゃいそう。

さて、今日はちょっと電子工作とかロボットとかそんな感じのことです。 
実は私、ロボカップジュニアという大会に2012年大会辺から2015年大会まで参加させて頂いてました。
サッカーAのライトウエイトに2回、レスキューBに1回、Cospace Rescueに1回出場いたしました。

今回は紹介しようかなぁと思うのはレスキューBに出場した時に使用した温度センサです。
09570-01

MLX90614です。非接触型の赤外線温度センサです。
確かNXT用の非接触温度センサにも使われていた気もします。
以下のような特徴があります。
  • 対象物と周囲の温度を計測できる
  • 内部にいろいろ入っているのでセンサとパスコンくらいで動く
  • SMBus通信(I2C互換通信)とPWMで値を出力できる
  • スレーブアドレスを変更できる
2番目とか3番目とかは楽でいいですよね。面倒な回路もいらないし,通信で値が取得できる!
その他詳しい仕様はリンク先やデータシートをご覧ください。

今回はSMBus通信での値の取得方法をちょこっと解説しようかなと思います。

SMBus通信はI2Cの互換通信です。詳しくは知りませんが…。
ですが,特にSMBusについて詳しくなくてもArduinoのWireライブラリとかで簡単に通信できちゃいます。
ただ,通信フローにちょこっと気をつけないといけない点があります。以下が通信フローです。

MLX90614
参考:データシートPP.15-16
 
まずMLX90614に読み出しコマンドを送信します。 その後に読み込み要求を出すとROMやRAMの値2バイトと,誤り検知用の1バイト(PEC)の合計3バイトが返ってきます。
一点目はこれですね。たとえMLX90614が3バイト返してきても最後の1バイトは誤り検知用なのでレジスタのデータとしてみてはいけません。

二点目は,コマンドの送信を行う通信と値の読み込みを行う通信の間はリピートスタートコンディションでつなぐ必要があるということです。 
コマンドの送信→ストップコンディションの発行→読み出しの開始,というような通信ではうまく行きません。
なぜかというと,SMBusだからです。たぶん。 

温度を読む際はCommandに0x06,0x07のいずれかを送ります。
それぞれ,周囲の温度,モジュールAの温度を2バイトで受信します。

そんなこんなを踏まえて,Arduinoスケッチを書いてみました。

/* include Wire header */
#include <Wire.h>

/* Function prototype */
void ReadFromMLX90614( char addr, char cmd, char *arry );
float ReadTempFromMLX90614( char addr, char type );


/* Slave address of MLX90614 (7bit) */
const char sermo_addr = 0x5A;    // default slave address


/* set up function */
void setup(){  
  Wire.begin();  
  Serial.begin( 9600 );
}

/* main routin */
void loop(){
  Serial.println( ReadTempFromMLX90614( sermo_addr, 'A' ) ); 
  delay(1000);
}


/* Function that recieves few bytes from resister of MLX90614 *
*    arguments                                                *
*     addr   : slave address                                  *
*     cmd    : resister address                               *
*     *array : address of array to contain data               *
**************************************************************/
void ReadFromMLX90614( char addr, char cmd, char *arry ){
  char i;
  
  Wire.beginTransmission( addr );     // start recieve process 
  Wire.write( cmd );                  // Write command
  Wire.endTransmission(false);        // publish repeat start condition
  
  Wire.requestFrom( addr, 3 );        // request data+pec byte
  while( Wire.available() < 3 );      // wait receive complete
  for( i = 0; i < 2; i++ ){
    arry[i] = Wire.read();            // read data
  }
  Wire.read();                        // pec byte
  Wire.endTransmission();             // end recieve process
}

/* Function that reads temperature from MLX90614 *
*      arguments                                 *
*        addr : slave address                    *
*        type : type of temperature              *
*                  'O' or 'o' : reads object     *
*                  other      : reads ambient    *
*      return                                    *
*        Degrees Celsius of temperature          *
*************************************************/
float ReadTempFromMLX90614( char addr, char type ){
  int tmp;
  char cmd;
  char dat[2];
  float temp;
  
  cmd = ( type == 'O' || type == 'o' )?( 0x07 ):( 0x06 ); // determin command
  ReadFromMLX90614( addr, cmd, dat );                     // recieve from MLX90614
  tmp = ( dat[1] << 8 ) + dat[0];                         // to temperature in marge two bytes
  temp = ( ( (float)tmp * 2.0 ) - 27315.0 ) / 100.0;      // convert Absolute temperature to Degrees Celsius of temperature
  
  return temp; 
}

MLX90614から周囲の温度を読みだしてセルシウス度でシリアルコンソールに出力するスケッチです。
簡単にスケッチの解説をします。

まずは,ReadFromMLX90614関数から。これは汎用読み出し関数です。
スレーブアドレスがaddrのMLX90614にcmdで3バイトの読み込み応答をかけ,読みだした順にarrayの要素に返していきます。
3バイトの要求を行っているのは誤り検知バイトの分です。実際に受信する際はこのバイトは捨ててしまうので,実際に配列に入れられるのは2バイトだけです。
この関数ででもっとも重要なのは, 40行目です。endTransmissionメソッドの引数にfalseを与えています。これがリピートスタートコンディションの発行になります。
ここに何も与えなかったりするとストップコンディションが発行されて通信がうまく来ません。

次にReadTempFromMLX90614関数は,温度をセルシウス度で返す関数です。
スレーブアドレスがaddrのMLX90614にtypeで温度の種類を指定して,戻り値に温度を返します。
温度の種類の指定は大文字か小文字のOであるかどうかで判定しています。Oは対象物の温度,それ以外は周囲の温度を指定できます。
対象物の温度を受信する場合はコマンドに0x07を,周囲の温度を受信する場合は0x06を送信してやります。この辺の指定は66行目でやってます。
67行目でReadFromMLX90614関数を呼び出して配列に値を格納し,返ってきた2バイトを68行目で足しあわせて絶対温度(熱力学的温度)にします。ケルビンですね。
ケルビンのままだと使いにくいので,69行目でセルシウス度に変換しています。

その他もろもろは下手くそな英語ですがコメントを読んでね!
そもそもI2CがわからないとかWireライブラリの使い方がわからないとかは自分で調べてくださいね。 
割りとテキトーに書いたスケッチなんで細かいバグとかはあるかもしれませんがあくまでサンプルということで。

最後に一つ注意点です。このセンサは3.3V動作品と5V動作品があります。 ストロベリー・リナックス様で販売している商品は3.3V動作品です。
ですので,Arduinoとかとつなぐ際はバスレベル変換などを介してつなぐようにしましょう。

使った所感ですが,あまり距離を読みません。なのでレンズとかと使えばいいと思います。
他のセンサと比べて比較的安いというメリットも有ります。
ぶっちゃけあんまり使えないような気もしますが,焦電センサとかで頑張って作るよりは楽だと思いますし,金銭的被害も小さいのでオススメです。

今回はスルーしましたが,このセンサはスレーブアドレスを書き換えることができます。
気が向いたらそれについても記事にするかもしれません。
では,長文失礼しました。