Scientific Journey

私の冒険記録。たまに回り道。

【赤外線温度センサ】MLX90614を複数個使ってみる。(Arduino)

赤外線温度センサを2つ使ってみるよ。

このMLX90614の赤外線温度センサはI2Cで温度を取得する際に、
アドレスが同じなので、このままでは複数個つなげられない。

そこで、MLX90164のアドレスを一つ書き替えたいと思います。

こちらのArudinoフォーラムを参考にすると、
以下のプログラムをArduinoに書き込み、
一つのMLX90614のみをつなげてアドレスを書き代える事ができるようです。(Connecting Infrared Thermometer MLX90614 to Wiring - Wiring参考

※i2cmaster.hを持っていないときはgithub
Arduino Playground - I2cScanner
で入手可能

#include "i2cmaster.h"
// Pins: Standard: SDA:A4  SCL:A5
//       Mega:     SDA:D20 SCL:D21

byte MLXAddr = 0x5A<<1;           // Default address
//byte MLXAddr = 0;               // Universal address

void setup(){
 Serial.begin(9600);
 Serial.println("Setup...");
 
 i2c_init();                              //Initialise the i2c bus
 PORTC = (1 << PORTC4) | (1 << PORTC5);   //enable pullups
 
 delay(5000);                    // Wait to allow serial connection
 ReadAddr(0);                    // Read current address bytes
 ChangeAddr(0x55, 0x00);         // Change address to new value
 //ChangeAddr(0x5A, 0xBE);       // Change address to default value
 ReadAddr(0);                    // Read address bytes
 delay(5000);                    // Cycle power to MLX during this pause
 ReadTemp(0);                    // Read temperature using default address
 ReadTemp(MLXAddr);              // Read temperature using new address
}

void loop(){
   delay(1000); // wait a second
}

word ChangeAddr(byte NewAddr1, byte NewAddr2) {

 Serial.println("> Change address");

 i2c_start_wait(0 + I2C_WRITE);    //send start condition and write bit
 i2c_write(0x2E);                  //send command for device to return address
 i2c_write(0x00);                  // send low byte zero to erase
 i2c_write(0x00);                  //send high byte zero to erase
 if (i2c_write(0x6F) == 0) {
   i2c_stop();                     //Release bus, end transaction
   Serial.println("  Data erased.");
 }
 else {
   i2c_stop();                     //Release bus, end transaction
   Serial.println("  Failed to erase data");
   return -1;
 }

 Serial.print("  Writing data: ");
 Serial.print(NewAddr1, HEX);
 Serial.print(", ");
 Serial.println(NewAddr2, HEX);

 for (int a = 0; a != 256; a++) {
   i2c_start_wait(0 + I2C_WRITE);  //send start condition and write bit
   i2c_write(0x2E);                //send command for device to return address
   i2c_write(NewAddr1);            // send low byte zero to erase
   i2c_write(NewAddr2);            //send high byte zero to erase
   if (i2c_write(a) == 0) {
     i2c_stop();                   //Release bus, end transaction
     delay(100);                   // then wait 10ms
     Serial.print("Found correct CRC: 0x");
     Serial.println(a, HEX);
     return a;
   }
 }
 i2c_stop();                       //Release bus, end transaction
 Serial.println("Correct CRC not found");
 return -1;
}

void ReadAddr(byte Address) {

 Serial.println("> Read address");

 Serial.print("  MLX address: ");
 Serial.print(Address, HEX);
 Serial.print(", Data: ");

 i2c_start_wait(Address + I2C_WRITE);  //send start condition and write bit
 i2c_write(0x2E);                  //send command for device to return address
 i2c_rep_start(Address + I2C_READ);
 
 Serial.print(i2c_readAck(), HEX); //Read 1 byte and then send ack
 Serial.print(", ");
 Serial.print(i2c_readAck(), HEX); //Read 1 byte and then send ack
 Serial.print(", ");
 Serial.println(i2c_readNak(), HEX);
 i2c_stop();
}

float ReadTemp(byte Address) {
 int data_low = 0;
 int data_high = 0;
 int pec = 0;

 Serial.println("> Read temperature");

 Serial.print("  MLX address: ");
 Serial.print(Address, HEX);
 Serial.print(", ");

 i2c_start_wait(Address + I2C_WRITE);
 i2c_write(0x07);                  // Address of temp bytes
 
 // read
 i2c_rep_start(Address + I2C_READ);
 data_low = i2c_readAck();         //Read 1 byte and then send ack
 data_high = i2c_readAck();        //Read 1 byte and then send ack
 pec = i2c_readNak();
 i2c_stop();
 
 //This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
 float Temperature = 0x0000;       // zero out the data
 
 // This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
 Temperature = (float)(((data_high & 0x007F) << 8) + data_low);
 Temperature = (Temperature * 0.02) - 273.16;
 
 Serial.print(Temperature);
 Serial.println(" C");
 return Temperature;
}

書き換え後に、もう一つのMLX90614をつないで、
I2CScannerで値の確認をしてみます。
以下は公式からgetしました。

I2CScanner
// --------------------------------------
// 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
//    http://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("Unknow 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
}

結果

f:id:an-modoki:20160930085702p:plain

I2Cのアドレスを2つ認識しています。
では、早速2つのMLX90614から温度を取得するプログラムを作ります。
続きにプログラムを載せました。

MLX90614を2個使ってみる。(Arduino

前ページでは、赤外線センサのアドレスを書き変えました。
書き変えたアドレスは、電源を落としても変わったままです。

今まで、MLX90614を使うのに専用のライブラリを使っていました。
an-modoki.hateblo.jp

そのままだと、アドレスを指定して実行するのにヘッダーを変えなくてはならなくて、
非常に面倒くさいので、ではアドレスを直接指定して実行しているプログラムを
ネットから拾ってくることにしました。

ここにありました。
引用します。

/**
 * Infrared Thermometer MLX90614
 * by Jaime Patarroyo
 * based on 'Is it hot? Arduino + MLX90614 IR Thermometer' by bildr.blog
 * 
 * Returns the temperature in Celcius and Fahrenheit from a MLX90614 
 * Infrared Thermometer, connected to the TWI/I²C pins (on the Wiring v1 
 * board 0 (SCL) and 1 (SDA) and on Wiring S board 8 (SCL) and 9 (SDA)).
 */

#include <i2cmaster.h>


int deviceAddress = 0x5A<<1;    // From MLX906114 datasheet's, 0x5A is 
                                // the default address for I²C communication.
                                // Shift the address 1 bit right, the 
                                // I²Cmaster library only needs the 7 most 
                                // significant bits for the address.

float celcius = 0;              // Variable to hold temperature in Celcius.
float fahrenheit = 0;           // Variable to hold temperature in Fahrenheit.

void setup() {
  Serial.begin(9600);           // Start serial communication at 9600bps.

  i2c_init();                               // Initialise the i2c bus.
  PORTC = (1 << PORTC4) | (1 << PORTC5);    // Enable pullups.
}

void loop() {
  celcius = temperatureCelcius(deviceAddress);  // Read's data from MLX90614
                                                // with the given address,
                                                // transform's it into
                                                // temperature in Celcius and
                                                // store's it in the Celcius
                                                // variable.
  
  fahrenheit = (celcius*1.8) + 32;     // Converts celcius into Fahrenheit 
                                       // and stores in Fahrenheit variable.

  Serial.print("Celcius: ");           // Prints both readings in the Serial 
  Serial.println(celcius);             // port.
  Serial.print("Fahrenheit: ");
  Serial.println(fahrenheit);

  delay(1000);                         // Wait a second before printing again.
}

float temperatureCelcius(int address) {
  int dev = address;
  int data_low = 0;
  int data_high = 0;
  int pec = 0;

  // Write
  i2c_start_wait(dev+I2C_WRITE);
  i2c_write(0x07);

  // Read
  i2c_rep_start(dev+I2C_READ);
  data_low = i2c_readAck();       // Read 1 byte and then send ack.
  data_high = i2c_readAck();      // Read 1 byte and then send ack.
  pec = i2c_readNak();
  i2c_stop();

  // This converts high and low bytes together and processes temperature, 
  // MSB is a error bit and is ignored for temps.
  double tempFactor = 0.02;       // 0.02 degrees per LSB (measurement 
                                  // resolution of the MLX90614).
  double tempData = 0x0000;       // Zero out the data
  int frac;                       // Data past the decimal point

  // This masks off the error bit of the high byte, then moves it left 
  // 8 bits and adds the low byte.
  tempData = (double)(((data_high & 0x007F) << 8) + data_low);
  tempData = (tempData * tempFactor)-0.01;
  float celcius = tempData - 273.15;
  
  // Returns temperature un Celcius.
  return celcius;
}

これを改良します。

int deviceAddress = 0x5A<<1; // From MLX906114 datasheet's, 0x5A is
// the default address for I²C communication.
// Shift the address 1 bit right, the
// I²Cmaster library only needs the 7 most
// significant bits for the address.

前ページで、I2CScannerを使ったときにもう一方を0x55と書き替えました。

この部分をもう一つ作ってもう一つのアドレスを指定してあげます。

以下が2個用に改良したもの。

/**
 * Infrared Thermometer MLX90614
 * by Jaime Patarroyo
 * based on 'Is it hot? Arduino + MLX90614 IR Thermometer' by bildr.blog
 * 
 * Returns the temperature in Celcius and Fahrenheit from a MLX90614 
 * Infrared Thermometer, connected to the TWI/I²C pins (on the Wiring v1 
 * board 0 (SCL) and 1 (SDA) and on Wiring S board 8 (SCL) and 9 (SDA)).
 */

#include <i2cmaster.h>


int deviceAddress = 0x5A<<1;    // From MLX906114 datasheet's, 0x5A is 
                                // the default address for I²C communication.
                                // Shift the address 1 bit right, the 
                                // I²Cmaster library only needs the 7 most 
                                // significant bits for the address.
                                
int deviceAddress2 = 0x55<<1;    // From MLX906114 datasheet's, 0x55 is 
                                // the changed address for I²C communication.
                                // Shift the address 1 bit right, the 
                                // I²Cmaster library only needs the 7 most 
                                // significant bits for the address.

float celcius = 0;              // Variable to hold temperature in Celcius.
float celcius2 = 0;             // Variable to hold temperature in Celcius.

float fahrenheit = 0;           // Variable to hold temperature in Fahrenheit.
float fahrenheit2 = 0;           // Variable to hold temperature in Fahrenheit.

void setup() {
  Serial.begin(9600);           // Start serial communication at 9600bps.

  i2c_init();                               // Initialise the i2c bus.
  PORTC = (1 << PORTC4) | (1 << PORTC5);    // Enable pullups.
}

void loop() {
  celcius = temperatureCelcius(deviceAddress);  // Read's data from MLX90614
                                                // with the given address,
                                                // transform's it into
                                                // temperature in Celcius and
                                                // store's it in the Celcius
                                                // variable.
                                                
  celcius2 = temperatureCelcius(deviceAddress2);  // Read's data from MLX90614
                                                // with the given address,
                                                // transform's it into
                                                // temperature in Celcius and
                                                // store's it in the Celcius
                                                // variable.
  
  fahrenheit = (celcius*1.8) + 32;     // Converts celcius into Fahrenheit 
                                       // and stores in Fahrenheit variable.
  fahrenheit2 = (celcius2*1.8) + 32;     // Converts celcius into Fahrenheit 
                                       // and stores in Fahrenheit variable.


  Serial.print("Celcius: ");           // Prints both readings in the Serial 
  Serial.println(celcius);             // port.
  Serial.print("Fahrenheit: ");
  Serial.println(fahrenheit);
  
  Serial.print("Celcius2: ");           // Prints both readings in the Serial 
  Serial.println(celcius2);             // port.
  Serial.print("Fahrenheit2: ");
  Serial.println(fahrenheit2);

  delay(1000);                         // Wait a second before printing again.
}

float temperatureCelcius(int address) {
  int dev = address;
  int data_low = 0;
  int data_high = 0;
  int pec = 0;

  // Write
  i2c_start_wait(dev+I2C_WRITE);
  i2c_write(0x07);

  // Read
  i2c_rep_start(dev+I2C_READ);
  data_low = i2c_readAck();       // Read 1 byte and then send ack.
  data_high = i2c_readAck();      // Read 1 byte and then send ack.
  pec = i2c_readNak();
  i2c_stop();

  // This converts high and low bytes together and processes temperature, 
  // MSB is a error bit and is ignored for temps.
  double tempFactor = 0.02;       // 0.02 degrees per LSB (measurement 
                                  // resolution of the MLX90614).
  double tempData = 0x0000;       // Zero out the data
  int frac;                       // Data past the decimal point

  // This masks off the error bit of the high byte, then moves it left 
  // 8 bits and adds the low byte.
  tempData = (double)(((data_high & 0x007F) << 8) + data_low);
  tempData = (tempData * tempFactor)-0.01;
  float celcius = tempData - 273.15;
  
  // Returns temperature un Celcius.
  return celcius;
}

結果

f:id:an-modoki:20160930103818p:plain
3つか4つ使いたかったんですけどね。
手元に2つしかセンサがないので,,,,,。

3つか4つの時もアドレスを他のものと変えてあげて、
アドレスがどれに代わっているか、見てやるといいかもです。

もしどなたか3つ、4つ成功していたら、コメント欄にどうぞ。