Melexis SMBus IR Thermometer - NFI

Ok, still no luck with this thing but this is the code I am using:

#include <Wire.h>
void setup()
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}
void loop()
{
  byte SA = 0x00;  // The Slave Address, melexis docs says all units will respond to 0 address
                         // Try 0x5A if this doesn't work or 0x00
  byte CMD = 0x07;       // The read RAM address 0x07 command.
  byte Expected = 0x03;   // we are expecting to get 3 bytes back
 
  Wire.beginTransmission(SA); // Open a connection to slave device
  Wire.send(CMD);             // Transmit the command.
  Wire.endTransmission();     // stop transmitting
 
  Wire.requestFrom(SA, Expected);    // request 3 bytes from slave device #0
  while(Wire.available())    // slave may send less than requested
  {
    int c = Wire.receive(); // receive a byte as integer
    Serial.print(c);         // print the int
  }
  delay(500);
}

Thes Melexis SMBus transfer datasheet shows this as the communication psuedocode:

Pseudo code example: Reading RAM address 0x07 (Tobj1)

  1. Send START bit
  2. Send Slave Address (0x00* for example) + Rd-Wr bit**
  3. Send Command (0b000x_xxxx + 0b0000_0111 -> 0b0000_0111)
  4. Send Repeated START_bit
  5. Send Slave Address + Rd-Wr bit**
  6. Read Data Byte Low (master must send ACK bit)
  7. Read Data Byte High (master must send ACK bit)
  8. Read PEC (master can send ACK or NACK)
  9. Send STOP bit
    Note* : Any MLX90614 will respond to address 0x00
    Note**: Bit Rd-Wr has no meaning for MLX90614

My question is, does my above code accurately reflect what the pseudo code ( supplied by melexis) is demonstrating. When I compile and run this code all
I get is 700 ( 7 first byte - data byte low, 0 data byte low, 0 PEC). The 7 however is just the command I sent it in the fist place.

Please help.

Hi,

Still working on this. I made a logic analyser from a website I found and managed to get a clock diagram of what is happening:

I think the issue is the stop bit after the send command. Reading the pseudocode I posted above it doesn't mention a stop bit so I am not sure if this is stuffing it up. All I know is
that I shouldn't be getting 255,255,255 as the three bytes. Reading the melexis forums (whom made the ir thermometer) they mention that a 0xFF suggests a timing issue.

I also read this on the SMBus spec sheet:

"Specific SMBus protocols require the master to generate a repeated START condition followed by the
slave device address without first generating a STOP condition."

I think, from my timing diagram, this is exactly what I am doing.

Please can anyone suggest how I would use the wire library but stop sending that stop bit halfway through until all the 3 bytes (low,high and PEC) are recieved.

Many thanks.

Current Code:

#include <Wire.h>
void setup()
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}
void loop()
{
  int SA = 0x5A;  // The Slave Address, shitfed one bit left.
                         // Try 0x5A if this doesn't work or 0x00
  //byte CMD = 00101110;       // The read RAM address 0x07 command.
  byte Expected = 0x03;   // we are expecting to get 3 bytes back
 
  Wire.beginTransmission(SA); // Open a connection to slave device
  Wire.send(0x03);
  Wire.endTransmission();
  
  Wire.beginTransmission(SA);
  Wire.requestFrom(0x5A, 2);    // request 6 bytes from slave device #2 
 
  while(Wire.available())    // slave may send less than requested 
  { 
    int c = Wire.receive(); // receive a byte as character 
    Serial.print(c);         // print the character 
  } 
  Wire.endTransmission();
    
}

Hi again,

I have had contact with the manufacturer of the device who has agreed that the stop bit should not be in there after the command only a repeated start condition. Does anyone know how to send only a repeated start condition and not a stop/start condition using the wire library?

I need it to look like this:

Start Condition
Send Slave Address
Get ACK
Send Command
Get ACK
Send Repeated Start Condition (This is where I stuff up now)
Send Slave Address
Get ACK
Receive 3 bytes (Data Hi, Data Low, Error Check Byte)
Send Stop Condition.

I CANNOT GET THE WIRE LIBRARY TO DO THIS. PLEASE HELP IF YOU CAN.

OK,

Well I got it working using a different library. It appears wiring cannot do it.

I used Peter Fleurys libray here:

http://homepage.hispeed.ch/peterfleury/avr-software.html

Scroll down and download the i2c master libraries. Create a folder in /{arduino root}/hardware/libraries and copy the
i2cmaster.h and which .c file you want (it supports hardware and software i2c, I only used the hardware one twimaster.c).

You need to change the .c file to .cpp (otherwise it won't link when compiling) and don't forget to change some of the values
in the .h and .c file depending on your mcu etc.

Once you load arduino ide, it should build a .o file in the folder you created and then you can import the library from the menu or put in
your code:

#include <i2cmaster.h>

Then write your program, the help file lists all the functions available and it worked on the first try for me. So I can say this library works for smbus devices. Here's my sample code using the library (NB: You must bit shift left whatever your device address is):

//Simple program to read data from smbus device and convert from degrees kelvin to degrees C and print to serial port.

#include <i2cmaster.h>

void setup()
{
  Serial.begin(9600);
  i2c_init();  //Initialise the i2c bus
  
}
void loop()
{ 
  int dev = 0x5A<<1; [b]//Address must be bit shifted left, library does not automatically do it[/b]
  int data_low = 0;
  int data_high = 0;
  int pec = 0;
  i2c_start_wait(dev+I2C_WRITE); //send start condition and write bit
  i2c_write(0x07); //send command for device to action
 
  
  i2c_rep_start(dev+I2C_READ); //send repeated start condition, device will ack
  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(); //Read error check byte and send Nack to tell device no more data to send
  i2c_stop(); //Release bus, end transaction
  
 //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
    int tempData = 0x0000;
    
// This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
    tempData = (int)(((data_high & 0x007F) << 8) + data_low);
    tempData = (tempData * tempFactor)-0.01;
    tempData = tempData - 273.15;
    Serial.println(tempData); //Print temp in degrees C to serial
    delay(1000);
}

Hope this can help somebody with i2c and smbus devices. It is a very simple library to use. His website I think only has limited traffic per day so it might not be available all the time.

Did you find out specifically what the device didn't like about wiring? Is it an I2C vs. smbus issue, something nonstandard in one of the devices, etc.?

-j

The device could not accept a stop/start condition transmission during a transaction. From what I understand, you cannot send a repeated start condition in the wire library without first sending a stop condition. This does not work with the smbus device I was using. I don't know if the device is out of spec or the wire simply doesn't support all smbus communication protocols.

CalculusAE, I hope you're still getting emails for this thread. I have been trying your code to get readings from the MLX90614 and haven't had any luck. Now I don't know if I might have damaged the device or what. I'm new to Arduino and as you know SMBus is not easy. Anyway, I have the pin voltage in the Arduino's 3.3V, and SDA into Analog4 and SCL into Analog 5. I have 4.7k pull-ups, but have also tried without.

It fires the init but doesn't get past the i2c_start_wait. Any help GREALY appreciated!

I'm also trying to figure this out. I don't have the MLX sensor yet, but am already a bit mystified.. Where should I tell the i2c library which Arduino port(s) to use?

I'm looking at Peter Fleury's twimaster.c library.

Yes, this confused me at first too! Pin 4 is data and pin 5 is the other one.

I2C: 4 (SDA) and 5 (SCL). Support I2C (TWI) communication using the Wire library.

I got it working very quickly with the TPA81, though I believe I had to add a (pull down) 1.4k transistor on each of those lines.

see: Wire - Arduino Reference

though I believe I had to add a (pull down) 1.4k transistor on each of those lines

Never come across a 1.4K transistor.

Do you mean a 1.4K resistor?

And while we are at it wasn't it pull up?
Anyway it should have been 4.7K, which is written 4K7.

To conclude put a 4K7 pull up resistor on each I2C line.

Where should I tell the i2c library which Arduino port(s) to use?

You don't there is no choice as it relies on hardware in the arduino that is only brought out to those two pins.

Thanks Grumpy Mike, wrote that thing too fast. I also failed to mention those are the resisters I used for the TPA, don't know if they'd work with the Melexis. Also, while I'm here. This is also a good thread on Sparkfun where I posted Dave Eaton's code.

http://forum.sparkfun.com/viewtopic.php?t=18771&start=0&postdays=0&postorder=asc&highlight=dave+eaton

Thanks maxr and Grumpy_Mike!

That helps in several ways, as I have a shortage of digital I/O pins, and would've ran out if I need to add another sensor to the system.

This looked a little intimidating at first but turned out to be really easy.....

I did the following steps:

  1. Hardware:
    Connect the MLX90614 (refer to the datasheet) as follows:
    Pin 1 on MLX (SCC) connect to ANALOG pin 5 on Arduino
    Pin 2 on MLX (SDA) connect to ANALOG pin 4 on Arduino
    Pin 3 on MLX (VDD) connect to 3.3V on Arduino
    Pin 4 on MLX (VSS) connect to GROUND on Arduino

Now use "pull ups" on the SCC and SDA lines by connecting a 4.7K ohm resistor from the Pin 3 VDD line to the SCC line and a 4.7K ohm resistor from the Pin 3 VDD line to the SDA line.

  1. Software:
    a) Download the I2c libraries as follows:

Go to:

homepage.hispeed.ch/peterfleury/avr-software.html

and download the i2cmaster.zip

Make a folder in /{arduino root}/hardware/libraries and extract the
i2cmaster.h and twimaster.c files. Now rename the .c file of twimaster to .cpp (YES I KNOW IT SOUNDS WIERD BUT RENAME AND MAKE SURE THESE FILES ARE IN THE RIGHT LOCATION I.E. IN THE LIBRARIES FOLDER OF THE ARDUINO CODE)

Make sure you restart Wiring if you load a new library into it so it can be found when it is called.....

  1. Now you need to modify twimaster.c
    Open it in a simple text editor and change the following if you are using an Arduino Duemilanove

Edit the twimaster.c to reflect the 16MHz clock, and change the bus frequency to 50Khz by changing the code at the beginning to:

#ifndef F_CPU
#define F_CPU 16000000UL
#endif

/* I2C clock in Hz */
#define SCL_CLOCK 50000L

  1. Now copy Dave Eaton's EXCELLENT code into Wiring. You may have issues with seeing the new libraries or with missing brackets when you verify the code.
    Dave's fix for the high precision (2 decimal place) thermal read also works well (this sensor is incredibly sensitive).

This is Dave's code as I used it:

#include <i2cmaster.h>

void setup()
{
Serial.begin(9600);
Serial.println("Hello!");
i2c_init(); //Initialise the i2c bus
Serial.println("Return from i2c_init");
PORTC = (1 << PORTC4) | (1 << PORTC5);//enable pullups
}
void loop()
{
int dev = 0x5A<<1;
int data_low = 0;
int data_high = 0;
int pec = 0;
i2c_start_wait(dev+I2C_WRITE);
i2c_write(0x07);

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
double tempData = 0x0000;
int frac;

// 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;
tempData = tempData - 273.15;
Serial.print((int)tempData); //Print temp in degrees C to serial
Serial.print(".");
tempData=tempData-(int)tempData;
frac=tempData*100;
Serial.println(frac);
delay(100);
}

Upload the code in the Arduino making sure you are on the correct COM port (obviously). Open the serial port viewer (COM port correct?) and make sure the baud rate corresponds with the the baud rate in the code and you should be able to see the temperatures streaming live from the serial port.

BTW....Hooking up an Xbee is easy using the standard breakout board and Xbee to USB converter for the PC end of things. Just make sure all of the baud rates are the same (in the code and in the Xbee based on the setup in the XCTU program from Digi).

Thanks to Dave Eaton for making a fun afternoon successful, CalculusAE for a great trail of technical breadcrumbs and Peter Fleury for posting the libraries.

Now....how does one get more than one MLX sensor to work on this two wire inter face? I assume the wiring is simply in parallel. How can I identify and call out different MLX sensors in the code?

Thanks Sensorjunkie for taking the time to put this all in one place! In the past few months I've learned a few things that might be helpful here.

First, most Melexis Thermopiles for sale have a wide field of view, about 35-degrees. There is a model F with a 10-degree field of view but seems difficult to get.

For reference, a standard webcam has a field of view of 40-degrees by 30 degrees. It's difficult to focus a thermopile because glass and plastic absorb around half of radiation between 3 and 20 micrometres. Germanium is the best material, but any research will show you that those lenses are a fortune. To get around the absorption issues in plastic you'll notice that the IR thermometer you buy from Sears uses a fresnal lens. These lenses magnify with the least amount of lensing material.

Also, let me point out a common misconception I come across, the infrared that the Melexis picks up has almost NOTHING in common with the infrared that your digital camera can be converted to see. Infrared photography deals with 800 to 1,000 nanometers, or just below the red spectrum. If you measure that distance as a football field, thermal radiation starts about 10 football fields lower. Silicon is basically transparent to that radition. YOU CANNOT MODIFY YOUR DIGITAL CAMERA OR BUY A LOW COST SECURITY CAMERA that sees true thermal radiation from your PC power supply, let's say.

In the January, 2010 issue of Nuts and Volts there is an interesting article by L. Paul Verhage, that uses a parallax thermopile for a near space infrared telescope. The Melexis could be used in similar applications. I think his technique for limited the field of view of the thermopile would work well with the Melexis.

Thanks again Sensorjunkie for making a go-to-place for getting the Melexis to work with Arduino!

Thank you for all the help with connecting the Melexis MLX90614. I have not been getting a response from the device whatsoever. By going through your code, I found that the code never progresses beyond the code line "i2c_start_wait(dev+I2C_WRITE);" I've yet to get a response by the MLX90614 any other method, and I would be very gracious if anyone could help explain to me what I'm doing wrong. Thanks!

I have been trying to get the i2cmaster.h library to work in my arduino development board with no success. I renamed the twinmaster.c to a .cpp file and changed the F_CPU definition as well as the SCL_CLOCK . . . However, when I include i2cmaster.h, I am not able to even get a "Hello World" program to work . . . here is some example code:

#include <i2cmaster.h>

void setup(){
Serial.begin(9600);
Serial.println("Hello World!");
i2c_init();
Serial.println("Return from i2c_init");
PORTC = (1 << PORTC4) | (1 << PORTC5);
}

Anyone have any idea why I am not able to println and have something show up on the serial monitor?

Make a folder in /{arduino root}/hardware/libraries and extract the
i2cmaster.h and twimaster.c files. Now rename the .c file of twimaster to .cpp (YES I KNOW IT SOUNDS WIERD BUT RENAME AND MAKE SURE THESE FILES ARE IN THE RIGHT LOCATION I.E. IN THE LIBRARIES FOLDER OF THE ARDUINO CODE)

Did you mean /{arduino root}/libraries ? Because there is no /{arduino root}/hardware/libraries folder . . . only /{arduino root}/hardware/arduino and /{arduino root}/hardware/tools

If you have a Arduino MEGA connect the SCL and SDA pins to 20 and 21.

Do not use Analogue pins 4 and 5.

This is just in case there is anyone else out there wondering why there comms isn´t working when the circuit and software is correct.

There must be another idiot like me??? wasted an hour on that.

The info on this thread is fantastic I would like to thank everyone who posted as it saved me a lot of time.

Neil C

Hey

I have the Melexis 90614ACC (That is the 5v version) of the sensor and i tried to read temp data from it using the i2c master library and dave eatons code as follows:

#include <i2cmaster.h>

void setup()
{
Serial.begin(9600);
Serial.println("Hello!");
i2c_init(); //Initialise the i2c bus
Serial.println("Return from i2c_init");
PORTC = (1 << PORTC4) | (1 << PORTC5);//enable pullups
}
void loop()
{
int dev = 0x5A<<1;
int data_low = 0;
int data_high = 0;
int pec = 0;
i2c_start_wait(dev+I2C_WRITE);
i2c_write(0x07);

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
double tempData = 0x0000;
int frac;

// 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;
tempData = tempData - 273.15;
Serial.print((int)tempData); //Print temp in degrees C to serial
Serial.print(".");
tempData=tempData-(int)tempData;
frac=tempData*100;
Serial.println(frac);
delay(100);
}

Is there any reason the code isnt working? i tried using Serial.println to see till what stage the program executes and it stops before

i2c_rep_start(dev+I2C_READ);

doesnt go beyond that
Any ideas?
Also does the 5v version require me to pull-up to 5v or 3.3because thts SMBus spec?
Also the melexis datasheet gives a figure which shows the pin configurations and is labelled "TOP VIEW"
Does that mean it shows the pins coming out of the page or the other way round??
Im confused

hello,

i have tried the codes too, but no output appear at all. i think it is stuck in the i2cmaster. even the printf doesnt work at all but when i remove the i2cmaster.h and comment out the unnecessary codes-left only the prints--it works. so i believe it is caaused by the i2cmaster.h

anybody could help?
i guess every1 is having the same problems too

thank you