XIAO ESP32C6 I2C Slave Pins?

I am trying to replace a XIAO ESP32S3 that I use as an I2C slave sender with an ESP32C6, but just cannot figure what I am doing wrong.

For the ESP32S3 I use the following code and it works great…

#include <Wire.h>

void I2C_TxHandler(void) {
  Wire.slaveWrite(TxByte, 4);
}

void setup() {
  Wire.onRequest(I2C_TxHandler);
  Wire.begin(0x42, 5, 6, 100000UL);
}

void loop() {
  delay(10);
}

But when I swap in the ESP32C6 and modify the pins it doesn’t work at all…

#include <Wire.h>

void I2C_TxHandler(void) {
  Wire.slaveWrite(TxByte, 4);
}

void setup() {
  Wire.onRequest(I2C_TxHandler);
  Wire.begin(0x42, 22, 23, 100000UL);
}

void loop() {
  delay(10);
}

D4 & D5 are the corresponding pins GPIO22 & GPIO23 according to the pin diagram.
Does anyone know what I am doing wrong?
Thank you!

As you say the pin swap should be correct… must be something in the Library support for C6

Hi there,

Which BSP are you using for the C6 compile ? The Pin macros may be used in this case if newer, ie. D4 & D5 in both cases.

Can you provide the compiler output for the S3 that works? the first 3 lines and the last 10 -12 before it uploads.
We can take a look. :+1:

HTH
GL :slight_smile: PJ :v:

meanwhile… Here is why it’s not working.

  • On ESP32-S3, Wire.onRequest() works because S3 supports I2C slave mode in hardware and in the Arduino core.

  • On ESP32-C6, only I2C Master mode is implemented in the Arduino Wire library as of BSP 3.2.0. "

  • Wire.onRequest() is not implemented for ESP32-C6 as of BSP v3.x (and even earlier).

  • Wire.slaveWrite() and I2C Slave Mode is not supported on the ESP32-C6 using the Arduino Wire library.

  • Even the call:

Wire.begin(0x42, 5, 6, 100000UL);

while syntactically valid, doesn’t actually configure the ESP32-C6 as an I2C slave, because slave mode isn’t available in the current ESP32-C6 Arduino core.
Wait for future support — but no timeline is guaranteed for I2C slave in ESP32-C6 via Arduino.

3 Likes

I knew it was a software support thing…

Thank you both for the explanation and feedback.
I will find a work around for the lack of the I2C slave mode on the C6.

1 Like

Hi there,

Good don’t give up. :+1:
I’m working on a BLE kind of a solution. Not sure it it would help , but I’ll post it up in the Round Display thread tomorrow. you may be able to use something from that.

HTH
GL :slight_smile: PJ

Espressif , should get there act together , let’s hope. :crossed_fingers:

1 Like

Thanks - I will check out your Round Display thread.
But my main controller does not have BLE or WiFi…I am actually using the XIAO to create an I2C “sensor” that forwards data from a BLE sensor.
That’s why I was using Slave mode.
I can convert the XIAO C6 to push the data to the controller set up with a 2nd I2C slave interface, but I preferred the controller just requesting the the data payload on-demand.
I am also looking at the MG24, but the Silicon Labs BLE stack is very very complicated for a novice like me who doesn’t know what they are doing.
I have been very lucky so far to get what I have working.

I have a BLE sensor that has a GATT update characteristic (not sure if that is the right name) you can subscribe to notifications, and it delivers a 50 byte payload.
I need to get that payload to a controller that has other I2C sensors and outputs.
I was able to get the ArduinoBLE library working with the ESP32-S3, but a recent firmware update for the BLE sensor somehow broke it and now the sensor crashes seconds after the subscription is made and the first few notifications are received.
I cannot expect changes to the BLE sensor to fix it.
When I tried the ESP32-C6, subscriptions work great, but the XIAO wasn’t sending the data, which you taught me is because it doesn’t support slave mode.
The nRF52840 kinda works, but it isn’t as stable or reliable as the ESP32’s, and the Discovery and Subscribe phases fail a lot of the time.

I just need to scan for a MAC, and when it finds it, subscribe to the notifications…not as simple as it sounds

Thanks again for your help!

Do you have any XIAO MG24’s available?

I did some work with I2C Slave on them and it should be easy enough to add a BLE central to the code to create an I2C/BLE converter :thinking:

Yes, I received a couple XIAO MG24’s.
I plugged one in yesterday and ran the ble_scan example and that’s when I learned about the BLE (Silabs) protocol stack, which was shocking.

I use Arduino and the ArduinoBLE library - that’s where I started.
Please don’t laugh too hard, but here is the code I eventually ended up with for the ESP32-S3…

#include <Wire.h>
#include <ArduinoBLE.h>

uint8_t probeStatusData[50] = {};

bool CPTscanning = false;
bool CPTdiscovered = false;
bool CPTsubscribed = false;

void I2C_TxHandler(void) {
  Wire.slaveWrite(probeStatusData, 50);
}

void setup() {
  Wire.onRequest(I2C_TxHandler);
  Wire.begin(0x42, 5, 6, 100000UL);

  BLE.begin();
  BLE.setEventHandler(BLEDiscovered, CPTdiscoveredHandler);
}

void loop() {
  if (!CPTscanning) {
    BLE.scanForAddress("xx:xx:xx:xx:xx:xx");
    CPTscanning = true;
  }
  BLE.poll();
  delay(100);
}

void readCPTvalue(BLECharacteristic characteristic) {
  characteristic.read();
  characteristic.readValue(&probeStatusData, 50);
}

void CPTdiscoveredHandler(BLEDevice peripheral) {
  BLE.stopScan();
  CPTscanning = false;
  peripheral.connect();

  while (!CPTdiscovered) {
    if (peripheral.discoverService("00000100-caab-3792-3d44-97ae51c1407a")) {
      CPTdiscovered = true;
    } else {
      peripheral.disconnect();
      peripheral.connect();
      delay(100);
    }
  }
  BLEService service = peripheral.service("00000100-caab-3792-3d44-97ae51c1407a");
  BLECharacteristic characteristic = service.characteristic("00000101-caab-3792-3d44-97ae51c1407a");
  if (characteristic.canRead()) {
    characteristic.read();
  }
  if (characteristic.canSubscribe()) {
    while (!CPTsubscribed) {
      if (characteristic.subscribe()) {
        CPTsubscribed = true;
      } else {
        delay(500);
        characteristic.subscribe();
        delay(500);
      }
    }
  }

  while (peripheral.connected()) {
    BLEService service = peripheral.service("00000100-caab-3792-3d44-97ae51c1407a");
    BLECharacteristic characteristic = service.characteristic("00000101-caab-3792-3d44-97ae51c1407a");
    if (characteristic.valueUpdated()) {
      readCPTvalue(service.characteristic("00000101-caab-3792-3d44-97ae51c1407a"));
    }
  }

  CPTdiscovered = false;
  CPTsubscribed = false;
}

I am sure that is a long way from optimal, but it works.

From what I learned so far the MG24 does not allow me to scan for a specific MAC address.
I went to this website https://docs.silabs.com/bluetooth/latest/bluetooth-stack-api/sl-bt-gatt and tried to understand it, but I am not a programmer and have very little experience.
This is the start of the code from what I could gather…

//sscanf(macAddress, "%X:%X:%X:%X:%X:%X", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
uint8_t target_mac[] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};

void setup() {
  // Start scanning (example: 1M PHY, generic discovery mode)
  sl_bt_scanner_start(sl_bt_scanner_scan_phy_1m, sl_bt_scanner_discover_generic);
}

void loop() {
  //
}

// In your event handler:
void handle_scanner_event(sl_bt_msg_t *evt) {
  if (SL_BT_MSG_ID(evt->header) == sl_bt_evt_scanner_legacy_advertisement_report_id) {
    // Compare evt->data.evt_scanner_legacy_advertisement_report.address to your target MAC
    if (memcmp(evt->data.evt_scanner_legacy_advertisement_report.address.addr, target_mac, 6) == 0) {
      // Found the device
    }
  }
}

// Enable notifications for a characteristic
void enable_notifications(uint8_t connection, uint16_t characteristic_handle) {
  sl_status_t sc = sl_bt_gatt_set_characteristic_notification(connection,
                                                              characteristic_handle,
                                                              sl_bt_gatt_notification);
  // Check status code
}

// Handle the characteristic value event (when notifications arrive)
void handle_characteristic_value(sl_bt_msg_t *evt) {
  if (evt->data.evt_gatt_characteristic_value.att_opcode == sl_bt_gatt_handle_value_notification) {
    // Process the notification data
    // The notification data is in evt->data.evt_gatt_characteristic_value.value
  }
}

No idea if that is the right direction or not.
Or if I need something called ‘sscanf’ to define the MAC address?
Any advice you have would be very greatly appreciated.

Thanks

Not going to happen. I’m an electronics engineer first and embedded coder second… my code is far from “beautiful” - but works :slight_smile:

I’ll have a look here to see what’s going on… I’m not sure how “fully implemented” the MG24 Arduino library is.

There is something odd with the Arduino I2C on the ESP32-C6. It all gets initialised but the I2C interrupts just fail to fire :thinking:
I ended up using basic functions from one of the Espressif examples and it worked fine.

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"

/*
  This Arduino Sketch configures the ESP32 C6 DEVKITC 1 as an I2C slave device
  using the ESP-IDF framework. The I2C bus uses GPIO 22 as SDA and GPIO 23 as
  SCL. The device responds to requests and receptions at the I2C address 0x74.
*/

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

#define I2C_ADDRESS 0x74
#define SDA_PIN 22
#define SCL_PIN 23
#define I2C_PORT I2C_NUM_0

static const char *TAG = "I2C_Slave";

void do_second_tick();

void receive_event(i2c_port_t i2c_num, uint8_t *data, size_t len)
{
  ESP_LOGI(TAG, "Received data:");
  for (size_t i = 0; i < len; i++)
  {
    ESP_LOGI(TAG, "%02x ", data[i]);
  }
}

void request_event(i2c_port_t i2c_num)
{
  uint8_t ack[] = "ACK";
  i2c_slave_write_buffer(i2c_num, ack, sizeof(ack), 1000 / portTICK_PERIOD_MS);
}

// Check the millisecond tick and log a message every second
void do_second_tick()
{
  static uint32_t last_tick = 0;
  uint32_t current_tick = xTaskGetTickCount() * portTICK_PERIOD_MS;

  if (current_tick - last_tick >= 1000)
  {
    ESP_LOGI(TAG, "One second has passed");
    last_tick = current_tick;
  }
} 

void app_main()
{
  i2c_config_t conf = {
      .mode = I2C_MODE_SLAVE,
      .sda_io_num = SDA_PIN,
      .scl_io_num = SCL_PIN,
      .sda_pullup_en = GPIO_PULLUP_ENABLE,
      .scl_pullup_en = GPIO_PULLUP_ENABLE,
      .slave = {
          .slave_addr = I2C_ADDRESS,
          .maximum_speed = 100000}};

  i2c_param_config(I2C_PORT, &conf);
  i2c_driver_install(I2C_PORT, conf.mode, 128, 128, 0);

  while (1)
  {
    uint8_t data[128];
    int len = i2c_slave_read_buffer(I2C_PORT, data, sizeof(data), 1000 / portTICK_PERIOD_MS);
    if (len > 0)
    {
      receive_event(I2C_PORT, data, len);
    }
    request_event(I2C_PORT);
    vTaskDelay(100 / portTICK_PERIOD_MS);

    do_second_tick();
  }
}

Thank you, I will look through your code and see what I can learn :slightly_smiling_face:

@PJ_Glasso confirmed that the ESP32-C6 is Master I2C only and I was having issues with other I2C peripherals as well, especially ones that implemented clock stretching that the C6 didn’t seem to tolerate.

The MG24 is really interesting and I would like to learn more about programming it.
Even though google searches indicate the MG24 is ArduinoBLE compatible, my code does not compile.
Thank you again for the feedback.

Unfortunately the “Arduino” I2C Slave port has a few “gotchas” so not worth pursuing. I made a few changes to the library to get it working but haven’t fixed the library (git pull).
I suggest using the ESP32-C6 with the Espressif sdk.

1 Like

Thank you for the feedback

Probably is not supported because slave is a 4 letter word now… What Da? Who is going to pick the grapes if we dont have IIC?