Обмен данными между двумя платами ESP32 по шине I2C.

Данное руководство представляет собой техническое описание процесса организации обмена данными между двумя микроконтроллерами ESP32 с использованием протокола I2C (Inter-Integrated Circuit). I2C - это синхронный последовательный протокол, широко применяемый для связи между микроконтроллерами и периферийными устройствами. В данном уроке одна плата ESP32 будет настроена как ведущее устройство (Master), а другая - как ведомое (Slave). Программирование микроконтроллеров будет осуществляться в среде разработки Arduino IDE. Руководство предоставляет подробные инструкции по подключению, настройке и программированию, а также примеры кода для реализации I2C-взаимодействия.

Обзор протокола I2C.

I2C (Inter-Integrated Circuit) – это синхронный, последовательный, полудуплексный протокол связи, разработанный компанией Philips. Он предназначен для соединения микроконтроллеров и различных периферийных устройств, таких как датчики, дисплеи, EEPROM и другие.

Основные характеристики I2C:

  • Синхронная передача: Данные передаются синхронно с тактовым сигналом (SCL), генерируемым ведущим устройством.
  • Двухпроводная шина: Для передачи данных используются всего два провода: SDA (Serial Data) для данных и SCL (Serial Clock) для тактового сигнала.
  • Поддержка нескольких ведущих и ведомых устройств: На одной шине I2C может находиться несколько ведущих (Master) и ведомых (Slave) устройств.
    • Мульти-мастер: Несколько устройств могут выступать в роли ведущих, управляя одним или несколькими ведомыми устройствами. Это требует реализации механизма арбитража шины.
    • Мульти-слейв: Несколько ведомых устройств могут быть подключены к одному ведущему.
  • Адресация устройств: Каждое ведомое устройство на шине I2C имеет уникальный 7-битный или 10-битный адрес. Ведущее устройство использует этот адрес для выбора конкретного ведомого устройства для обмена данными.
  • Подтверждение приема (ACK/NACK): После передачи каждого байта ведомое устройство отправляет бит подтверждения (ACK) или неподтверждения (NACK), сигнализируя об успешном или неуспешном приеме данных.
  • Различные скорости передачи: Поддерживаются различные скорости передачи данных, такие как стандартный режим (100 кбит/с), быстрый режим (400 кбит/с) и высокоскоростной режим (до 3.4 Мбит/с и выше).

Интерфейсы I2C на ESP32.

Интерфейсы I2C на ESP32.

Микроконтроллер ESP32 оснащен двумя аппаратными интерфейсами I2C, которые могут быть настроены как в режиме ведущего, так и в режиме ведомого устройства. Согласно технической документации ESP32, интерфейсы I2C поддерживают:

  • Стандартный режим (Standard Mode): Скорость передачи данных до 100 кбит/с.
  • Быстрый режим (Fast Mode): Скорость передачи данных до 400 кбит/с.
  • Высокоскоростной режим: Скорость передачи данных до 5 МГц, но ограничена сопротивлением подтягивающих резисторов на линии SDA.
  • 7-битная и 10-битная адресация: Позволяет подключать большое количество устройств на одну шину.
  • Двойная адресация: Режим, позволяющий ведомому устройству иметь два разных адреса.
  • Гибкость управления: Программируемые командные регистры предоставляют разработчику широкие возможности по управлению интерфейсами I2C.

Примечание: Подтягивающие резисторы (pull-up resistors) на линиях SDA и SCL являются неотъемлемой частью шины I2C. Они обеспечивают высокий уровень сигнала на линиях, когда шина неактивна. Типичное значение подтягивающих резисторов составляет 4.7 кОм.

Конфигурация ESP32 Master и ESP32 Slave.

Для организации I2C-взаимодействия между двумя платами ESP32 будут использованы следующие компоненты и настройки:

  • Библиотека Wire.h: Стандартная библиотека Arduino IDE для работы с I2C.
  • Стандартные выводы I2C: Для каждой платы ESP32 определены стандартные выводы для подключения к шине I2C (SDA и SCL). Важно! Расположение этих выводов может отличаться в зависимости от модели платы ESP32. Необходимо сверяться с распиновкой конкретной используемой платы.
    • Пример: Для платы ESP32 DOIT V1 стандартными выводами I2C являются GPIO 21 (SDA) и GPIO 22 (SCL). Для плат ESP32-C3, ESP32-S3 и других моделей выводы могут быть другими.

Схема подключения двух плат ESP32 по I2C.

Схема подключения двух плат ESP32 по I2C.

Для установления соединения между двумя платами ESP32 по шине I2C необходимо выполнить следующие подключения:

ESP32 Master (Ведущий) ESP32 Slave (Ведомый)
SDA (GPIO 21)* SDA (GPIO 21)*
SCL (GPIO 22)* SCL (GPIO 22)*
GND GND

* Указаны стандартные выводы для ESP32 DOIT V1.

Важно:

  • Обязательно соедините выводы GND обеих плат ESP32 для обеспечения общего уровня земли.
  • Убедитесь, что используемые выводы SDA и SCL соответствуют распиновке ваших плат ESP32.


Принцип I2C-взаимодействия между двумя ESP32.

Общая схема взаимодействия между ведущим (Master) и ведомым (Slave) устройствами ESP32 по I2C выглядит следующим образом:

ESP32 Master (Ведущий) ESP32 Slave (Ведомый)

1. Устанавливает свой I2C-адрес.

2. Устанавливает функции обратного вызова (callback functions):
- для чтения принятых сообщений (onReceive);
- для обработки запросов от ведущего (onRequest).
3. Инициализирует шину I2C с указанием адреса ведомого устройства.
4. Начинает I2C-взаимодействие на шине.
5. Отправляет сообщение ведомому устройству (начинает передачу).

6. Читает принятое сообщение.
7. Запрашивает данные у ведомого устройства.

8. Отправляет данные ведущему устройству.

Программный код для ESP32 Slave (Ведомый).

В качестве примера кода для настройки ESP32 в качестве ведомого устройства I2C используется пример из библиотеки Wire.h.

/*
*Обмен данными между двумя платами ESP32 по шине I2C.
* https://arduino-tex.ru/news/208/obmen-dannymi-mezhdu-dvumya-platami-esp32-po-shine-i2c.html
*/

#include "Wire.h"
#define I2C_DEV_ADDR 0x55 // I2C-адрес ведомого устройства
uint32_t i = 0; // Переменная-счетчик
// Функция обратного вызова, выполняемая при запросе данных от ведущего
void onRequest() {
  Wire.print(i++); // Отправка значения счетчика
  Wire.print(" Packets.");
  Serial.println("onRequest"); // Вывод в последовательный порт
  Serial.println();
}
// Функция обратного вызова, выполняемая при получении данных от ведущего
void onReceive(int len) {
  Serial.printf("onReceive[%d]: ", len); // Вывод длины принятого сообщения
  while (Wire.available()) {
    Serial.write(Wire.read()); // Чтение и вывод принятых байтов
  }
  Serial.println();
}
void setup() {
  Serial.begin(115200); // Инициализация последовательного порта
  Serial.setDebugOutput(true);
  Wire.onReceive(onReceive); // Установка функции обратного вызова для приема данных
  Wire.onRequest(onRequest); // Установка функции обратного вызова для отправки данных
  Wire.begin((uint8_t)I2C_DEV_ADDR); // Инициализация I2C с указанием адреса ведомого
  /*#if CONFIG_IDF_TARGET_ESP32 //Потенциально нужная часть кода для некоторых версий ESP32.
    char message[64];
    snprintf(message, 64, "%lu Packets.", i++);
    Wire.slaveWrite((uint8_t *)message, strlen(message));
    Serial.print("Printing config %lu", i);
  #endif*/
}
void loop() {
  // Ведомое устройство не выполняет никаких действий в основном цикле
}

Пояснения к коду (Slave):

  • #include "Wire.h": Подключение библиотеки Wire.h для работы с I2C.
  • #define I2C_DEV_ADDR 0x55: Определение I2C-адреса ведомого устройства.
  • onRequest(): Функция, вызываемая, когда ведущее устройство запрашивает данные. В данном примере отправляется значение переменной i и строка " Packets.".
  • onReceive(): Функция, вызываемая, когда ведомое устройство получает данные от ведущего. В данном примере принятые данные выводятся в последовательный порт.
  • Wire.onReceive(onReceive);: Регистрация функции onReceive как обработчика события приема данных.
  • Wire.onRequest(onRequest);: Регистрация функции onRequest как обработчика события запроса данных.
  • Wire.begin((uint8_t)I2C_DEV_ADDR);: Инициализация I2C-интерфейса в режиме ведомого устройства с указанным адресом. Используются стандартные выводы SDA и SCL.
  • Закомментированный блок #if CONFIG_IDF_TARGET_ESP32 - в документации упоминается метод Wire.slaveWrite(), который, теоретически, должен записывать данные для ответа в буфер перед приемом запроса от ведущего, но на практике код работает и без него. Возможно, этот метод необходим для каких-то конкретных реализаций ESP32, поэтому оставим его закомментированным.

Программный код для ESP32 Master (Ведущий).

В качестве примера кода для настройки ESP32 в качестве ведущего устройства I2C используется пример из библиотеки Wire.h.

/*
*Обмен данными между двумя платами ESP32 по шине I2C.
* https://arduino-tex.ru/news/208/obmen-dannymi-mezhdu-dvumya-platami-esp32-po-shine-i2c.html
*/

#include "Wire.h"
#define I2C_DEV_ADDR 0x55  // I2C-адрес ведомого устройства
uint32_t i = 0;            // Переменная-счетчик
void setup() {
  Serial.begin(115200);  // Инициализация последовательного порта
  Serial.setDebugOutput(true);
  Wire.begin();  // Инициализация I2C в режиме ведущего
}
void loop() {
  delay(5000);  // Задержка 5 секунд
  // Отправка сообщения ведомому устройству
  Wire.beginTransmission(I2C_DEV_ADDR);           // Начало передачи данных ведомому устройству
  Wire.printf("Hello World! %lu", i++);           // Отправка сообщения
  uint8_t error = Wire.endTransmission(true);     // Завершение передачи
  Serial.printf("endTransmission: %u\n", error);  // Вывод кода ошибки
  // Запрос данных у ведомого устройства
  uint8_t bytesReceived = Wire.requestFrom(I2C_DEV_ADDR, 15);  // Запрос 15 байт данных
  Serial.printf("requestFrom: %u\n", bytesReceived);

  if (bytesReceived > 0) {  // Если получены данные
    uint8_t temp[bytesReceived];
    Wire.readBytes(temp, bytesReceived);  // Чтение принятых байтов

    Serial.print("Received data: ");
    for (uint8_t i = 0; i < bytesReceived; i++) {
      Serial.printf("0x%02X ", temp[i]);  // Вывод в шестнадцатеричном формате
    }
    Serial.write(temp, bytesReceived);
    Serial.println();
  }
}

Пояснения к коду (Master):

  • #include "Wire.h": Подключение библиотеки Wire.h.
  • #define I2C_DEV_ADDR 0x55: Определение I2C-адреса ведомого устройства (должно совпадать с адресом, установленным в коде ведомого устройства).
  • Wire.begin();: Инициализация I2C-интерфейса в режиме ведущего устройства. Используются стандартные выводы SDA и SCL.
  • Wire.beginTransmission(I2C_DEV_ADDR);: Начало передачи данных ведомому устройству с указанным адресом.
  • Wire.printf("Hello World! %lu", i++);: Формирование и отправка сообщения ведомому устройству.
  • Wire.endTransmission(true);: Завершение передачи данных. Параметр true указывает на отправку стоп-бита, сигнализирующего об окончании передачи.
  • Wire.requestFrom(I2C_DEV_ADDR, 16);: Запрос 16 байт данных у ведомого устройства с указанным адресом.
  • Wire.readBytes(temp, bytesReceived);: Чтение принятых байтов в массив temp.
  • В оригинальном коде вместо Serial.write(temp, bytesReceived); использовалась функция log_print_buf(temp, bytesReceived), выводящая принятый буфер, но ее описание отсутствует. Для корректного вывода данных в серийный порт заменяем вызов.

Демонстрация обмена данными между двумя ESP32 по I2C.

Для демонстрации обмена данными необходимо:

  1. Загрузить код для ведомого устройства (Slave) на одну плату ESP32.
  2. Загрузить код для ведущего устройства (Master) на другую плату ESP32.
  3. Открыть два окна среды разработки Arduino IDE для одновременного просмотра вывода в последовательный порт обеих плат.

Демонстрация обмена данными между двумя ESP32 по I2C.

Ожидаемые результаты:

  • ESP32 Slave: В последовательном порту будут отображаться сообщения "onRequest" при каждом запросе данных от ведущего устройства, а также принятые сообщения "Hello World!" от ведущего устройства.ESP32 Slave
  • ESP32 Master: В последовательном порту будут отображаться сообщения, отправленные ведомому устройству ("Hello World!"), и данные, полученные от ведомого устройства ("X Packets.", где X - номер пакета).ESP32 Master

Заключение:

Данное руководство предоставило подробную информацию о настройке и использовании протокола I2C для организации обмена данными между двумя платами ESP32. Были рассмотрены основные принципы работы I2C, аппаратные особенности ESP32, схема подключения, а также приведены примеры программного кода для реализации режима ведущего и ведомого устройства. Предоставленная информация позволяет разработчикам создавать собственные проекты, использующие I2C-взаимодействие между микроконтроллерами ESP32, а также интегрировать ESP32 с различными I2C-периферийными устройствами.


Дополнительная информация к данному уроку:

Понравился урок: Обмен данными между двумя платами ESP32 по шине I2C? Не забудь поделиться с друзьями в соц. сетях.

А также подписаться на наш канал на YouTube, вступить в группу Вконтакте, в Telegram.

Спасибо за внимание!

Технологии начинаются с простого!

Фотографии к статье

Файлы для скачивания

Код для ESP32 Slave Код для ESP32 Slave.ino2 Kb 45 Скачать
Код для ESP32 Master Код для ESP32 Master.ino2 Kb 42 Скачать

Комментарии

Ваше Имя*


Разработка проектов