Библиотека Wire и шина I2C
Обновлено
Скачивать не нужно. Wire входит в пакет платы: для UNO, Nano и Mega это пакет Arduino AVR Boards, он ставится вместе со средой. В менеджере библиотек её нет — и искать там не надо.
Шина I2C: два провода, десятки устройств с разными адресами. Сканер адресов, чтение регистров, связь двух плат.
Wire — стандартная библиотека Arduino для шины I2C, по которой к плате подключают дисплеи, часы, гироскопы и датчики давления. Файл Wire.h отдельно не скачивают: он уже лежит в пакете платы. Файла i2c.h в Arduino нет — шиной I2C занимается именно Wire.
Не путайте с 1-Wire: несмотря на похожее имя, это другая шина с одним проводом данных, для датчиков DS18B20. Для неё нужна OneWire.
Напрямую с Wire пишут реже, чем через неё: библиотеки дисплеев и датчиков используют её внутри. Но без неё не обойтись в трёх случаях: найти адрес неизвестного модуля сканером, прочитать регистры чипа, для которого нет библиотеки, и связать две платы между собой.
Как скачать и установить
Ничего ставить не нужно: достаточно строки подключения в начале скетча. Если среда пишет Wire.h: No such file or directory, проверьте, какая плата выбрана в меню «Инструменты»: у некоторых пакетов плат этой библиотеки нет или она называется иначе.
Строка подключения:
#include <Wire.h>Создание объекта — обычно до setup():
Wire.begin(); // в setup(); объект Wire уже создан библиотекой
Wire.setClock(400000); // необязательно: 400 кГц вместо 100 кГц, если все устройства умеютШина I2C и выводы на платах
I2C — два провода на все устройства сразу: данные SDA и такт SCL, плюс общая земля. Каждое устройство слушает свой адрес — семибитное число, обычно его пишут шестнадцатеричным: 0x27 у переходника дисплея 1602, 0x68 у часов DS3231 и гироскопа MPU6050, 0x3C у OLED. Плата-ведущий обращается к ним по очереди.
Линиям нужна подтяжка к питанию — резисторы 4,7 кОм. На модулях она почти всегда уже стоит: на схеме ниже это два резистора с маркировкой 472 на плате часов. Своих ставить не надо, пока модулей на шине два-три и провода короткие.

- Arduino UNO
- SDA: A4SCL: A5
Те же линии выведены отдельными гнёздами SDA и SCL рядом с AREF.
- Arduino Nano
- SDA: A4SCL: A5
Как у UNO.
- Arduino Pro Mini
- SDA: A4SCL: A5
На массовом клоне — отверстия внутри платы, над A2 и A3.
- Arduino Mega
- SDA: 20SCL: 21
Не A4 и A5, как у UNO. Дублируются гнёздами SDA и SCL у AREF.
- Leonardo, Micro
- SDA: 2SCL: 3
Выводы 2 и 3 заняты шиной — прерывания на них с I2C не совместить.
- ESP8266, Wemos D1 mini
- SDA: D2 (GPIO4)SCL: D1 (GPIO5)
Можно назначить другие:
Wire.begin(sda, scl). - ESP32
- SDA: GPIO21SCL: GPIO22
Любые другие —
Wire.begin(sda, scl).
Встроенная подтяжка — только на коротких проводах
Wire.begin() на AVR включает подтяжку внутри микроконтроллера, но она слабая, десятки килоом. С модулем, на котором резисторов нет, шина работает на проводах в пару сантиметров и сбоит на макетке. Тогда ставят свои 4,7 кОм от SDA и SCL к питанию.
Модуль на 3,3 В и плата на 5 В
Если у модуля свой стабилизатор и подтяжка к 3,3 В, с UNO он обычно работает напрямую. Голый чип на 3,3 В без стабилизатора подключают через преобразователь уровней: встроенная подтяжка UNO тянет линии к 5 В.
Провода — десятки сантиметров, не метры
I2C придумана для связи внутри одной платы. На длинных проводах растёт ёмкость, фронты заваливаются, и обмен сбоит. Помогают частота 100 кГц, подтяжка посильнее — 2,2 кОм — и витая пара; для метров есть удлинители шины.
Методы
Только то, что нужно в обычном скетче. Сигнатуры сверены по заголовкам: ядро AVR 1.8.8.
Запуск
void begin()
Включить шину ведущим устройством — так работают почти все скетчи. Вызывают в setup() один раз, до любого обмена. На AVR заодно включает встроенную подтяжку на SDA и SCL.
Wire.begin();
void begin(uint8_t address)
Включить плату ведомым устройством со своим адресом — когда она сама отвечает другой плате.
- address
- адрес 8–119: младшие и старшие адреса зарезервированы стандартом.
Wire.begin(8);
void end()
Выключить шину и отпустить выводы. Нужно редко — например, чтобы перед сном платы снять подтяжку и не тратить на неё ток.
Wire.end();
void setClock(uint32_t clock)
Частота шины. Звать после begin(). Быстрее — только если все устройства на шине её поддерживают, а провода короткие.
- clock
- 100000 — по умолчанию, 400000 — быстрый режим; большинство дисплеев и датчиков его умеют.
Wire.setClock(400000);
Передача
void beginTransmission(uint8_t address)
Начать посылку устройству. Ничего не отправляет — только готовит буфер; байты кладёт write(), уходят они в endTransmission().
- address
- семибитный адрес устройства, 0x08–0x77. Если в даташите 0x4E или 0xD0 — это адрес со сдвигом, делите на два.
Wire.beginTransmission(0x68);
size_t write(uint8_t data)
Положить байт в буфер посылки. Буфер — 32 байта: всё сверх молча отбрасывается.
- data
- байт: номер регистра, значение, команда.
Возвращает: 1 — байт в буфере, 0 — буфер полон.
Wire.write(0x6B); // номер регистра Wire.write(0); // значение
size_t write(const uint8_t* data, size_t quantity)
Положить в буфер сразу несколько байт — массив или переменную целиком.
- data
- откуда: имя массива или
(uint8_t*)&переменная. - quantity
- сколько байт; вместе с уже лежащими — не больше 32.
Возвращает: сколько байт поместилось.
Wire.write((uint8_t*)&n, sizeof(n));
uint8_t endTransmission()
Отправить буфер и отпустить шину. По коду возврата видно, что пошло не так, — на нём построен сканер.
Возвращает: 0 — доставлено; 2 — по адресу никто не ответил; 3 — устройство не приняло байт данных; 4 — другая ошибка шины; 5 — таймаут, если включён setWireTimeout().
byte err = Wire.endTransmission();
uint8_t endTransmission(uint8_t sendStop)
То же, но можно не отпускать шину — повторный старт. Так читают регистр: записать его номер и сразу, не отдавая шину, запросить ответ.
- sendStop
- true — отпустить шину, как без аргумента; false — удержать, следом обязательно
requestFrom().
Возвращает: те же коды 0–5.
Wire.endTransmission(false);
Чтение
uint8_t requestFrom(uint8_t address, uint8_t quantity)
Запросить байты у устройства. Ждёт, пока они придут, и складывает в буфер приёма — дальше их забирает read().
- address
- адрес устройства.
- quantity
- сколько байт. Больше 32 не бывает: буфер приёма 32 байта, лишнее обрезается.
Возвращает: сколько байт пришло; 0 — устройство не ответило.
if (Wire.requestFrom(0x68, 7) == 7) { /* читаем */ }uint8_t requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop)
То же с выбором, отпускать ли шину после чтения. Нужно, когда за чтением сразу идёт следующая посылка тому же устройству.
- sendStop
- true — по умолчанию, отпустить шину; false — удержать.
Возвращает: сколько байт пришло.
Wire.requestFrom(0x68, 2, true);
uint8_t requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop)
Чтение регистра одним вызовом: библиотека сама отправит номер регистра и сделает повторный старт. Короче, чем связка write + endTransmission(false).
- iaddress
- номер регистра.
- isize
- сколько байт в номере регистра: 1 у датчиков, 2 у больших EEPROM вроде 24C256.
- sendStop
- true — отпустить шину после чтения.
Возвращает: сколько байт пришло.
Wire.requestFrom(0x68, 1, 0x75, 1, true); // WHO_AM_I
int available()
Сколько принятых байт ещё не прочитано.
Возвращает: 0–32.
while (Wire.available()) buf[i++] = Wire.read();
int read()
Забрать следующий принятый байт.
Возвращает: байт 0–255; -1 — читать нечего.
uint8_t sec = Wire.read();
int peek()
Посмотреть следующий байт, не забирая его.
Возвращает: байт 0–255; -1 — читать нечего.
if (Wire.peek() == -1) Serial.println("Читать нечего");Ведомое устройство
void onReceive(void (*handler)(int))
Назначить функцию, которую библиотека вызовет, когда ведущий пришлёт данные. Функция работает в прерывании: коротко, без delay() и Serial.print().
- handler
- функция
void имя(int count), где count — сколько байт пришло.
Wire.onReceive(receiveEvent);
void onRequest(void (*handler)(void))
Назначить функцию, которая отвечает, когда ведущий просит данные. Внутри неё — write() с ответом, тоже без задержек.
- handler
- функция
void имя()без аргументов.
Wire.onRequest(requestEvent);
Таймаут
void setWireTimeout(uint32_t timeout = 25000, bool reset_with_timeout = false)
Не ждать шину вечно. По умолчанию таймаут выключен: если датчик отключили посреди обмена или он завис, прижав линию, скетч встаёт навсегда.
- timeout
- микросекунды; 25000 — 25 мс, предел по стандарту SMBus. 0 — выключить.
- reset_with_timeout
- true — после таймаута перезапустить шину самой библиотеке. Обычно ставят true.
Wire.setWireTimeout(25000, true);
bool getWireTimeoutFlag()
Проверить, был ли таймаут с момента последнего сброса флага.
Возвращает: true — был.
if (Wire.getWireTimeoutFlag()) Serial.println("Шина зависала");void clearWireTimeoutFlag()
Сбросить флаг таймаута, чтобы заметить следующий.
Wire.clearWireTimeoutFlag();
Примеры
Сканер I2C: найти адрес модуля
#include <Wire.h>
// сканер I2C: печатает адреса всех устройств на шине
void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("Ищу устройства...");
byte found = 0;
for (byte addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) { // 0 — кто-то ответил
Serial.print("Найден адрес 0x");
if (addr < 16) Serial.print('0');
Serial.println(addr, HEX);
found++;
}
}
Serial.print("Готово, устройств: ");
Serial.println(found);
}
void loop() {}Первое, что запускают с любым новым I2C-модулем. Если сканер ничего не нашёл — дело в проводах, питании или подтяжке, а не в библиотеке дисплея.
Монитор порта:
Ищу устройства...\nНайден адрес 0x27\nНайден адрес 0x68\nГотово, устройств: 2
Чтение регистра с повторным стартом
#include <Wire.h>
const uint8_t MPU = 0x68; // MPU6050; с AD0 на питании — 0x69
// прочитать один регистр: записать его номер, повторный старт, прочитать байт
uint8_t readRegister(uint8_t addr, uint8_t reg) {
Wire.beginTransmission(addr);
Wire.write(reg); // какой регистр нужен
Wire.endTransmission(false); // false — без стопа: шину не отпускаем
Wire.requestFrom(addr, (uint8_t)1);
return Wire.available() ? Wire.read() : 0xFF;
}
void setup() {
Serial.begin(9600);
Wire.begin();
uint8_t id = readRegister(MPU, 0x75); // регистр WHO_AM_I
Serial.print("WHO_AM_I = 0x");
Serial.println(id, HEX);
}
void loop() {}Главный приём работы с датчиком без библиотеки: записать номер регистра, не отпуская шину, и прочитать ответ. У MPU6050 в регистре 0x75 лежит 0x68. Ответ 0x70 значит, что на плате MPU6500 под видом 6050, 0x71 — MPU9250, 0xFF — устройство не ответило.
Монитор порта:
WHO_AM_I = 0x68
Время из DS3231 без библиотеки
#include <Wire.h>
const uint8_t RTC = 0x68; // DS3231
// часы хранят числа в двоично-десятичном виде: 0x59 — это 59
uint8_t bcd2dec(uint8_t v) { return (v >> 4) * 10 + (v & 0x0F); }
void print2(uint8_t v, char after) {
if (v < 10) Serial.print('0');
Serial.print(v);
if (after) Serial.print(after);
}
void setup() {
Serial.begin(9600);
Wire.begin();
}
void loop() {
Wire.beginTransmission(RTC);
Wire.write(0x00); // читать с регистра секунд
Wire.endTransmission(false);
if (Wire.requestFrom(RTC, (uint8_t)7) == 7) {
uint8_t s = bcd2dec(Wire.read());
uint8_t m = bcd2dec(Wire.read());
uint8_t h = bcd2dec(Wire.read() & 0x3F); // 24-часовой режим
Wire.read(); // день недели пропускаем
uint8_t d = bcd2dec(Wire.read());
uint8_t mo = bcd2dec(Wire.read() & 0x1F); // старший бит — признак века
uint8_t y = bcd2dec(Wire.read());
print2(h, ':'); print2(m, ':'); print2(s, ' ');
print2(d, '.'); print2(mo, '.'); print2(y, 0);
Serial.println();
} else {
Serial.println("DS3231 не отвечает");
}
delay(1000);
}Семь регистров подряд одним запросом: секунды, минуты, часы, день недели, число, месяц, год. Часы хранят их в двоично-десятичном виде, поэтому каждое число переводится bcd2dec(). У DS3231 и MPU6050 один адрес, 0x68: на одной шине их разводят перемычкой AD0 на гироскопе — тогда он переезжает на 0x69.
Монитор порта:
14:05:07 29.09.26\n14:05:08 29.09.26\n14:05:09 29.09.26
Две Arduino по I2C: ведущая (master) и ведомая (slave)
#include <Wire.h>
const bool MASTER = true; // на второй плате — false
const uint8_t SLAVE_ADDR = 8; // адрес ведомой платы, любой из 8–119
volatile int16_t lastValue = 0; // ведомая: последнее принятое число
// ведомая: ведущая прислала данные
void receiveEvent(int count) {
if (count >= 2) {
int16_t v = Wire.read();
v |= Wire.read() << 8;
lastValue = v;
}
while (Wire.available()) Wire.read(); // лишнее выбрасываем
}
// ведомая: ведущая просит ответ
void requestEvent() {
int16_t reply = lastValue * 2;
Wire.write((uint8_t*)&reply, sizeof(reply));
}
void setup() {
Serial.begin(9600);
if (MASTER) {
Wire.begin();
} else {
Wire.begin(SLAVE_ADDR);
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
}
}
void loop() {
if (!MASTER) return; // ведомая работает в обработчиках
static int16_t n = 0;
n++;
Wire.beginTransmission(SLAVE_ADDR);
Wire.write((uint8_t*)&n, sizeof(n));
byte err = Wire.endTransmission();
if (err) {
Serial.print("Ошибка ");
Serial.println(err); // 2 — ведомая не ответила
} else if (Wire.requestFrom(SLAVE_ADDR, (uint8_t)2) == 2) {
int16_t r = Wire.read();
r |= Wire.read() << 8;
Serial.print(n); Serial.print(" → "); Serial.println(r);
}
delay(1000);
}Один скетч на обе платы — меняется только константа MASTER. Соединяют A4 с A4, A5 с A5 и землю с землёй. Ведущая шлёт число, ведомая запоминает его и в ответ на запрос отдаёт удвоенным. Обработчики ведомой работают в прерывании, поэтому в них нет Serial.print().
Монитор порта:
1 → 2\n2 → 4\n3 → 6\nОшибка 2 ← ведомую выключили
На чём попадаются
Дисплей молчит, сканер ничего не находит
Перепутаны SDA и SCL, нет общей земли или модуль без подтяжки на длинных проводах. Сначала сканер: если он не видит устройство, библиотека дисплея не поможет.
Плата зависает при отключении датчика
Wire по умолчанию ждёт шину бесконечно. Wire.setWireTimeout(25000, true) в setup() — и вместо зависания endTransmission() вернёт 5.
Запросил 64 байта — пришло 32
Буфер Wire на AVR — 32 байта и на приём, и на передачу. requestFrom() молча обрезает запрос, write() сверх 32 байт возвращает 0. Длинные данные — EEPROM, картинки для дисплея — читают и пишут кусками.
После Wire.begin() сломался A4 или A5
На UNO и Nano эти выводы принадлежат шине. pinMode() и digitalWrite() на них ломают обмен, а analogRead() показывает не датчик, а уровень шины. Аналоговые датчики — на A0–A3.
Два устройства с одним адресом
На одной шине так нельзя. У многих модулей адрес меняется перемычкой — у MPU6050 это AD0, у переходника 1602 — площадки A0–A2. Иначе — вторая шина на других выводах (ESP32) или мультиплексор TCA9548A.
Адрес в даташите 0x4E, а сканер показывает 0x27
Даташиты иногда пишут адрес вместе с битом чтения-записи. Wire работает с семибитным: делите на два.
Где используется на сайте
- Дисплей LCD 1602: схема подключения и скетч
- OLED SSD1306: схема подключения и скетч
- Часы DS3231: схема подключения и скетч
- Гироскоп MPU6050: схема подключения и скетч
- Датчик давления BMP280: схема подключения и скетч