Page 1 of 1

SI1145 wrong reads in ESP-IDF v.5.3.0

Posted: Thu May 29, 2025 4:32 pm
by Ivcm55
Hello!
I am trying to read the values of UV, VIS and IR from the SI1145 (I2C device), but I get wrong results in a dark room with low level of light. I made the SI1145 component similar to Arduino's library for this Adafuit's sensor.

I am coding on Visual Studio with ESP-IDF v.5.3.0

Any suggestions to read properly the sensor?? Thank you so much.

I attach an image with the results:
si1145_reads.png
si1145_reads.png (11.66 KiB) Viewed 141 times
And here is my code:

i2c_config component:
- i2c_config.h:

Code: Select all

#ifndef _I2C_CONFIG_H
#define _I2C_CONFIG_H

#define I2C_MASTER_SCL_IO                           CONFIG_I2C_MASTER_SCL_IO                /* GPIO number used for I2C master clock */
#define I2C_MASTER_SDA_IO                           CONFIG_I2C_MASTER_SDA_IO                /* GPIO number used for I2C master data  */
#define I2C_MASTER_NUM                              1                                       /* I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
#define I2C_MASTER_FREQ_HZ                          100000                                  /* I2C master clock frequency */
#define I2C_MASTER_TX_BUF_DISABLE                   0                                       /* I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE                   0                                       /* I2C master doesn't need buffer */
#define I2C_MASTER_TIMEOUT_MS                       1000

#define ACK_CHECK_EN                                0x1                                     /* I2C master will check ack from slave*/
#define ACK_CHECK_DIS                               0x0                                     /* I2C master will not check ack from slave */
#define ACK_VAL                                     0x0                                     /* I2C ack value */
#define NACK_VAL                                    0x1                                     /* I2C nack value */

void i2c_master_init();

#endif /* _I2C_CONFIG_H */
- i2c_config.c:

Code: Select all

#include "driver/i2c.h"
#include "i2c_config.h"

void i2c_master_init()
{
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = I2C_MASTER_FREQ_HZ,
    };

    i2c_param_config(I2C_MASTER_NUM, &conf);

    i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}
SI1145 component:
- si1145.h:

Code: Select all

#ifndef _SI1145_H_
#define _SI1145_H_

/* ADDRESS */
#define SI1145_ADDR                         0x60        

/* REGISTERS */
#define SI1145_REG_PART_ID                  0x00
#define SI1145_REG_REV_ID                   0x01
#define SI1145_REG_SEQ_ID                   0x02

#define SI1145_REG_INTCFG                   0x03
#define SI1145_REG_INTCFG_INTOE             0x01
#define SI1145_REG_INTCFG_INTMODE           0x02

#define SI1145_REG_IRQEN                    0x04
#define SI1145_REG_IRQEN_ALSEVERYSAMPLE     0x01
#define SI1145_REG_IRQEN_PS1EVERYSAMPLE     0x04
#define SI1145_REG_IRQEN_PS2EVERYSAMPLE     0x08
#define SI1145_REG_IRQEN_PS3EVERYSAMPLE     0x10

#define SI1145_REG_IRQMODE1                 0x05
#define SI1145_REG_IRQMODE2                 0x06

#define SI1145_REG_HWKEY                    0x07
#define SI1145_REG_MEASRATE0                0x08
#define SI1145_REG_MEASRATE1                0x09
#define SI1145_REG_PSRATE                   0x0A
#define SI1145_REG_PSLED21                  0x0F
#define SI1145_REG_PSLED3                   0x10
#define SI1145_REG_UCOEF0                   0x13
#define SI1145_REG_UCOEF1                   0x14
#define SI1145_REG_UCOEF2                   0x15
#define SI1145_REG_UCOEF3                   0x16
#define SI1145_REG_PARAMWR                  0x17
#define SI1145_REG_COMMAND                  0x18
#define SI1145_REG_RESPONSE                 0x20
#define SI1145_REG_IRQSTAT                  0x21
#define SI1145_REG_IRQSTAT_ALS              0x01

#define SI1145_REG_ALSVISDATA0              0x22
#define SI1145_REG_ALSVISDATA1              0x23
#define SI1145_REG_ALSIRDATA0               0x24
#define SI1145_REG_ALSIRDATA1               0x25
#define SI1145_REG_PS1DATA0                 0x26
#define SI1145_REG_PS1DATA1                 0x27
#define SI1145_REG_PS2DATA0                 0x28
#define SI1145_REG_PS2DATA1                 0x29
#define SI1145_REG_PS3DATA0                 0x2A
#define SI1145_REG_PS3DATA1                 0x2B
#define SI1145_REG_UVINDEX0                 0x2C
#define SI1145_REG_UVINDEX1                 0x2D
#define SI1145_REG_PARAMRD                  0x2E
#define SI1145_REG_CHIPSTAT                 0x30

/* PARAMETERS */
#define SI1145_PARAM_I2CADDR                0x00
#define SI1145_PARAM_CHLIST                 0x01
#define SI1145_PARAM_CHLIST_ENUV            0x80
#define SI1145_PARAM_CHLIST_ENAUX           0x40
#define SI1145_PARAM_CHLIST_ENALSIR         0x20
#define SI1145_PARAM_CHLIST_ENALSVIS        0x10
#define SI1145_PARAM_CHLIST_ENPS1           0x01
#define SI1145_PARAM_CHLIST_ENPS2           0x02
#define SI1145_PARAM_CHLIST_ENPS3           0x04

#define SI1145_PARAM_PSLED12SEL             0x02
#define SI1145_PARAM_PSLED12SEL_PS2NONE     0x00
#define SI1145_PARAM_PSLED12SEL_PS2LED1     0x10
#define SI1145_PARAM_PSLED12SEL_PS2LED2     0x20
#define SI1145_PARAM_PSLED12SEL_PS2LED3     0x40
#define SI1145_PARAM_PSLED12SEL_PS1NONE     0x00
#define SI1145_PARAM_PSLED12SEL_PS1LED1     0x01
#define SI1145_PARAM_PSLED12SEL_PS1LED2     0x02
#define SI1145_PARAM_PSLED12SEL_PS1LED3     0x04

#define SI1145_PARAM_PSLED3SEL              0x03
#define SI1145_PARAM_PSENCODE               0x05
#define SI1145_PARAM_ALSENCODE              0x06

#define SI1145_PARAM_PS1ADCMUX              0x07
#define SI1145_PARAM_PS2ADCMUX              0x08
#define SI1145_PARAM_PS3ADCMUX              0x09
#define SI1145_PARAM_PSADCOUNTER            0x0A
#define SI1145_PARAM_PSADCGAIN              0x0B
#define SI1145_PARAM_PSADCMISC              0x0C
#define SI1145_PARAM_PSADCMISC_RANGE        0x20
#define SI1145_PARAM_PSADCMISC_PSMODE       0x04

#define SI1145_PARAM_ALSIRADCMUX            0x0E
#define SI1145_PARAM_AUXADCMUX              0x0F

#define SI1145_PARAM_ALSVISADCOUNTER        0x10
#define SI1145_PARAM_ALSVISADCGAIN          0x11
#define SI1145_PARAM_ALSVISADCMISC          0x12
#define SI1145_PARAM_ALSVISADCMISC_VISRANGE 0x20

#define SI1145_PARAM_ALSIRADCOUNTER         0x1D
#define SI1145_PARAM_ALSIRADCGAIN           0x1E
#define SI1145_PARAM_ALSIRADCMISC           0x1F
#define SI1145_PARAM_ALSIRADCMISC_RANGE     0x20

#define SI1145_PARAM_ADCCOUNTER_511CLK      0x70

#define SI1145_PARAM_ADCMUX_SMALLIR         0x00
#define SI1145_PARAM_ADCMUX_LARGEIR         0x03

/* COMMANDS */
#define SI1145_PARAM_QUERY                  0x80
#define SI1145_PARAM_SET                    0xA0
#define SI1145_NOP                          0x00
#define SI1145_RESET                        0x01
#define SI1145_BUSADDR                      0x02
#define SI1145_PS_FORCE                     0x05
#define SI1145_ALS_FORCE                    0x06
#define SI1145_PSALS_FORCE                  0x07
#define SI1145_PS_PAUSE                     0x09
#define SI1145_ALS_PAUSE                    0x0A
#define SI1145_PSALS_PAUSE                  0xB
#define SI1145_PS_AUTO                      0x0D
#define SI1145_ALS_AUTO                     0x0E
#define SI1145_PSALS_AUTO                   0x0F
#define SI1145_GET_CAL                      0x12

static const char *TAG_SI1145 = "SI1145";

void si1145_read_register_16(uint8_t reg, uint16_t *data);
void si1145_read_register(uint8_t reg, uint8_t *data);
void si1145_write_register(uint8_t reg, uint8_t value);
void si1145_write_command(uint8_t command);
void si1145_write_param(uint8_t param, uint8_t value);
void si1145_reset();
void si1145_configure(void);

#endif /*_SI1145_H_*/
- si1145.c:

Code: Select all

#include <stdio.h>
#include "driver/i2c.h"
#include "esp_log.h"
#include "i2c_config.h"
#include "SI1145.h"

void si1145_read_register_16(uint8_t reg, uint16_t *data)
{
    uint8_t read_data[2] = {0, 0};

    i2c_cmd_handle_t cmd = i2c_cmd_link_create();

    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, SI1145_ADDR << 1 | 0, ACK_CHECK_EN);
    i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
    vTaskDelay(0.02 / portTICK_PERIOD_MS);
    i2c_master_stop(cmd);

    i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);
    vTaskDelay(90 / portTICK_PERIOD_MS);

    cmd = i2c_cmd_link_create();

    i2c_master_start(cmd);
    // i2c_master_write_byte(cmd, (SI1145_ADDR << 1) | I2C_MASTER_WRITE, true);
    // i2c_master_write_byte(cmd, reg, true);
    // i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (SI1145_ADDR << 1) | I2C_MASTER_READ, true);
    i2c_master_read_byte(cmd, &read_data[0], 0x00); //I2C_MASTER_ACK
    i2c_master_read_byte(cmd, &read_data[1], 0x01); //I2C_MASTER_NACK
    i2c_master_stop(cmd);
    i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);
    vTaskDelay(pdMS_TO_TICKS(100));

    *data = ((uint16_t)(read_data[0] << 8)) | ((uint16_t)(read_data[1] & 0xFF));
}

void si1145_read_register(uint8_t reg, uint8_t *data)
{
    uint8_t read_data = 0;

    i2c_cmd_handle_t cmd = i2c_cmd_link_create();

    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (SI1145_ADDR << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, reg, true);
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (SI1145_ADDR << 1) | I2C_MASTER_READ, true);
    i2c_master_read_byte(cmd, &read_data, I2C_MASTER_LAST_NACK);
    i2c_master_stop(cmd);
    i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);
    vTaskDelay(pdMS_TO_TICKS(100));

    *data = read_data;
}

void si1145_write_register(uint8_t reg, uint8_t value)
{
    uint8_t data[2] = {reg, value};

    i2c_cmd_handle_t cmd = i2c_cmd_link_create();

    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (SI1145_ADDR << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write(cmd, data, sizeof(data), true);
    i2c_master_stop(cmd);
    i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 100 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);

    vTaskDelay(pdMS_TO_TICKS(100));
}

void si1145_write_command(uint8_t command)
{
    si1145_write_register(SI1145_REG_COMMAND, command);
}

void si1145_write_param(uint8_t param, uint8_t value)
{
    uint8_t value_read = 0;
    si1145_write_register(SI1145_REG_PARAMWR, value);
    si1145_write_command(param | SI1145_PARAM_SET);
    si1145_read_register(SI1145_REG_PARAMRD, &value_read);
}

void si1145_reset()
{
    si1145_write_register(SI1145_REG_MEASRATE0, 0);
    si1145_write_register(SI1145_REG_MEASRATE1, 0);
    si1145_write_register(SI1145_REG_IRQEN, 0);
    si1145_write_register(SI1145_REG_IRQMODE1, 0);
    si1145_write_register(SI1145_REG_IRQMODE2, 0);
    si1145_write_register(SI1145_REG_INTCFG, 0);
    si1145_write_register(SI1145_REG_IRQSTAT, 0xFF);

    si1145_write_command(SI1145_RESET);
    vTaskDelay(pdMS_TO_TICKS(10));

    si1145_write_register(SI1145_REG_HWKEY, 0x17);
    vTaskDelay(pdMS_TO_TICKS(10));
}

void si1145_configure(void)
{
    si1145_reset();

    /* Enable UV Index mesurements coefficients */
    si1145_write_register(SI1145_REG_UCOEF0, 0x29);
    si1145_write_register(SI1145_REG_UCOEF1, 0x89);
    si1145_write_register(SI1145_REG_UCOEF2, 0x02);
    si1145_write_register(SI1145_REG_UCOEF3, 0x00);

    /* Enable UV sensor */
    si1145_write_param(SI1145_PARAM_CHLIST, SI1145_PARAM_CHLIST_ENUV | SI1145_PARAM_CHLIST_ENALSIR | SI1145_PARAM_CHLIST_ENALSVIS | SI1145_PARAM_CHLIST_ENPS1);

    /* Enable interrupt on every sample */
    si1145_write_register(SI1145_REG_INTCFG, SI1145_REG_INTCFG_INTOE);
    si1145_write_register(SI1145_REG_IRQEN, SI1145_REG_IRQEN_ALSEVERYSAMPLE);

    /* *********  PROX SENSE 1  ********* */

    /* Program LED Current */
    si1145_write_register(SI1145_REG_PSLED21, 0x03);    // 20mA for LED 1 only
    si1145_write_param(SI1145_PARAM_PS1ADCMUX, SI1145_PARAM_ADCMUX_LARGEIR);
    // prox sensor #1 uses LED #1
    si1145_write_param(SI1145_PARAM_PSLED12SEL, SI1145_PARAM_PSLED12SEL_PS1LED1);
    // fastest clocks, clock div 1
    si1145_write_param(SI1145_PARAM_PSADCGAIN, 0);
    // take 511 clocks to measure
    si1145_write_param(SI1145_PARAM_PSADCOUNTER, SI1145_PARAM_ADCCOUNTER_511CLK);
    // in prox mode, high range
    si1145_write_param(SI1145_PARAM_PSADCMISC, SI1145_PARAM_PSADCMISC_RANGE | SI1145_PARAM_PSADCMISC_PSMODE);

    si1145_write_param(SI1145_PARAM_ALSIRADCMUX, SI1145_PARAM_ADCMUX_SMALLIR);
    // fastest clocks, clock div 1
    si1145_write_param(SI1145_PARAM_ALSIRADCGAIN, 0);
    // take 511 clocks to measure
    si1145_write_param(SI1145_PARAM_ALSIRADCOUNTER, SI1145_PARAM_ADCCOUNTER_511CLK);
    // in high range mode
    si1145_write_param(SI1145_PARAM_ALSIRADCMISC, SI1145_PARAM_ALSIRADCMISC_RANGE);
    // fastest clocks, clock div 1
    si1145_write_param(SI1145_PARAM_ALSVISADCGAIN, 0);
    // take 511 clocks to measure
    si1145_write_param(SI1145_PARAM_ALSVISADCOUNTER, SI1145_PARAM_ADCCOUNTER_511CLK);
    // in high range mode (not normal signal)
    si1145_write_param(SI1145_PARAM_ALSVISADCMISC, SI1145_PARAM_ALSVISADCMISC_VISRANGE);

    /* ********************************************* */

    /* Measurement rate for auto */
    si1145_write_register(SI1145_REG_MEASRATE0, 0xFF);  // 255 * 31.25uS = 8ms

    /* auto run */
    si1145_write_command(SI1145_PSALS_AUTO);

    ESP_LOGI(TAG_SI1145, "Config Si1145 complete");
}
MAIN:
- main.c:

Code: Select all

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/i2c.h"
#include "i2c_config.h"
#include "SI1145.h"

static const char *TAG = "SI1145_test";

void app_main(void)
{
    i2c_master_init();
    vTaskDelay(pdMS_TO_TICKS(100));

    si1145_configure();
    vTaskDelay(pdMS_TO_TICKS(100));

    while (1) 
    {
        uint16_t uv_index, visible, ir;

        // Read UV, VIS and IR
        si1145_read_register_16(SI1145_REG_UVINDEX0, &uv_index);
        ESP_LOGI(TAG, "UV Index: %f", (((float)uv_index) / 100));
        vTaskDelay(pdMS_TO_TICKS(1000));

        si1145_read_register_16(SI1145_REG_ALSVISDATA0, &visible);
        ESP_LOGI(TAG, "Visible Light: %d", (int)visible);
        vTaskDelay(pdMS_TO_TICKS(1000));

        si1145_read_register_16(SI1145_REG_ALSIRDATA0, &ir);
        ESP_LOGI(TAG, "IR Light: %d", (int)ir);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

Re: SI1145 wrong reads in ESP-IDF v.5.3.0

Posted: Mon Jul 21, 2025 10:04 am
by eriksl
I have experience with many I2C sensors and I never got this one work reliably and believe me I tried.... So either it must one or more bugs or the documentation is incomplete. At least I know for sure that none of the problems are related to the ESP 32 I2C module.

Re: SI1145 wrong reads in ESP-IDF v.5.3.0

Posted: Wed Jul 23, 2025 3:50 pm
by StanFred
I have experience with many I2C sensors and I never got this one work reliably and believe me I tried.... So either it must one or more bugs or the documentation is incomplete. At least I know for sure that none of the problems are related to the ESP 32 I2C module.
I’ve worked with the MLX90614 too and had random failures. What helped a bit was adding clock stretching handling and slowing down the I2C speed to 50kHz. Still not perfect, though — feels like the sensor’s I2C timing is just off-spec. Definitely not an ESP32 issue in my case either.

Re: SI1145 wrong reads in ESP-IDF v.5.3.0

Posted: Thu Aug 14, 2025 5:26 pm
by eriksl
No the problem is not there, not in the I2C communication. The I2C communication is perfectly fine. The SI114x have an interface module and a microcontroller interface. They are connected using a (see documentation) "mailbox". Which is extremely hard to use. Plenty of room for bugs in the microcontroller or in the interface module, without affecting the actual I2C communication.

I challenge anyone to get a valid and trustworthy temperature reading from this chip (which, yes, the documentation says is possible).

Also note that the "UV light" measured is actually an approximation from measured visible and infrared. The chip doesn't even have a proper UV sensor. Most of them don't have one, actually.

I'd be very happy to have a sensor that measures UV in "UV strength"-like scale 1 - 12. Incorporating all of the required UV wave lengths that are necessary to do the actual calculation. This is what's we want to know, whether it's safe to go outside without or with little sun screen. I don't think there are many other uses to knowing UV light strength, especially if you don't even know what wavelength it's sensing. UV is a vast part of the radio spectrum, so simply "UV" isn't saying much.