
Создаём выразительные анимированные глаза на OLED-дисплее 0.96 дюйма с Arduino. Глаза моргают, смотрят по сторонам, меняют настроение. Полный код, схема подключения и советы для учителей.
Когда школьники собирают робота, у него обычно есть колёса, датчики и корпус. Но чего не хватает -- так это характера. OLED-дисплей размером всего 0.96 дюйма способен превратить безликую конструкцию в персонажа с настроением. Анимированные глаза моргают, оглядываются по сторонам, хмурятся и удивляются -- всё это создаётся программно, без единой движущейся детали.
Проект основан на OLED-дисплее с разрешением 128x64 пикселя и контроллером SSD1306, который подключается к Arduino по интерфейсу I2C всего четырьмя проводами. Это один из самых простых в подключении дисплеев, и при этом он даёт чёткое изображение с высокой контрастностью.
Проект подходит для учеников 6--9 классов и собирается за один урок. Пайка не требуется -- все соединения выполняются на макетной плате.
Общая стоимость компонентов составляет около 4 000--6 000 тенге. OLED-дисплеи SSD1306 широко доступны в магазинах электроники и на маркетплейсах.

OLED-дисплей подключается по протоколу I2C, для которого нужны всего два сигнальных провода плюс питание.
Подключение OLED к Arduino Uno:
| OLED-дисплей | Arduino |
|---|---|
| GND | GND |
| VCC | 3.3V |
| SCL (SCK) | A5 |
| SDA | A4 |
Важный момент: рабочее напряжение OLED-дисплея составляет 3.3V, поэтому питание берётся от вывода 3.3V Arduino, а не от 5V. Подключение к 5V может повредить дисплей.
Для Arduino Nano используются те же пины A4 (SDA) и A5 (SCL). Если на дисплее не появляется изображение, проверьте I2C-адрес -- он может быть 0x3C или 0x3D в зависимости от производителя.
Для работы потребуются две библиотеки. Установите их через менеджер библиотек Arduino IDE (Скетч -- Подключить библиотеку -- Управление библиотеками):
1. Adafruit SSD1306 -- драйвер дисплея
2. Adafruit GFX Library -- графические функции (установится автоматически как зависимость)
```cpp
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// Настройки дисплея
#define SCREEN_WIDTH 128 // Ширина экрана в пикселях
#define SCREEN_HEIGHT 64 // Высота экрана в пикселях
#define OLED_RESET -1 // Пин сброса (-1 если не используется)
// Создаём объект дисплея
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Параметры глаз
const int eyeWidth = 36; // Ширина глаза
const int eyeHeight = 36; // Высота глаза
const int eyeSpacing = 10; // Расстояние между глазами
const int pupilSize = 10; // Размер зрачка
const int eyeY = 14; // Вертикальное положение глаз
// Вычисляем центры глаз
const int leftEyeX = (SCREEN_WIDTH / 2) - (eyeSpacing / 2) - (eyeWidth / 2);
const int rightEyeX = (SCREEN_WIDTH / 2) + (eyeSpacing / 2) + (eyeWidth / 2);
// Смещение зрачка относительно центра глаза
int pupilOffsetX = 0;
int pupilOffsetY = 0;
// Функция рисования обоих глаз с текущим положением зрачков
void drawEyes(int offsetX, int offsetY, int lidTop, int lidBottom) {
display.clearDisplay();
// Рисуем левый глаз (белок -- заполненный скруглённый прямоугольник)
display.fillRoundRect(leftEyeX - eyeWidth/2, eyeY, eyeWidth, eyeHeight, 8, SSD1306_WHITE);
// Зрачок левого глаза (чёрный круг внутри белого)
display.fillCircle(leftEyeX + offsetX, eyeY + eyeHeight/2 + offsetY, pupilSize, SSD1306_BLACK);
// Рисуем правый глаз
display.fillRoundRect(rightEyeX - eyeWidth/2, eyeY, eyeWidth, eyeHeight, 8, SSD1306_WHITE);
// Зрачок правого глаза
display.fillCircle(rightEyeX + offsetX, eyeY + eyeHeight/2 + offsetY, pupilSize, SSD1306_BLACK);
// Веки -- чёрные прямоугольники, закрывающие глаза сверху и снизу
if (lidTop > 0) {
display.fillRect(leftEyeX - eyeWidth/2, eyeY, eyeWidth, lidTop, SSD1306_BLACK);
display.fillRect(rightEyeX - eyeWidth/2, eyeY, eyeWidth, lidTop, SSD1306_BLACK);
}
if (lidBottom > 0) {
display.fillRect(leftEyeX - eyeWidth/2, eyeY + eyeHeight - lidBottom, eyeWidth, lidBottom, SSD1306_BLACK);
display.fillRect(rightEyeX - eyeWidth/2, eyeY + eyeHeight - lidBottom, eyeWidth, lidBottom, SSD1306_BLACK);
}
display.display();
}
// Анимация моргания
void blink() {
// Закрываем глаза постепенно
for (int i = 0; i <= eyeHeight/2; i += 4) {
drawEyes(pupilOffsetX, pupilOffsetY, i, i);
delay(20);
}
delay(60);
// Открываем глаза обратно
for (int i = eyeHeight/2; i >= 0; i -= 4) {
drawEyes(pupilOffsetX, pupilOffsetY, i, i);
delay(20);
}
}
// Плавное перемещение взгляда
void lookAt(int targetX, int targetY, int steps) {
int startX = pupilOffsetX;
int startY = pupilOffsetY;
for (int i = 1; i <= steps; i++) {
pupilOffsetX = startX + (targetX - startX) * i / steps;
pupilOffsetY = startY + (targetY - startY) * i / steps;
drawEyes(pupilOffsetX, pupilOffsetY, 0, 0);
delay(30);
}
}
// Выражение "злость" -- верхние веки наполовину опущены
void angryFace() {
for (int i = 0; i < 8; i++) {
drawEyes(0, 0, i, 0);
delay(40);
}
delay(1500);
for (int i = 7; i >= 0; i--) {
drawEyes(0, 0, i, 0);
delay(40);
}
}
// Выражение "удивление" -- глаза широко раскрыты, зрачки маленькие
void surprisedFace() {
display.clearDisplay();
// Рисуем увеличенные глаза
display.fillRoundRect(leftEyeX - eyeWidth/2 - 2, eyeY - 2, eyeWidth + 4, eyeHeight + 4, 10, SSD1306_WHITE);
display.fillCircle(leftEyeX, eyeY + eyeHeight/2, pupilSize - 3, SSD1306_BLACK);
display.fillRoundRect(rightEyeX - eyeWidth/2 - 2, eyeY - 2, eyeWidth + 4, eyeHeight + 4, 10, SSD1306_WHITE);
display.fillCircle(rightEyeX, eyeY + eyeHeight/2, pupilSize - 3, SSD1306_BLACK);
display.display();
delay(2000);
}
void setup() {
Serial.begin(9600);
// Инициализация дисплея (адрес 0x3C -- стандартный для большинства модулей)
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("Ошибка: OLED не найден! Проверьте подключение.");
while (true); // Останавливаем программу
}
display.clearDisplay();
display.display();
delay(500);
}
void loop() {
// Нейтральный взгляд вперёд
drawEyes(0, 0, 0, 0);
delay(2000);
// Моргание
blink();
delay(1000);
// Смотрим влево
lookAt(-8, 0, 5);
delay(800);
// Смотрим вправо
lookAt(8, 0, 5);
delay(800);
// Возвращаемся в центр
lookAt(0, 0, 5);
delay(500);
// Моргание
blink();
delay(1200);
// Злое выражение
angryFace();
delay(800);
// Моргание
blink();
delay(600);
// Удивление
surprisedFace();
delay(500);
// Возвращаемся к нейтральному
pupilOffsetX = 0;
pupilOffsetY = 0;
}
`

Геометрия глаз. Глаза рисуются с помощью графических примитивов библиотеки Adafruit GFX: fillRoundRect создаёт белый скруглённый прямоугольник (белок глаза), а fillCircle -- чёрный круг внутри него (зрачок). Смещая зрачок относительно центра, мы имитируем направление взгляда.
Анимация моргания. Веки реализованы как чёрные прямоугольники, которые постепенно закрывают глаз сверху и снизу. Цикл for увеличивает высоту прямоугольников с шагом 4 пикселя, создавая плавную анимацию. Задержка между кадрами -- 20 мс, что даёт около 50 кадров в секунду.
Плавное перемещение взгляда. Функция lookAt использует линейную интерполяцию: зрачок перемещается из текущей позиции в целевую за заданное количество шагов. Это создаёт естественное, не дёрганое движение.
Эмоции. Разные выражения лица создаются комбинацией положения зрачков и формы век. Опущенные верхние веки -- злость. Увеличенные глаза с маленькими зрачками -- удивление. Добавить новые эмоции несложно.
display.setRotation(2); в setup после инициализации.Этот проект наглядно демонстрирует работу с I2C-интерфейсом, графическими библиотеками и принципами покадровой анимации. Готовые глаза можно встроить в корпус робота из картона или напечатанный на 3D-принтере -- результат впечатлит и одноклассников, и жюри на конкурсе.
Все компоненты для этого проекта -- Arduino, OLED-дисплей, провода и макетные платы -- доступны в наборах Alashed Hardware. А для написания и загрузки кода удобно использовать Alashed CodeStudio -- онлайн-среду разработки, которая позволяет писать, компилировать и загружать скетчи прямо из браузера, без установки Arduino IDE на школьные компьютеры.
Подключите школу к пилоту. Генерируйте КМЖ за 2 минуты, ведите CodeStudio уроки, заказывайте оборудование — всё в одном месте.