Go Down

Topic: Melexis SMBus IR Thermometer - NFI (Read 21624 times) previous topic - next topic

CalculusAE

Hi,  

I have entered the world of Arduino and have gone through some tutorials and have picked up the language ok. I also purchased a Melexis IR Thermometer model # MLX90614ESF-AAA. It uses a SMBus data connection and from my reading its the same as I2c. This unit however only supports the commands read word and write word. I've been playing with the Wire library to get it to read data from the device but I'm not having much luck.   Specifically the problem is working out how to send a command to the unit and how the ram addressing and eeprom addressing works.

I'd appreciate it if anybody could give me some clues and have a quick read of the spec sheets from the melexis website
http://www.melexis.com/Sensor_ICs_Infrared_and_Optical/Infrared/Digital_plug__play_infrared_thermometer_in_a_TO-can_615.aspx

Its got some assembler examples there but I would like to use the Wire library to do talk to this thing.  The spec sheets says the default SMBus address is 0x5A (90) but then I read in the wire library the addressing is using only the top 7 digits so it becomes 45. Is this right?  I can get numbers back from the device but I don't know what they mean, I'm supposed to be reading the temperatures from the device but I don't think thats what I'm doing.

I think if it I just got on the right track with how to communicate to this thing I should be ok.

Any help would be soooo appreciated.

CalculusAE

#1
Jul 13, 2008, 11:27 am Last Edit: Jul 13, 2008, 01:02 pm by CalculusAE Reason: 1
Ok, still no luck with this thing but this is the code I am using:

Code: [Select]


#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.

CalculusAE

#2
Jul 27, 2008, 05:35 am Last Edit: Jul 27, 2008, 10:49 am by CalculusAE Reason: 1
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:

Code: [Select]

#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();
   
}






CalculusAE

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.

CalculusAE

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:
Code: [Select]

#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):

Code: [Select]

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

kg4wsv

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


CalculusAE

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.

maxr

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!

pnuu

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.

maxr

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: http://arduino.cc/en/Reference/Wire

Grumpy_Mike

#10
Jan 20, 2010, 03:18 pm Last Edit: Jan 20, 2010, 03:20 pm by Grumpy_Mike Reason: 1
Quote
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.

Quote
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.

maxr

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

pnuu

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.

Sensorjunkie

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.

2) 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.....

3) 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


4) 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?

maxr

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!

Go Up