Read EEPROM from MLX90614 over I2C

Hello everybody!

I have some problems to read out the EEPROM of my MLX90614 DCI infrared sensor, which is connected via I2C to my Arduino Uno. So the problem is less the sensor, than my limited programing skills..

To set a new emissivity I need to change the values in two EEPROM cells of the MLX, 0x04 and 0x0F. However, I'm not familiar with the wire library and only wanted to read out the current values by using to following code:

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_MLX90614.h>


Adafruit_MLX90614 mlx = Adafruit_MLX90614();
#define MLX90614_I2CADRESS 0x5A

//float emissivity = 0.95;  //0xF332 in hex, which is the new value, but not used here


void setup() {
  Serial.begin(9600);
  Wire.begin();
  mlx.begin();

}

void loop() {
  
Wire.beginTransmission(MLX90614_I2CADRESS);
 Wire.requestFrom(0x04, 2);     //cell with the emissivity, factory default 1 (0xFFFF), 16bit long

  while(Wire.available()) { 
    char c = Wire.read();    
    Serial.print(c);

Wire.endTransmission();
 }
}

But it is not working. There is no error occuring when I upload the code on the Arduino, but there is nothing shown in the serial monitor.

I found something similar in the forum: Problem with Reading/Writing to the EEPROM of a MLX90614 - Sensors - Arduino Forum
Unfortunately, I cannot find the used i2cmaster library which is used there and more over I don't really know what the code is all about. I only want to read the content of the cells and write some new values to them. This shouldn't be that much code or am I wrong?

Thanks a lot in advance!
Regards,
banff

Moderator: updated the title to better reflect the problem

I had a closer look on the adafruit MLX90614 library and i found something that might help to get rid of my problem.

class Adafruit_MLX90614  {
 public:
  Adafruit_MLX90614(uint8_t addr = MLX90614_I2CADDR);
  boolean begin();
  uint32_t readID(void);

  double readObjectTempC(void);
  double readAmbientTempC(void);
  double readObjectTempF(void);
  double readAmbientTempF(void);

 private:
  float readTemp(uint8_t reg);

  uint8_t _addr;
  uint16_t read16(uint8_t addr);
  void write16(uint8_t addr, uint16_t data);
};

read16() and write16 should work for my purpose. uint8_t is the adress, as it is 8bit long and uint16_t for my desired value, right?

But I was irritated by the following: The Melexis datasheet says that 0x04 is the adress for the emissivity. In the Adafruit library it is: #define MLX90614_EMISS 0x24
So which one is it?

Thanks and regards,
banff

I would trust the datasheet.

The correction can be applied after you make the measurement -- there is no need to update the sensor. For the record, you need to acquire both the ambient (chip) temperature Ta, and the object temperature Tom in Kelvin. The corrected object temperature Toc is given by the following expression in C, where eps is the actual surface emissivity:

Toc = pow( ( ( (pow(Tom,4)-pow(Ta,4))/eps) + pow(Ta,4) ), 0.25);

jremington:
I would trust the datasheet.

The correction can be applied after you make the measurement -- there is no need to update the sensor. For the record, you need to acquire both the ambient (chip) temperature Ta, and the object temperature Tom in Kelvin. The corrected object temperature Toc is given by the following expression in C, where eps is the actual surface emissivity:

Toc = pow( ( ( (pow(Tom,4)-pow(Ta,4))/eps) + pow(Ta,4) ), 0.25);

It was not passible to test the adress, because the functions in the mlx90614 library I mentioned are private.. So there will be no way to use them right?
However, with your calculation I got very similar results (+- 0.1°C) to my previous, where I simply multiplied the measured value of the object temperature with the emissivity.
Thank you.

Toc = pow( ( ( (pow(Tom,4)-pow(Ta,4))/eps) + pow(Ta,4) ), 0.25);

quite expensive formula, can be done substantial faster

Tom4 = Tom * Tom;
Tom4 = Tom4Tom4;
Ta4 = Ta * Ta;
ta4 = Ta4
Ta4;

Toc = pow(((Tom4 -Ta4)/eps) + Ta4), 0.25);

the latter line can be optimized to Toc = sqrt(sqrt((((Tom4 - Ta4) / eps) + Ta4)));

testsketch

//
//    FILE: performance.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// PURPOSE: demo
//    DATE: 2015-12-30
//     URL: http://forum.arduino.cc/index.php?topic=368545
//
// Released to the public domain
//

uint32_t start;
uint32_t stop;

float Tom = 300.0;
float Ta = 1.23;
float Tom4, Ta4, Toc;
float eps = 0.1;

void setup()
{
  Serial.begin(115200);
  Serial.print("Start ");
  Serial.println(__FILE__);

  start = micros();
  Toc = pow((((pow(Tom, 4) - pow(Ta, 4)) / eps) + pow(Ta, 4) ), 0.25);
  stop = micros();
  Serial.print(stop - start);
  Serial.print("\t");
  Serial.println(Toc, 7);
  delay(1000);

  start = micros();
  Tom4 = Tom * Tom;
  Tom4 = Tom4 * Tom4;
  Ta4 = Ta * Ta;
  Ta4 = Ta4 * Ta4;

  Toc = pow((((Tom4 - Ta4) / eps) + Ta4), 0.25);
  stop = micros();
  Serial.print(stop - start);
  Serial.print("\t");
  Serial.println(Toc, 7);
  delay(1000);

  start = micros();
  Tom4 = Tom * Tom;
  Tom4 = Tom4 * Tom4;
  Ta4 = Ta * Ta;
  Ta4 = Ta4 * Ta4;
  Toc = sqrt(sqrt((((Tom4 - Ta4) / eps) + Ta4)));
  stop = micros();
  Serial.print(stop - start);
  Serial.print("\t");
  Serial.println(Toc, 7);
  delay(1000);
}

void loop()
{
}

output:

1156	533.4833984
424	533.4837036
152	533.4838256

so 7x faster is possible

robtillaart:

Toc = pow( ( ( (pow(Tom,4)-pow(Ta,4))/eps) + pow(Ta,4) ), 0.25);

quite expensive formula, can be done substantial faster

Tom4 = Tom * Tom;
Tom4 = Tom4Tom4;
Ta4 = Ta * Ta;
ta4 = Ta4
Ta4;

Toc = pow(((Tom4 -Ta4)/eps) + Ta4), 0.25);

the latter line can be optimized to Toc = sqrt(sqrt((((Tom4 - Ta4) / eps) + Ta4)));

optimization is good, but I don't understand why TomTom = Tom4 and Tom4Tom4 = Tom4 also? And the same with Ta.
What does the 4 mean? To the power of 4?

Thanks again

banff:
optimization is good, but I don't understand why TomTom = Tom4 and Tom4Tom4 = Tom4 also? And the same with Ta.
What does the 4 mean? To the power of 4?

Thanks again

indeed, the 4 indicates to the power of 4.
More descriptive names are better however you got the idea. Maybe introduce Tom2 for tom squared but that would introduce an extra variable (probably the compiler would optimize it away). Would be easier to understand.

Tom2 = Tom * Tom;
Tom4 = Tom2 * Tom2;

robtillaart:

Toc = pow( ( ( (pow(Tom,4)-pow(Ta,4))/eps) + pow(Ta,4) ), 0.25);

quite expensive formula, can be done substantial faster

Tom4 = Tom * Tom;
Tom4 = Tom4Tom4;
Ta4 = Ta * Ta;
ta4 = Ta4
Ta4;

Toc = pow(((Tom4 -Ta4)/eps) + Ta4), 0.25);

the latter line can be optimized to Toc = sqrt(sqrt((((Tom4 - Ta4) / eps) + Ta4)));

robtillaart, can you tell me where you got this formula from?
Thanks,
banff

Rob Tillaart optimized for calculation speed a formula that I posted.

The procedure is outlined here: Error - Melexis

Apparently you now have to register with Melexis to read it.

jremington:
Rob Tillaart optimized for calculation speed a formula that I posted.

Yes, sorry, you're right.

jremington:
The procedure is outlined here: Error - Melexis

Apparently you now have to register with Melexis to read it.

However, I can't read it, but I'm registered. Whenever I copy your link, I'm forwarded to the page which asks for registration.
But if it is the PDF which explaines the change of emissivity from Melexis, I already got it.
The formula of emissivity: E=(TOnew^4-TAnew^4)/(TOreal^4-TAreal^4). Do you solve for TOnew or TOreal?

Tanks, banff

I found their notation confusing, so I rewrote the formula for eps as follows (assuming Ta is constant)

        Tom^4 - Ta^4
eps= -----------------
        Toc^4 - Ta^4

and solved for Toc, the corrected object temperature. This requires that eps in the sensor is set to 1.0

banff:
I had a closer look on the adafruit MLX90614 library and i found something that might help to get rid of my problem.

class Adafruit_MLX90614  {

public:
  Adafruit_MLX90614(uint8_t addr = MLX90614_I2CADDR);
  boolean begin();
  uint32_t readID(void);

double readObjectTempC(void);
  double readAmbientTempC(void);
  double readObjectTempF(void);
  double readAmbientTempF(void);

private:
  float readTemp(uint8_t reg);

uint8_t _addr;
  uint16_t read16(uint8_t addr);
  void write16(uint8_t addr, uint16_t data);
};




read16() and write16 should work for my purpose. uint8_t is the adress, as it is 8bit long and uint16_t for my desired value, right?

But I was irritated by the following: The Melexis datasheet says that 0x04 is the adress for the emissivity. In the Adafruit library it is: #define MLX90614_EMISS 0x24
So which one is it?

Thanks and regards,
banff

Though the datasheet says that the address of emissivity value is 0x04, the command opcode for EEPROM starts with 001XXXXX. Hence the address which needs to be sent is 0x24 and not 0x04.

Sending 0x04 access the RAM of the device.

banff:
But I was irritated by the following: The Melexis datasheet says that 0x04 is the adress for the emissivity. In the Adafruit library it is: #define MLX90614_EMISS 0x24
So which one is it?

Hello, I'll drop this here for anyone who has came to this post and have the same concern:
The EEPROM address, for emissivity in this case, is 0x04. But the SMBus command to write that EEPROM address is 0x04 (0x20 command for writting in EEPROM + 0x04 address for emissivity)