Problemas al usar I2C en ESP32 para leer el sensor de temperatura/humedad (SI7020)

Estoy tratando de leer un sensor de temperatura y humedad (SI7020) usando I2C en el ESP32.

Modifiqué su código de ejemplo para leer solo desde un dispositivo esclavo (0x40) pero algunos comandos no funcionan. También intenté restablecer el dispositivo con 0xFE.

Puedo leer el ID del dispositivo (0xFA):

*******************
TASK[0]  MASTER READ SENSOR( SI7020 )
*******************
data_h: 48
data_l: 52
sensor val: 15428.333333

ID de dispositivo de lectura

Sin embargo, si trato de leer la temperatura (0xE3), obtengo:

data_h: 00
data_l: 00

ingrese la descripción de la imagen aquí

Si uso 0xE0 siempre obtengo:

data_h: 39
data_l: 9f

¿Por qué no puedo leer la temperatura del SI7020?

El código que estoy usando:

#include <stdio.h>
#include "driver/i2c.h"


#define DATA_LENGTH          512  /*!<Data buffer length for test buffer*/
#define RW_TEST_LENGTH       127  /*!<Data length for r/w test, any value from 0-DATA_LENGTH*/
#define DELAY_TIME_BETWEEN_ITEMS_MS   2000 /*!< delay time between different test items */

#define I2C_EXAMPLE_MASTER_SCL_IO    19    /*!< gpio number for I2C master clock */
#define I2C_EXAMPLE_MASTER_SDA_IO    18    /*!< gpio number for I2C master data  */
#define I2C_EXAMPLE_MASTER_NUM I2C_NUM_1   /*!< I2C port number for master dev */
#define I2C_EXAMPLE_MASTER_TX_BUF_DISABLE   0   /*!< I2C master do not need buffer */
#define I2C_EXAMPLE_MASTER_RX_BUF_DISABLE   0   /*!< I2C master do not need buffer */
#define I2C_EXAMPLE_MASTER_FREQ_HZ    100000     /*!< I2C master clock frequency */

#define SI7020_SENSOR_ADDR  0x40    /*!< slave address for SI7020 sensor */
#define SI7020_CMD_READ    0xE0    /*!< Command to set measure mode */
//#define ESP_SLAVE_ADDR 0x28         /*!< ESP32 slave address, you can set any 7bit value */
#define WRITE_BIT  I2C_MASTER_WRITE /*!< I2C master write */
#define READ_BIT   I2C_MASTER_READ  /*!< I2C master read */
#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 */

xSemaphoreHandle print_mux;

/**
 * @brief test code to write esp-i2c-slave
 *
 * 1. set mode
 * _________________________________________________________________
 * | start | slave_addr + wr_bit + ack | write 1 byte + ack  | stop |
 * --------|---------------------------|---------------------|------|
 * 2. wait more than 24 ms
 * 3. read data
 * ______________________________________________________________________________________
 * | start | slave_addr + rd_bit + ack | read 1 byte + ack  | read 1 byte + nack | stop |
 * --------|---------------------------|--------------------|--------------------|------|
 */
static esp_err_t i2c_example_master_sensor_test(i2c_port_t i2c_num, uint8_t* data_h, uint8_t* data_l)
{
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, SI7020_SENSOR_ADDR << 1 | WRITE_BIT, ACK_CHECK_DIS);
    i2c_master_write_byte(cmd, SI7020_CMD_READ, ACK_CHECK_EN);
    i2c_master_stop(cmd);
    int ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);
    if (ret == ESP_FAIL) {
        return ret;
    }
    vTaskDelay(1000 / portTICK_RATE_MS);

    cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, SI7020_SENSOR_ADDR << 1 | READ_BIT, ACK_CHECK_DIS);
    i2c_master_read_byte(cmd, data_h, ACK_VAL);
    i2c_master_read_byte(cmd, data_l, NACK_VAL);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);
    if (ret == ESP_FAIL) {
        return ESP_FAIL;
    }
    return ESP_OK;
}

/**
 * @brief i2c master initialization
 */
static void i2c_example_master_init()
{
    int i2c_master_port = I2C_EXAMPLE_MASTER_NUM;
    i2c_config_t conf;
    conf.mode = I2C_MODE_MASTER;
    conf.sda_io_num = I2C_EXAMPLE_MASTER_SDA_IO;
    conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
    conf.scl_io_num = I2C_EXAMPLE_MASTER_SCL_IO;
    conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
    conf.master.clk_speed = I2C_EXAMPLE_MASTER_FREQ_HZ;
    i2c_param_config(i2c_master_port, &conf);
    i2c_driver_install(i2c_master_port, conf.mode,
                       I2C_EXAMPLE_MASTER_RX_BUF_DISABLE,
                       I2C_EXAMPLE_MASTER_TX_BUF_DISABLE, 0);
}

static void i2c_test_task(void* arg)
{
    int i = 0;
    int ret;
    uint32_t task_idx = (uint32_t) arg;
    uint8_t* data = (uint8_t*) malloc(DATA_LENGTH);
    uint8_t sensor_data_h, sensor_data_l;

    while (1) {
        ret = i2c_example_master_sensor_test( I2C_EXAMPLE_MASTER_NUM, &sensor_data_h, &sensor_data_l);
        xSemaphoreTake(print_mux, portMAX_DELAY);
        printf("*******************\n");
        printf("TASK[%d]  MASTER READ SENSOR( SI7020 )\n", task_idx);
        printf("*******************\n");
        if (ret == ESP_OK) {
            printf("data_h: %02x\n", sensor_data_h);
            printf("data_l: %02x\n", sensor_data_l);
            printf("sensor val: %f\n", ( sensor_data_h << 8 | sensor_data_l ) / 1.2);
        } else {
            printf("No ack, sensor not connected...skip...\n");
        }
        xSemaphoreGive(print_mux);
        vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS);

        //---------------------------------------------------
        for (i = 0; i < DATA_LENGTH; i++) {
            data[i] = i;
        }
    }
}

void app_main()
{
    print_mux = xSemaphoreCreateMutex();
    //i2c_example_slave_init();
    i2c_example_master_init();

    xTaskCreate(i2c_test_task, "i2c_test_task_0", 1024 * 2, (void* ) 0, 10, NULL);
    //xTaskCreate(i2c_test_task, "i2c_test_task_1", 1024 * 2, (void* ) 1, 10, NULL);
}
¿En qué modo intentaste mantener el modo maestro o no mantener el modo maestro? No sé si realmente está relacionado, pero para mí con ESP8266 y SI7021 solo no funcionó el modo maestro de espera. Mantenga el modo maestro solo disponible para lecturas de zumbido y temperatura, así que tal vez esa sea la razón por la que otros comandos funcionen para usted.
Por lo tanto, el comando E0no funcionará solo, ya que devolverá un valor de temperatura medido durante la medición de humedad anterior. Entonces, antes E0siempre necesitará usar E5o F5comandos primero. Con E3usted está utilizando el modo maestro de retención, el estiramiento del reloj I2C y posiblemente ESP o su código no puede manejar eso. Use el F3comando, con eso Si7020 no se estirará el reloj. Para obtener el resultado, debe esperar entre 9 y 10 ms después de enviar el F3y luego debe ejecutar una operación de lectura I2C para obtener el valor temporal.
@Bence Kaulics F3 me está dando la misma respuesta que E3 en mi publicación original. E incluso después de emitir E5 y F5, E0 da la misma respuesta que antes. Las líneas 43-69 creo que son consistentes con la secuencia que se muestra en la hoja de datos SI7020 .

Respuestas (1)

Su oscilograma muestra una transacción de lectura aislada, mientras que la hoja de datos muestra una transacción de escritura que precede a un reinicio y la transacción de lectura. Ya he visto algunos dispositivos I2C con errores que solo implementan un subconjunto de las posibles transacciones I2C, podría ser el caso de este. Sugiero probar una transacción que se vea exactamente como se muestra en la hoja de datos.

Según tengo entendido, debe usar el comando 0xF3 varias veces en lugar de un solo 0xE0.