DFRobot / DFRobot_MLX90614

DFRobot IR Thermometer Sensor MLX90614
MIT License
8 stars 16 forks source link

Possibly incorrect emissivity setting method #3

Open CrouchingPanda opened 1 year ago

CrouchingPanda commented 1 year ago

In the application note it says to enter EEPROM address 0x0F unlock key and then to change both 0x0F and 0x04 registers for MLX90614xCx type models, which is one used on your evaluation board.

It seems this is not being followed in your setEmissivityCorrectionCoefficient method: https://github.com/DFRobot/DFRobot_MLX90614/blob/3d196b3d3a25c9d0b3278dddf3d0c504ffa89c6a/DFRobot_MLX90614.cpp#L39

A snippet from Melexis' oficial repo:

int MLX90614_SetEmissivity(uint8_t slaveAddr, float value) 
{
    int error = 0;
    uint16_t data;
    uint16_t curE;
    uint16_t newE = 0;
    float temp = 0;

    if(value > 1.0 || value < 0.05)
    {
        return -6;
    }

    temp = value * 65535 + 0.5;
    newE = temp;

    error = MLX90614_SMBusRead(slaveAddr, 0x24, &curE);

    if(error == 0)
    {
        error = MLX90614_SMBusRead(slaveAddr, 0x2F, &data);

        if(error == 0)
        {
            temp = curE*data;
            temp = temp/newE + 0.5;
            data = temp;

            if(data > 0x7FFF)
            {
                return -6;
            }

            error = MLX90614_SendCommand(slaveAddr, 0x60);

            if(error == 0)
            {                
                error = MLX90614_SMBusWrite(slaveAddr, 0x24, 0x0000);

                if(error == 0)
                {
                    error = MLX90614_SMBusWrite(slaveAddr, 0x24, newE);

                    if(error == 0)
                    {
                        error = MLX90614_SMBusWrite(slaveAddr, 0x2F, 0x0000);
                        if(error == 0)
                        {
                            error = MLX90614_SMBusWrite(slaveAddr, 0x2F, data);

                            if(error == 0)
                            {
                                error = MLX90614_SendCommand(slaveAddr, 0x61);
                            }
                        }       
                    }
                }       
            }        
        }            
    }

    return error; 
}
qsjhyy commented 1 year ago

I have revisited the previous datasheet (https://dfimg.dfrobot.com/nobody/wiki/05ea64977eabd2a2fdbcb0a6a4a1ef1e.pdf) and compared it with the manual you mentioned. I noticed that the previous manual mentions the "MLX90614 family" but indeed does not include the C series of sensors. image

For A, B, and D types of the MLX90614, it appears that register 0x0F is a reserved register. image

I was using the MLX90614-DCI (https://wiki.dfrobot.com/IR_Thermometer_Sensor_MLX90614_SKU__SEN0206) for testing, and the emissivity setting functionality worked correctly.

Therefore, to ensure that the module I am testing, the MLX90614-DCI, continues to function correctly, I may not make changes to the existing code. Thank you for this information. Perhaps, in the future, I should consider adding some comments to prevent users of the C series from using the emissivity setting feature when using this library.

CrouchingPanda commented 1 year ago

Okay. Your MLX90614-DCI is a "C series device" per the datasheet:

MLX90614-xCx 
MLX90614-DCI

Per the datasheet "C" in that position refers to the chip's correction for internal thermal gradients. I presume that is why one needs to set another register for correct temperature measurement. This is specifically for narrower FOV models - like yours.

I am not making a point that the current method is not working. I am saying that per the application note for your type of device in addition to changing 0x04, which you already do, and which stays unchanged, you possibly need to add the rest of what is described on pages 4 & 5 of the application note. Point being - I just don't know whether the device works exactly how it should if both registers are not changed as described in the note.

qsjhyy commented 1 year ago

Thank you again for your information! However, after conducting some tests, I've encountered several issues:

  1. The unlock command 0x60 and lock command 0x61 don't seem to be having the intended effect. Even after sending the lock command, I'm still able to modify the data at the EEPROM address 0x0f.

  2. In the latest version of the datasheet (https://www.melexis.com/en/documents/documentation/datasheets/datasheet-mlx90614), I still couldn't find any relevant descriptions regarding these two commands.

  3. Regarding the actual effect of address 0x0f, I haven't found any corresponding descriptions either. I can only deduce some insights from my modification tests. However, I can't even be sure about what the value at 0x0f should be when 0x04 is set to 0xFFFF (emissivity 1.0).

Due to these issues, I can't directly modify the library code since I can't confirm whether it's the correct approach. In the past, I relied on the chip's manual and actual test results. So, encountering a situation like this, where the chip's manual doesn't provide detailed information, and I can't find reliable sources online to confirm my test results, is quite frustrating.

Do you have more detailed information that can help me address these current issues, or do I have to wait for Melexis official personnel to provide answers before making progress?

CrouchingPanda commented 1 year ago

Thanks for your feedback. I really appreciate your attention to this.

What I have been able to gather on this is as follows:

Additionally, I am very curious to see, if possible, what code you used for tests in point 1 above.

I am all for rectifying this. If it's a Melexis documentation error, it could be brough up to them, so long as we are sure their instructions have been followed exactly yet the expected result was not achieved.

qsjhyy commented 1 year ago

Thank you for the information you provided!

Regarding the first point you mentioned, I also noticed it. However, I believe that "Keep Factory Calibration" may not be suitable for all situations. After reading the description of emissivity settings in the manual, I think it's essential to inform more people who might use this feature. Moreover, based on my previous tests, it seems that this feature works well.

As for the second point, I saw it as well. However, the values in that example are different from the values of the sensor I'm using. It could be that these values are arbitrary or not universally applicable.

Lastly, I'm still using the MLX90614-DCI for testing. I added a function to my library to send commands, and I sent that locking command (0x61) to the MLX90614-DCI. I thought that if the lock was open by default, that's why I could write successfully. However, I found that I can still freely change the value of register 0x0f (my module defaults to 0x733, while the application note states it should be 0x99A).

I hope this clarifies the situation.


void DFRobot_MLX90614_I2C::sendCommand(uint8_t cmd)
{
  if (cmd != 0x60 && cmd != 0x61) {
    return;
  }

  unsigned char crc_write[3] = { (uint8_t)(_deviceAddr << 1), cmd, '\0' };   // the array prepared for calculating the check code

  _pWire->beginTransmission(_deviceAddr);
  _pWire->write(cmd);

  _pWire->write(crc8Polyomial107(crc_write, 2));
  _pWire->endTransmission();
}

void DFRobot_MLX90614::setEmissivityCorrectionCoefficient(float calibrationValue)
{
  if (calibrationValue > 1.0 || calibrationValue < 0.1) {
    return;
  }

  uint16_t emissivity = round(65535 * calibrationValue);
  DBG(emissivity, HEX);
  uint8_t buf[2] = { 0 };

  // sendCommand(0x61);   // lock key

  // writeReg(MLX90614_SMBUS_ADDR + 1, buf);
  // delay(10);

  // uint16_t temp = 0x700;
  // buf[0] = (temp & 0x00FF);
  // buf[1] = ( (temp & 0xFF00) >> 8 );
  // DBG((uint16_t)(buf[1] << 8) | buf[0], HEX);
  // writeReg(MLX90614_SMBUS_ADDR + 1, buf);
  // delay(10);

  writeReg(MLX90614_EMISSIVITY, buf);
  delay(10);

  buf[0] = (emissivity & 0x00FF);
  buf[1] = ( (emissivity & 0xFF00) >> 8 );
  DBG((uint16_t)(buf[1] << 8) | buf[0], HEX);
  writeReg(MLX90614_EMISSIVITY, buf);
  delay(10);

//   readReg(MLX90614_SMBUS_ADDR + 1, buf);
//   DBG((uint16_t)(buf[1] << 8) | buf[0], HEX);
//   delay(10);
//   readReg(MLX90614_EMISSIVITY, buf);
//   DBG((uint16_t)(buf[1] << 8) | buf[0], HEX);
}
CrouchingPanda commented 1 year ago

Thanks! A couple points in response:

  1. The fact that the datasheet says not to tinker with those specifc registers should not be the reason not to change them for any reason, since the manufacturer even published the documentation on how to adjust 0x0F and digital filters, etc. I think we both think we should be able to change the emissivity as we see fit however we see fit.
  2. Regarding the value of 0x0F, there is no guarantee of what it is supposed to be when you first buy the sensor. All the application note says is how to change the value based on the old value, the old value of 0x04 register and a new value for emissivity. The values in their example calculation, like 0x99A you mentioned, are example values.
  3. Your new method looks fine.
  4. 0x61 command is not documented anywhere at all, as far as my searches. 0x60 is documented, at least minimally. Perhaps you could ask Melexis to document what 0x61 command does. For example, I don't know how the internal state machine works on the chip, so maybe the command really does nothing until you have performed certain actions. Maybe you have to unlock first for the lock to happen, we just don't know, because it's not documented.
  5. Is it correct that 0x733 was the value of 0x0F on your chip before any changed were done to it?
  6. If 5 is true, then I would like to see if you could do the following at your convenience. Imagine the emissivity is 1.0. Then, from there do the exact calculations from the application note, changing both 0x0F and 0x04 to set the emissivity to, say, 0.90, then power cycle and ensure the new values are there. Then take temperature of something, preferably something hot and cold, black and white, metal and non-metal. With something hot try keeping the sensor real close to the object, to try and create thermal gradients in the package. Preferably tests should be very repeatable. Having recorded the data, set the value of 0x0F to the original value, power cycle, make sure it's there and then do the same tests again. Look at the results you get. This way we will be able to see whether changing 0x0F has any effect or not, and will be able to have a word with Melexis on this, depending on the outcome.
qsjhyy commented 1 year ago

Thanks!

For the fourth point, I've already tried it, and it doesn't seem to be the case.

For the fifth point, I got 0x733 on the first read, but I'm not sure if it's the initial value. Besides, I currently only have this one MLX90614-DCI sensor.

For the sixth point, that's exactly why I'm hesitating. After changing this value, I noticed that it does affect the temperature output, and the impact is not negligible. When measuring the temperature at the same location, I found that the temperature fluctuation range has become larger. As you mentioned, this test is quite complex, and without the relevant information to confirm whether its settings are viable, there can be many twists and turns. Perhaps it's best to wait for more detailed information from the official source. Alternatively, I might modify the library code this way and advise against modifying 0x0f when it's not well understood.

void DFRobot_MLX90614::setEmissivityCorrectionCoefficient(float calibrationValue, bool set0X0F)
{
  if (calibrationValue > 1.0 || calibrationValue < 0.1) {
    return;
  }
  uint16_t emissivity = round(65535 * calibrationValue);
  DBG(emissivity, HEX);

  uint8_t buf[2] = { 0 };   // Avoid endianness
  uint16_t curE = 0;
  uint16_t data = 0;
  if (set0X0F) {
    readReg(MLX90614_EMISSIVITY, buf);
    curE = TWO_BYTES_CONCAT(buf);
    DBG(curE, HEX);
    readReg(MLX90614_FOR_EMISSIVITY, buf);
    data = TWO_BYTES_CONCAT(buf);
    DBG(data, HEX);
    // https://github.com/melexis/mlx90614-library/blob/6ba8c8919db9512b6f92d99378386ae4ae822954/functions/MLX90614_API.cpp#L183
    data = round(((float)data / emissivity * curE));
    DBG(data, HEX);
    if (data > 0x7FFF) {
      return;
    }

    sendCommand(0x60);   // unlock key
    // sendCommand(0x61);   // lock key
  }

  memset(buf, 0, sizeof(buf));
  writeReg(MLX90614_EMISSIVITY, buf);   // 0x04
  delay(10);
  buf[0] = (emissivity & 0x00FF);
  buf[1] = ((emissivity & 0xFF00) >> 8);
  writeReg(MLX90614_EMISSIVITY, buf);
  delay(10);

  readReg(MLX90614_EMISSIVITY, buf);
  DBG(TWO_BYTES_CONCAT(buf), HEX);

  if (set0X0F) {
    memset(buf, 0, sizeof(buf));
    writeReg(MLX90614_FOR_EMISSIVITY, buf);   // 0x0F
    delay(10);

    buf[0] = (data & 0x00FF);
    buf[1] = ((data & 0xFF00) >> 8);
    writeReg(MLX90614_FOR_EMISSIVITY, buf);
    delay(10);

    readReg(MLX90614_FOR_EMISSIVITY, buf);
    DBG(TWO_BYTES_CONCAT(buf), HEX);

    sendCommand(0x61);   // lock key
  }
}
CrouchingPanda commented 1 year ago

I get it. Okay, so changing 0x0F does affect the readings. That's good to know. Well, for the model you have - DCI - the procedure is to recalculate both 0x04 and 0x0F per the application note. The fact the initial value 0x0F is not known does not allow to do what the note says, because you would have to play from the initial value of 0x0F when you go through the emissivity adjustment procedure for the first time. Perhaps you, being an owner of their device, have a right to find out from Melexis what default value 0x0F has for your model and then safely go from there.

The problem I am trying to solve is I need to switch to either DCI or DCH model for my application - so far I've been prototyping using BAA model, but it's FOV doesn't cut it. Both DCI and DCH are xCx type models, so the procedure to change emissivity is different from the model I used (which was simply changing 0x04 register). And the issue is that there is no Arduino library out there which would incorporate this emissivity adjustment procedure with both 0x0F and 0x04, as if no one buys xCx type models. Since this library attempts to be a driver specifically for DCI model, which is a xCx model, I think this is the place to get it fully implemented.

qsjhyy commented 1 year ago

Well. I'll try to check with the Melexis official and when I get around to it, maybe I'll re-test the module to make sure it works. Thank you very much!

CrouchingPanda commented 1 year ago

Yeah, okay, thank you! I think the issue should remain open at least until an answer from Melexis has been received.