Page 1 of 1

Porting Arduino I2C method to ESP32?

Posted: Sun Apr 09, 2017 7:48 pm
by Scalpel78
Hi,
I'm working with a custom PCB that has a TCA9548 I2C multiplexer. From one of my previous boards were I used a Teensy and Arduino to control the TCA9548, I've used this method to change the channel:

Code: Select all

void setActiveSensor(uint8_t i)
{
  if (i > 7) return;

  Wire.beginTransmission(TCA_ADDRESS);
  Wire.write(1 << i);
  Wire.endTransmission();

  activeSensor = i;
}
Now, I'm trying to port this code over to ESP32. I've got I2C configured, and when I just query for the existence of the device I get a successful ACK back.

This is the code I'm using to replace the above Arduino code:

Code: Select all

esp_err_t tca9548_set_channel(int channel)
{
  //Contact the TCA9548. Does it respond with an ACK?
  i2c_cmd_handle_t cmd = i2c_cmd_link_create();
  ESP_ERROR_CHECK(i2c_master_start(cmd));
  ESP_ERROR_CHECK(i2c_master_write_byte(cmd, (TCA_ADDRESS << 1)| I2C_MASTER_WRITE, ACK_CHECK_ON));
  //i2c_master_write_byte(cmd, (1 << channel) | I2C_MASTER_WRITE, 1 /* expect ack */);
  ESP_ERROR_CHECK(i2c_master_stop(cmd));

  //Send the I2C command
  esp_err_t result = i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
  i2c_cmd_link_delete(cmd);

  if (result == ESP_OK)
  {
    printf("[OK] Found TCA9548!\n");
    //The TCA9548 responded
    uint8_t data[1];

    //Write to the register to set the active channel.
    cmd = i2c_cmd_link_create();
    ESP_ERROR_CHECK(i2c_master_start(cmd));
    ESP_ERROR_CHECK(i2c_master_write_byte(cmd, (TCA_ADDRESS << 1) | I2C_MASTER_WRITE, ACK_CHECK_ON));
    ESP_ERROR_CHECK(i2c_master_write_byte(cmd, (channel << 1), ACK));
    ESP_ERROR_CHECK(i2c_master_stop(cmd));
    result = i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);

    return result;
  }
  else
  {
    //ESP_ERROR_CHECK(result);
    printf("[ERROR] [TCA9548] I2C ERROR: %d\n", result);
    return -1;
  }
}
I believe I can read data from the IC (at least it doesn't fail) using this code. This is for reading the same data that was written using the tca9548_set_channel(int) method.

Code: Select all

uint8_t tca9548_get_channel()
{
  //Contact the TCA9548. Does it respond with an ACK?
  i2c_cmd_handle_t cmd = i2c_cmd_link_create();
  ESP_ERROR_CHECK(i2c_master_start(cmd));
  ESP_ERROR_CHECK(i2c_master_write_byte(cmd, (TCA_ADDRESS << 1)| I2C_MASTER_WRITE, ACK_CHECK_ON));
  //i2c_master_write_byte(cmd, (1 << channel) | I2C_MASTER_WRITE, 1 /* expect ack */);
  ESP_ERROR_CHECK(i2c_master_stop(cmd));

  //Send the I2C command
  esp_err_t result = i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
  i2c_cmd_link_delete(cmd);

  if (result == ESP_OK)
  {
    uint8_t data[1];

    //Read the active channel from the IC
    cmd = i2c_cmd_link_create();
    ESP_ERROR_CHECK(i2c_master_start(cmd));
    ESP_ERROR_CHECK(i2c_master_write_byte(cmd, (TCA_ADDRESS << 1) | I2C_MASTER_READ, ACK_CHECK_ON));
    ESP_ERROR_CHECK(i2c_master_read_byte(cmd, data, ACK));
    ESP_ERROR_CHECK(i2c_master_stop(cmd));
    ESP_ERROR_CHECK(i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS));
    i2c_cmd_link_delete(cmd);
    return data;
  }
  else
  {
    //ESP_ERROR_CHECK(result);
    printf("[ERROR] [TCA9548] I2C ERROR: %d\n", result);
    return -1;
  }
The problem is that the tca9548_get_channel() method always returns 0. So either the tca9548_set_channel() method isn't doing what it should, or the tca9548_get_channel isn't working properly, or both.

Can you see anything obvious I'm doing wrong here?

Re: Porting Arduino I2C method to ESP32?

Posted: Mon Apr 10, 2017 12:05 am
by telanoc
I'm not familiar with the TCA9548, but don't you have to give the address of the register you're reading from? I know on the MCP23017 you do something like this:

Code: Select all

  // Specify register to read
   cmd = i2c_cmd_link_create();
   i2c_master_start(cmd);
   i2c_master_write_byte (cmd, (addr<<1) | I2C_MASTER_WRITE, ACK_CHECK_ON);
   i2c_master_write_byte (cmd, REG_GPIOA, ACK);
   i2c_master_cmd_begin (I2C_NUM_0, cmd, 100/ portTICK_PERIOD_MS);
   i2c_cmd_link_delete(cmd);
   
   // Now read a byte from that register
   cmd = i2c_cmd_link_create();
   i2c_master_start(cmd);
   i2c_master_write_byte (cmd, (addr<<1) | I2C_MASTER_READ, ACK_CHECK_ON);
   uint8_t res;
   i2c_master_read_byte (cmd, &res, ACK);
   i2c_master_cmd_begin (I2C_NUM_0, cmd, 100/ portTICK_PERIOD_MS);
   i2c_cmd_link_delete(cmd);
   
   return res;

Re: Porting Arduino I2C method to ESP32?

Posted: Mon Apr 10, 2017 9:17 am
by Scalpel78
Hi telanoc, thanks for your reply.

Yes, in most cases you have to specify which register you want to read from, but for devices that only have one register you don't need to specify which one you want. In the TCA9548 datasheet, in section 8.5.3, this is described as a single-register device.

From the datasheet:
Some devices are simple and contain only 1 register, which may be written to directly by sending the register data immediately
after the slave address, instead of addressing a register. The TCA9548A is example of a single-register device, which is controlled via I2C commands. Since it has 1 bit to enable or disable a channel, there is only 1 register needed, and the master merely writes the register data after the slave address, skipping the register number.


Note also that the Arduino code I'm porting from works.