Соединение двух Arduino по I2C: мастер и слейв
Введение
Связь между двумя Arduino полезна, когда одна плата собирает данные с датчиков, а другая выполняет обработку, отображение или управление. I2C (Inter-Integrated Circuit) — простой двухпроводной протокол, подходящий для коротких удалённостей на одной плате или соседних макетных платах.
Важно: I2C требует общей земли (GND) у всех устройств на шине и подтягивающие резисторы (pull-up) на линиях SDA и SCL.
Что такое I2C?
I2C — последовательный двухпроводный протокол связи для обмена данными между микросхемами и микроконтроллерами. Кратко: мастер инициирует передачу, у каждого слейва есть уникальный адрес, линии — SDA (данные) и SCL (тактирование).
Определение в одну строку: I2C — это двухпроводная шина для обмена небольшими сообщениями между устройствами с адресацией.
Как работает I2C?
I2C использует две двунаправленные линии:
- SDA — последовательные данные.
- SCL — тактовая линия.
Мастер генерирует тактовые импульсы и старт/стоп-фреймы. Передача идёт блоками байт: адрес слейва + бит чтения/записи, затем подтверждения (ACK/NACK) и полезная нагрузка.
Ошибка адресации или отсутствие подтягивающих резисторов часто вызывает зависания шины.
Плюсы и ограничения
- Плюсы: простая проводка, несколько мастеров/слейвов, короткие сообщения без сложной физики.
- Ограничения: скорость ограничена (стандартно до 100 кбит/с, быстрый режим 400 кбит/с и выше у некоторых устройств), чувствительность к шуму и длине проводов.
Примечание по питанию: устройства с уровнями 3.3 В можно подключать к 5 В шине только через согласующие преобразователи уровней; альтернативно используйте подтягивание к 3.3 В и убедитесь, что все устройства поддерживают такой уровень.
Аппаратное подключение Arduino по I2C
Необходимые компоненты:
- Две платы Arduino (одна мастер, другая слейв).
- Макетка (breadboard) или провода для прямого соединения.
- Провода (jumper wires).
- Два подтягивающих резистора 4.7 кОм (4.7 kΩ → 4.7 кОм).
Подключение:
- Соедините SDA обеих плат вместе.
- Соедините SCL обеих плат вместе.
- Соедините GND плат между собой (общая земля).
- Подтяните SDA и SCL к линии питания через резисторы 4.7 кОм (обычно к 5 В или к 3.3 В, в зависимости от логики устройств).
Изображение для Arduino Uno (расположение выводов SDA/SCL):
Изображение для Arduino Nano (распиновка I2C):
Подтягивающие резисторы нужны даже если на плате уже есть встроенные подтяжки в некоторых модулях — внешние 4.7 кОм дают предсказуемые уровни и лучшую совместимость.
Библиотека Wire
Wire — встроенная библиотека Arduino для работы с I2C. Она упрощает отправку и приём данных между платами.
Короткий пример подключения заголовка:
#include Основные функции Wire (одна строка объяснения каждой):
- Wire.begin() — подключение к шине I2C (мастер или слейв в зависимости от аргумента).
- Wire.beginTransmission(addr) — начать передачу к слейву с адресом addr.
- Wire.write(data) — записать байт(ы) в буфер передачи.
- Wire.endTransmission() — завершить передачу и отправить данные.
- Wire.requestFrom(addr, qty) — запросить qty байт от слейва с адресом addr.
- Wire.available() — проверить, есть ли ожидаемые байты для чтения.
- Wire.read() — прочитать байт из входного буфера.
- Wire.onReceive(handler) — зарегистрировать обработчик прихода данных (в слейве).
- Wire.onRequest(handler) — зарегистрировать обработчик на чтение запросов мастера (в слейве).
Где находятся выводы SDA/SCL на разных Arduino
- На Uno: SDA — A4, SCL — A5 (старые платы), также есть выводы SDA/SCL рядом с коннектором.
- На Nano: SDA — A4, SCL — A5 (или отдельные пины у некоторых версий).
Всегда проверяйте распиновку конкретной модели.
Пример: простой мастер, читающий данные со слейва
Ниже пример кода мастера, который запрашивает 6 байт у слейва с адресом 8 и печатает их в Serial Monitor.
#include
void setup() {
Wire.begin(); // подключиться к I2C как мастер
Serial.begin(9600); // старт Serial для вывода
}
void receiveData() {
int address = 8;
int bytesToRead = 6;
Wire.requestFrom(address, bytesToRead);
while (Wire.available()) {
char data = Wire.read();
Serial.print(data);
}
delay(500);
}
void loop() {
receiveData();
} Пояснения:
- Wire.requestFrom() блокирует до тех пор, пока не придут байты или таймаут.
- Всегда проверяйте, сколько байт реально пришло через Wire.available().
Пример: простой слейв, отвечающий на запросы мастера
Код слейва, который отвечает строкой “hello “ (6 байт) при запросе мастера:
#include
void setup() {
Wire.begin(8); // присоединиться к шине как слейв с адресом 8
Wire.onRequest(requestEvent); // зарегистрировать обработчик запросов
}
void loop() {
delay(100);
}
void requestEvent() {
Wire.write("hello "); // отправить 6 байт в ответ мастеру
} Важно: Wire.write в слейве отправляет данные только при вызове requestEvent; не пытайтесь вызывать отправку вне контекста запроса.
Пример: пересылка температуры с DHT11 через I2C
Схема: датчик DHT11 подключён к слейв-Arduino (например, к пину 4). Слейв читает температуру и, по запросу мастера, отправляет одно байтовое значение температуры.
Код мастера (запрашивает 1 байт и печатает температуру):
#include
void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("Мастер инициализирован!");
}
void loop() {
Wire.requestFrom(8, 1); // запросить 1 байт температуры у слейва
if (Wire.available()) {
byte temperature = Wire.read(); // прочитать байт температуры
Serial.print("Температура: ");
Serial.print(temperature);
Serial.println(" °C");
}
delay(2000); // ждать 2 секунды
} Код слейва с DHT11 (читает значение и отвечает на запрос мастера):
#include
#include
#define DHTPIN 4
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
byte temperature;
void setup() {
Wire.begin(8); // адрес слейва 8
Wire.onRequest(requestEvent);
dht.begin();
}
void loop() {
delay(2000); // ждать стабилизации датчика
float t = dht.readTemperature();
if (!isnan(t)) {
temperature = (byte)t; // округление до целого, при необходимости используйте другое представление
}
}
void requestEvent() {
Wire.write(temperature); // отправить температуру мастеру
} Советы:
- Если температура может быть отрицательной либо с дробной частью, используйте два байта или фиксированный формат (например, целая часть ×10).
- DHT сенсоры медленны; уважайте требования по времени между измерениями (обычно ≥2 с).
Поиск адресов устройств (I2C Scanner)
Иногда на шине уже есть устройства, и вы не знаете их адрес. Скрипт-сканер перебирает адреса от 1 до 126 и сообщает, где есть ответ.
#include
void setup() {
Wire.begin();
Serial.begin(9600);
while (!Serial);
Serial.println("\nI2C Scanner");
}
void loop() {
byte error, address;
int nDevices = 0;
Serial.println("Scanning...");
for (address = 1; address < 127; address++) {
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("I2C device found at address 0x");
if (address < 16) Serial.print("0");
Serial.print(address, HEX);
Serial.println(" !");
nDevices++;
} else if (error == 4) {
Serial.print("Unknown error at address 0x");
if (address < 16) Serial.print("0");
Serial.println(address, HEX);
}
}
if (nDevices == 0) Serial.println("No I2C devices found\n");
else Serial.println("done\n");
delay(5000);
} Типичные проблемы и их решения
- Шина “виснет” (нет ответов): проверьте общую землю, подтягивающие резисторы и целостность проводки.
- Несоответствие уровней питания: если один модуль 3.3 В, а другой 5 В — используйте уровень-переводчик (level shifter) или подтягивайте к 3.3 В и убедитесь, что 5 В плата допускает 3.3 В как логический HIGH.
- Неправильный адрес: используйте I2C Scanner.
- Медленные или потерянные данные: уменьшите длину проводов, экранируйте или используйте более высокое качество проводки.
- Конфликт нескольких мастеров: реализуйте арбитраж и убедитесь в корректной логике перехвата шины (сложно вручную).
Мини‑методология: как быстро настроить проект
- Подключите GND между платами.
- Соедините SDA и SCL.
- Поставьте подтягивающие резисторы 4.7 кОм к соответствующему уровню питания (3.3 В или 5 В).
- Загрузите скетч-сканер и убедитесь, что устройства видны.
- Назначьте адрес слейву и протестируйте простую передачу строки.
- Переключитесь на реальный формат данных (температура, строка, массив байт).
- Добавьте обработку ошибок и таймауты.
Чеклисты по ролям
Разработчик прошивки:
- Проверить распиновку и питание.
- Написать модульный код для мастера/слейва.
- Добавить логирование (Serial).
Интегратор аппаратной части:
- Убедиться в наличии общей земли.
- Установить подтягивающие резисторы 4.7 кОм.
- Проверить согласование уровней напряжения.
Тестировщик/QA:
- Запустить I2C Scanner.
- Проверить устойчивость при перезагрузке одного устройства.
- Смоделировать потерю данных и проверить поведение мастера.
Критерии приёмки
- Мастер успешно запрашивает и получает данные от слейва в 5 из 5 последовательных запросов.
- При отключении датчика слейв не блокирует шину (обрабатывает или возвращает корректный код ошибки).
- При разном напряжении (3.3 В / 5 В) подключение защищено уровневым преобразователем.
1‑строчный глоссарий
I2C — двухпроводной протокол связи (SDA, SCL) с адресацией устройств; мастер инициирует передачу, слейв отвечает.
Диаграмма принятия решения (Mermaid)
flowchart TD
A[Нужно ли несколько плат?] -->|Да| B{Короткая шина и много устройств?}
B -->|Да| C[I2C]
B -->|Нет, нужна высокая скорость| D[SPI]
A -->|Нет| E[Одна плата — не нужно I2C]
C --> F[Реализовать Wire и подтяжки]
D --> G[Реализовать SPI: MOSI/MISO/SCLK/SS]Заключение
I2C — практичный выбор для простого обмена данными между несколькими Arduino на одной шине. Библиотека Wire обеспечивает базовый функционал для реализации мастер/слейв взаимодействия. Всегда проверяйте адреса устройств, согласование уровней и наличие общих GND. Начните с простого примера и постепенно добавляйте обработку ошибок и форматирование данных.
Важное: протестируйте систему в реальной аппаратной конфигурации и используйте I2C Scanner перед развёртыванием.
Image credits: Arduino I2C documentation
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone