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.

OPT3004: Can't change the sensor configuration

Part Number: OPT3004

Tool/software:

Hello, I am working in implementing the OPT3004 Ambient Light Sensor to the firmware of an ESP32-S3.

I was able to establish I2C connection reading the values of all registers just fine. However, when trying to change the configuration register it doesn't take effect.

Here is a picture of the logic analyzer when writing and then reading the configuration register:

The image doesn't have the best resolution for some reason, here is the data of the image:

Write 0x44 0x01 0xD0 0x7C

Then, the read procedure is: Write 0x44 0x01 Read 0x44 0xD0 0x1C,

Here is the code of my implementation when configuring:

static const uint8_t OPT3004_I2C_ADDR = 0x44; // I2C Address OPT3004
i2c_master_dev_handle_t opt3004_i2c_dev_handle;

typedef enum
{
  OPT3004_REG_RESULT = 0x00,
  OPT3004_REG_CONFIG = 0x01,
  OPT3004_REG_LO_THRESH = 0x02,
  OPT3004_REG_HI_THRESH = 0x03,
  OPT3004_REG_MANUFACTURER_ID = 0x7E,
  OPT3004_REG_DEVICE_ID = 0x7F
} opt3004_reg_addr_t;

typedef union{
  uint16_t reg;
  struct{
    uint16_t exponent:4;
    uint16_t fraction:12;
  } bytes;
} opt3004_reg_t;

typedef union{
  uint16_t conf;
  struct{
    uint16_t range_num_field:4; // R/W full scale range selection
    uint16_t conversion_time:1; // R/W time for conversion 0:100ms 1:800ms
    uint16_t conversion_mode:2; // R/W mode of conversion 00:shutdown 01:singleshot 10-11:continuous
    uint16_t overflow:1; // R 0:light between selected scale 1:light exceeds selected scale
    uint16_t conversion_ready:1; // R 0:conversion not ready 1:conversion ready
    uint16_t flag_high:1; // R 0:light below high threshold 1:light above high threshold
    uint16_t flag_low:1; // R 0:light above low threshold 1:light below low threshold
    uint16_t latch_field:1; // R/W 0:interrupt with no user-controlled clear 1:interrupt with user-controlled clear
    uint16_t polarity:1; // R/W 0:active low 1:active high
    uint16_t mask_exponent:1; // R/W 0:no change to exponent value of register 1:changes the value of exponent to 0000b when range is manually selected
    uint16_t fault_count:2; // R/W number of faults to trigger interrupt 00:1 fault 01:2 faults 10:4 faults 11:8 faults
  } bits;
} opt3004_conf_t;

opt3004_status_t opt3004_init(opt3004_t *opt3004_params)
{
  ESP_LOGI(TAG, "%s - Initializing OPT3004", __func__);
  if (opt3004_params == NULL)
  {
    ESP_LOGE(TAG, "%s - NULL Params", __func__);
    return OPT3004_ERR;
  }
  OPT3004_PARAMS = opt3004_params;
  //Init I2C
  if (opt3004_i2c_init_device() != OPT3004_OK)
  {
    ESP_LOGE(TAG, "%s - Error init I2C", __func__);
    return OPT3004_ERR;
  }
  //Read Manufacturer ID
  uint16_t manufacturerID = 0;
  if (opt3004_read_reg_i2c(OPT3004_REG_MANUFACTURER_ID, &manufacturerID, 0) != OPT3004_OK)
  {
    ESP_LOGE(TAG, "%s - Error reading Manufacturer ID", __func__);
    return OPT3004_ERR;
  }
  ESP_LOGI(TAG, "%s - Manufacturer ID: %04X", __func__, manufacturerID);
  //Read Device ID
  uint16_t deviceID = 0;
  if (opt3004_read_reg_i2c(OPT3004_REG_DEVICE_ID, &deviceID, 0) != OPT3004_OK)
  {
    ESP_LOGE(TAG, "%s - Error reading Device ID", __func__);
    return OPT3004_ERR;
  }
  ESP_LOGI(TAG, "%s - Device ID: %04X", __func__, deviceID);
  //Read Config
  uint16_t conf_reg = 0;
  if (opt3004_read_reg_i2c(OPT3004_REG_CONFIG, &conf_reg, 0) != OPT3004_OK)
  {
    ESP_LOGE(TAG, "%s - Error reading Config", __func__);
    return OPT3004_ERR;
  }
  ESP_LOGI(TAG, "%s - Config register %04X", __func__, conf_reg);
  opt3004_conf_t conf;
  conf.conf = conf_reg;
  ESP_LOGI(TAG, "%s - Configuration", __func__);
  ESP_LOGI(TAG, "%s - Range: %s", __func__, conf.bits.range_num_field == 0b1100 ? "Automatic" : "Manual");
  ESP_LOGI(TAG, "%s - Convertion time: %s", __func__, conf.bits.conversion_time == 0b1 ? "800ms" : "100ms");
  ESP_LOGI(TAG, "%s - Conversion mode: %s", __func__, conf.bits.conversion_mode == 0b00 ? "Shutdown" : conf.bits.conversion_mode == 0b01 ? "One shot" : "Continuos conversion");
  ESP_LOGI(TAG, "%s - Latch: %s", __func__, conf.bits.latch_field == 0b0 ? "Transparent Hysteresis" : "Window Hysteresis");
  ESP_LOGI(TAG, "%s - Polarity: %s", __func__, conf.bits.polarity == 0b0 ? "Active Low Interrupt" : "Active High Interrupt");
  ESP_LOGI(TAG, "%s - Exponent: %s", __func__, conf.bits.mask_exponent == 0b0 ? "No change to exponent value of register" : "Change to exponent value of register");
  ESP_LOGI(TAG, "%s - Fault count: %s", __func__, conf.bits.fault_count == 0b00 ? "1 fault" : conf.bits.fault_count == 0b01 ? "2 faults" : conf.bits.fault_count == 0b10 ? "4 faults" : "8 faults");
  ESP_LOGI(TAG, "%s - Flag high: %d", __func__, conf.bits.flag_high);
  ESP_LOGI(TAG, "%s - Flag low: %d", __func__, conf.bits.flag_low);
  ESP_LOGI(TAG, "%s - Convertion overflow: %d", __func__, conf.bits.overflow);
  ESP_LOGI(TAG, "%s - Convertion ready: %d", __func__, conf.bits.conversion_ready);
  //New configuration
  conf.bits.range_num_field = 0b1100; // Automatic full-scale setting
  conf.bits.conversion_time = 0b1; // 800ms conversion time
  conf.bits.conversion_mode = 0b11; // Continuous conversion mode
  ESP_LOGI(TAG, "New Config %04X", conf.conf);
  //Write new configuration
  if (opt3004_escribir_reg_i2c(OPT3004_REG_CONFIG, conf.conf) != OPT3004_OK)
  {
    ESP_LOGE(TAG, "%s - Error writing new configuration", __func__);
    return OPT3004_ERR;
  }
  //Read new configuration
  if (opt3004_read_reg_i2c(OPT3004_REG_CONFIG, &conf_reg, 0) != OPT3004_OK)
  {
    ESP_LOGE(TAG, "%s - Error al leer registro de configuración", __func__);
    return OPT3004_ERR;
  }
  ESP_LOGI(TAG, "%s - New config Read %04X", __func__, conf_reg);
  ESP_LOGI(TAG, "%s - Sensor OPT3004 initialized", __func__);
  return OPT3004_OK;
}

Here is the logged data of the ESP32-S3 firmware when interacting with the OPT3004:

I (8505) OPT3004: opt3004_init - Initializing OPT3004
I (8575) OPT3004: opt3004_init - Manufacturer ID: 5449
I (8615) OPT3004: opt3004_init - Device ID: 3001
I (8625) OPT3004: opt3004_init - Config register D01C
I (8635) OPT3004: opt3004_init - Configuration
I (8635) OPT3004: opt3004_init - Range: Automatic
I (8645) OPT3004: opt3004_init - Convertion time: 800ms
I (8645) OPT3004: opt3004_init - Convertion mode: Shutdown
I (8655) OPT3004: opt3004_init - Latch: Transparent Hysteresis
I (8655) OPT3004: opt3004_init - Polarity: Active High Interrupt
I (8665) OPT3004: opt3004_init - Exponent: No change to exponent value of register
I (8675) OPT3004: opt3004_init - Fault count: 8 faults
I (8675) OPT3004: opt3004_init - Flag high: 0
I (8685) OPT3004: opt3004_init - Flag low: 0
I (8685) OPT3004: opt3004_init - Convertion overflow: 0
I (8685) OPT3004: opt3004_init - Convertion ready: 0
I (8695) OPT3004: New Config D07C
I (8705) OPT3004: opt3004_init - New config Read D01C
I (8705) OPT3004: opt3004_init - Sensor OPT3004 initialized


I'm not sure what to do in order to change the conversion mode from "shutdown" to "continuous".

I hope you can help me figure out what is causing this issue.

  • Hello Ivan, 

    I think you may not be properly addressing the write command. 

    try 0x45 on the write 

    Thanks, 

    Joseph Scherphorn

  • Welp, the issue was the union I used to parse the config data, I had the same problem when parsing the result data. Here is the snippet with both union corrected:

    typedef union{
      uint16_t reg;
      struct{
        uint16_t fraction:12;
        uint16_t exponent:4;
      } bytes;
    } opt3004_reg_t;
    
    typedef union{
      uint16_t conf;
      struct{
        uint16_t fault_count:2; // R/W number of faults to trigger interrupt 00:1 fault 01:2 faults 10:4 faults 11:8 faults
        uint16_t mask_exponent:1; // R/W 0:no change to exponent value of register 1:changes the value of exponent to 0000b when range is manually selected
        uint16_t polarity:1; // R/W 0:active low 1:active high
        uint16_t latch_field:1; // R/W 0:interrupt with no user-controlled clear 1:interrupt with user-controlled clear
        uint16_t flag_low:1; // R 0:light above low threshold 1:light below low threshold
        uint16_t flag_high:1; // R 0:light below high threshold 1:light above high threshold
        uint16_t conversion_ready:1; // R 0:conversion not ready 1:conversion ready
        uint16_t overflow:1; // R 0:light between selected scale 1:light exceeds selected scale
        uint16_t conversion_mode:2; // R/W mode of conversion 00:shutdown 01:singleshot 10-11:continuous
        uint16_t conversion_time:1; // R/W time for conversion 0:100ms 1:800ms
        uint16_t range_num_field:4; // R/W full scale range selection
      } bits;
    } opt3004_conf_t;

    It's all about the endianess of the bytes, the sensor sends MSB First and the ESP32-S3 firmware works with LSB First....

    Also, I had a bit of an overflow destroying the data buffer in the read operation:

    *res = ((uint16_t)result[0] << 8) | result[1];

    I wasn't casting the "result[0]" to uint16_t, which destroyed the entire buffer for some obvious reason.

  • Thank you for following up and sharing your findings.