Page 1 of 1

Help with I2C communication between 2 ESP32

Posted: Sun Jan 08, 2023 1:59 pm
by starofslytherin
Hello,

My project is to measure water level of tanks. As I'm using more than 3 UARTs I want to use 2 ESP32s.

Slave ESP32: Get data from the water level sensor and send it to the Master ESP32 using I2C.

Master ESP32: Collect that data and pass it on to NodeRED for processing and Visualisation.

My issue: I'm not able to get I2C communication working between ESP32s.

My basic code is here:

Slave Code:

Code: Select all

#include <stdio.h>

#include "esp_log.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

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

static const char *TAG = "i2c-slave";

#define _I2C_NUMBER(num) I2C_NUM_##num
#define I2C_NUMBER(num) _I2C_NUMBER(num)

#define DATA_LENGTH 512                  /*!< Data buffer length of test buffer */
#define RW_TEST_LENGTH 128               /*!< Data length for r/w test, [0,DATA_LENGTH] */
#define DELAY_TIME_BETWEEN_ITEMS_MS 1000 /*!< delay time between different test items */

#define I2C_SLAVE_SCL_IO 14                    /*!< gpio number for i2c slave clock */
#define I2C_SLAVE_SDA_IO 12                    /*!< gpio number for i2c slave data */
#define I2C_SLAVE_NUM I2C_NUMBER(0)            /*!< I2C port number for slave dev */
#define I2C_SLAVE_TX_BUF_LEN (2 * DATA_LENGTH) /*!< I2C slave tx buffer size */
#define I2C_SLAVE_RX_BUF_LEN (2 * DATA_LENGTH) /*!< I2C slave rx buffer size */
#define I2C_SLAVE_ADDR 0x45                    /*!< I2C slave address */

typedef struct tankData
{
    int tankID;
    int waterLevel;
} tankData;

tankData tankOne;

/**
 * @brief i2c slave initialization
 */
static esp_err_t i2c_slave_init(void)
{
    int i2c_slave_port = I2C_SLAVE_NUM;
    i2c_config_t conf_slave = {
        .sda_io_num = I2C_SLAVE_SDA_IO,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_io_num = I2C_SLAVE_SCL_IO,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .mode = I2C_MODE_SLAVE,
        .slave.addr_10bit_en = 0,
        .slave.slave_addr = I2C_SLAVE_ADDR,
        .slave.maximum_speed = 100000,
    };
    esp_err_t err = i2c_param_config(i2c_slave_port, &conf_slave);
    if (err != ESP_OK)
    {
        return err;
    }
    return i2c_driver_install(i2c_slave_port, conf_slave.mode, I2C_SLAVE_RX_BUF_LEN, I2C_SLAVE_TX_BUF_LEN, 0);
}

void app_main(void)
{
    int counter = 0;
    int i2c_data = 0;

    ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_slave_init());

    while (true)
    {
        tankOne.tankID = counter++;
        tankOne.waterLevel = (counter % 100);
        ESP_LOGI(TAG, "Tank ID: %d | Water Level: %d", tankOne.tankID, tankOne.waterLevel);
        i2c_data = i2c_slave_write_buffer(I2C_SLAVE_NUM, (uint8_t *)&tankOne, sizeof(tankOne), (1000 / portTICK_RATE_MS));

        if (i2c_data != ESP_FAIL)
        {
            ESP_LOGI(TAG, "I2C Data Length: %d Bytes", i2c_data);
        }
        else
        {
            ESP_LOGE(TAG, "Failed to generate data");
        }

        vTaskDelay(10000 / portTICK_RATE_MS);
    }
}

Master Code:

Code: Select all

#include <stdio.h>
#include <string.h>

#include "esp_log.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "driver/i2c.h"

static const char *TAG = "i2c-master";

#define _I2C_NUMBER(num) I2C_NUM_##num
#define I2C_NUMBER(num) _I2C_NUMBER(num)

#define DATA_LENGTH 512                  /*!< Data buffer length of test buffer */
#define RW_TEST_LENGTH 128               /*!< Data length for r/w test, [0,DATA_LENGTH] */
#define DELAY_TIME_BETWEEN_ITEMS_MS 1000 /*!< delay time between different test items */

#define I2C_MASTER_SCL_IO 25         /*!< gpio number for I2C master clock */
#define I2C_MASTER_SDA_IO 27         /*!< gpio number for I2C master data  */
#define I2C_MASTER_NUM I2C_NUMBER(0) /*!< I2C port number for master dev */
#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 ESP_SLAVE_ADDR 0x45          /*!< I2C slave address */

#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 */

typedef struct tankData
{
    int tankID;
    int waterLevel;
} tankData;

tankData tankOne;

/**
 * @brief i2c master initialization
 */
static esp_err_t i2c_master_init(void)
{
    int i2c_master_port = I2C_MASTER_NUM;
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = I2C_MASTER_FREQ_HZ,
    };
    esp_err_t err = i2c_param_config(i2c_master_port, &conf);
    if (err != ESP_OK)
    {
        return err;
    }
    return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}

static esp_err_t __attribute__((unused)) i2c_master_read_slave(i2c_port_t i2c_num, uint8_t *data_rd, size_t size)
{
    if (size == 0) {
        return ESP_OK;
    }
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1) | READ_BIT, ACK_CHECK_EN);
    if (size > 1) {
        i2c_master_read(cmd, data_rd, size - 1, ACK_VAL);
    }
    i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL);
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);
    return ret;
}

void app_main(void)
{
    uint8_t *data = (uint8_t *)malloc(DATA_LENGTH);
    esp_err_t i2c_read_err;

    ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_init());

    while (true)
    {
        // i2c_read_err = i2c_master_read_from_device(I2C_MASTER_NUM, ESP_SLAVE_ADDR, data, sizeof(data), (1000 / portTICK_RATE_MS));

        i2c_read_err = i2c_master_read_slave(I2C_NUM_0, data, DATA_LENGTH);

        if (i2c_read_err == ESP_OK)
        {
            memcpy(&tankOne, data, sizeof(tankOne));
            ESP_LOGI(TAG, "tankID: %d", tankOne.tankID);
            ESP_LOGI(TAG, "waterLevel: %d", tankOne.waterLevel);
        }
        else
        {
            ESP_LOGE(TAG, "[%d] I2C Error: %s", i2c_read_err, esp_err_to_name(i2c_read_err));
        }

        vTaskDelay(10000 / portTICK_RATE_MS);
    }
}