Главная chevron_right Уроки chevron_right Связь chevron_right Протокол I2C (Inter-Integrated Circuit) и библиотека Wire

Протокол I2C (Inter-Integrated Circuit) и библиотека Wire

Подключайте датчики, дисплеи и другие устройства к Arduino по двухпроводной шине I2C, используя библиотеку Wire — минимум проводов, максимум возможностей.

Введение

Если хочется добавить в проект новые возможности, не утопая при этом в проводах, — протокол I2C именно то, что нужно. Он поддерживается на всех платах Arduino и позволяет подключить сразу несколько периферийных устройств — датчики, дисплеи, драйверы моторов и многое другое — всего по двум линиям. Это ускоряет прототипирование и оставляет остальные пины свободными для других задач.

В этом уроке разберём, как работает I2C, какие стандарты разъёмов существуют, и как пользоваться библиотекой Wire Library для создания собственных I2C-устройств.

Что такое I2C?

Протокол I2C использует всего две линии:

  • SCL (Serial Clock) — тактовый сигнал, который генерирует контроллер;
  • SDA (Serial Data) — линия, по которой передаются данные между устройствами.

В шине I2C всегда есть одно управляющее устройство (контроллер) и одно или несколько периферийных. Все они подключены к общим линиям SCL и SDA.

Каждый раз, когда тактовый сигнал переходит из LOW в HIGH (передний фронт), по линии SDA передаётся один бит данных. Побитово складывается 7- или 8-битный адрес устройства, за которым следует команда или данные. Нужное устройство распознаёт свой адрес, выполняет запрос и при необходимости отправляет ответ обратно — по той же линии I2C, используя тактовый сигнал контроллера на SCL как ориентир по времени.

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

Структура сообщения I2C

An I2C Message

Структура сообщения I2C: 7-битный адрес устройства → бит Read/Write → подтверждение (ACK) → пакет данных 1 → подтверждение → пакет данных 2 → подтверждение → Stop.

Вот как устроено типичное I2C-сообщение:

  1. Контроллер выставляет на шине I2C адрес нужного устройства через линию SDA.
  2. Следует бит, указывающий направление передачи: чтение или запись.
  3. Получатель подтверждает приём (ACK), и контроллер переходит к следующим данным.
  4. Передаётся 8 бит данных, затем снова ACK.
  5. Передача повторяется нужное количество раз.

Линия SCL синхронизирует все устройства на шине — именно она гарантирует, что каждый участник «читает» биты в правильный момент.

На практике вам почти никогда не придётся думать об этих деталях: библиотека Wire library берёт всё на себя.

Пины I2C на платах Arduino

В таблице ниже указаны пины I2C для разных форм-факторов плат:

Form factorSDASCLSDA1SCL1SDA2SCL2
UNOSDA/A4SCL/A5
NanoA4A5
MKRD11D12
GIGAD20D21D102D101D9D8
Mega & DueD20D21

Совет: на большинстве плат Arduino пины SDA и SCL подписаны прямо на плате. Если подписей нет — ищите их в документации или распиновке.

Подключение устройств по I2C

Существует несколько способов подключить I2C-модули к Arduino. Выбор зависит от конкретного модуля и ваших предпочтений.

Обычные модули (Breakout Boards)

Некоторые модули подключаются напрямую через макетную плату обычными проводами. Схема подключения:

  • VCC* → пин 5V или 3V3 (зависит от модуля)
  • GND* → GND
  • SDA → SDA
  • SCL → SCL

Названия пинов могут отличаться: VCC иногда подписан как «VIN» или «+», а GND — как «−».

Пример подключения датчика к UNO R4 WiFi без специального разъёма:

Bare I2C Wiring on UNO R4 WiFi

Подключение по I2C к Arduino UNO R4 WiFi без специального разъёма: линии SDA, SCL, 3V3 и GND соединяются напрямую.

Qwiic и STEMMA QT

На рынке существуют целые экосистемы, построенные вокруг протокола I2C. Два самых популярных стандарта — Qwiic от SparkFun и STEMMA QT от Adafruit®. Оба используют 4-контактный разъём JST SH, в котором объединены питание, земля и линии SDA/SCL для I2C. Один кабель — и всё подключено.

I2C on a Qwiic/STEMMA QT connector with UNO R4 WiFi

Подключение по I2C через разъём Qwiic/STEMMA QT к Arduino UNO R4 WiFi — один кабель вместо четырёх отдельных проводов.

В чём разница между Qwiic и STEMMA QT?

Внешне модули практически неотличимы, но между стандартами есть принципиальное различие:

  • Qwiic имеет сдвиг уровней и стабилизатор напряжения на контроллере, поэтому работает только с логикой 3.3 В. Меньше нюансов при сборке схемы.
  • STEMMA QT поддерживает как 3.3 В, так и 5 В логику. Больше гибкости, но нужно внимательнее следить за уровнями напряжения.

Порядок контактов в STEMMA QT совпадает с Qwiic, что обеспечивает кросс-совместимость между стандартами.

Grove

Grove — ещё один стандарт разъёмов, разработанный Seeed Studio. Среди Grove-модулей встречаются устройства с поддержкой I2C, хотя не все из них используют именно этот протокол. Ни одна плата Arduino не имеет встроенного Grove-разъёма, но для подключения можно использовать MKR connector carrier, Nano Grove Shield или Base Shield от Arduino Sensor Kit.

Grove OLED over I2C

Подключение Grove IMU по I2C к Arduino UNO R4 WiFi: линии SCL, SDA, 3V3 и GND.

Библиотека Wire

Для работы с I2C-устройствами в Arduino используется библиотека Wire. Она входит в состав всех пакетов плат, устанавливать её вручную не нужно.

Полная документация API доступна здесь: documentation page

Основные функции:

  • begin() — инициализация шины I2C
  • end() — закрытие шины I2C
  • requestFrom() — запрос байт от периферийного устройства
  • beginTransmission() — начало формирования передачи
  • endTransmission() — отправка накопленных байт и завершение передачи
  • write() — запись данных (от периферии к контроллеру или наоборот)
  • available() — возвращает количество байт, доступных для чтения
  • read() — читает один байт, полученный от периферии
  • setClock() — изменяет частоту тактового сигнала
  • onReceive() — регистрирует функцию-обработчик входящей передачи (для периферии)
  • onRequest() — регистрирует функцию-обработчик запроса от контроллера
  • setWireTimeout() — устанавливает таймаут для передачи в режиме контроллера
  • clearWireTimeoutFlag() — сбрасывает флаг таймаута
  • getWireTimeoutFlag() — проверяет, произошёл ли таймаут с момента последнего сброса флага

Библиотеки поверх Wire

Почти каждый I2C-модуль поставляется с собственной библиотекой, которая упрощает работу с ним. Как правило, такая библиотека построена поверх Wire и использует её внутри. Она добавляет удобные функции — например, для чтения температуры одной строкой кода.

Хороший пример — датчик MCP9808 от Adafruit®. Достаточно установить библиотеку Adafruit_MCP9808 через менеджер библиотек IDEs, и вы сможете вызывать tempsensor.readTempC() вместо того, чтобы вручную писать весь Wire-код запроса и разбора ответа.

Как устанавливать библиотеки — читайте в нашем руководстве: guide to installing libraries

Практические замечания

  • Подтягивающие резисторы: линии SDA и SCL — это линии с открытым коллектором. Для корректной работы они должны быть подтянуты к питанию через резисторы (обычно 4.7 кОм для 5 В или 2.2 кОм для 3.3 В). Многие готовые модули уже имеют встроенные подтягивающие резисторы — проверьте схему вашего модуля.
  • Адреса устройств: если два устройства на шине имеют одинаковый адрес, возникнет конфликт. У многих модулей адрес можно изменить с помощью перемычки или пина ADDR.
  • Длина линий: I2C хорошо работает на коротких расстояниях (до нескольких метров). На длинных линиях возможны помехи — снижайте частоту через setClock().

Типичные ошибки

  • Перепутаны SDA и SCL — устройство не отвечает.
  • Неправильное напряжение питания модуля (5 В вместо 3.3 В или наоборот) — модуль может повредиться.
  • Забыты подтягивающие резисторы при использовании модулей без встроенных — шина не работает.
  • Неверный адрес устройства в коде — используйте I2C-сканер для определения реального адреса.

Примеры

Controller Reader (контроллер читает данные)

Arduino Boards connected via I2C

В этом примере два Arduino обмениваются данными в конфигурации «контроллер читает / периферия отправляет» через I2C synchronous serial protocol. Arduino 1 (контроллер) запрашивает 6 байт у периферии по уникальному адресу и выводит полученные данные в Serial Monitor. Используются функции библиотеки Wire Library.

Скетч контроллера (Controller Reader)

// Wire: контроллер читает данные
// автор Nicholas Zambetti [http://www.zambetti.com](http://www.zambetti.com)

// Демонстрация использования библиотеки Wire
// Чтение данных с периферийного устройства I2C/TWI
// Используйте совместно с примером "Wire: периферийное устройство отправляет данные"

// Создан 29 марта 2006

// Код примера находится в общественном достоянии.


#include <Wire.h>

void setup() {
  Wire.begin();        // подключение к шине I2C (адрес для master необязателен)
  Serial.begin(9600);  // запуск Serial для вывода данных
}

void loop() {
  Wire.requestFrom(8, 6);    // запрос 6 байт от периферийного устройства #8

  while (Wire.available()) { // периферийное устройство может отправить меньше, чем запрошено
    char c = Wire.read(); // принять байт как символ
    Serial.print(c);         // вывести символ
  }

  delay(500);
}

Скетч периферии (Peripheral Sender)

// Wire: периферийное устройство отправляет данные
// автор Nicholas Zambetti [http://www.zambetti.com](http://www.zambetti.com)

// Демонстрация использования библиотеки Wire
// Отправка данных как периферийного устройства I2C/TWI
// Используйте совместно с примером "Wire: контроллер читает данные"

// Создан 29 марта 2006

// Код примера находится в общественном достоянии.


#include <Wire.h>

void setup() {
  Wire.begin(8);                // подключение к шине I2C с адресом #8
  Wire.onRequest(requestEvent); // регистрация события
}

void loop() {
  delay(100);
}

// функция, вызываемая при запросе данных от master
// эта функция зарегистрирована как событие, см. setup()
void requestEvent() {
  Wire.write("hello "); // ответить сообщением из 6 байт
  // как и ожидает master
}

Два Arduino, соединённых по шине I2C: линии SCL, SDA и GND соединяются между платами.

Controller Writer (контроллер записывает данные)

Arduino Boards connected via I2C

В этом примере два Arduino работают в конфигурации «контроллер пишет / периферия принимает» через I2C synchronous serial protocol. Arduino 1 (контроллер) отправляет 6 байт каждые полсекунды на периферию по уникальному адресу. Периферия выводит принятые данные в Serial Monitor на компьютере с USB. Используются функции библиотеки Wire Library.

Скетч контроллера (Controller Writer)

// Wire: контроллер записывает данные
// автор Nicholas Zambetti [http://www.zambetti.com](http://www.zambetti.com)

// Демонстрация использования библиотеки Wire
// Запись данных на периферийное устройство I2C/TWI
// Используйте совместно с примером "Wire: периферийное устройство принимает данные"

// Создан 29 марта 2006

// Код примера находится в общественном достоянии.


#include <Wire.h>

void setup()
{
  Wire.begin(); // подключение к шине I2C (адрес для master необязателен)
}

byte x = 0;

void loop()
{
  Wire.beginTransmission(4); // передача устройству #4
  Wire.write("x is ");        // отправка пяти байт
  Wire.write(x);              // отправка одного байта
  Wire.endTransmission();    // завершение передачи

  x++;
  delay(500);
}

Скетч периферии (Peripheral Receiver)

// Wire: периферийное устройство принимает данные
// автор Nicholas Zambetti [http://www.zambetti.com](http://www.zambetti.com)

// Демонстрация использования библиотеки Wire
// Приём данных как периферийного устройства I2C/TWI
// Используйте совместно с примером "Wire: контроллер записывает данные"

// Создан 29 марта 2006

// Код примера находится в общественном достоянии.


#include <Wire.h>

void setup()
{
  Wire.begin(4);                // подключение к шине I2C с адресом #4
  Wire.onReceive(receiveEvent); // регистрация события
  Serial.begin(9600);           // запуск Serial для вывода данных
}

void loop()
{
  delay(100);
}

// функция, вызываемая при получении данных от master
// эта функция зарегистрирована как событие, см. setup()
void receiveEvent(int howMany)
{
  while(1 < Wire.available()) // перебор всех байт, кроме последнего
  {
    char c = Wire.read(); // принять байт как символ
    Serial.print(c);         // вывести символ
  }
  int x = Wire.read();    // принять байт как целое число
  Serial.println(x);         // вывести целое число
}

Акселерометр

Grove IMU over I2C

Этот скетч читает данные акселерометра с устройства Grove 6-Axis Accelerometer module через библиотеку seeed arduino LSM6DS3 library:

#include "LSM6DS3.h"
#include "Wire.h"

//Создание экземпляра класса Accelerometer
LSM6DS3 accelerometer(I2C_MODE, 0x6A);

void setup() {
    // код настройки, выполняется один раз:
    Serial.begin(9600);
    while (!Serial);

    if (accelerometer.begin() != 0) {
        Serial.println("LSM6DS3 not found, check your wiring.");
    } else {
        Serial.println("LSM6DS3 found!");
    }
}

void loop() {
    //Гироскоп
    Serial.print("\nGyroscope:\n");
    Serial.print(" X1 = ");
    Serial.println(accelerometer.readFloatGyroX(), 4);
    Serial.print(" Y1 = ");
    Serial.println(accelerometer.readFloatGyroY(), 4);
    Serial.print(" Z1 = ");
    Serial.println(accelerometer.readFloatGyroZ(), 4);

    //Акселерометр
    Serial.print("\nAccelerometer:\n");
    Serial.print(" X1 = ");
    Serial.println(accelerometer.readFloatAccelX(), 4);
    Serial.print(" Y1 = ");
    Serial.println(accelerometer.readFloatAccelY(), 4);
    Serial.print(" Z1 = ");
    Serial.println(accelerometer.readFloatAccelZ(), 4);

    delay(1000);
}

I2C BMP280

Qwiic BMP280 module

Пример чтения температуры по I2C с модуля BMP280 от Adafruit®:

#include <Wire.h>
#include <Adafruit_BMP280.h>

//Создание экземпляра датчика BMP280
Adafruit_BMP280 bmp;

void setup() {
  Serial.begin(9600);

  // Запуск датчика и проверка его наличия
  if (!bmp.begin()) {
    Serial.println("Sensor not found");
    while (1){}
  }

}

void loop() {
  // Чтение значений
  float temperature = bmp.readTemperature();

  // Вывод в Serial Monitor
  Serial.print("Temperature: ");
  Serial.print(temperature);
  Serial.println(" C");

  Serial.println();
  delay(2000);
}

Модуль Qwiic BMP280, подключённый к Arduino UNO R4 WiFi.

I2C OLED

Grove OLED over I2C

Этот пример рисует версию логотипа Arduino на 128×64 Grove OLED-дисплее через I2C:

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 display(128, 64, &Wire, -1);

// Логотип Arduino в формате bitmap
const uint8_t arduinoLogo[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x30, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x70, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x07, 0xff, 0xff, 0x80, 0x50, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xe0, 0x40, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xf0, 0x20, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x01, 0xff, 0xfc, 0x0f, 0xff, 0xe0, 0x07, 0xff, 0xf0, 0x3f, 0xff, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x01, 0xff, 0xc0, 0x00, 0xff, 0xf0, 0x0f, 0xff, 0x00, 0x07, 0xff, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x03, 0xff, 0x80, 0x00, 0x7f, 0xf8, 0x1f, 0xfc, 0x00, 0x01, 0xff, 0xc0, 0x00, 0x00,
  0x00, 0x00, 0x07, 0xfe, 0x00, 0x00, 0x1f, 0xfc, 0x3f, 0xf8, 0x00, 0x00, 0x7f, 0xe0, 0x00, 0x00,
  0x00, 0x00, 0x07, 0xfc, 0x00, 0x00, 0x0f, 0xfe, 0x7f, 0xf0, 0x00, 0x00, 0x3f, 0xe0, 0x00, 0x00,
  0x00, 0x00, 0x0f, 0xf8, 0x00, 0x00, 0x07, 0xff, 0x7f, 0xe0, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00,
  0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x03, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00,
  0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x01, 0xff, 0xff, 0x80, 0x0f, 0x00, 0x0f, 0xf8, 0x00, 0x00,
  0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x0f, 0x80, 0x07, 0xf8, 0x00, 0x00,
  0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x0f, 0x80, 0x07, 0xf8, 0x00, 0x00,
  0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x0f, 0x80, 0x07, 0xf8, 0x00, 0x00,
  0x00, 0x00, 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x0f, 0x80, 0x03, 0xf8, 0x00, 0x00,
  0x00, 0x00, 0x1f, 0xc0, 0x1f, 0xff, 0x00, 0x1f, 0xf8, 0x00, 0xff, 0xf8, 0x03, 0xfc, 0x00, 0x00,
  0x00, 0x00, 0x1f, 0xc0, 0x3f, 0xff, 0x00, 0x1f, 0xf8, 0x00, 0xff, 0xf8, 0x03, 0xfc, 0x00, 0x00,
  0x00, 0x00, 0x1f, 0xc0, 0x3f, 0xff, 0x00, 0x1f, 0xf0, 0x00, 0xff, 0xf8, 0x03, 0xfc, 0x00, 0x00,
  0x00, 0x00, 0x1f, 0xc0, 0x1f, 0xff, 0x00, 0x1f, 0xf8, 0x00, 0xff, 0xf8, 0x03, 0xfc, 0x00, 0x00,
  0x00, 0x00, 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x0f, 0x80, 0x03, 0xf8, 0x00, 0x00,
  0x00, 0x00, 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x0f, 0x80, 0x07, 0xf8, 0x00, 0x00,
  0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x0f, 0x80, 0x07, 0xf8, 0x00, 0x00,
  0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x0f, 0x80, 0x07, 0xf8, 0x00, 0x00,
  0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x01, 0xff, 0xff, 0x80, 0x0f, 0x00, 0x0f, 0xf8, 0x00, 0x00,
  0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x03, 0xff, 0xff, 0x80, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00,
  0x00, 0x00, 0x0f, 0xf8, 0x00, 0x00, 0x07, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00,
  0x00, 0x00, 0x07, 0xfc, 0x00, 0x00, 0x0f, 0xfe, 0x7f, 0xf0, 0x00, 0x00, 0x3f, 0xe0, 0x00, 0x00,
  0x00, 0x00, 0x07, 0xfe, 0x00, 0x00, 0x1f, 0xfc, 0x3f, 0xf8, 0x00, 0x00, 0x7f, 0xe0, 0x00, 0x00,
  0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x7f, 0xf8, 0x1f, 0xfc, 0x00, 0x00, 0xff, 0xc0, 0x00, 0x00,
  0x00, 0x00, 0x01, 0xff, 0xc0, 0x01, 0xff, 0xf0, 0x0f, 0xff, 0x00, 0x03, 0xff, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x01, 0xff, 0xf8, 0x07, 0xff, 0xf0, 0x07, 0xff, 0xe0, 0x1f, 0xff, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x07, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x80, 0x00, 0x00, 0x01, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x0c, 0x07, 0x80, 0xf0, 0x04, 0x18, 0xff, 0x88, 0x18, 0x1c, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x1e, 0x0f, 0xf8, 0xff, 0x1e, 0x1c, 0xff, 0x9e, 0x1c, 0x7f, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x1f, 0x0f, 0xfc, 0xff, 0x9e, 0x1c, 0xff, 0x9f, 0x1c, 0xff, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x3f, 0x0e, 0x3c, 0xe3, 0xce, 0x1c, 0x1c, 0x1f, 0x1d, 0xe3, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x3f, 0x0e, 0x1c, 0xe1, 0xce, 0x1c, 0x1c, 0x1f, 0x99, 0xc3, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x33, 0x8e, 0x1c, 0xe1, 0xce, 0x1c, 0x1c, 0x1f, 0x99, 0xc3, 0xc0, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x73, 0x8f, 0x3c, 0xe1, 0xce, 0x1c, 0x1c, 0x1d, 0xd9, 0xc3, 0xc0, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x73, 0x8f, 0xf8, 0xe1, 0xce, 0x1c, 0x1c, 0x1d, 0xf9, 0xc3, 0xc0, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x7f, 0x8f, 0xf0, 0xe1, 0xce, 0x1c, 0x1c, 0x1c, 0xf9, 0xc3, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x00, 0xff, 0xce, 0x70, 0xe3, 0xce, 0x1c, 0x1c, 0x1c, 0xf9, 0xc3, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x00, 0xff, 0xce, 0x78, 0xe7, 0x8e, 0x3c, 0x3c, 0x1c, 0x7d, 0xe7, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x00, 0xe1, 0xce, 0x3c, 0xff, 0x8f, 0xf8, 0xff, 0x9c, 0x7c, 0xff, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x01, 0xe1, 0xee, 0x3c, 0xff, 0x07, 0xf0, 0xff, 0x9c, 0x3c, 0x7e, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

void setup() {
  Serial.begin(9600);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 not found"));
    while(1){}
  }

  display.clearDisplay();

  display.drawBitmap(0, 0, arduinoLogo, 128, 64, SSD1306_WHITE);
  display.display();

}

void loop() {
}

Подключение Grove OLED по I2C к Arduino UNO R4 WiFi: линии SCL, SDA, 3V3 и GND.