I am struggling to find a real conversion formula for converting the RAW value into result that makes sense. I am using an OPT3001 sensor from TI and ATTiny25 as MUC.

I have found one example online, but it does not give the correct value in Lux, the equation used there is this:

/*Convert to LUX*/
//extract result & exponent data from raw readings
result = raw&0x0FFF;
exponent = (raw>>12)&0x000F;

This gives wrong values according to my Lux meter that I have as refference and, for example, lux meter says its 420 lux inside Office.. the equation above gives me the result 110 Lux, which is not realistic. Sensor is working fine as I replaced it new one, in order to be 100% that it is not sensor related issue.

The result is very close if I taked only result = raw&0x0FFF; , but then it fails at higher values (when I put my sensor under the Lamp light bulb).

I do not think it's an error inside my I2C code, but I will attach here also the part of my coude for UpdateLight:

// I2C OPT address
#define OPT_ADDR_W  0x88
#define OPT_ADDR_R  0x89

// I2C OPT registers
#define OPT_RESULT_REG  0x00
#define OPT_CONFIG_REG  0x01

// OPT config bits - automatic full scale mode, 100 ms conversion, single-shot

void UpdateLight(void)
    uint8_t error = 0;
    // start conversion
    error |= I2C_Write(OPT_ADDR_W);
    error |= I2C_Write(OPT_CONFIG_REG);
    error |= I2C_Write(OPT_CONFIGURATION_H);
    error |= I2C_Write(OPT_CONFIGURATION_L);

    // wait for conversion

    // set result register
    error |= I2C_Write(OPT_ADDR_W);
    error |= I2C_Write(OPT_RESULT_REG);

    // read data and update scratchpad
    error |= I2C_Write(OPT_ADDR_R);
    if (error == 0)
        scratchpad[1] = I2C_Read(1);
        scratchpad[0] = I2C_Read(0);
        // report error value
        scratchpad[1] = 0xFF;
        scratchpad[0] = 0xFF;

And this is my old formula that I came up with, but it gives even more ridiculous results than the above one from the internet:

   // calculate light ( LUX = (MANTISA << EXPONENT) / 100 )
   light = (int16_t)((float)((uint32_t)((spad[SPAD_MSB] << 8) | spad[SPAD_LSB]) << (uint32_t)((spad[SPAD_MSB] >> 4) & 0x0F)) / (83865.60 / LIGHT_MULTIPLIER));
   debug_printf("L_RAW: %02X%02X\r\n", spad[SPAD_MSB], spad[SPAD_LSB]);
   debug_printf("L_VAL: %d\r\n", light); 

Adding the output lines of the above print statements:

L: 5460 - 824

L: 5464 - 824

L: 5462 - 824

L: 5462 - 824

The first value should be RAW MSB and LSB, and the second is the converted value to Lux, using the formula above this.

Moreover, I am surprised that inside the Datasheet, TI did not provide a conversion formula ?

Any help is greatly appreaciated.

  • \$\begingroup\$ What is the raw value you are getting? BTW, it looks(by the conversion example) that the representation is actually a standard floating point one.. \$\endgroup\$ – Eugene Sh. Aug 17 '15 at 14:40
  • \$\begingroup\$ @EugeneSh. Raw1: 5463 , Raw2: 5461 taken at Office Light ambient. The equation above gives me result 148.16Lux, which is incorrect. More closer to real result is if I only take result=raw&0x0FFF ... However, this one then fails at higher brightnes (lamp shining above it). Weird.. \$\endgroup\$ – David Kasabji Aug 17 '15 at 14:53
  • \$\begingroup\$ The numbers look suspicious as 5461 in hex is 0x1555 which are alternating zeros and ones. Could it be that you have swapped the SDA and SCL? \$\endgroup\$ – Eugene Sh. Aug 17 '15 at 14:55
  • \$\begingroup\$ @EugeneSh. hm. Could be, but that I need to check in lab again, wasn't expecting that kind of issue, was hopping it was software oriented. here is one raw example under light: 8437 (hex) \$\endgroup\$ – David Kasabji Aug 17 '15 at 14:57
  • \$\begingroup\$ Oh well, if you have some different values, I guess the connection should be fine. \$\endgroup\$ – Eugene Sh. Aug 17 '15 at 14:58

On page 20 of the datasheet the detailed description of the calculation is given. From the given example value from the comments 0x5461 we are getting the 0x5 for the exponent value, which is corresponding to 0.32 lux per LSB. The mantissa is 0x461 or 1121 in decimal. The resulting value is 1121*0.32=358.72lux which is close to the expected range.

The working conversion code:

#include <stdio.h>
#include <stdint.h>

int main(void) {
    uint16_t raw = 0x5461;
    uint16_t result, exponent;
    float lux;

    result = raw & 0x0FFF;
    exponent = (raw>>12) & 0x000F;


    printf("%f\n", lux);
    return 0;

Or run it here.

Please pay a close attention to the datatypes used, they might have causing trouble to you as well..

| improve this answer | |
  • \$\begingroup\$ so you think that the value 5461¸and others are not HEX values ? You consider them decimal values ? I thought these were HEX values \$\endgroup\$ – David Kasabji Aug 17 '15 at 15:32
  • \$\begingroup\$ I'm still wondering where that 110 lux are coming from, if I put in the wrong values I get only 27 lux... But yeah endianness is a big trap in data transfer, I guess everyone stumbles upon that. \$\endgroup\$ – Arsenal Aug 17 '15 at 15:32
  • \$\begingroup\$ I don't know, you tell me. How are you reading them? \$\endgroup\$ – Eugene Sh. Aug 17 '15 at 15:32
  • \$\begingroup\$ I have added the part of the code where I convert the raw value and then print it out. \$\endgroup\$ – David Kasabji Aug 17 '15 at 15:36
  • \$\begingroup\$ @AndroidNFC if there is no indication (like hex or 0x or _h) most people will read them as decimal. If you take those values as hex, you end up with 360 lux and not 148 lux as stated in your comment. \$\endgroup\$ – Arsenal Aug 17 '15 at 15:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.