Infrared Thermometer - MLX90614

Hi, I have some trouble getting an MLX90614 infrared thermometer working with an Arduino Mega 2560. I've followed those instructions: http://bildr.org/2011/02/mlx90614-arduino/ and used the provided i2cmaster library. I'm using an MLX90614E-SF-AAA I bought on EBAY so its a bit different (...5v etc...). I've modified the sketch a bit for debugging purpose and to set the pullup resistor on the correct pins (20 and 21) for the mega:

#include <i2cmaster.h>


void setup(){
  Serial.begin(9600);
  Serial.println("Setup...");

  i2c_init(); //Initialise the i2c bus
  Serial.println("I2C initialized");
  PORTD = (1 << PORTD1) | (1 << PORTD0);//enable pullups
}

void loop(){
  int device = 0x5A<<1;
  int data_low = 0;
  int data_high = 0;
  int pec = 0;

  Serial.println("Variable initialized");

  i2c_start_wait(device+I2C_WRITE);
  Serial.println("Starting Communication");
  i2c_write(0x07);
  Serial.println("Writing shit");

  // read
  i2c_rep_start(device+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;
  float fahrenheit = (celcius*1.8) + 32;

  Serial.print("Celcius: ");
  Serial.println(celcius);

  Serial.print("Fahrenheit: ");
  Serial.println(fahrenheit);

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

It seems I cant even start communication because in my serial monitor the last line I see is; Variable initialized.

Please help I don't know what is wrong.

Sorry, just found the mistake I made and its ridiculous... I just found out after like 4 hours of hard work that my breadboard bus is split in the midle, therefore, the device wasn't even powered.... :roll_eyes:

Anyway I might need some help down the road. If anyone is interested, the key for the 2560 Mega is to plug SDA and SCL in pin 20 and 21 and to use this line instead for the pullup setup:

PORTD = (1 << PORTD1) | (1 << PORTD0);//enable pullups

With this line you don't need the external pullup.

Hi, sorry for Bumping this thread back up. I’m posting because I got quite confused in the process of using this sensor and especially multiple ones. But now that I have gone through all this trouble I wanted to clarify the process for the upcoming community that might use this pyrometer.

First, download Error - Melexis the MLX90614 Datasheet as it can come handy.

Second, the explanation and visual wiring diagram on this site http://bildr.org/2011/02/mlx90614-arduino/ is a good place to start wiring the sensor up. It also includes the necessary library and example to start you up. There is a couple of things I want to say about it thought. First, you don’t absolutely need the pull-up resistors and same goes for the capacitor. Some internal pull-up resistors are setted up in the code and the capacitor is just needed in case you would have parasitic noise on your power lines. I do recommend keeping the real pull-up resistors for those beginners who might get some wires wrong and burn things up thought. Also it says nothing about using multiple sensors.

I have made some modification on the code so people get less confused. First, I’ve added one line for the internal pull-up resistor set-up for the Arduino MEGA. I also made the reading part a function, so it can be called easily without messing up your code. The read functions takes two arguments for flexibility, one is the sensor address, and the other is the unit (C for Celsius, K for kelvin, or F for farehneit). Be aware that the default address of the sensor is 0x5A. So if you want to speak to more than one sensor, you will need to assign them different address first.

Here is the modified code I found on this forum that you need to run on each sensor separately to assign them a new address (different for each one). The only thing you need to change is to uncomment the right pull-up resistor code line, and to change MLXAddr for your given address. When you run this, open up serial monitor, and when asked, you need to power cycle the sensor (deconnect/reconnect +5V or +3V wire).

#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
  byte MLXAddr = 0x0A<<1;           // New Address: Change this value to wathever you want the new adress of the MLX to be.

void setup(){
  Serial.begin(9600);
  Serial.println("Setup...");
  
  i2c_init();                                // Initialise the i2c bus
  //PORTC = (1 << PORTC4) | (1 << PORTC5);   // Enable pullups resistors (uncomment for std arduinos)
  PORTD = (1 << PORTD1) | (1 << PORTD0);     // Enable pullups resistors (uncomment for arduino MEGA)
  
  delay(5000);                    // Wait to allow serial connection
  ReadAddr(0);                    // Read current address bytes
  ChangeAddr(MLXAddr>>1, 0x00);   // Change address to new value
  ReadAddr(0);                    // Read address bytes
  Serial.println("Manually cycle power the MLX, you have 10 seconds.");
  delay(10000);                    // Cycle power to MLX during this pause
  Serial.println("Cycle power delay expired.");
  delay(2000);                     // Additionnal delay buffer
  Serial.println("Testing temperature reading on universal address.");
  ReadTemp(0);                    // Read temperature using default address
  Serial.println("Testing temperature reading on new address.");
  ReadTemp(MLXAddr);              // Read temperature using new address
  Serial.println("Done");
}

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;
}

See next post for actual temperature reading code.

And here is the basic code for reading temperature on 3 different sensors. It is then as simple as removing/adding some lines to this code to fit your need and device address (remember, default address of the sensor is 0x5A). Again don't forget to uncomment the correct pull-up resistor code line for std arduinos vs MEGA.

#include <i2cmaster.h>                        // Necessary for MLX90614
// Pins: Standard: SDA:A4  SCL:A5
//           Mega: SDA:D20 SCL:D21

// Multiple MLX Address`s
byte MLXaddress1 = 0x0A<<1;
byte MLXaddress2 = 0x0B<<1;
byte MLXaddress3 = 0x0C<<1;


void setup() {

  Serial.begin(9600);                        // Serial monitor Setup 

  // MLX Setup
  i2c_init();                                // Initialise the i2c bus for MLX90614
  
  //PORTC = (1 << PORTC4) | (1 << PORTC5);   // Enable pullups resistors (uncomment for std arduinos)
  PORTD = (1 << PORTD1) | (1 << PORTD0);     // Enable pullups resistors (uncomment for arduino MEGA)
}


void loop() {

  // Reading temperatures on multiple MLX
  float IRTemp1 = readIRTemperature(MLXaddress1, 'C');
  float IRTemp2 = readIRTemperature(MLXaddress2, 'C');
  float IRTemp3 = readIRTemperature(MLXaddress3, 'C');
  
  // Printing Temperature values to Serial Monitor
  Serial.print("Temp1: ");
  Serial.print(IRTemp1);
  Serial.print(" Temp2: ");
  Serial.print(IRTemp2);
  Serial.print(" Temp3: ");
  Serial.println(IRTemp3);
  delay(1000);

}


//______________Function for reading IR Temperature on MLX90614_____________

// To Call this function  : readIRTemperature(Address, '?')
// Where '?' is replaced by 'C', 'K' or 'F' for Celsius, Kelvin or Fahrenheit readings.

float readIRTemperature(int address, char tempUnit){
  int data_low = 0;
  int data_high = 0;
  int pec = 0;

  i2c_start_wait(address+I2C_WRITE);
  i2c_write(0x07);

  // 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
  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;

  // Calculate and return the demanded temperature unit
  switch (tempUnit) { 
  case 'K':
    {
      float kelvin = tempData;                             // Return temperature in Kelvin
      return kelvin;
      break;
    }

  case 'C':
    {
      float celcius = tempData - 273.15;                   // Return temperature in Celsius
      return celcius;
      break;
    }

  case 'F':
    {
      float fahrenheit = ((tempData - 273.15)*1.8) + 32;   // Return temperature in Fahrenheit
      return fahrenheit;
      break;
    }
  default:
    { 
      return 999.99;                                       // If nothing else matches, return 999.99 as an error code.
    }
  }

}

Hope this help for some of you. Cheers !

Alright... just found this website wich is the best how-to I've seen so far:
http://wiki.wiring.co/wiki/Connecting_Infrared_Thermometer_MLX90614_to_Wiring#Connecting_two_or_more_MLX90614
Still, I like the way my function can return 3 different temperature unit as desired.

It's useful that you can change the address like that.

Thank you antotabo for your useful topic.
It help me so much.
There are a little bit thing I still can't understand :
why we need to power cycle the sensor after we change the address ?
Thanks