This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

OPT4048: OPT4048DTSR

Part Number: OPT4048

Tool/software:

I'm working on the OPT4048 Light sensor, for light luminance and CCT values. I'm reading 0x0,0x2,0x04, 0x06 registers and conversion steps I'm following as below 

I2C_TransferReturn_TypeDef ret = I2C_transaction(I2C_FLAG_WRITE_READ, OPT4048_I2C_BUS_ADDRESS, &reg, 1, data, 3);

if (ret != i2cTransferDone) {

*channels[i] = 0;

return false;

}

uint8_t exponent = (data[0] >> 4) & 0x0F;

uint32_t mantissa = ((data[0] & 0x0F) << 16) | (data[1] << 8) | data[2];

*channels[i] = mantissa * 0.00215f * pow(2, exponent);

I'm receiving light data from channel 0 and using channel 1,2,3 data for CCT calculation.

But the issue I'm facing is that the readings are constant every time, even when the light intensity is changed.

When i restart the device, the correct readings are updated. In the configuration register, I'm using continuous mode, writing to config reg with {0x0A, 0x0C, 0x9F}

Do I need to add more delay or should I use the interrupt pin to get the updated readings?

  • Hi Gayathri,

    I have a couple of questions:

    1. Could you please send the rest of the configuration? It seems as though part of the configuration you were listing was cut off in your text. Could you send me the values of registers 0x0A and 0x0B?

    2. You mentioned that the readings are constant, even when light intensity is changed. Is this true for the first couple of samples only? Does it eventually send updated readings?

    It could be the case that you are reading the sensor output before the sensor has enough time to update, resulting in multiple duplicate readings. In this case, adding more delay or using the interrupt pin could work. 

    I think the best option here would be to enable the interrupt to trigger at the end of each conversion. That way, the MCU is notified when a conversion is complete and initiate an I2C read.

    Thanks,
    Daniel

  • Hi Daniel, 

    I'm sharing my code snippet. I haven't used 0x0B register in my code. Values are remaining the same whole time not just for few samples. Also, read function is called after every 10s. 

    Thanks,

    Gayathri

     /*
     * ALS_OPT4048.cpp
     *
     */
    
    #include <math.h>
    #include "AppEvent.h"
    #include "TextOnlyLogging.h"
    
    
    // Add this to the top of your ALS_OPT4048.cpp file:
    #include <stdio.h>
    #include "ALS_OPT4048.h"
    #include "I2C_sensors.h"
    #include "sl_sleeptimer.h"
    #include "sl_i2cspm.h"
     #define OPT4048_I2C_BUS_ADDRESS OPT4048_ADDR_GND
    
    
    // Improved initialization with better error checking
    bool OPT4048::init()
    {
        ChipLogDetail(AppServer, "Starting OPT4048 initialization...");
    
        if (!checkDeviceID()) {
            ChipLogError(AppServer, "OPT4048 Device ID check failed!");
            return false;
        }
    
        ChipLogDetail(AppServer, "OPT4048 Device ID verified successfully");
    
        uint8_t reset[3] = { OPT4048_REG_CONFIG, 0x00, 0x01 };
        if (I2C_transaction(I2C_FLAG_WRITE, OPT4048_I2C_BUS_ADDRESS, reset, 3, nullptr, 0) != i2cTransferDone) {
            ChipLogError(AppServer, "OPT4048 soft reset failed");
            return false;
        }
    
        sl_sleeptimer_delay_millisecond(100);
    
        uint8_t config[3] = { OPT4048_REG_CONFIG, 0x0C, 0x9F };
        if (I2C_transaction(I2C_FLAG_WRITE, OPT4048_I2C_BUS_ADDRESS, config, 3, nullptr, 0) != i2cTransferDone) {
            ChipLogError(AppServer, "OPT4048 configuration failed");
            return false;
        }
    
        ChipLogDetail(AppServer, "OPT4048 configured, waiting for data...");
        sl_sleeptimer_delay_millisecond(100);  // Increased for stabilization
    
        debugReadAllRegisters();
        return true;
    }
    
    bool OPT4048::checkDeviceID()
    {
        uint8_t reg = OPT4048_REG_DEVICE_ID;
        uint8_t data[2];
    
        if (I2C_transaction(I2C_FLAG_WRITE_READ, OPT4048_I2C_BUS_ADDRESS, &reg, 1, data, 2) != i2cTransferDone)
            return false;
    
        bool idValid = ((data[0] == OPT4048_DEVICE_ID_L) && (data[1] == OPT4048_DEVICE_ID_H));
        if (!idValid)
            ChipLogError(AppServer, "Device ID mismatch: 0x%02X 0x%02X", data[0], data[1]);
    
        return idValid;
    }
    
    // Debug function to read and display all important registers
    bool OPT4048::debugReadAllRegisters()
    {
        uint8_t reg;
        uint8_t data[3];
        I2C_TransferReturn_TypeDef ret;
    
        // Read Device ID
        reg = OPT4048_REG_DEVICE_ID;
        ret = I2C_transaction(I2C_FLAG_WRITE_READ, OPT4048_I2C_BUS_ADDRESS, &reg, 1, data, 2);
        if (ret == i2cTransferDone) {
            uint16_t deviceID = (data[0] << 8) | data[1];
            ChipLogDetail(AppServer, "Device ID: 0x%04X (expected: 0x8021)", deviceID);
        } else {
            ChipLogError(AppServer, "Failed to read Device ID, I2C error: %d", ret);
            return false;
        }
    
        // Read Configuration Register
        reg = OPT4048_REG_CONFIG;
        ret = I2C_transaction(I2C_FLAG_WRITE_READ, OPT4048_I2C_BUS_ADDRESS, &reg, 1, data, 2);
        if (ret == i2cTransferDone) {
            uint16_t config = (data[0] << 8) | data[1];
            ChipLogDetail(AppServer, "Config Register: 0x%04X", config);
            ChipLogDetail(AppServer, "  - Mode: %d (0=powerdown, 1=forced, 2=oneshot, 3=continuous)",
                         (config >> 2) & 0x03);
            ChipLogDetail(AppServer, "  - Conversion Time: %d", (config >> 4) & 0x0F);
            ChipLogDetail(AppServer, "  - Range: %d", config & 0x0F);
        } else {
            ChipLogError(AppServer, "Failed to read Config Register");
        }
    
        // Read all channel registers
        // Use correct MSB+LSB pair per channel
        const uint8_t channelRegs[4][2] = {
            { 0x00, 0x01 },  // CH0
            { 0x02, 0x03 },  // CH1
            { 0x04, 0x05 },  // CH2
            { 0x06, 0x07 }   // CH3
        };
    
    
    
        for (int i = 0; i < 4; i++) {
    
            uint8_t upper = 10, lower = 20;
    
            // Read MSB + exponent
            reg = channelRegs[i][0];
            ret = I2C_transaction(I2C_FLAG_WRITE_READ, OPT4048_I2C_BUS_ADDRESS, &reg, 1, &upper, 1);
            if (ret != i2cTransferDone) continue;
    
            // Optional short delay (some chips need > tBUF between register reads)
            sl_sleeptimer_delay_millisecond(1);
    
            // Read LSB + CRC
            reg = channelRegs[i][1];
            ret = I2C_transaction(I2C_FLAG_WRITE_READ, OPT4048_I2C_BUS_ADDRESS, &reg, 1, &lower, 1);
            if (ret != i2cTransferDone) continue;
    
            uint8_t exponent = (upper >> 4) & 0x0F;
            uint16_t mantissa = ((upper & 0x0F) << 8) | (lower >> 4);
            float lux = mantissa * 0.00215f * pow(2, exponent);
    
            ChipLogDetail(AppServer, "CH%d Lux: %.2f (Exp=%u, Mant=%u)", i, lux, exponent, mantissa);
        }
    
    
        return true;
    }
    
    
    
    /*float OPT4048::readChannels(float &ch0, float &ch1, float &ch2, float &ch3)
    {// Optional: Wait for conversion to complete
      sl_sleeptimer_delay_millisecond(120);  // Match your conversion time
    
      // Read 3 bytes from CH3 MSB register (0x06)
      uint8_t reg = 0x06;
      uint8_t data[3];
      sl_sleeptimer_delay_millisecond(500);  // Between reads
      I2C_TransferReturn_TypeDef ret = I2C_transaction(I2C_FLAG_WRITE_READ, OPT4048_I2C_BUS_ADDRESS, &reg, 1, data, 3);
      if (ret != i2cTransferDone)
          return false;
    
      uint8_t msb = data[0];
      uint8_t mid = data[1];
      uint8_t lsb = data[2];
    
      //uint8_t exponent = (msb >> 4) & 0x0F;
      uint32_t mantissa = ((msb & 0x0F) << 16) | (mid << 8) | lsb;
    
      // Recommended lux calculation (datasheet Equation 9)
      float lux = mantissa * 0.00215f;
    
      // Store or scale as needed
      Luminance_Val = lux;
    
      return Luminance_Val;
    }*/
    
    bool OPT4048::readAllChannels(float & ch0, float & ch1, float & ch2, float & ch3)
    {
        const uint8_t channelRegs[4] = { 0x00, 0x02, 0x04, 0x06 };
        float * channels[4] = { &ch0, &ch1, &ch2, &ch3 };
    
        for (int i = 0; i < 4; i++)
        {
            uint8_t reg = channelRegs[i];
            uint8_t data[3] = {0};
    
            I2C_TransferReturn_TypeDef ret = I2C_transaction(I2C_FLAG_WRITE_READ, OPT4048_I2C_BUS_ADDRESS, &reg, 1, data, 3);
            if (ret != i2cTransferDone) {
                *channels[i] = 0;
                return false;
            }
    
            uint8_t exponent = (data[0] >> 4) & 0x0F;
            uint32_t mantissa = ((data[0] & 0x0F) << 16) | (data[1] << 8) | data[2];
            *channels[i] = mantissa * 0.00215f * pow(2, exponent);
        }
        sl_sleeptimer_delay_millisecond(500);  // Increased for stabilization
    
        return true;
    }
    
    bool OPT4048::calculateCCT(float ch1, float ch2, float ch3, float & cie_x, float & cie_y, float & cct)
    {
        float X = 0.4124f * ch1 + 0.3576f * ch2 + 0.1805f * ch3;
        float Y = 0.2126f * ch1 + 0.7152f * ch2 + 0.0722f * ch3;
        float Z = 0.0193f * ch1 + 0.1192f * ch2 + 0.9505f * ch3;
    
        float sum = X + Y + Z;
        if (sum == 0.0f) {
            cie_x = cie_y = cct = 0.0f;
            return false;
        }
    
        cie_x = X / sum;
        cie_y = Y / sum;
    
        float n = (cie_x - 0.3320f) / (0.1858f - cie_y);
        cct = 449.0f * powf(n, 3) + 3525.0f * powf(n, 2) + 6823.3f * n + 5520.33f;
        return true;
    }
    
    
    

  • Hi Gayathri,

    Could you please point out the section of code where the device configuration is being set? I don't see a I2C write to the configuration register 0x0A. Could you please give me the contents of the 0x0A register? 

    Thanks,

    Daniel

  • uint8_t config[3] = { 0x0A, 0x0C, 0x9F };

    I2C_transaction(I2C_FLAG_WRITE, OPT4048_I2C_BUS_ADDRESS, config, 3, nullptr, 0)

  • Hi Gayathri,

    It looks like you are writing 0x0C9F to the configuration register, correct? In that case, the operation mode bits (5-4) are actually 01, which means you are in oneshot mode and not continuous mode. This also explains why you are only seeing the registers update to the correct value after you reset the device. When you resend the device configuration, you are triggering a oneshot measurement which updates these registers to reflect the new light intensity.

    Thanks,

    Daniel

  • Thank you issue was with the 0x0A register all along. I changed it to 0x3230 after rechecking the datasheet. My values are updating fine.

    Thanks,

    Gayathri