Allsky camera

Connecting MLX90614 infrared thermometer to the Raspberry PI

MLX90614 is a cheap and popular infrared thermometer from Melexis. This device is made in different version for different purposes.
You can get generic purpose version with 0.5 accuracy or for medical purposes with accuracy up to 0.2.

Also you can choose voltage you needed: 3.3v or 5v versions is available.
With MLX90614 you can distantly measure temperature of the objects, even temperature of the sky. This enables a wide range of tasks in science and technics.
In this article i’ll show how to connect this device to the Raspberry PI microcomputer via i2c bus.

Inside hermetic package you can find two sensors – one infrared and one thermopile for classical temperature sensing. Thermopile may be used for some calibration of the main infrared sensor.

Sensors values is digitized by the 17-bit ADC and processed with powerful DSP unit.
The operation of the MLX90614 is controlled by an internal state machine, which controls the measurements and calculations of the object and ambient temperatures and does the post-processing of the temperatures to output them through the PWM output or the SMBus compatible interface. In this article we connecting this device to the i2c interface which is mostly compatible to SMBus (in our case).
PWM mode enables stand alone usage of this device as thermostat and won’t be  discussed in this article (well, almost).

Most important characteristics of this device you can find in a table below.
For full specifications, description and complete usage please use the official datasheet.

MLX90614 specifications

  
Power supply5V for MLX90614Axx
3.3V for MLX90614Bxx
Temperature ranges-40...+125 ̊C for ambient sensor
-70...+380 ̊C for infrared sensor
Accuracy0.5°C for general purpose version
0.2°C for medical version
Both sensors
Infrared sensor field of view90° for MLX90614xAA
Infrared sensor wavelengths5.5 – 14 μm
Ambient temperature ranges-40...+85 C
Max current2 mA

 Device pinout

VSS – power supply (3.3 or 5 volts).

SCL – i2c/SMBus clock.

SDA – i2c/SMBus data or PWM output.

GND – ground.

Please always carefully check voltages. 5v is able to kill your 3.3 v device!

Connecting to the Raspberry PI.

It’s simple.

R1 and R2 – 4K7, optional, because Raspberry contains such resistors on a board. May be used only in case of “long” line.

C1 capacitor is important and should be placed as close as possible to the device.

 Another important note about voltages! Raspberry PI is 3.3v device and not tolerant to 5v inputs. So in case of 5v MLX device additional voltage shifter should be used!

By default device is working in SMBus mode, but my sample was in PWM mode what was puzzled me for some time, i2c bus was full of random numbers. But after connecting the oscilloscope i finally figured out whats going on and switched MLX to the “normal” SMBus mode.

Switching is simple – just pull SCL pin to the ground for a short time. After reset device will return to the original mode, so for permanent switching some EEPROM value should be corrected. EEPROM reading and writing will discussed below.

Programming

There are two ways to work with i2c devices.

  1. Using hardware i2c bus through i2c_bcm2708 kernel module and libi2c library.
  2. Using popular bcm2835 library with software emulation of the i2c protocol.

Looking ahead i can tell you that i don’t had any problems with bcm2835. MLX device worked like a charm.
But i didn’t prefer this way due to it programmatically nature. Why emulate some hardware bus when we already had one? Especially on such weak device like a Raspberry.

So i choosed the i2c_bcm2708 and this is was the begginning of the interesting debugging with magic…

First of all we need to load i2c_bcm2708 kernel module. We can do it whith a modprobe command: sudo modprobe i2c_bcm2708

For automatically loading of this module on every boot just add module name to the end of the /etc/modules file.

After successfull module loading you can find two new devices: /dev/i2c-0 and /dev/i2c-1. This is two separate i2c buses and in case of first generation of the Raspberry – only i2c-1 is available on the GPIO header. i2c-0 is available for manual soldering. In later Raspberry’s models both buses is available on GPIO header.

To check that MLX device is properly connected and worked run this command: i2cdetecty 1 (1 means /dev/i2c-1 device). This utility is available in i2c-tools package.

Default MLX address is 0x5A. And if everything is OK and MLX device is lonely on the bus – you can see such output:

In case of numbers chaos your device might be in PWM mode and should be switched as described earlier.

Okay, let’s write a simple program.

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <linux/i2c-dev.h>

///

int main()
{
    int fdev = open("/dev/i2c-1", O_RDWR); // open i2c bus

    if (fdev < 0) {
        fprintf(stderr, "Failed to open I2C interface %s Error: %s\n", dev_path, strerror(errno));
        return -1;
    }

    unsigned char i2c_addr = 0x5A;

    // set slave device address, default MLX is 0x5A
    if (ioctl(fdev, I2C_SLAVE, i2c_addr) < 0) {
        fprintf(stderr, "Failed to select I2C slave device! Error: %s\n", strerror(errno));
        return -1;
    }

    // enable checksums control
    if (ioctl(fdev, I2C_PEC, 1) < 0) {
        fprintf(stderr, "Failed to enable SMBus packet error checking, error: %s\n", strerror(errno));
        return -1;
    }


    // trying to read something from the device unsing SMBus READ request
    i2c_data data;
    char command = 0x06; // command 0x06 is reading thermopile sensor, see datasheet for all commands

    // build request structure
    struct i2c_smbus_ioctl_data sdat = {
        .read_write = I2C_SMBUS_READ,
        .command = command,
        .size = I2C_SMBUS_WORD_DATA,
        .data = &data
    };

    // do actual request
    if (ioctl(fdev, I2C_SMBUS, &sdat) < 0) {
        fprintf(stderr, "Failed to perform I2C_SMBUS transaction, error: %s\n", strerror(errno));
        return -1;
    }

    // calculate temperature in Celsius by formula from datasheet
    double temp = (double) data.word;
    temp = (temp * 0.02)-0.01;
    temp = temp - 273.15;

    // print result
    printf("Tamb = %04.2f\n", temp);

    return 0;
}

Compile and run:

gcc test.c -o test && ./test

And Oops! We got error “Failed to perform I2C_SMBUS transaction, error: bad message

So what the problem? Sure, we doing everything correctly and sending correct messages to the device.

I’ve spent few days trying to figure out whats is going on. Digging Raspberry PI forums, other code examples and even Linux kernel.

As i already noted, using bcm2835 library caused no errors.

Finally i’ve connected my logic analyzer to the Raspberry i2c bus and captured both communications – using bcm2835 code and this example code.

Looking datasheet we can found an example of the proper SMBus communication.

But analyzer showed something strange.
Long pause after ACK is caused by extra stop bit (red dot), just after ACK packet.
This situation is recognized by device as two separate writes. But both this writes is incomplete and incorrect, of course.

And  for example, this is a request made with bcm2835 library.

Everything is ok as expected.

Digging the forums i found that such behavior is a feature of the Raspberry i2c_bcm2708 driver. Driver should be switched to the “combined” mode to stop such packet splitting.

Fortunately this can be done with simple command (as root):

echo -n 1 > /sys/module/i2c_bcm2708/parameters/combined

Run our test utility and magic happens.

sudo ./test
Tamb = 19.4

It works!

Now it’s time to write fully functional utility.

You can find all EEPROM and RAM addresses in the MLX datasheet.

PWCTRL is what we need to permanently switch off PWM mode.

Register description

So to disable PWM mode just set first (0) bit to the zero value. Simple.

Temperatures is reading from RAM.

Here you can see infrared channels RAW data (some versions of the MLX device contains two infrared sensor, see documentation), thermopile sensor temperature value (Ta) and infrared sensors calculated values (Tobj1 and Tobj2). For the single IR sensor device temperature value is read from Tojb1.

I’ve made a header file with all useful addresses values.

// RAM
#define MLX90614_RAWIR1 0x04
#define MLX90614_RAWIR2 0x05
#define MLX90614_TA 0x06
#define MLX90614_TOBJ1 0x07
#define MLX90614_TOBJ2 0x08

// EEPROM
#define MLX90614_TOMAX 0x20
#define MLX90614_TOMIN 0x21
#define MLX90614_PWMCTRL 0x22
#define MLX90614_TARANGE 0x23
#define MLX90614_EMISS 0x24
#define MLX90614_CONFIG 0x25
#define MLX90614_ADDR 0x2E
#define MLX90614_ID1 0x1C
#define MLX90614_ID2 0x1D
#define MLX90614_ID3 0x1E
#define MLX90614_ID4 0x1F

Full source code of the utility with Makefile and good Readme you can find on my github.

Building is simple, just make.

Let’s test this program.

Reading infrared temperature from the device on i2c bus 1 and with address 0x5A:

./read_mlx90614 --bus 1 --i2c_addr 0x5a -i
Tobj = 21.3

Seems plausible.

Now thermopile:

./read_mlx90614 --bus 1 --i2c_addr 0x5a -a
Tamb = 19.4

Checking state of the PWM mode:

./read_mlx90614 --bus 1 --i2c_addr 0x5a -p
PWM mode - enabled

Disable PWM mode:

./read_mlx90614 --bus 1 --i2c_addr 0x5a --pwm_mode=0 -w

And enable (why not):

./read_mlx90614 --bus 1 --i2c_addr 0x5a --pwm_mode=1 -w

You can use additional –debug option to see what actually going on

./read_mlx90614 --bus 1 --i2c_addr 0x5a --pwm_mode=1 -w –debug

   Opening i2c interface /dev/i2c-1
   Setting up slave address 0x5A
   Perfoming I2C_SMBUS_READ request to device, command = 0x22
   Ok, got answer from device
   EEPROM cell = 0x22 current value = 0x0201
   Erasing EEPROM cell = 0x22
   Trying to store value = 0x0203 to the EEPROM cell = 0x22
   PWM mode is now enabled

Offtop. Sensor usage

One of the most interesting usage of this device is to measure the sky temperature.  Why does it make sens?

Well. Sky temperature – is a median temperature of the flipped conus from our sensor to the stratosphere.

This temperature depends on quantity of water vapor in the air. This water vapor is heated by the infrared radiation re-emitted by the Earth surface.  More water – higher temperature. Temperature of the sky without any vapor and any gases is striving to temperature of the outer space. So measuring of the sky temperature can tell us how many water vapor in the air.
Concentration of the vapor is called clouds. Thus with this measurements we can judge how cloudy is today.

(image credit: Forrest M. Mims III., mynasadata.larc.nasa.gov)

 

As was found experimentally if sky temperature is less of ambient temperature by 5 or less degrees – sky is full of clouds. Difference by 20 or more is always clearest sky.


Thanks for reading.
Hope this material will be helpful.

 

Tagged , , , ,

6 thoughts on “Connecting MLX90614 infrared thermometer to the Raspberry PI

  1. Hi Oleg! Which is the difference between TOBJ1, TOBJ2, RAWIR1 and RAWIR2? I can see that all of them give quite different results. I read the Melexis datashet but I didn’t get well why the output are different. Can you help me?

    1. Hi!

      It’s simple.
      RAWIR – just a instant filtered value from the ADC, some voltage.
      Sensor workflow in this case:
      [IR SENSE] (analog voltage output) –> [Amplifier] –> [FIR and Lowpass filters] (programmable) –> [RAW Register]

      TOBJ – calculated object temperature in Kelvins. Internal DSP uses filtered voltage value (RAW Register), applies some additional corrections/gain control and compensations to calculate resulting temperature value.
      Simply speaking – there is a some linear dependecy between sensor analog voltage and measured temperature. Some voltage is considered as zero temperature. Voltage changes in some steps which means corresponding temperature changes. Knowing these correspondences – DSP can calculate temperature.

      All calculation and filtering steps uses calibration data stored in the EEPROM.

      In a real world applications you will never needed RAW value, maybe only in some very Special cases. Just use TOBJ to calculate temperature in Celsius or in Fahrenheit:

      TC = ((Tobj * 0.02) – 0.01) – 273.15
      TF = (((Tobj * 0.02) – 0.01) – 273.15) × 1.8 + 32

      TOBJ1/TOBJ2 (RAWIR1/RAWIR2) only means IR channel number. There are 2 types of MLX devices – with single and with double IR sensors. All values can be read seperately by accessing corresponding register TOBJx
      In case of single IR sensor you can use only TOBJ1 (second channel, probably, can return some garbage, i don’t know). In case of double channel IR sensor you can read different values from the sensors. This happens due to different FOV of both sensors, see Figure 20 in the MLX datasheet.

  2. Many thanks for the detailed reply! Using TOBJ2 on a single channel sensor gives something that looks like a temperature but much more noisy than using TOBJ1. That’s why I asked you for some explanations. Now it’s clear, thanks!

  3. Hello,
    I could read my mlx sensor thanks to your codes, Now I hava a challenge, I need to read two sensor, can you give me some ideas on how to do that ?

    1. Hi!
      You means two different sensors on the same i2c bus?
      Solution is very simple. Just change address of the one sensor by using my code.

      Connect one sensor to the bus and change default 5A to something different (and not used on your bus), 2A for example:
      $ ./read_mlx90614 –bus 1 –i2c_addr 0x5A -w –new_addr=0x2A

      Now you can can connect second sensor and read both without any troubles, just using different addresses on reading.

      Read sensor with changed address:
      $ ./read_mlx90614 –bus 1 –i2c_addr 0x2A -i

      Read second sensor with unchanged adress:
      $ ./read_mlx90614 –bus 1 –i2c_addr 0x5A -i

      I’m using this technique in my projects to avoid conflicts with other i2c devices which may have the same address as mlx sensor.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.