====== Электронное приложение к набору «IO.KIT Робот Ампи: Голова» ======
{{ :kits:io-kit:robot-ampie-head:io-kit-robot-ampie-head-preview.png?nolink&400 |}}
На этой странице ты найдёшь все нужные материалы для проектов набора [[amp>product/io-kit-robot-ampie-head?utm_source=man&utm_campaign=io-kit-robot-ampie-head&utm_medium=wiki|Робот Ампи: Голова]] из серии [[amp>collection/io-kits?utm_source=man&utm_campaign=io-kit-robot-ampie-head&utm_medium=wiki|IO.KIT]]:
* Схему подключения модулей.
* Исходный код программ (копируй его в редактор Arduino IDE).
* Дополнительные материалы: программные библиотеки, даташиты и т. п.
**Обрати внимание**
Для сборки и функционирования головы робота Ампи тебе понадобится [[amp>product/io-kit-basic?utm_source=man&utm_campaign=io-kit-robot-ampie-head&utm_medium=wiki|IO.KIT Базовый]]!
===== Схема =====
{{ :kits:io-kit:robot-head:io-kit-robot-head-drawing-wiring-cmyk.png?nolink |}}
===== Проекты =====
Прежде чем приступать к экспериментам, нужно подготовить свой компьютер:
* Установи среду программирования [[https://amperka.ru/page/arduino-ide?utm_source=announce&utm_campaign=io-kit-robot-ampie-head&utm_medium=wiki|Arduino IDE]] и копируй туда готовый код проектов.
* Установи [[#библиотеки|дополнительные библиотеки]] для Arduino IDE, пользуясь [[программирование:библиотеки|нашим руководством]].
**Драйвер чипа CH340**
Установи [[articles:driver-ch340|драйвер CH340 для Windows]] или [[projects:installing-the-ch340-on-linux|Linux]], чтобы твой компьютер мог корректно распознать и прошить плату Iskra Nano.
==== №1. Электронные глаза ====
Активируй светодиодные глаза робота Ампи.
{{ :kits:io-kit:robot-head:010-drawing-example-cmyk.png?nolink&300 |}}
// Подключаем библиотеку для работы со светодиодными матрицами
#include
// Создаём объект матрицы левого глаза
// на шине I²C с адресом 0x60 (указан по умолчанию)
TroykaLedMatrix matrixL;
// Создаём объект матрицы правого глаза
// на шине I²C с адресом 0x63
TroykaLedMatrix matrixR(0x63);
// Создаём иконку «Взгляд прямо» в шестнадцатеричной системе HEX
// Иконка для левого и правого глаза одинаковая
// Эмоции рисуем в редакторе изображений для LED-матрицы
// https://amperka.github.io/led-matrix-editor/
constexpr uint8_t ICON_EYE_STRAIGHT[] {
0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e
};
void setup() {
// Инициализируем матрицы
matrixL.begin();
matrixR.begin();
// Очищаем матрицы
matrixL.clear();
matrixR.clear();
// Устанавливаем яркость матриц
matrixL.setCurrentLimit(ROW_CURRENT_05MA);
matrixR.setCurrentLimit(ROW_CURRENT_05MA);
// Отображаем на матрицах иконку «Взгляд прямо»
matrixL.drawBitmap(ICON_EYE_STRAIGHT);
matrixR.drawBitmap(ICON_EYE_STRAIGHT);
}
void loop() {
}
[[https://amperka.github.io/led-matrix-editor/#7e8181999981817e|«Электронные глаза» в редакторе изображений для LED-матрицы]]
==== №2. Осмотр местности ====
{{ :kits:io-kit:robot-head:020-drawing-example-cmyk.png?nolink |}}
// Подключаем библиотеку для работы со светодиодными матрицами
#include
// Создаём объект матрицы левого глаза
// на шине I²C с адресом 0x60 (указан по умолчанию)
TroykaLedMatrix matrixL;
// Создаём объект матрицы правого глаза
// на шине I²C с адресом 0x63
TroykaLedMatrix matrixR(0x63);
// Создаём массив иконок анимации «Осмотр местности по сторонам» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
// Эмоции рисуем в редакторе изображений для LED-матрицы
// https://amperka.github.io/led-matrix-editor/
constexpr uint8_t ICON_EYE_LOOK_AROUND[][8] {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x7e, 0x81, 0x81, 0x87, 0x87, 0x81, 0x81, 0x7e },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x7e, 0x81, 0x81, 0xe1, 0xe1, 0x81, 0x81, 0x7e },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x7e, 0x99, 0x99, 0x81, 0x81, 0x81, 0x81, 0x7e },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x7e, 0x81, 0x81, 0x81, 0x81, 0x99, 0x99, 0x7e }
};
// Создаём массив иконок анимации «Осмотр местности по углам» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
constexpr uint8_t ICON_EYE_LOOK_CORNER[][8] {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x7e, 0x87, 0x87, 0x81, 0x81, 0x81, 0x81, 0x7e },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x7e, 0x81, 0x81, 0x81, 0x81, 0x87, 0x87, 0x7e },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x7e, 0x81, 0x81, 0x81, 0x81, 0xe1, 0xe1, 0x7e },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x7e, 0xe1, 0xe1, 0x81, 0x81, 0x81, 0x81, 0x7e }
};
// Вычисляем количество кадров в массиве «Осмотр местности»
constexpr uint8_t COUNT_FRAMES_LOOK_AROUND = sizeof(ICON_EYE_LOOK_AROUND)
/ sizeof(ICON_EYE_LOOK_AROUND[0]);
// Вычисляем количество кадров в массиве «Осмотр местности по диагонали»
constexpr uint8_t COUNT_FRAMES_LOOK_CORNER = sizeof(ICON_EYE_LOOK_CORNER)
/ sizeof(ICON_EYE_LOOK_CORNER[0]);
void setup() {
// Инициализируем матрицы
matrixL.begin();
matrixR.begin();
// Очищаем матрицы
matrixL.clear();
matrixR.clear();
}
void loop() {
// Перебираем массив «Осмотр местности» по кадрам
for (int i = 0; i < COUNT_FRAMES_LOOK_AROUND; i++) {
// Отображаем на матрицах текущий кадр
matrixL.drawBitmap(ICON_EYE_LOOK_AROUND[i]);
matrixR.drawBitmap(ICON_EYE_LOOK_AROUND[i]);
// Ждём 500 мс
delay(500);
}
}
* [[https://tinyurl.com/2sb8epaz|«Осмотр местности по сторонам» в редакторе изображений для LED-матрицы]]
* [[https://tinyurl.com/yca2e5xf|«Осмотр местности по углам» в редакторе изображений для LED-матрицы]]
==== №3. Отображение эмоций ====
{{ :kits:io-kit:robot-head:030-drawing-example-cmyk.png?nolink |}}
// Подключаем библиотеку для работы со светодиодными матрицами
#include
// Создаём объект матрицы левого глаза
// на шине I²C с адресом 0x60 (указан по умолчанию)
TroykaLedMatrix matrixL;
// Создаём объект матрицы правого глаза
// на шине I²C с адресом 0x63
TroykaLedMatrix matrixR(0x63);
// Создаём структуру анимации
struct AnimationFrame {
uint8_t* iconEyeL;
uint8_t* iconEyeR;
int timeFrame;
};
// Создаём массив иконок анимации «Моргание» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
// Эмоции рисуем в редакторе изображений для LED-матрицы
// https://amperka.github.io/led-matrix-editor/
constexpr uint8_t ICON_EYE_BLINK[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x00, 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x7e },
{ 0x00, 0x00, 0x7e, 0x81, 0x99, 0x99, 0x81, 0x7e },
{ 0x00, 0x00, 0x00, 0x7e, 0x99, 0x99, 0x7e, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x99, 0x7e, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7e, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x99, 0x7e, 0x00 },
{ 0x00, 0x00, 0x00, 0x7e, 0x99, 0x99, 0x7e, 0x00 },
{ 0x00, 0x00, 0x7e, 0x81, 0x99, 0x99, 0x81, 0x7e },
{ 0x00, 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x7e },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Злость»
// в шестнадцатеричной системе HEX для левого глаза
constexpr uint8_t ICON_EYE_ANGRY_L[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x78, 0x84, 0x82, 0x9a, 0x9a, 0x82, 0x82, 0x7c },
{ 0x70, 0x88, 0x84, 0x9a, 0x9a, 0x82, 0x82, 0x7c },
{ 0x60, 0x90, 0x88, 0x9c, 0x9a, 0x82, 0x82, 0x7c },
{ 0x40, 0xa0, 0x90, 0x88, 0xb4, 0xb2, 0x82, 0x7c },
{ 0x80, 0xc0, 0xa0, 0x90, 0xa8, 0xb4, 0x82, 0x7c },
{ 0x40, 0xa0, 0x90, 0x88, 0xb4, 0xb2, 0x82, 0x7c },
{ 0x60, 0x90, 0x88, 0x9c, 0x9a, 0x82, 0x82, 0x7c },
{ 0x70, 0x88, 0x84, 0x9a, 0x9a, 0x82, 0x82, 0x7c },
{ 0x78, 0x84, 0x82, 0x9a, 0x9a, 0x82, 0x82, 0x7c },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Злость»
// в шестнадцатеричной системе HEX для правого глаза
constexpr uint8_t ICON_EYE_ANGRY_R[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x1e, 0x21, 0x41, 0x59, 0x59, 0x41, 0x41, 0x3e },
{ 0x0e, 0x11, 0x21, 0x59, 0x59, 0x41, 0x41, 0x3e },
{ 0x06, 0x09, 0x11, 0x39, 0x59, 0x41, 0x41, 0x3e },
{ 0x02, 0x05, 0x09, 0x11, 0x2d, 0x4d, 0x41, 0x3e },
{ 0x01, 0x03, 0x05, 0x09, 0x15, 0x2d, 0x41, 0x3e },
{ 0x02, 0x05, 0x09, 0x11, 0x2d, 0x4d, 0x41, 0x3e },
{ 0x06, 0x09, 0x11, 0x39, 0x59, 0x41, 0x41, 0x3e },
{ 0x0e, 0x11, 0x21, 0x59, 0x59, 0x41, 0x41, 0x3e },
{ 0x1e, 0x21, 0x41, 0x59, 0x59, 0x41, 0x41, 0x3e }
};
// Создаём массив иконок анимации «Грусть»
// в шестнадцатеричной системе HEX для левого глаза
constexpr uint8_t ICON_EYE_SAD_L[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x3e, 0x41, 0x81, 0x99, 0x99, 0x81, 0x82, 0x7c },
{ 0x1e, 0x21, 0x41, 0x99, 0x99, 0x81, 0x82, 0x7c },
{ 0x0e, 0x11, 0x21, 0x41, 0x8d, 0x8d, 0x82, 0x7c },
{ 0x0e, 0x11, 0x21, 0x41, 0x4d, 0x4d, 0x42, 0x3c },
{ 0x06, 0x09, 0x11, 0x21, 0x4d, 0x4d, 0x42, 0x3c },
{ 0x0e, 0x11, 0x21, 0x41, 0x4d, 0x4d, 0x42, 0x3c },
{ 0x0e, 0x11, 0x21, 0x41, 0x8d, 0x8d, 0x82, 0x7c },
{ 0x1e, 0x21, 0x41, 0x99, 0x99, 0x81, 0x82, 0x7c },
{ 0x3e, 0x41, 0x81, 0x99, 0x99, 0x81, 0x82, 0x7c },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Грусть»
// в шестнадцатеричной системе HEX для правого глаза
constexpr uint8_t ICON_EYE_SAD_R[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x7c, 0x82, 0x81, 0x99, 0x99, 0x81, 0x41, 0x3e },
{ 0x78, 0x84, 0x82, 0x99, 0x99, 0x81, 0x41, 0x3e },
{ 0x70, 0x88, 0x84, 0x82, 0xb1, 0xb1, 0x41, 0x3e },
{ 0x70, 0x88, 0x84, 0x82, 0xb2, 0xb2, 0x42, 0x3c },
{ 0x60, 0x90, 0x88, 0x84, 0xb2, 0xb2, 0x42, 0x3c },
{ 0x70, 0x88, 0x84, 0x82, 0xb2, 0xb2, 0x42, 0x3c },
{ 0x70, 0x88, 0x84, 0x82, 0xb1, 0xb1, 0x41, 0x3e },
{ 0x78, 0x84, 0x82, 0x99, 0x99, 0x81, 0x41, 0x3e },
{ 0x7c, 0x82, 0x81, 0x99, 0x99, 0x81, 0x41, 0x3e },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Радость» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
constexpr uint8_t ICON_EYE_HAPPY[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x00, 0x7e, 0x81, 0x99, 0x99, 0x81, 0x7e, 0x00 },
{ 0x00, 0x7e, 0x81, 0x99, 0x99, 0xff, 0x00, 0x00 },
{ 0x00, 0x7e, 0xdb, 0x99, 0xff, 0x81, 0x00, 0x00 },
{ 0x00, 0x3c, 0x5a, 0xff, 0xc3, 0x81, 0x00, 0x00 },
{ 0x00, 0x3c, 0x7e, 0xc3, 0xc3, 0x81, 0x00, 0x00 },
{ 0x00, 0x3c, 0x5a, 0xff, 0xc3, 0x81, 0x00, 0x00 },
{ 0x00, 0x7e, 0xdb, 0x99, 0xff, 0x81, 0x00, 0x00 },
{ 0x00, 0x7e, 0x81, 0x99, 0x99, 0xff, 0x00, 0x00 },
{ 0x00, 0x7e, 0x81, 0x99, 0x99, 0x81, 0x7e, 0x00 },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Удивление» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
constexpr uint8_t ICON_EYE_AMAZED[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x7e, 0x81, 0x99, 0xbd, 0xbd, 0x99, 0x81, 0x7e },
{ 0x7e, 0x81, 0xbd, 0xbd, 0xbd, 0xbd, 0x81, 0x7e },
{ 0x7e, 0x81, 0x99, 0xbd, 0xbd, 0x99, 0x81, 0x7e },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Любовь» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
constexpr uint8_t ICON_EYE_LOVE[][8] PROGMEM {
{ 0x66, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x18 },
{ 0x00, 0x00, 0x24, 0x7e, 0x7e, 0x3c, 0x18, 0x00 }
};
// Создаём массив иконок анимации Сон» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
constexpr uint8_t ICON_EYE_SLEEP[][8] PROGMEM {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7e, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, 0x7e, 0x00 }
};
// Создаём анимацию «Моргание»
AnimationFrame animationBlink[] = {
{ ICON_EYE_BLINK[0], ICON_EYE_BLINK[0], 500 },
{ ICON_EYE_BLINK[1], ICON_EYE_BLINK[1], 25 },
{ ICON_EYE_BLINK[2], ICON_EYE_BLINK[2], 25 },
{ ICON_EYE_BLINK[3], ICON_EYE_BLINK[3], 25 },
{ ICON_EYE_BLINK[4], ICON_EYE_BLINK[4], 25 },
{ ICON_EYE_BLINK[5], ICON_EYE_BLINK[5], 25 },
{ ICON_EYE_BLINK[6], ICON_EYE_BLINK[6], 25 },
{ ICON_EYE_BLINK[7], ICON_EYE_BLINK[7], 25 },
{ ICON_EYE_BLINK[8], ICON_EYE_BLINK[8], 25 },
{ ICON_EYE_BLINK[9], ICON_EYE_BLINK[9], 25 },
{ ICON_EYE_BLINK[10], ICON_EYE_BLINK[10], 500 }
};
// Создаём анимацию «Злость»
AnimationFrame animationAngry[] = {
{ ICON_EYE_ANGRY_L[0], ICON_EYE_ANGRY_R[0], 500 },
{ ICON_EYE_ANGRY_L[1], ICON_EYE_ANGRY_R[1], 50 },
{ ICON_EYE_ANGRY_L[2], ICON_EYE_ANGRY_R[2], 50 },
{ ICON_EYE_ANGRY_L[3], ICON_EYE_ANGRY_R[3], 50 },
{ ICON_EYE_ANGRY_L[4], ICON_EYE_ANGRY_R[4], 50 },
{ ICON_EYE_ANGRY_L[5], ICON_EYE_ANGRY_R[5], 2000 },
{ ICON_EYE_ANGRY_L[6], ICON_EYE_ANGRY_R[6], 50 },
{ ICON_EYE_ANGRY_L[7], ICON_EYE_ANGRY_R[7], 50 },
{ ICON_EYE_ANGRY_L[8], ICON_EYE_ANGRY_R[8], 50 },
{ ICON_EYE_ANGRY_L[9], ICON_EYE_ANGRY_R[9], 50 },
{ ICON_EYE_ANGRY_L[10], ICON_EYE_ANGRY_R[10], 500 }
};
// Создаём анимацию «Грусть»
AnimationFrame animationSad[] = {
{ ICON_EYE_SAD_L[0], ICON_EYE_SAD_R[0], 500 },
{ ICON_EYE_SAD_L[1], ICON_EYE_SAD_R[1], 50 },
{ ICON_EYE_SAD_L[2], ICON_EYE_SAD_R[2], 50 },
{ ICON_EYE_SAD_L[3], ICON_EYE_SAD_R[3], 50 },
{ ICON_EYE_SAD_L[4], ICON_EYE_SAD_R[4], 50 },
{ ICON_EYE_SAD_L[5], ICON_EYE_SAD_R[5], 3000 },
{ ICON_EYE_SAD_L[6], ICON_EYE_SAD_R[6], 50 },
{ ICON_EYE_SAD_L[7], ICON_EYE_SAD_R[7], 50 },
{ ICON_EYE_SAD_L[8], ICON_EYE_SAD_R[8], 50 },
{ ICON_EYE_SAD_L[9], ICON_EYE_SAD_R[9], 50 },
{ ICON_EYE_SAD_L[10], ICON_EYE_SAD_R[10], 500 }
};
// Создаём анимацию «Радость»
AnimationFrame animationHappy[] = {
{ ICON_EYE_HAPPY[0], ICON_EYE_HAPPY[0], 500 },
{ ICON_EYE_HAPPY[1], ICON_EYE_HAPPY[1], 40 },
{ ICON_EYE_HAPPY[2], ICON_EYE_HAPPY[2], 40 },
{ ICON_EYE_HAPPY[3], ICON_EYE_HAPPY[3], 40 },
{ ICON_EYE_HAPPY[4], ICON_EYE_HAPPY[4], 40 },
{ ICON_EYE_HAPPY[5], ICON_EYE_HAPPY[5], 2000 },
{ ICON_EYE_HAPPY[6], ICON_EYE_HAPPY[6], 40 },
{ ICON_EYE_HAPPY[7], ICON_EYE_HAPPY[7], 40 },
{ ICON_EYE_HAPPY[8], ICON_EYE_HAPPY[8], 40 },
{ ICON_EYE_HAPPY[9], ICON_EYE_HAPPY[9], 40 },
{ ICON_EYE_HAPPY[10], ICON_EYE_HAPPY[10], 500 }
};
// Создаём анимацию «Удивление»
AnimationFrame animationAmazed[] = {
{ ICON_EYE_AMAZED[0], ICON_EYE_AMAZED[0], 500 },
{ ICON_EYE_AMAZED[1], ICON_EYE_AMAZED[1], 100 },
{ ICON_EYE_AMAZED[2], ICON_EYE_AMAZED[2], 2000 },
{ ICON_EYE_AMAZED[3], ICON_EYE_AMAZED[3], 100 },
{ ICON_EYE_AMAZED[4], ICON_EYE_AMAZED[4], 500 }
};
// Создаём анимацию «Любовь»
AnimationFrame animationLove[] = {
{ ICON_EYE_LOVE[0], ICON_EYE_LOVE[0], 1000 },
{ ICON_EYE_LOVE[1], ICON_EYE_LOVE[1], 300 }
};
// Создаём анимацию «Сон»
AnimationFrame animationSleep[] = {
{ ICON_EYE_SLEEP[0], ICON_EYE_SLEEP[0], 750 },
{ ICON_EYE_SLEEP[1], ICON_EYE_SLEEP[1], 750 }
};
// Вычисляем количество кадров в анимации «Моргание»
constexpr uint8_t COUNT_FRAMES_BLINK = sizeof(animationBlink) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Злость»
constexpr uint8_t COUNT_FRAMES_ANGRY = sizeof(animationAngry) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Грусть»
constexpr uint8_t COUNT_FRAMES_SAD = sizeof(animationSad) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Радость»
constexpr uint8_t COUNT_FRAMES_HAPPY = sizeof(animationHappy) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Удивление»
constexpr uint8_t COUNT_FRAMES_AMAZED = sizeof(animationAmazed) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Бьющее серде»
constexpr uint8_t COUNT_FRAMES_LOVE = sizeof(animationLove) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Сон»
constexpr uint8_t COUNT_FRAMES_SLEEP = sizeof(animationSleep) / sizeof(AnimationFrame);
// Создаём константу для хранения повторений эмоции
constexpr uint8_t COUNT_REPEAT_EMOTION = 3;
void setup() {
// Инициализируем матрицы
matrixL.begin();
matrixR.begin();
// Очищаем матрицы
matrixL.clear();
matrixR.clear();
}
void loop() {
// Перебираем количество повторений анимации «Моргание»
for (int i = 0; i < COUNT_REPEAT_EMOTION; i++) {
// Отображаем анимацию «Моргание»
drawAnimation(animationBlink, COUNT_FRAMES_BLINK);
}
// Перебираем количество повторений анимации «Злость»
for (int i = 0; i < COUNT_REPEAT_EMOTION; i++) {
// Отображаем анимацию «Злость»
drawAnimation(animationAngry, COUNT_FRAMES_ANGRY);
}
// Перебираем количество повторений анимации «Грусть»
for (int i = 0; i < COUNT_REPEAT_EMOTION; i++) {
// Отображаем анимацию «Грусть»
drawAnimation(animationSad, COUNT_FRAMES_SAD);
}
// Перебираем количество повторений анимации «Радость»
for (int i = 0; i < COUNT_REPEAT_EMOTION; i++) {
// Отображаем анимацию «Радость»
drawAnimation(animationHappy, COUNT_FRAMES_HAPPY);
}
// Перебираем количество повторений анимации «Удивление»
for (int i = 0; i < COUNT_REPEAT_EMOTION; i++) {
// Отображаем анимацию «Удивление»
drawAnimation(animationAmazed, COUNT_FRAMES_AMAZED);
}
// Перебираем количество повторений анимации «Любовь»
for (int i = 0; i < COUNT_REPEAT_EMOTION; i++) {
// Отображаем анимацию «Любовь»
drawAnimation(animationLove, COUNT_FRAMES_LOVE);
}
}
// Функция отображения анимации на матрицах
void drawAnimation(AnimationFrame* animationFrame, int countFrames) {
// Перебираем анимацию по кадрам
for (int indexFrame = 0; indexFrame < countFrames; indexFrame++) {
// Отображаем на матрицах текущий кадр
matrixL.drawBitmapF(animationFrame[indexFrame].iconEyeL);
matrixR.drawBitmapF(animationFrame[indexFrame].iconEyeR);
// Ждём время задержки текущего кадра
delay(animationFrame[indexFrame].timeFrame);
}
}
* [[https://tinyurl.com/2x769776|Эмоция «Моргание» в редакторе изображений для LED-матрицы]]
* [[https://tinyurl.com/3csf9hhv|Эмоция «Злость» в редакторе изображений для LED-матрицы]]
* [[https://tinyurl.com/3hafurww|Эмоция «Грусть» в редакторе изображений для LED-матрицы]]
* [[https://tinyurl.com/4hphjwcz|Эмоция «Радость» в редакторе изображений для LED-матрицы]]
* [[https://tinyurl.com/45ecj77e|Эмоция «Удивление» в редакторе изображений для LED-матрицы]]
* [[https://tinyurl.com/46vpjrv6|Эмоция «Любовь» в редакторе изображений для LED-матрицы]]
* [[https://tinyurl.com/cz67vwk4|Эмоция «Сон» в редакторе изображений для LED-матрицы]]
==== №4. Эмоции со звуком ====
{{ :kits:io-kit:robot-head:040-drawing-example-cmyk.png?nolink |}}
// Подключаем библиотеку для работы с мелодиями в формате RTTTL
#include
// Подключаем библиотеку для работы с таймером millis
#include
// Подключаем библиотеку для работы со светодиодными матрицами
#include
// Даём понятное имя пину 3 с пищалкой
constexpr uint8_t BUZZER_PIN = 3;
// Создаём объект для работы с таймером
TimerMs timer;
// Создаём объект матрицы левого глаза
// на шине I²C с адресом 0x60 (указан по умолчанию)
TroykaLedMatrix matrixL;
// Создаём объект матрицы правого глаза
// на шине I²C с адресом 0x63
TroykaLedMatrix matrixR(0x63);
// Создаём массив данных мелодии «Моргание» в формате RTTTL
constexpr uint8_t melodyBlink[] PROGMEM = "Blink:d=4,o=4,b=120:"
"16e,16p,16g,16p,16e,16p,16g,16p,";
// Создаём массив данных мелодии «Злость» в формате RTTTL
constexpr uint8_t melodyAngry[] PROGMEM = "Angry:d=4,o=4,b=200:"
"16g,16g,16f#,16f#,16e,16e,16d#,16d#,"
"16c,16c,16b4,16b4,16a4,16a4,16g4,16g4";
// Создаём массив данных мелодии «Грусть» в формате RTTTL
constexpr uint8_t melodySad[] PROGMEM = "Sad:d=4,o=4,b=25:"
"16g,16a#,16d,16f,"
"16d,16c,16a#";
constexpr uint8_t melodyHappy[] PROGMEM = "Happy:d=4,o=4,b=140:"
"8c4,8e4,8g4,8c4,8e4,8g4,8c4,8e4"
"8g4,8c4,8e4,8g4";
// Создаём массив данных мелодии «Удивление» в формате RTTTL
constexpr uint8_t melodyAmazed[] PROGMEM = "Amazed:d=4,o=4,b=180:"
"16c,16d,16e,16f,16g,16a,16b,16c,"
"16b,16a,16g,16f,16e,16d,16c,16d";
// Создаём массив данных мелодии «Любовь» в формате RTTTL
constexpr uint8_t melodyLove[] PROGMEM = "Love:d=4,o=4,b=100:"
"8g,8p,8g,8p,8a,8p,8g,8p,"
"8g,8p,8g,8p,8f,8e,8d";
// Создаём массив данных мелодии «Сонливость» в формате RTTTL
constexpr uint8_t melodySleep[] PROGMEM = "Sleep:d=4,o=4,b=45:"
"8c,8c,8d,8e,8d,8c,8d,8e";
// Создаём перечисление эмоций с соответствующей переменной
enum Emotion {
EMOTION_BLINK,
EMOTION_ANGRY,
EMOTION_SAD,
EMOTION_HAPPY,
EMOTION_AMAZED,
EMOTION_LOVE,
EMOTION_SLEEP,
} emotion;
// Создаём структуру анимации
struct AnimationFrame {
uint8_t* iconEyeL;
uint8_t* iconEyeR;
int timeFrame;
};
// Создаём объект структуры анимации
AnimationFrame* animationFrame;
// Создаём массив иконок анимации «Моргание» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
// Эмоции рисуем в редакторе изображений для LED-матрицы
// https://amperka.github.io/led-matrix-editor/
constexpr uint8_t ICON_EYE_BLINK[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x00, 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x7e },
{ 0x00, 0x00, 0x7e, 0x81, 0x99, 0x99, 0x81, 0x7e },
{ 0x00, 0x00, 0x00, 0x7e, 0x99, 0x99, 0x7e, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x99, 0x7e, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7e, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x99, 0x7e, 0x00 },
{ 0x00, 0x00, 0x00, 0x7e, 0x99, 0x99, 0x7e, 0x00 },
{ 0x00, 0x00, 0x7e, 0x81, 0x99, 0x99, 0x81, 0x7e },
{ 0x00, 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x7e },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Злость»
// в шестнадцатеричной системе HEX для левого глаза
constexpr uint8_t ICON_EYE_ANGRY_L[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x78, 0x84, 0x82, 0x9a, 0x9a, 0x82, 0x82, 0x7c },
{ 0x70, 0x88, 0x84, 0x9a, 0x9a, 0x82, 0x82, 0x7c },
{ 0x60, 0x90, 0x88, 0x9c, 0x9a, 0x82, 0x82, 0x7c },
{ 0x40, 0xa0, 0x90, 0x88, 0xb4, 0xb2, 0x82, 0x7c },
{ 0x80, 0xc0, 0xa0, 0x90, 0xa8, 0xb4, 0x82, 0x7c },
{ 0x40, 0xa0, 0x90, 0x88, 0xb4, 0xb2, 0x82, 0x7c },
{ 0x60, 0x90, 0x88, 0x9c, 0x9a, 0x82, 0x82, 0x7c },
{ 0x70, 0x88, 0x84, 0x9a, 0x9a, 0x82, 0x82, 0x7c },
{ 0x78, 0x84, 0x82, 0x9a, 0x9a, 0x82, 0x82, 0x7c },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Злость»
// в шестнадцатеричной системе HEX для правого глаза
constexpr uint8_t ICON_EYE_ANGRY_R[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x1e, 0x21, 0x41, 0x59, 0x59, 0x41, 0x41, 0x3e },
{ 0x0e, 0x11, 0x21, 0x59, 0x59, 0x41, 0x41, 0x3e },
{ 0x06, 0x09, 0x11, 0x39, 0x59, 0x41, 0x41, 0x3e },
{ 0x02, 0x05, 0x09, 0x11, 0x2d, 0x4d, 0x41, 0x3e },
{ 0x01, 0x03, 0x05, 0x09, 0x15, 0x2d, 0x41, 0x3e },
{ 0x02, 0x05, 0x09, 0x11, 0x2d, 0x4d, 0x41, 0x3e },
{ 0x06, 0x09, 0x11, 0x39, 0x59, 0x41, 0x41, 0x3e },
{ 0x0e, 0x11, 0x21, 0x59, 0x59, 0x41, 0x41, 0x3e },
{ 0x1e, 0x21, 0x41, 0x59, 0x59, 0x41, 0x41, 0x3e }
};
// Создаём массив иконок анимации «Грусть»
// в шестнадцатеричной системе HEX для левого глаза
constexpr uint8_t ICON_EYE_SAD_L[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x3e, 0x41, 0x81, 0x99, 0x99, 0x81, 0x82, 0x7c },
{ 0x1e, 0x21, 0x41, 0x99, 0x99, 0x81, 0x82, 0x7c },
{ 0x0e, 0x11, 0x21, 0x41, 0x8d, 0x8d, 0x82, 0x7c },
{ 0x0e, 0x11, 0x21, 0x41, 0x4d, 0x4d, 0x42, 0x3c },
{ 0x06, 0x09, 0x11, 0x21, 0x4d, 0x4d, 0x42, 0x3c },
{ 0x0e, 0x11, 0x21, 0x41, 0x4d, 0x4d, 0x42, 0x3c },
{ 0x0e, 0x11, 0x21, 0x41, 0x8d, 0x8d, 0x82, 0x7c },
{ 0x1e, 0x21, 0x41, 0x99, 0x99, 0x81, 0x82, 0x7c },
{ 0x3e, 0x41, 0x81, 0x99, 0x99, 0x81, 0x82, 0x7c },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Грусть»
// в шестнадцатеричной системе HEX для правого глаза
constexpr uint8_t ICON_EYE_SAD_R[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x7c, 0x82, 0x81, 0x99, 0x99, 0x81, 0x41, 0x3e },
{ 0x78, 0x84, 0x82, 0x99, 0x99, 0x81, 0x41, 0x3e },
{ 0x70, 0x88, 0x84, 0x82, 0xb1, 0xb1, 0x41, 0x3e },
{ 0x70, 0x88, 0x84, 0x82, 0xb2, 0xb2, 0x42, 0x3c },
{ 0x60, 0x90, 0x88, 0x84, 0xb2, 0xb2, 0x42, 0x3c },
{ 0x70, 0x88, 0x84, 0x82, 0xb2, 0xb2, 0x42, 0x3c },
{ 0x70, 0x88, 0x84, 0x82, 0xb1, 0xb1, 0x41, 0x3e },
{ 0x78, 0x84, 0x82, 0x99, 0x99, 0x81, 0x41, 0x3e },
{ 0x7c, 0x82, 0x81, 0x99, 0x99, 0x81, 0x41, 0x3e },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Радость» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
constexpr uint8_t ICON_EYE_HAPPY[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x00, 0x7e, 0x81, 0x99, 0x99, 0x81, 0x7e, 0x00 },
{ 0x00, 0x7e, 0x81, 0x99, 0x99, 0xff, 0x00, 0x00 },
{ 0x00, 0x7e, 0xdb, 0x99, 0xff, 0x81, 0x00, 0x00 },
{ 0x00, 0x3c, 0x5a, 0xff, 0xc3, 0x81, 0x00, 0x00 },
{ 0x00, 0x3c, 0x7e, 0xc3, 0xc3, 0x81, 0x00, 0x00 },
{ 0x00, 0x3c, 0x5a, 0xff, 0xc3, 0x81, 0x00, 0x00 },
{ 0x00, 0x7e, 0xdb, 0x99, 0xff, 0x81, 0x00, 0x00 },
{ 0x00, 0x7e, 0x81, 0x99, 0x99, 0xff, 0x00, 0x00 },
{ 0x00, 0x7e, 0x81, 0x99, 0x99, 0x81, 0x7e, 0x00 },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Удивление» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
constexpr uint8_t ICON_EYE_AMAZED[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x7e, 0x81, 0x99, 0xbd, 0xbd, 0x99, 0x81, 0x7e },
{ 0x7e, 0x81, 0xbd, 0xbd, 0xbd, 0xbd, 0x81, 0x7e },
{ 0x7e, 0x81, 0x99, 0xbd, 0xbd, 0x99, 0x81, 0x7e },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Любовь» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
constexpr uint8_t ICON_EYE_LOVE[][8] PROGMEM {
{ 0x66, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x18 },
{ 0x00, 0x00, 0x24, 0x7e, 0x7e, 0x3c, 0x18, 0x00 }
};
// Создаём массив иконок анимации Сон» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
constexpr uint8_t ICON_EYE_SLEEP[][8] PROGMEM {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7e, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, 0x7e, 0x00 }
};
// Создаём анимацию «Моргание»
AnimationFrame animationBlink[] = {
{ ICON_EYE_BLINK[0], ICON_EYE_BLINK[0], 500 },
{ ICON_EYE_BLINK[1], ICON_EYE_BLINK[1], 25 },
{ ICON_EYE_BLINK[2], ICON_EYE_BLINK[2], 25 },
{ ICON_EYE_BLINK[3], ICON_EYE_BLINK[3], 25 },
{ ICON_EYE_BLINK[4], ICON_EYE_BLINK[4], 25 },
{ ICON_EYE_BLINK[5], ICON_EYE_BLINK[5], 25 },
{ ICON_EYE_BLINK[6], ICON_EYE_BLINK[6], 25 },
{ ICON_EYE_BLINK[7], ICON_EYE_BLINK[7], 25 },
{ ICON_EYE_BLINK[8], ICON_EYE_BLINK[8], 25 },
{ ICON_EYE_BLINK[9], ICON_EYE_BLINK[9], 25 },
{ ICON_EYE_BLINK[10], ICON_EYE_BLINK[10], 500 }
};
// Создаём анимацию «Злость»
AnimationFrame animationAngry[] = {
{ ICON_EYE_ANGRY_L[0], ICON_EYE_ANGRY_R[0], 500 },
{ ICON_EYE_ANGRY_L[1], ICON_EYE_ANGRY_R[1], 50 },
{ ICON_EYE_ANGRY_L[2], ICON_EYE_ANGRY_R[2], 50 },
{ ICON_EYE_ANGRY_L[3], ICON_EYE_ANGRY_R[3], 50 },
{ ICON_EYE_ANGRY_L[4], ICON_EYE_ANGRY_R[4], 50 },
{ ICON_EYE_ANGRY_L[5], ICON_EYE_ANGRY_R[5], 2000 },
{ ICON_EYE_ANGRY_L[6], ICON_EYE_ANGRY_R[6], 50 },
{ ICON_EYE_ANGRY_L[7], ICON_EYE_ANGRY_R[7], 50 },
{ ICON_EYE_ANGRY_L[8], ICON_EYE_ANGRY_R[8], 50 },
{ ICON_EYE_ANGRY_L[9], ICON_EYE_ANGRY_R[9], 50 },
{ ICON_EYE_ANGRY_L[10], ICON_EYE_ANGRY_R[10], 500 }
};
// Создаём анимацию «Грусть»
AnimationFrame animationSad[] = {
{ ICON_EYE_SAD_L[0], ICON_EYE_SAD_R[0], 500 },
{ ICON_EYE_SAD_L[1], ICON_EYE_SAD_R[1], 50 },
{ ICON_EYE_SAD_L[2], ICON_EYE_SAD_R[2], 50 },
{ ICON_EYE_SAD_L[3], ICON_EYE_SAD_R[3], 50 },
{ ICON_EYE_SAD_L[4], ICON_EYE_SAD_R[4], 50 },
{ ICON_EYE_SAD_L[5], ICON_EYE_SAD_R[5], 3000 },
{ ICON_EYE_SAD_L[6], ICON_EYE_SAD_R[6], 50 },
{ ICON_EYE_SAD_L[7], ICON_EYE_SAD_R[7], 50 },
{ ICON_EYE_SAD_L[8], ICON_EYE_SAD_R[8], 50 },
{ ICON_EYE_SAD_L[9], ICON_EYE_SAD_R[9], 50 },
{ ICON_EYE_SAD_L[10], ICON_EYE_SAD_R[10], 500 }
};
// Создаём анимацию «Радость»
AnimationFrame animationHappy[] = {
{ ICON_EYE_HAPPY[0], ICON_EYE_HAPPY[0], 500 },
{ ICON_EYE_HAPPY[1], ICON_EYE_HAPPY[1], 40 },
{ ICON_EYE_HAPPY[2], ICON_EYE_HAPPY[2], 40 },
{ ICON_EYE_HAPPY[3], ICON_EYE_HAPPY[3], 40 },
{ ICON_EYE_HAPPY[4], ICON_EYE_HAPPY[4], 40 },
{ ICON_EYE_HAPPY[5], ICON_EYE_HAPPY[5], 2000 },
{ ICON_EYE_HAPPY[6], ICON_EYE_HAPPY[6], 40 },
{ ICON_EYE_HAPPY[7], ICON_EYE_HAPPY[7], 40 },
{ ICON_EYE_HAPPY[8], ICON_EYE_HAPPY[8], 40 },
{ ICON_EYE_HAPPY[9], ICON_EYE_HAPPY[9], 40 },
{ ICON_EYE_HAPPY[10], ICON_EYE_HAPPY[10], 500 }
};
// Создаём анимацию «Удивление»
AnimationFrame animationAmazed[] = {
{ ICON_EYE_AMAZED[0], ICON_EYE_AMAZED[0], 500 },
{ ICON_EYE_AMAZED[1], ICON_EYE_AMAZED[1], 100 },
{ ICON_EYE_AMAZED[2], ICON_EYE_AMAZED[2], 2000 },
{ ICON_EYE_AMAZED[3], ICON_EYE_AMAZED[3], 100 },
{ ICON_EYE_AMAZED[4], ICON_EYE_AMAZED[4], 500 }
};
// Создаём анимацию «Любовь»
AnimationFrame animationLove[] = {
{ ICON_EYE_LOVE[0], ICON_EYE_LOVE[0], 1000 },
{ ICON_EYE_LOVE[1], ICON_EYE_LOVE[1], 300 },
{ ICON_EYE_LOVE[0], ICON_EYE_LOVE[0], 1000 },
{ ICON_EYE_LOVE[1], ICON_EYE_LOVE[1], 300 },
{ ICON_EYE_LOVE[0], ICON_EYE_LOVE[0], 1000 },
{ ICON_EYE_LOVE[1], ICON_EYE_LOVE[1], 300 },
{ ICON_EYE_LOVE[0], ICON_EYE_LOVE[0], 1000 },
{ ICON_EYE_LOVE[1], ICON_EYE_LOVE[1], 300 },
};
// Создаём анимацию «Сон»
AnimationFrame animationSleep[] = {
{ ICON_EYE_SLEEP[0], ICON_EYE_SLEEP[0], 750 },
{ ICON_EYE_SLEEP[1], ICON_EYE_SLEEP[1], 750 },
{ ICON_EYE_SLEEP[0], ICON_EYE_SLEEP[0], 750 },
{ ICON_EYE_SLEEP[1], ICON_EYE_SLEEP[1], 750 },
{ ICON_EYE_SLEEP[0], ICON_EYE_SLEEP[0], 750 },
{ ICON_EYE_SLEEP[1], ICON_EYE_SLEEP[1], 750 },
{ ICON_EYE_SLEEP[0], ICON_EYE_SLEEP[0], 750 },
{ ICON_EYE_SLEEP[1], ICON_EYE_SLEEP[1], 750 }
};
// Вычисляем количество кадров в анимации «Моргание»
constexpr uint8_t COUNT_FRAMES_BLINK = sizeof(animationBlink) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Злость»
constexpr uint8_t COUNT_FRAMES_ANGRY = sizeof(animationAngry) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Грусть»
constexpr uint8_t COUNT_FRAMES_SAD = sizeof(animationSad) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Радость»
constexpr uint8_t COUNT_FRAMES_HAPPY = sizeof(animationHappy) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Удивление»
constexpr uint8_t COUNT_FRAMES_AMAZED = sizeof(animationAmazed) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Любовь»
constexpr uint8_t COUNT_FRAMES_LOVE = sizeof(animationLove) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Сон»
constexpr uint8_t COUNT_FRAMES_SLEEP = sizeof(animationSleep) / sizeof(AnimationFrame);
// Создаём константу для хранения общего количества повторений эмоции
constexpr int COUNT_REPEAT_EMOTION = 3;
// Создаём переменную для хранения текущего счётчика количества повторений эмоции
int countRepeatEmotion;
// Создаём переменную для хранения счётчика текущего кадра
int indexFrame;
// Создаём переменную для хранения количества кадров
int countFrames;
// Создаём переменную для хранения флага последнего кадра
bool lastFrame;
void setup() {
// Инициализируем матрицы
matrixL.begin();
matrixR.begin();
// Очищаем матрицы
matrixL.clear();
matrixR.clear();
// Настраиваем пин с пищалкой в режим выхода
pinMode(BUZZER_PIN, OUTPUT);
// Сбрасываем все настройки переменных по умолчанию
resetState();
// Устанавливаем таймер в режим остановки после срабатывания
timer.setTimerMode();
// Передаём случайное число с пина A1
// для последующей генерации случайных чисел
randomSeed(analogRead(A1));
// Устанавливаем анимацию «Моргание»
setEmotion(EMOTION_BLINK);
}
void loop() {
// Переходим в функцию обработки эмоций
emotionHandler();
}
// Функция обработки эмоций
void emotionHandler() {
// Вызываем функцию обновление анимации
updateEmotion();
// Разрешаем играть выбранный трек
anyrtttl::nonblocking::play();
}
// Функция установки текущей эмоции
void setEmotion(Emotion emotionNow) {
// Обновляем полученную эмоцию
emotion = emotionNow;
// Проверяем состояние текущей эмоции
switch (emotion) {
// Эмоция «Моргание»
case EMOTION_BLINK:
// Устанавливаем анимацию, кол-во кадров и мелодию для эмоции «Моргание»
animationFrame = animationBlink;
countFrames = COUNT_FRAMES_BLINK;
anyrtttl::nonblocking::begin_P(BUZZER_PIN, melodyBlink);
break;
// Эмоция «Злость»
case EMOTION_ANGRY:
// Устанавливаем анимацию, кол-во кадров и мелодию для эмоции «Злость»
animationFrame = animationAngry;
countFrames = COUNT_FRAMES_ANGRY;
anyrtttl::nonblocking::begin_P(BUZZER_PIN, melodyAngry);
break;
// Эмоция «Грусть»
case EMOTION_SAD:
// Устанавливаем анимацию, кол-во кадров и мелодию для эмоции «Грусть»
animationFrame = animationSad;
countFrames = COUNT_FRAMES_SAD;
anyrtttl::nonblocking::begin_P(BUZZER_PIN, melodySad);
break;
// Эмоция «Радость»
case EMOTION_HAPPY:
// Устанавливаем анимацию, кол-во кадров и мелодию для эмоции «Радость»
animationFrame = animationHappy;
countFrames = COUNT_FRAMES_HAPPY;
anyrtttl::nonblocking::begin_P(BUZZER_PIN, melodyHappy);
break;
// Эмоция «Удивление»
case EMOTION_AMAZED:
// Устанавливаем анимацию, кол-во кадров и мелодию для эмоции «Удивление»
animationFrame = animationAmazed;
countFrames = COUNT_FRAMES_AMAZED;
anyrtttl::nonblocking::begin_P(BUZZER_PIN, melodyAmazed);
break;
// Эмоция «Любовь»
case EMOTION_LOVE:
// Устанавливаем анимацию, кол-во кадров и мелодию для эмоции «Любовь»
animationFrame = animationLove;
countFrames = COUNT_FRAMES_LOVE;
anyrtttl::nonblocking::begin_P(BUZZER_PIN, melodyLove);
break;
// Эмоция «Сон»
case EMOTION_SLEEP:
// Устанавливаем анимацию, кол-во кадров и мелодию для эмоции «Сон»
animationFrame = animationSleep;
countFrames = COUNT_FRAMES_SLEEP;
anyrtttl::nonblocking::begin_P(BUZZER_PIN, melodySleep);
break;
}
// Устанавливаем в таймер значение счетчика 0
timer.setTime(0);
// Запускаем таймер
timer.start();
}
// Функция переключения на следующую эмоцию
// Для активации смены эмоции, необходимо вызвать функцию установки эмоции setEmotion()
void nextEmotion() {
if (emotion == EMOTION_BLINK) {
emotion = EMOTION_ANGRY;
} else if (emotion == EMOTION_ANGRY) {
emotion = EMOTION_SAD;
} else if (emotion == EMOTION_SAD) {
emotion = EMOTION_HAPPY;
} else if (emotion == EMOTION_HAPPY) {
emotion = EMOTION_AMAZED;
} else if (emotion == EMOTION_AMAZED) {
emotion = EMOTION_LOVE;
} else if (emotion == EMOTION_LOVE) {
emotion = EMOTION_SLEEP;
} else if (emotion == EMOTION_SLEEP) {
emotion = EMOTION_BLINK;
}
}
// Функция выбора случайной эмоции
// Для активации смены эмоции, необходимо вызвать функцию установки эмоции setEmotion()
void randomEmotion() {
emotion = random(0, 8);
if (emotion == 0) {
emotion = EMOTION_BLINK;
} else if (emotion == 1) {
emotion = EMOTION_ANGRY;
} else if (emotion == 2) {
emotion = EMOTION_SAD;
} else if (emotion == 3) {
emotion = EMOTION_HAPPY;
} else if (emotion == 4) {
emotion = EMOTION_AMAZED;
} else if (emotion == 5) {
emotion = EMOTION_LOVE;
} else if (emotion == 6) {
emotion = EMOTION_SLEEP;
} else if (emotion == 7) {
emotion = EMOTION_BLINK;
}
}
// Функция обновления эмоции
void updateEmotion() {
// Если таймер досчитал до конца
if (timer.tick()) {
// Если флаг последнего кадра в состоянии «не активен»
if (lastFrame == false) {
// Отображаем текущий кадр анимации
matrixL.drawBitmapF(animationFrame[indexFrame].iconEyeL);
matrixR.drawBitmapF(animationFrame[indexFrame].iconEyeR);
// Устанавливаем в таймер значение счетчика задержки текущего кадра анимации
timer.setTime(animationFrame[indexFrame].timeFrame);
// Запускаем таймер
timer.start();
// Если текущий кадр не последний в анимации
if (indexFrame < countFrames - 1) {
// Переходим на следующий кадр
indexFrame++;
} else {
// Устанавливаем флаг последнего кадра в состояние «активен»
lastFrame = true;
}
} else {
// Останавливаем таймер
timer.stop();
// Проверяем сколько раз повторялась текущая анимация
if (countRepeatEmotion < COUNT_REPEAT_EMOTION - 1) {
// Увеличиваем счётчик повторения текущей анимации
countRepeatEmotion++;
} else {
// Вызываем функцию переключение на следующую эмоцию
nextEmotion();
// Обнуляем счётчик повторения текущей анимации
countRepeatEmotion = 0;
}
// Устанавливаем текущую анимацию
setEmotion(emotion);
// Устанавливаем флаг последнего кадра в состояние «не активен»
lastFrame = false;
// Обнуляем счётчик кадров
indexFrame = 0;
}
}
}
// Функция сброса всех настроек переменных по умолчанию
void resetState() {
// Обнуляем переменную для хранения количества повторений эмоции
countRepeatEmotion = 0;
// Обнуляем счётчик кадра
indexFrame = 0;
// Устанавливаем флаг последнего кадра в состояние «не активен»
lastFrame = false;
}
==== №5. Обзор влево-вправо ====
{{ :kits:io-kit:robot-head:050-drawing-example-cmyk.png?nolink |}}
// Подключаем библиотеку для работы с сервоприводом
#include
// Создаём объект сервопривода
Servo servoYaw;
// Даём понятное имя пину A3 с сервоприводом
constexpr uint8_t SERVO_YAW_PIN = A3;
// Задаём максимально доступные углы поворота сервопривода головы влево-вправо
constexpr uint8_t MAX_ANGLE_YAW_R = 0;
constexpr uint8_t MAX_ANGLE_YAW_L = 180;
// Вычисляем средний угол поворота сервопривода головы влево-вправо
constexpr uint8_t MID_ANGLE_YAW = (MAX_ANGLE_YAW_R + MAX_ANGLE_YAW_L) / 2;
// Создаём константу для хранения паузы между поворотом вала сервопривода
constexpr uint8_t DELAY_SWEEP_SERVO_YAW = 20;
void setup() {
// Подключаем сервомотор
servoYaw.attach(SERVO_YAW_PIN);
}
void loop() {
// Перебираем значения угла сервы от max слева до max справа
for (int pos = MAX_ANGLE_YAW_R; pos <= MAX_ANGLE_YAW_L; pos++) {
// Отправляем текущий угол на сервопривод
servoYaw.write(pos);
// Выжидаем паузу
delay(DELAY_SWEEP_SERVO_YAW);
}
// Перебираем значения угла сервы от max справа до max слева
for (int pos = MAX_ANGLE_YAW_L; pos >= MAX_ANGLE_YAW_R; pos--) {
// Отправляем текущий угол на сервопривод
servoYaw.write(pos);
// Выжидаем паузу
delay(DELAY_SWEEP_SERVO_YAW);
}
}
==== №6. Обзор вверх-вниз ====
{{ :kits:io-kit:robot-head:060-drawing-example-cmyk.png?nolink |}}
// Подключаем библиотеку для работы с сервоприводом
#include
// Создаём объект сервопривода
Servo servoPitch;
// Даём понятное имя пину A0 с сервоприводом
constexpr uint8_t SERVO_PITCH_PIN = A0;
// Задаём максимально доступные углы поворота сервопривода головы вверх-вниз
constexpr uint8_t MAX_ANGLE_PITCH_DOWN = 60;
constexpr uint8_t MAX_ANGLE_PITCH_UP = 120;
// Вычисляем средний угол поворота сервопривода головы вверх-вниз
constexpr uint8_t MID_ANGLE_PITCH = (MAX_ANGLE_PITCH_DOWN + MAX_ANGLE_PITCH_UP) / 2;
// Создаём константу для хранения паузы между поворотом вала сервопривода
constexpr uint8_t DELAY_SWEEP_SERVO_PITCH = 20;
void setup() {
// Подключаем сервомотор
servoPitch.attach(SERVO_PITCH_PIN);
}
void loop() {
// Перебираем значения угла сервы от max вниз до max вверх
for (int pos = MAX_ANGLE_PITCH_DOWN; pos <= MAX_ANGLE_PITCH_UP; pos++) {
// Отправляем текущий угол на сервопривод
servoPitch.write(pos);
// Выжидаем паузу
delay(DELAY_SWEEP_SERVO_PITCH);
}
// Перебираем значения угла сервы от max вверх до max вниз
for (int pos = MAX_ANGLE_PITCH_UP; pos >= MAX_ANGLE_PITCH_DOWN; pos--) {
// Отправляем текущий угол на сервопривод
servoPitch.write(pos);
// Выжидаем паузу
delay(DELAY_SWEEP_SERVO_PITCH);
}
}
==== №7. Консольный ИК-приёмник ====
{{ :kits:io-kit:robot-head:070-drawing-example-cmyk.png?nolink |}}
**Проверка пульта**
Протестируй свой пульт и сравни коды кнопок в консоли с целевыми кодами из таблицы ниже.
* Если полученные коды совпадают, можно смело пользоваться нашими скетчами ''.ino'' без изменений.
* Если коды не совпали, значит тебе попался пульт другой модификации. Ничего страшного!
//Запиши фактические коды кнопок в таблицу. Они пригодятся тебе для подстановки в последующие проекты с ИК-пультом и приёмником.//
^ Имя кнопки ^ Код кнопки ^^
^ ::: ^ Целевой ^ По факту ^
| Power | 0x0B | |
| ⊖ | 0x07 | |
| ⊕ | 0x1B | |
| 🔴 | 0x00 | |
| 🟢 | 0x03 | |
| 🔵 | 0x01 | |
| x | 0x09 | |
| ▢ | 0x05 | |
| △ | 0x02 | |
| ↖ | 0x0A | |
| ↑ | 0x06 | |
| ↗ | 0x1A | |
| ← | 0x08 | |
| ▶ | 0x1C | |
| → | 0x19 | |
| ↙ | 0x18 | |
| ↓ | 0x1D | |
| ↘ | 0x04 | |
| X | 0x0C | |
| Y | 0x0D | |
| Z | 0x0E | |
// Подключаем библиотеку для работы с ИК-приёмником
#include
// Даём понятное имя пину 2 с ИК-приёмником
constexpr uint8_t IR_RECEIVE_PIN = 2;
void setup() {
// Открываем монитор Serial-порта
Serial.begin(9600);
// Инициализируем ИК-приёмник
IrReceiver.begin(IR_RECEIVE_PIN);
}
void loop() {
// Если пришёл новый сигнал на ИК-приёмник, декодируем его
if (IrReceiver.decode()) {
// Игнорируем повторное нажатие кнопки
if ((IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT)) {
// Разрешаем обрабатывать следующий сигнал
IrReceiver.resume();
// Выходим из всего цикла
return;
}
// Создаём переменную и присваиваем ей декодируемый код кнопки
uint32_t code = IrReceiver.decodedIRData.command;
// Печатем код кнопки в консоль с форматированием
printCode(code);
// Разрешаем обрабатывать следующий сигнал
IrReceiver.resume();
}
}
// Функция печати кода кнопки в консоль с форматированием
void printCode(uint32_t code) {
Serial.print("0x");
if (code < 15) {
Serial.print("0");
}
Serial.println(code, HEX);
}
==== №8. Звёздный бот ====
{{ :kits:io-kit:robot-head:080-drawing-example-cmyk.png?nolink&400 |}}
// Подключаем библиотеку для работы с мелодиями в формате RTTTL
#include
// Подключаем библиотеку для работы с ИК-приёмником
#include
// Подключаем библиотеку для работы со светодиодными матрицами
#include
// Даём понятное имя пину 3 с пищалкой
constexpr uint8_t BUZZER_PIN = 3;
// Даём понятное имя пину 2 с ИК-приёмником
constexpr uint8_t IR_RECEIVE_PIN = 2;
// Создаём объект матрицы левого глаза
// на шине I²C с адресом 0x60 (указан по умолчанию)
TroykaLedMatrix matrixL;
// Создаём объект матрицы правого глаза
// на шине I²C с адресом 0x63
TroykaLedMatrix matrixR(0x63);
// Создаём константу для хранения базовой частоты
constexpr int FREQUENCY = 2000;
// Создаём константы для хранения минимальной и максимальной частоты
constexpr int MIN_FREQUENCY = FREQUENCY - (0.25 * FREQUENCY);
constexpr int MAX_FREQUENCY = FREQUENCY + (0.25 * FREQUENCY);
// Создаём иконку «Взгляд прямо» в шестнадцатеричной системе HEX
// Иконка для левого и правого глаза одинаковая
// Эмоции рисуем в редакторе изображений для LED-матрицы
// https://amperka.github.io/led-matrix-editor/
constexpr uint8_t ICON_EYE_STRAIGHT[] PROGMEM {
0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e
};
// Создаём иконку «Глаза выключены» в шестнадцатеричной системе HEX
// Иконка для левого и правого глаза одинаковая
constexpr uint8_t ICON_EYE_OFF[] PROGMEM {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
void setup() {
// Инициализируем матрицы
matrixL.begin();
matrixR.begin();
// Очищаем матрицы
matrixL.clear();
matrixR.clear();
// Настраиваем пин с пищалкой в режим выхода
pinMode(BUZZER_PIN, OUTPUT);
// Инициализируем ИК-приёмник
IrReceiver.begin(IR_RECEIVE_PIN);
// Передаём случайное число с пина A1
// для последующей генерации случайных чисел
randomSeed(analogRead(A1));
}
void loop() {
// Переходим в функцию обработки ИК-приёмника
remoteHandler();
}
// Функция отображения иконки на матрицах
void drawIcon(uint8_t* iconEyeL, uint8_t* iconEyeR) {
matrixL.drawBitmapF(iconEyeL);
matrixR.drawBitmapF(iconEyeR);
}
// Функция обработки ИК-приёмника
void remoteHandler() {
// Если пришёл новый сигнал на ИК-приёмник, декодируем его
if (IrReceiver.decode()) {
// Создаём переменную и присваиваем ей декодируемый код кнопки
uint32_t code = IrReceiver.decodedIRData.command;
// Если код кнопки равен 0, пришёл шум и игнорируем его
if (code == 0x00) {
// Разрешаем обрабатывать следующий сигнал
IrReceiver.resume();
// Выходим из функции
return;
}
// Отключаем ИК-приёмник
IrReceiver.stop();
// Вызываем функцию генератора последовательных длинных семплов
toneLongSamples();
// Вызываем функцию генератора непоследовательных коротких семплов
toneShortSamples();
// Запускаем ИК-приёмник
IrReceiver.start();
// Разрешаем обрабатывать команды с ИК-приёмника
IrReceiver.resume();
}
}
// Функция генерации последовательных длинных семплов
void toneLongSamples() {
// Генерируем случайное количество семплов от 1 до 6
int countSamples = random(1, 7);
// Перебираем нумерацию семплов в цикле
for (int i = 0; i < countSamples; i++) {
// Генерируем случайное число: true или false
bool typeSamples = random(0, 2);
// Если тип семпла true
if (typeSamples) {
// Вызываем функцию генерации тона toneSlowDownFastUp:
// Сначала медленный убывающий тон со случайными задержками
// Затем быстрый возрастающий тон со случайными задержками
toneSlowDownFastUp();
} else {
// Если тип семпла false
// Вызываем функцию генерации тона toneSlowUpFastDown:
// Сначала медленный возрастающий тон со случайными задержками
// Затем быстрый убывающий тон со случайными задержками
toneSlowUpFastDown();
}
}
}
// Функция генерации непоследовательных коротких семплов
void toneShortSamples() {
// Генерируем случайное количество семплов от 3 до 9
int countSamples = random(3, 10);
// Генерируем случайную частоту на базе FREQUENCY
int frequency = random(MIN_FREQUENCY, MAX_FREQUENCY);
for (int i = 0; i <= countSamples; i++) {
// Активируем глаза
drawIcon(ICON_EYE_STRAIGHT, ICON_EYE_STRAIGHT);
// Генерируем случайный интервал на базе FREQUENCY
int range = random(-FREQUENCY, FREQUENCY);
tone(BUZZER_PIN, frequency + range);
// Выполняем случайную задержку от 70 до 170 мс
delay(random(70, 170));
// Деактивируем глаза
drawIcon(ICON_EYE_OFF, ICON_EYE_OFF);
// Выключаем звук
noTone(BUZZER_PIN);
// Выполняем случайную задержку от 0 до 30 мс
delay(random(0, 30));
}
}
// Функция генерации звуковой последовательности
// Сначала медленный убывающий тон со случайными задержками
// Затем быстрый возрастающий тон со случайными задержками
void toneSlowDownFastUp() {
// Генерируем случайную частоту на базе FREQUENCY
int frequency = random(MIN_FREQUENCY, MAX_FREQUENCY);
// Активируем глаза
drawIcon(ICON_EYE_STRAIGHT, ICON_EYE_STRAIGHT);
// Генерируем медленный понижающий тон со случайными задержками
for (int range = 0; range <= random(100, 1000); range++) {
tone(BUZZER_PIN, (range * 2));
delayMicroseconds(random(0, 1000));
}
// Деактивируем глаза
drawIcon(ICON_EYE_OFF, ICON_EYE_OFF);
// Генерируем быстрый повышающий тон со случайными задержками
for (int range = 0; range <= random(100, 1000); range++) {
tone(BUZZER_PIN, frequency + (range * 10));
delayMicroseconds(random(0, 1000));
}
}
// Функция генерации звуковой последовательности
// Сначала медленный возрастающий тон со случайными задержками
// Затем быстрый убывающий тон со случайными задержками
void toneSlowUpFastDown() {
// Генерируем случайную частоту на базе FREQUENCY
int frequency = random(MIN_FREQUENCY, MAX_FREQUENCY);
// Активируем глаза
drawIcon(ICON_EYE_STRAIGHT, ICON_EYE_STRAIGHT);
// Генерируем медленный возрастающий тон со случайными задержками
for (int range = 0; range <= random(100, 1000); range++) {
tone(BUZZER_PIN, (range * 2));
delayMicroseconds(random(0, 1000));
}
// Деактивируем глаза
drawIcon(ICON_EYE_OFF, ICON_EYE_OFF);
// Генерируем быстрый понижающий тон со случайными задержками
for (int range = 0; range <= random(100, 1000); range++) {
tone(BUZZER_PIN, (range * 10));
delayMicroseconds(random(0, 1000));
}
}
==== №9. Эмо-бот ====
{{ :kits:io-kit:robot-head:090-drawing-example-cmyk.png?nolink |}}
**Изменение кода кнопок пульта**
Вернись к шпаргалке с таблицей из эксперимента №7 «Консольный ИК-приёмник» и подставь фактические коды кнопок твоего пульта в строках ''62''–''70'', когда откроешь скетч ''IrEmotions.ino'' в Arduino IDE.
enum Emotion {
EMOTION_BLINK = 0x1C,
EMOTION_ANGRY = 0x02,
EMOTION_SAD = 0xC,
EMOTION_HAPPY = 0x0D,
EMOTION_AMAZED = 0x05,
EMOTION_LOVE = 0x09,
EMOTION_SLEEP = 0x0E,
} emotion;
// Подключаем библиотеку для работы с мелодиями в формате RTTTL
#include
// Подключаем библиотеку для работы с ИК-приёмником
#include
// Подключаем библиотеку для работы с таймером millis
#include
// Подключаем библиотеку для работы со светодиодными матрицами
#include
// Даём понятное имя пину 3 с пищалкой
constexpr uint8_t BUZZER_PIN = 3;
// Даём понятное имя пину 2 с ИК-приёмником
constexpr uint8_t IR_RECEIVE_PIN = 2;
// Создаём объект для работы с таймером
TimerMs timer;
// Создаём объект матрицы левого глаза
// на шине I²C с адресом 0x60 (указан по умолчанию)
TroykaLedMatrix matrixL;
// Создаём объект матрицы правого глаза
// на шине I²C с адресом 0x63
TroykaLedMatrix matrixR(0x63);
// Создаём массив данных мелодии «Моргание» в формате RTTTL
constexpr uint8_t melodyBlink[] PROGMEM = "Blink:d=4,o=4,b=120:"
"16e,16p,16g,16p,16e,16p,16g,16p,";
// Создаём массив данных мелодии «Злость» в формате RTTTL
constexpr uint8_t melodyAngry[] PROGMEM = "Angry:d=4,o=4,b=200:"
"16g,16g,16f#,16f#,16e,16e,16d#,16d#,"
"16c,16c,16b4,16b4,16a4,16a4,16g4,16g4";
// Создаём массив данных мелодии «Грусть» в формате RTTTL
constexpr uint8_t melodySad[] PROGMEM = "Sad:d=4,o=4,b=25:"
"16g,16a#,16d,16f,"
"16d,16c,16a#";
constexpr uint8_t melodyHappy[] PROGMEM = "Happy:d=4,o=4,b=140:"
"8c4,8e4,8g4,8c4,8e4,8g4,8c4,8e4"
"8g4,8c4,8e4,8g4";
// Создаём массив данных мелодии «Удивление» в формате RTTTL
constexpr uint8_t melodyAmazed[] PROGMEM = "Amazed:d=4,o=4,b=180:"
"16c,16d,16e,16f,16g,16a,16b,16c,"
"16b,16a,16g,16f,16e,16d,16c,16d";
// Создаём массив данных мелодии «Любовь» в формате RTTTL
constexpr uint8_t melodyLove[] PROGMEM = "Love:d=4,o=4,b=100:"
"8g,8p,8g,8p,8a,8p,8g,8p,"
"8g,8p,8g,8p,8f,8e,8d";
// Создаём массив данных мелодии «Сонливость» в формате RTTTL
constexpr uint8_t melodySleep[] PROGMEM = "Sleep:d=4,o=4,b=45:"
"8c,8c,8d,8e,8d,8c,8d,8e";
// Создаём перечисление эмоций с соответствующей переменной
enum Emotion {
EMOTION_BLINK = 0x1C,
EMOTION_ANGRY = 0x02,
EMOTION_SAD = 0xC,
EMOTION_HAPPY = 0x0D,
EMOTION_AMAZED = 0x05,
EMOTION_LOVE = 0x09,
EMOTION_SLEEP = 0x0E,
} emotion;
// Создаём структуру анимации
struct AnimationFrame {
uint8_t* iconEyeL;
uint8_t* iconEyeR;
int timeFrame;
};
// Создаём объект структуры анимации
AnimationFrame* animationFrame;
// Создаём иконку «Взгляд прямо» в шестнадцатеричной системе HEX
// Иконка для левого и правого глаза одинаковая
// Эмоции рисуем в редакторе изображений для LED-матрицы
// https://amperka.github.io/led-matrix-editor/
constexpr uint8_t ICON_EYE_STRAIGHT[] PROGMEM {
0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e
};
// Создаём иконку «Глаза выключены» в шестнадцатеричной системе HEX
// Иконка для левого и правого глаза одинаковая
constexpr uint8_t ICON_EYE_OFF[] PROGMEM {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// Создаём массив иконок анимации «Моргание» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
constexpr uint8_t ICON_EYE_BLINK[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x00, 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x7e },
{ 0x00, 0x00, 0x7e, 0x81, 0x99, 0x99, 0x81, 0x7e },
{ 0x00, 0x00, 0x00, 0x7e, 0x99, 0x99, 0x7e, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x99, 0x7e, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7e, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x99, 0x7e, 0x00 },
{ 0x00, 0x00, 0x00, 0x7e, 0x99, 0x99, 0x7e, 0x00 },
{ 0x00, 0x00, 0x7e, 0x81, 0x99, 0x99, 0x81, 0x7e },
{ 0x00, 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x7e },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Злость»
// в шестнадцатеричной системе HEX для левого глаза
constexpr uint8_t ICON_EYE_ANGRY_L[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x78, 0x84, 0x82, 0x9a, 0x9a, 0x82, 0x82, 0x7c },
{ 0x70, 0x88, 0x84, 0x9a, 0x9a, 0x82, 0x82, 0x7c },
{ 0x60, 0x90, 0x88, 0x9c, 0x9a, 0x82, 0x82, 0x7c },
{ 0x40, 0xa0, 0x90, 0x88, 0xb4, 0xb2, 0x82, 0x7c },
{ 0x80, 0xc0, 0xa0, 0x90, 0xa8, 0xb4, 0x82, 0x7c },
{ 0x40, 0xa0, 0x90, 0x88, 0xb4, 0xb2, 0x82, 0x7c },
{ 0x60, 0x90, 0x88, 0x9c, 0x9a, 0x82, 0x82, 0x7c },
{ 0x70, 0x88, 0x84, 0x9a, 0x9a, 0x82, 0x82, 0x7c },
{ 0x78, 0x84, 0x82, 0x9a, 0x9a, 0x82, 0x82, 0x7c },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Злость»
// в шестнадцатеричной системе HEX для правого глаза
constexpr uint8_t ICON_EYE_ANGRY_R[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x1e, 0x21, 0x41, 0x59, 0x59, 0x41, 0x41, 0x3e },
{ 0x0e, 0x11, 0x21, 0x59, 0x59, 0x41, 0x41, 0x3e },
{ 0x06, 0x09, 0x11, 0x39, 0x59, 0x41, 0x41, 0x3e },
{ 0x02, 0x05, 0x09, 0x11, 0x2d, 0x4d, 0x41, 0x3e },
{ 0x01, 0x03, 0x05, 0x09, 0x15, 0x2d, 0x41, 0x3e },
{ 0x02, 0x05, 0x09, 0x11, 0x2d, 0x4d, 0x41, 0x3e },
{ 0x06, 0x09, 0x11, 0x39, 0x59, 0x41, 0x41, 0x3e },
{ 0x0e, 0x11, 0x21, 0x59, 0x59, 0x41, 0x41, 0x3e },
{ 0x1e, 0x21, 0x41, 0x59, 0x59, 0x41, 0x41, 0x3e }
};
// Создаём массив иконок анимации «Грусть»
// в шестнадцатеричной системе HEX для левого глаза
constexpr uint8_t ICON_EYE_SAD_L[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x3e, 0x41, 0x81, 0x99, 0x99, 0x81, 0x82, 0x7c },
{ 0x1e, 0x21, 0x41, 0x99, 0x99, 0x81, 0x82, 0x7c },
{ 0x0e, 0x11, 0x21, 0x41, 0x8d, 0x8d, 0x82, 0x7c },
{ 0x0e, 0x11, 0x21, 0x41, 0x4d, 0x4d, 0x42, 0x3c },
{ 0x06, 0x09, 0x11, 0x21, 0x4d, 0x4d, 0x42, 0x3c },
{ 0x0e, 0x11, 0x21, 0x41, 0x4d, 0x4d, 0x42, 0x3c },
{ 0x0e, 0x11, 0x21, 0x41, 0x8d, 0x8d, 0x82, 0x7c },
{ 0x1e, 0x21, 0x41, 0x99, 0x99, 0x81, 0x82, 0x7c },
{ 0x3e, 0x41, 0x81, 0x99, 0x99, 0x81, 0x82, 0x7c },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Грусть»
// в шестнадцатеричной системе HEX для правого глаза
constexpr uint8_t ICON_EYE_SAD_R[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x7c, 0x82, 0x81, 0x99, 0x99, 0x81, 0x41, 0x3e },
{ 0x78, 0x84, 0x82, 0x99, 0x99, 0x81, 0x41, 0x3e },
{ 0x70, 0x88, 0x84, 0x82, 0xb1, 0xb1, 0x41, 0x3e },
{ 0x70, 0x88, 0x84, 0x82, 0xb2, 0xb2, 0x42, 0x3c },
{ 0x60, 0x90, 0x88, 0x84, 0xb2, 0xb2, 0x42, 0x3c },
{ 0x70, 0x88, 0x84, 0x82, 0xb2, 0xb2, 0x42, 0x3c },
{ 0x70, 0x88, 0x84, 0x82, 0xb1, 0xb1, 0x41, 0x3e },
{ 0x78, 0x84, 0x82, 0x99, 0x99, 0x81, 0x41, 0x3e },
{ 0x7c, 0x82, 0x81, 0x99, 0x99, 0x81, 0x41, 0x3e },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Радость» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
constexpr uint8_t ICON_EYE_HAPPY[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x00, 0x7e, 0x81, 0x99, 0x99, 0x81, 0x7e, 0x00 },
{ 0x00, 0x7e, 0x81, 0x99, 0x99, 0xff, 0x00, 0x00 },
{ 0x00, 0x7e, 0xdb, 0x99, 0xff, 0x81, 0x00, 0x00 },
{ 0x00, 0x3c, 0x5a, 0xff, 0xc3, 0x81, 0x00, 0x00 },
{ 0x00, 0x3c, 0x7e, 0xc3, 0xc3, 0x81, 0x00, 0x00 },
{ 0x00, 0x3c, 0x5a, 0xff, 0xc3, 0x81, 0x00, 0x00 },
{ 0x00, 0x7e, 0xdb, 0x99, 0xff, 0x81, 0x00, 0x00 },
{ 0x00, 0x7e, 0x81, 0x99, 0x99, 0xff, 0x00, 0x00 },
{ 0x00, 0x7e, 0x81, 0x99, 0x99, 0x81, 0x7e, 0x00 },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Удивление» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
constexpr uint8_t ICON_EYE_AMAZED[][8] PROGMEM {
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e },
{ 0x7e, 0x81, 0x99, 0xbd, 0xbd, 0x99, 0x81, 0x7e },
{ 0x7e, 0x81, 0xbd, 0xbd, 0xbd, 0xbd, 0x81, 0x7e },
{ 0x7e, 0x81, 0x99, 0xbd, 0xbd, 0x99, 0x81, 0x7e },
{ 0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e }
};
// Создаём массив иконок анимации «Любовь» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
constexpr uint8_t ICON_EYE_LOVE[][8] PROGMEM {
{ 0x66, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x18 },
{ 0x00, 0x00, 0x24, 0x7e, 0x7e, 0x3c, 0x18, 0x00 }
};
// Создаём массив иконок анимации Сон» в шестнадцатеричной системе HEX
// Массив иконок для левого и правого глаза одинаковый
constexpr uint8_t ICON_EYE_SLEEP[][8] PROGMEM {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7e, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, 0x7e, 0x00 }
};
// Создаём анимацию «Моргание»
AnimationFrame animationBlink[] = {
{ ICON_EYE_BLINK[0], ICON_EYE_BLINK[0], 500 },
{ ICON_EYE_BLINK[1], ICON_EYE_BLINK[1], 25 },
{ ICON_EYE_BLINK[2], ICON_EYE_BLINK[2], 25 },
{ ICON_EYE_BLINK[3], ICON_EYE_BLINK[3], 25 },
{ ICON_EYE_BLINK[4], ICON_EYE_BLINK[4], 25 },
{ ICON_EYE_BLINK[5], ICON_EYE_BLINK[5], 25 },
{ ICON_EYE_BLINK[6], ICON_EYE_BLINK[6], 25 },
{ ICON_EYE_BLINK[7], ICON_EYE_BLINK[7], 25 },
{ ICON_EYE_BLINK[8], ICON_EYE_BLINK[8], 25 },
{ ICON_EYE_BLINK[9], ICON_EYE_BLINK[9], 25 },
{ ICON_EYE_BLINK[10], ICON_EYE_BLINK[10], 500 }
};
// Создаём анимацию «Злость»
AnimationFrame animationAngry[] = {
{ ICON_EYE_ANGRY_L[0], ICON_EYE_ANGRY_R[0], 500 },
{ ICON_EYE_ANGRY_L[1], ICON_EYE_ANGRY_R[1], 50 },
{ ICON_EYE_ANGRY_L[2], ICON_EYE_ANGRY_R[2], 50 },
{ ICON_EYE_ANGRY_L[3], ICON_EYE_ANGRY_R[3], 50 },
{ ICON_EYE_ANGRY_L[4], ICON_EYE_ANGRY_R[4], 50 },
{ ICON_EYE_ANGRY_L[5], ICON_EYE_ANGRY_R[5], 2000 },
{ ICON_EYE_ANGRY_L[6], ICON_EYE_ANGRY_R[6], 50 },
{ ICON_EYE_ANGRY_L[7], ICON_EYE_ANGRY_R[7], 50 },
{ ICON_EYE_ANGRY_L[8], ICON_EYE_ANGRY_R[8], 50 },
{ ICON_EYE_ANGRY_L[9], ICON_EYE_ANGRY_R[9], 50 },
{ ICON_EYE_ANGRY_L[10], ICON_EYE_ANGRY_R[10], 500 }
};
// Создаём анимацию «Грусть»
AnimationFrame animationSad[] = {
{ ICON_EYE_SAD_L[0], ICON_EYE_SAD_R[0], 500 },
{ ICON_EYE_SAD_L[1], ICON_EYE_SAD_R[1], 50 },
{ ICON_EYE_SAD_L[2], ICON_EYE_SAD_R[2], 50 },
{ ICON_EYE_SAD_L[3], ICON_EYE_SAD_R[3], 50 },
{ ICON_EYE_SAD_L[4], ICON_EYE_SAD_R[4], 50 },
{ ICON_EYE_SAD_L[5], ICON_EYE_SAD_R[5], 3000 },
{ ICON_EYE_SAD_L[6], ICON_EYE_SAD_R[6], 50 },
{ ICON_EYE_SAD_L[7], ICON_EYE_SAD_R[7], 50 },
{ ICON_EYE_SAD_L[8], ICON_EYE_SAD_R[8], 50 },
{ ICON_EYE_SAD_L[9], ICON_EYE_SAD_R[9], 50 },
{ ICON_EYE_SAD_L[10], ICON_EYE_SAD_R[10], 500 }
};
// Создаём анимацию «Радость»
AnimationFrame animationHappy[] = {
{ ICON_EYE_HAPPY[0], ICON_EYE_HAPPY[0], 500 },
{ ICON_EYE_HAPPY[1], ICON_EYE_HAPPY[1], 40 },
{ ICON_EYE_HAPPY[2], ICON_EYE_HAPPY[2], 40 },
{ ICON_EYE_HAPPY[3], ICON_EYE_HAPPY[3], 40 },
{ ICON_EYE_HAPPY[4], ICON_EYE_HAPPY[4], 40 },
{ ICON_EYE_HAPPY[5], ICON_EYE_HAPPY[5], 2000 },
{ ICON_EYE_HAPPY[6], ICON_EYE_HAPPY[6], 40 },
{ ICON_EYE_HAPPY[7], ICON_EYE_HAPPY[7], 40 },
{ ICON_EYE_HAPPY[8], ICON_EYE_HAPPY[8], 40 },
{ ICON_EYE_HAPPY[9], ICON_EYE_HAPPY[9], 40 },
{ ICON_EYE_HAPPY[10], ICON_EYE_HAPPY[10], 500 }
};
// Создаём анимацию «Удивление»
AnimationFrame animationAmazed[] = {
{ ICON_EYE_AMAZED[0], ICON_EYE_AMAZED[0], 500 },
{ ICON_EYE_AMAZED[1], ICON_EYE_AMAZED[1], 100 },
{ ICON_EYE_AMAZED[2], ICON_EYE_AMAZED[2], 2000 },
{ ICON_EYE_AMAZED[3], ICON_EYE_AMAZED[3], 100 },
{ ICON_EYE_AMAZED[4], ICON_EYE_AMAZED[4], 500 }
};
// Создаём анимацию «Любовь»
AnimationFrame animationLove[] = {
{ ICON_EYE_LOVE[0], ICON_EYE_LOVE[0], 1000 },
{ ICON_EYE_LOVE[1], ICON_EYE_LOVE[1], 300 },
{ ICON_EYE_LOVE[0], ICON_EYE_LOVE[0], 1000 },
{ ICON_EYE_LOVE[1], ICON_EYE_LOVE[1], 300 },
{ ICON_EYE_LOVE[0], ICON_EYE_LOVE[0], 1000 },
{ ICON_EYE_LOVE[1], ICON_EYE_LOVE[1], 300 },
{ ICON_EYE_LOVE[0], ICON_EYE_LOVE[0], 1000 },
{ ICON_EYE_LOVE[1], ICON_EYE_LOVE[1], 300 },
};
// Создаём анимацию «Сон»
AnimationFrame animationSleep[] = {
{ ICON_EYE_SLEEP[0], ICON_EYE_SLEEP[0], 750 },
{ ICON_EYE_SLEEP[1], ICON_EYE_SLEEP[1], 750 },
{ ICON_EYE_SLEEP[0], ICON_EYE_SLEEP[0], 750 },
{ ICON_EYE_SLEEP[1], ICON_EYE_SLEEP[1], 750 },
{ ICON_EYE_SLEEP[0], ICON_EYE_SLEEP[0], 750 },
{ ICON_EYE_SLEEP[1], ICON_EYE_SLEEP[1], 750 },
{ ICON_EYE_SLEEP[0], ICON_EYE_SLEEP[0], 750 },
{ ICON_EYE_SLEEP[1], ICON_EYE_SLEEP[1], 750 },
};
// Вычисляем количество кадров в анимации «Моргание»
constexpr uint8_t COUNT_FRAMES_BLINK = sizeof(animationBlink) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Злость»
constexpr uint8_t COUNT_FRAMES_ANGRY = sizeof(animationAngry) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Грусть»
constexpr uint8_t COUNT_FRAMES_SAD = sizeof(animationSad) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Радость»
constexpr uint8_t COUNT_FRAMES_HAPPY = sizeof(animationHappy) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Удивление»
constexpr uint8_t COUNT_FRAMES_AMAZED = sizeof(animationAmazed) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Любовь»
constexpr uint8_t COUNT_FRAMES_LOVE = sizeof(animationLove) / sizeof(AnimationFrame);
// Вычисляем количество кадров в анимации «Сон»
constexpr uint8_t COUNT_FRAMES_SLEEP = sizeof(animationSleep) / sizeof(AnimationFrame);
// Создаём переменную для хранения счётчика текущего кадра
int indexFrame;
// Создаём переменную для хранения количества кадров
int countFrames;
// Создаём переменную для хранения флага последнего кадра
bool lastFrame;
void setup() {
// Инициализируем матрицы
matrixL.begin();
matrixR.begin();
// Очищаем матрицы
matrixL.clear();
matrixR.clear();
// Инициализируем ИК-приёмник
IrReceiver.begin(IR_RECEIVE_PIN);
// Сбрасываем все настройки переменных по умолчанию
resetState();
// Устанавливаем таймер в режим остановки после срабатывания
timer.setTimerMode();
}
void loop() {
// Переходим в функцию обработки эмоций
emotionHandler();
// Переходим в функцию обработки ИК-приёмника
remoteHandler();
}
// Функция отображения иконки на матрицах
void drawIcon(uint8_t* iconEyeL, uint8_t* iconEyeR) {
matrixL.drawBitmapF(iconEyeL);
matrixR.drawBitmapF(iconEyeR);
}
// Функция обработки эмоций
void emotionHandler() {
// Вызываем функцию обновление анимации
updateEmotion();
// Разрешаем играть выбранный трек
anyrtttl::nonblocking::play();
}
// Функция обработки ИК-приёмника
void remoteHandler() {
// Если пришёл новый сигнал на ИК-приёмник, декодируем его
if (IrReceiver.decode()) {
// Создаём переменную и присваиваем ей декодируемый код кнопки
uint32_t code = IrReceiver.decodedIRData.command;
// Проверяем код кнопки
switch (code) {
// Если нажата кнопка с кодом эмоции «Моргание»
case EMOTION_BLINK:
setEmotion(EMOTION_BLINK);
break;
// Если нажата кнопка с кодом эмоции «Злость»
case EMOTION_ANGRY:
// Отображаем анимацию «Злость»
setEmotion(EMOTION_ANGRY);
break;
// Если нажата кнопка с кодом эмоции «Грусть»
case EMOTION_SAD:
// Отображаем анимацию «Грусть»
setEmotion(EMOTION_SAD);
break;
// Если нажата кнопка с кодом эмоции «Радость»
case EMOTION_HAPPY:
// Отображаем анимацию «Радость»
setEmotion(EMOTION_HAPPY);
break;
// Если нажата кнопка с кодом эмоции «Удивление
case EMOTION_AMAZED:
// Отображаем анимацию «Удивление»
setEmotion(EMOTION_AMAZED);
break;
// Если нажата кнопка с кодом эмоции «Любовь»
case EMOTION_LOVE:
// Отображаем анимацию «Любовь»
setEmotion(EMOTION_LOVE);
break;
// Если нажата кнопка с кодом эмоции «Сон»
case EMOTION_SLEEP:
// Отображаем анимацию «Сон»
setEmotion(EMOTION_SLEEP);
break;
}
// Разрешаем обрабатывать следующий сигнал
IrReceiver.resume();
}
}
// Функция установки текущей эмоции
void setEmotion(Emotion emotionNow) {
// Обновляем полученную эмоцию
emotion = emotionNow;
// Проверяем состояние текущей эмоции
switch (emotion) {
// Эмоция «Моргание»
case EMOTION_BLINK:
// Устанавливаем анимацию, кол-во кадров и мелодию для эмоции «Моргание»
animationFrame = animationBlink;
countFrames = COUNT_FRAMES_BLINK;
anyrtttl::nonblocking::begin_P(BUZZER_PIN, melodyBlink);
break;
// Эмоция «Злость»
case EMOTION_ANGRY:
// Устанавливаем анимацию, кол-во кадров и мелодию для эмоции «Злость»
animationFrame = animationAngry;
countFrames = COUNT_FRAMES_ANGRY;
anyrtttl::nonblocking::begin_P(BUZZER_PIN, melodyAngry);
break;
// Эмоция «Грусть»
case EMOTION_SAD:
// Устанавливаем анимацию, кол-во кадров и мелодию для эмоции «Грусть»
animationFrame = animationSad;
countFrames = COUNT_FRAMES_SAD;
anyrtttl::nonblocking::begin_P(BUZZER_PIN, melodySad);
break;
// Эмоция «Радость»
case EMOTION_HAPPY:
// Устанавливаем анимацию, кол-во кадров и мелодию для эмоции «Радость»
animationFrame = animationHappy;
countFrames = COUNT_FRAMES_HAPPY;
anyrtttl::nonblocking::begin_P(BUZZER_PIN, melodyHappy);
break;
// Эмоция «Удивление»
case EMOTION_AMAZED:
// Устанавливаем анимацию, кол-во кадров и мелодию для эмоции «Удивление»
animationFrame = animationAmazed;
countFrames = COUNT_FRAMES_AMAZED;
anyrtttl::nonblocking::begin_P(BUZZER_PIN, melodyAmazed);
break;
// Эмоция «Любовь»
case EMOTION_LOVE:
// Устанавливаем анимацию, кол-во кадров и мелодию для эмоции «Любовь»
animationFrame = animationLove;
countFrames = COUNT_FRAMES_LOVE;
anyrtttl::nonblocking::begin_P(BUZZER_PIN, melodyLove);
break;
// Эмоция «Сон»
case EMOTION_SLEEP:
// Устанавливаем анимацию, кол-во кадров и мелодию для эмоции «Сон»
animationFrame = animationSleep;
countFrames = COUNT_FRAMES_SLEEP;
anyrtttl::nonblocking::begin_P(BUZZER_PIN, melodySleep);
break;
}
// Отключаем ИК-приёмник
IrReceiver.stop();
// Устанавливаем в таймер значение счетчика 0
timer.setTime(0);
// Запускаем таймер
timer.start();
}
// Функция обновления эмоции
void updateEmotion() {
// Если таймер досчитал до конца
if (timer.tick()) {
// Если флаг последнего кадра в состоянии «не активен»
if (lastFrame == false) {
// Отображаем текущий кадр анимации
matrixL.drawBitmapF(animationFrame[indexFrame].iconEyeL);
matrixR.drawBitmapF(animationFrame[indexFrame].iconEyeR);
// Устанавливаем в таймер значение счетчика задержки текущего кадра анимации
timer.setTime(animationFrame[indexFrame].timeFrame);
// Запускаем таймер
timer.start();
// Если текущий кадр не последний в анимации
if (indexFrame < countFrames - 1) {
// Переходим на следующий кадр
indexFrame++;
} else {
// Устанавливаем флаг последнего кадра в состояние «активен»
lastFrame = true;
}
} else {
// Останавливаем таймер
timer.stop();
// Запускаем ИК-приёмник
IrReceiver.start();
// Сбрасываем все настройки по умолчанию
resetState();
}
}
}
// Функция сброса всех настроек по умолчанию
void resetState() {
// Обнуляем счётчик кадра
indexFrame = 0;
// Устанавливаем флаг последнего кадра в состояние «не активен»
lastFrame = false;
// Отображаем на матрицах иконку «Взгляд прямо»
drawIcon(ICON_EYE_STRAIGHT, ICON_EYE_STRAIGHT);
}
==== №10. Робоголова ====
{{ :kits:io-kit:robot-head:100-drawing-example-cmyk.png?nolink&500 |}}
**Изменение кода кнопок пульта**
Вернись к шпаргалке с таблицей из эксперимента №7 «Консольный ИК-приёмник» и подставь фактические коды кнопок твоего пульта в строках ''144''–''157'', когда откроешь скетч ''RobotHead.ino'' в Arduino IDE.
enum CODE {
POWER = 0x0B,
LEFT = 0x08,
RIGHT = 0x19,
UP = 0x06,
DOWN = 0x1D,
UP_LEFT = 0x0A,
UP_RIGHT = 0x1A,
DOWN_LEFT = 0x18,
DOWN_RIGHT = 0x04,
RED = 0x00,
GREEN = 0x03,
BLUE = 0x01
} code;
// Подключаем библиотеку для работы с мелодиями в формате RTTTL
#include
// Подключаем библиотеку для работы с ИК-приёмником
#include
// Подключаем библиотеку для работы с сервоприводом
#include
// Подключаем библиотеку для работы с таймером millis
#include
// Подключаем библиотеку для работы со светодиодными матрицами
#include
// Даём понятное имя пину 3 с пищалкой
constexpr uint8_t BUZZER_PIN = 3;
// Даём понятное имя пину 2 с ИК-приёмником
constexpr uint8_t IR_RECEIVE_PIN = 2;
// Даём понятное имя пину A3 с сервоприводом
constexpr uint8_t SERVO_YAW_PIN = A3;
// Даём понятное имя пину A0 с сервоприводом
constexpr uint8_t SERVO_PITCH_PIN = A0;
// Создаём объект сервопривода влево-вправо
Servo servoYaw;
// Создаём объект сервопривода вверх-вниз
Servo servoPitch;
// Создаём объект для работы с таймером
TimerMs timer;
// Создаём объект матрицы левого глаза
// на шине I²C с адресом 0x60 (указан по умолчанию)
TroykaLedMatrix matrixL;
// Создаём объект матрицы правого глаза
// на шине I²C с адресом 0x63
TroykaLedMatrix matrixR(0x63);
// Создаём константу для хранения базовой частоты
constexpr int FREQUENCY = 2000;
// Создаём константы для хранения минимальной и максимальной частоты
constexpr int MIN_FREQUENCY = FREQUENCY - (0.25 * FREQUENCY);
constexpr int MAX_FREQUENCY = FREQUENCY + (0.25 * FREQUENCY);
// Задаём максимально доступные углы поворота сервопривода головы влево-вправо
constexpr uint8_t MAX_ANGLE_YAW_R = 0;
constexpr uint8_t MAX_ANGLE_YAW_L = 180;
// Задаём максимально доступные углы поворота сервопривода головы вверх-вниз
constexpr uint8_t MAX_ANGLE_PITCH_DOWN = 60;
constexpr uint8_t MAX_ANGLE_PITCH_UP = 120;
// Вычисляем средний угол поворота сервопривода головы влево-вправо
constexpr uint8_t MID_ANGLE_YAW = (MAX_ANGLE_YAW_R + MAX_ANGLE_YAW_L) / 2;
// Вычисляем средний угол поворота сервопривода головы вверх-вниз
constexpr uint8_t MID_ANGLE_PITCH = (MAX_ANGLE_PITCH_DOWN + MAX_ANGLE_PITCH_UP) / 2;
// Создаём переменную для хранения текущего положения сервопривода головы влево-вправо
uint8_t angleYaw = MID_ANGLE_YAW;
// Создаём переменную для хранения текущего положения сервопривода головы вверх-вниз
uint8_t anglePitch = MID_ANGLE_PITCH;
// Создаём константу для хранения паузы между поворотом вала сервопривода
constexpr uint8_t ANGLE_RANGE = 2;
// Создаём иконку «Взгляд прямо» в шестнадцатеричной системе HEX
// Иконка для левого и правого глаза одинаковая
// Эмоции рисуем в редакторе изображений для LED-матрицы
// https://amperka.github.io/led-matrix-editor/
constexpr uint8_t ICON_EYE_STRAIGHT[] PROGMEM {
0x7e, 0x81, 0x81, 0x99, 0x99, 0x81, 0x81, 0x7e
};
// Создаём иконку «Взгляд влево» в шестнадцатеричной системе HEX
// Иконка для левого и правого глаза одинаковая
constexpr uint8_t ICON_EYE_LEFT[] PROGMEM {
0x7e, 0x81, 0x81, 0xe1, 0xe1, 0x81, 0x81, 0x7e
};
// Создаём иконку «Взгляд вправо» в шестнадцатеричной системе HEX
// Иконка для левого и правого глаза одинаковая
constexpr uint8_t ICON_EYE_RIGHT[] PROGMEM {
0x7e, 0x81, 0x81, 0x87, 0x87, 0x81, 0x81, 0x7e
};
// Создаём иконку «Взгляд вверх» в шестнадцатеричной системе HEX
// Иконка для левого и правого глаза одинаковая
constexpr uint8_t ICON_EYE_UP[] PROGMEM {
0x7e, 0x99, 0x99, 0x81, 0x81, 0x81, 0x81, 0x7e
};
// Создаём иконку «Взгляд вниз» в шестнадцатеричной системе HEX
// Иконка для левого и правого глаза одинаковая
constexpr uint8_t ICON_EYE_DOWN[] PROGMEM {
0x7e, 0x81, 0x81, 0x81, 0x81, 0x99, 0x99, 0x7e
};
// Создаём иконку «Взгляд вверх-влево» в шестнадцатеричной системе HEX
// Иконка для левого и правого глаза одинаковая
constexpr uint8_t ICON_EYE_UP_LEFT[] PROGMEM {
0x7e, 0xe1, 0xe1, 0x81, 0x81, 0x81, 0x81, 0x7e
};
// Создаём иконку «Взгляд вверх-вправо» в шестнадцатеричной системе HEX
// Иконка для левого и правого глаза одинаковая
constexpr uint8_t ICON_EYE_UP_RIGHT[] PROGMEM {
0x7e, 0x87, 0x87, 0x81, 0x81, 0x81, 0x81, 0x7e
};
// Создаём иконку «Взгляд вниз-влево» в шестнадцатеричной системе HEX
// Иконка для левого и правого глаза одинаковая
constexpr uint8_t ICON_EYE_DOWN_LEFT[] PROGMEM {
0x7e, 0x81, 0x81, 0x81, 0x81, 0xe1, 0xe1, 0x7e
};
// Создаём иконку «Взгляд вниз-вправо» в шестнадцатеричной системе HEX
// Иконка для левого и правого глаза одинаковая
constexpr uint8_t ICON_EYE_DOWN_RIGHT[] PROGMEM {
0x7e, 0x81, 0x81, 0x81, 0x81, 0x87, 0x87, 0x7e
};
// Создаём иконку «Глаза выключены» в шестнадцатеричной системе HEX
// Иконка для левого и правого глаза одинаковая
constexpr uint8_t ICON_EYE_OFF[] PROGMEM {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// Создаём константу для хранения статуса слежения глазами за поворотом робоголовы
constexpr bool STATE_LOOK_TURN = false;
// Создаём перечисление состояний робота с соответствующей переменной
enum {
ROBOT_ON, // Робот включен
ROBOT_OFF, // Робот выключен
} robotState;
// Создаём перечисление кодов и ИК-пульта с соответствующей переменной
enum CODE {
POWER = 0x0B,
LEFT = 0x08,
RIGHT = 0x19,
UP = 0x06,
DOWN = 0x1D,
UP_LEFT = 0x0A,
UP_RIGHT = 0x1A,
DOWN_LEFT = 0x18,
DOWN_RIGHT = 0x04,
RED = 0x00,
GREEN = 0x03,
BLUE = 0x01
} code;
void setup() {
// Подключаем сервомоторы головы влево-вправо и вниз-вверх
servoYaw.attach(SERVO_YAW_PIN);
servoPitch.attach(SERVO_PITCH_PIN);
// Приравниваем текущим положениям вала сервоприводов
// влево-вправо и вверх-вниз среднее значение
angleYaw = MID_ANGLE_YAW;
anglePitch = MID_ANGLE_PITCH;
// Устанавливаем углы сервоприводов по умолчанию
servoYaw.write(angleYaw);
servoPitch.write(anglePitch);
// Инициализируем ИК-приёмник
IrReceiver.begin(IR_RECEIVE_PIN);
// Инициализируем матрицы
matrixL.begin();
matrixR.begin();
// Очищаем матрицы
matrixL.clear();
matrixR.clear();
// Отображаем на матрицах иконку «Взгляд прямо»
drawIcon(ICON_EYE_STRAIGHT, ICON_EYE_STRAIGHT);
// Устанавливаем таймер в режим остановки после срабатывания
timer.setTimerMode();
// Передаём случайное число с пина A1
// для последующей генерации случайных чисел
randomSeed(analogRead(A1));
// Устанавливаем режим «Робот включен»
robotState = ROBOT_ON;
}
void loop() {
// Если робот включен
if (robotState == ROBOT_ON) {
// Переходим в функцию обработки режима «Робот включен»
handleRobotOn();
}
// Если робот выключен
if (robotState == ROBOT_OFF) {
// Переходим в функцию обработки режима «Робот выключен»
handleRobotOff();
}
}
// Функция отображения иконки поворотов на матрицах
void drawIconTurn(uint8_t* iconEyeL, uint8_t* iconEyeR) {
// Если статус активности слежение глаз за поворотом включен
if (STATE_LOOK_TURN) {
// Отображаем на матрицах полученные иконки
matrixL.drawBitmapF(iconEyeL);
matrixR.drawBitmapF(iconEyeR);
}
}
// Функция отображения иконки на матрицах
void drawIcon(uint8_t* iconEyeL, uint8_t* iconEyeR) {
// Отображаем на матрицах полученные иконки
matrixL.drawBitmapF(iconEyeL);
matrixR.drawBitmapF(iconEyeR);
}
// Функция обработки режима «Робот выключен»
void handleRobotOff() {
// Выключаем глаза
drawIcon(ICON_EYE_OFF, ICON_EYE_OFF);
// Если робот выключен
while (robotState == ROBOT_OFF) {
// Переходим в функцию обработки ИК-приёмника в режиме «Робот выключен»
remoteHandlerRobotOff();
}
}
// Функция обработки режима «Робот включен»
void handleRobotOn() {
// Включаем глаза
drawIcon(ICON_EYE_STRAIGHT, ICON_EYE_STRAIGHT);
// Если робот включен
while (robotState == ROBOT_ON) {
// Переходим в функцию обработки ИК-приёмника в режиме «Робот включен»
remoteHandlerRobotOn();
}
}
// Функция обработки ИК-приёмника в режиме «Робот выключен»
void remoteHandlerRobotOff() {
// Если пришёл новый сигнал на ИК-приёмник, декодируем его
if (IrReceiver.decode()) {
// Создаём переменную и присваиваем ей декодируемый код кнопки
uint32_t code = IrReceiver.decodedIRData.command;
// Если нажата кнопка «Включение/выключение»
if (code == POWER) {
// Даём роботу 500 мс на включение
delay(500);
// Включаем робота
robotState = ROBOT_ON;
}
// Разрешаем обрабатывать следующий сигнал
IrReceiver.resume();
}
}
// Функция обработки ИК-приёмника в режиме «Робот включен»
void remoteHandlerRobotOn() {
// Если пришёл новый сигнал на ИК-приёмник, декодируем его
if (IrReceiver.decode()) {
// Создаём переменную и присваиваем ей декодируемый код кнопки
uint32_t code = IrReceiver.decodedIRData.command;
// Устанавливаем в таймер значение счетчика 500
timer.setTime(500);
// Запускаем таймер
timer.start();
// Выбираем действие в зависимости от текущего режима игры
switch (code) {
// Если нажата кнопка «Включение/выключение»
case POWER:
// Даём роботу 500 мс на выключение
delay(500);
// Выключаем робота
robotState = ROBOT_OFF;
break;
// Если нажата кнопка «Влево»
case LEFT:
// Обновляем переменную положение головы влево-вправо
angleYaw = angleYaw + ANGLE_RANGE;
// Обновляем положения вала влево-вправо
servoYaw.write(angleYaw);
// Отображаем на матрицах иконку «Взгляд влево»
drawIconTurn(ICON_EYE_LEFT, ICON_EYE_LEFT);
break;
// Если нажата кнопка «Вправо»
case RIGHT:
// Обновляем переменную положение головы влево-вправо
angleYaw = angleYaw - ANGLE_RANGE;
// Обновляем положения вала влево-вправо
servoYaw.write(angleYaw);
// Отображаем на матрицах иконку «Взгляд вправо»
drawIconTurn(ICON_EYE_RIGHT, ICON_EYE_RIGHT);
break;
// Если нажата кнопка «Вверх»
case UP:
// Обновляем переменную положение головы вверх-вниз
anglePitch = anglePitch + ANGLE_RANGE;
// Обновляем положения вала вверх-вниз
servoPitch.write(anglePitch);
// Отображаем на матрицах иконку «Взгляд вверх»
drawIconTurn(ICON_EYE_UP, ICON_EYE_UP);
break;
// Если нажата кнопка «Вниз»
case DOWN:
// Обновляем переменную положение головы вверх-вниз
anglePitch = anglePitch - ANGLE_RANGE;
// Обновляем положения вала вверх-вниз
servoPitch.write(anglePitch);
// Отображаем на матрицах иконку «Взгляд вниз»
drawIconTurn(ICON_EYE_DOWN, ICON_EYE_DOWN);
break;
// Если нажата кнопка «Вверх-влево»
case UP_LEFT:
// Обновляем переменную положение головы влево-вправо
angleYaw = angleYaw + ANGLE_RANGE;
// Обновляем переменную положение головы вверх-вниз
anglePitch = anglePitch + ANGLE_RANGE;
// Обновляем положения вала влево-вправо
servoYaw.write(angleYaw);
// Обновляем положения вала вверх-вниз
servoPitch.write(anglePitch);
// Отображаем на матрицах иконку «Взгляд вверх-влево»
drawIconTurn(ICON_EYE_UP_LEFT, ICON_EYE_UP_LEFT);
break;
// Если нажата кнопка «Вверх-вправо»
case UP_RIGHT:
// Обновляем переменную положение головы влево-вправо
angleYaw = angleYaw - ANGLE_RANGE;
// Обновляем переменную положение головы вверх-вниз
anglePitch = anglePitch + ANGLE_RANGE;
// Обновляем положения вала влево-вправо
servoYaw.write(angleYaw);
// Обновляем положения вала вверх-вниз
servoPitch.write(anglePitch);
// Отображаем на матрицах иконку «Взгляд вверх-влево»
drawIconTurn(ICON_EYE_UP_RIGHT, ICON_EYE_UP_RIGHT);
break;
// Если нажата кнопка «Вниз-влево»
case DOWN_LEFT:
// Обновляем переменную положение головы влево-вправо
angleYaw = angleYaw + ANGLE_RANGE;
// Обновляем переменную положение головы вверх-вниз
anglePitch = anglePitch - ANGLE_RANGE;
// Обновляем положения вала влево-вправо
servoYaw.write(angleYaw);
// Обновляем положения вала вверх-вниз
servoPitch.write(anglePitch);
// Отображаем на матрицах иконку «Взгляд вниз-влево»
drawIconTurn(ICON_EYE_DOWN_LEFT, ICON_EYE_DOWN_LEFT);
break;
// Если нажата кнопка «Вниз-вправо»
case DOWN_RIGHT:
// Обновляем переменную положение головы влево-вправо
angleYaw = angleYaw - ANGLE_RANGE;
// Обновляем переменную положение головы вверх-вниз
anglePitch = anglePitch - ANGLE_RANGE;
// Обновляем положения вала влево-вправо
servoYaw.write(angleYaw);
// Обновляем положения вала вверх-вниз
servoPitch.write(anglePitch);
// Отображаем на матрицах иконку «Взгляд вниз-влево»
drawIconTurn(ICON_EYE_DOWN_RIGHT, ICON_EYE_DOWN_RIGHT);
break;
case RED:
// Отключаем ИК-приёмник
IrReceiver.stop();
// Вызываем функцию генератора последовательных длинных семплов
toneLongSamples();
// Вызываем функцию генератора непоследовательных коротких семплов
toneShortSamples();
// Запускаем ИК-приёмник
IrReceiver.start();
break;
case GREEN:
// Отключаем ИК-приёмник
IrReceiver.stop();
// Вызываем функцию генератора последовательных длинных семплов
toneLongSamples();
// Вызываем функцию генератора непоследовательных коротких семплов
toneShortSamples();
// Запускаем ИК-приёмник
IrReceiver.start();
break;
case BLUE:
// Отключаем ИК-приёмник
IrReceiver.stop();
// Вызываем функцию генератора последовательных длинных семплов
toneLongSamples();
// Вызываем функцию генератора непоследовательных коротких семплов
toneShortSamples();
// Запускаем ИК-приёмник
IrReceiver.start();
break;
}
// Разрешаем обрабатывать следующий сигнал
IrReceiver.resume();
}
// Если таймер досчитал до конца
if (timer.tick()) {
// Отображаем на матрицах иконку «Взгляд прямо»
drawIcon(ICON_EYE_STRAIGHT, ICON_EYE_STRAIGHT);
}
}
// Функция генерации последовательных длинных семплов
void toneLongSamples() {
// Генерируем случайное количество семплов от 1 до 6
int countSamples = random(1, 7);
// Перебираем нумерацию семплов в цикле
for (int i = 0; i < countSamples; i++) {
// Генерируем случайное число: true или false
bool typeSamples = random(0, 2);
// Если тип семпла true
if (typeSamples) {
// Вызываем функцию генерации тона toneSlowDownFastUp:
// Сначала медленный убывающий тон со случайными задержками
// Затем быстрый возрастающий тон со случайными задержками
toneSlowDownFastUp();
} else {
// Если тип семпла false
// Вызываем функцию генерации тона toneSlowUpFastDown:
// Сначала медленный возрастающий тон со случайными задержками
// Затем быстрый убывающий тон со случайными задержками
toneSlowUpFastDown();
}
}
}
// Функция генерации непоследовательных коротких семплов
void toneShortSamples() {
// Генерируем случайное количество семплов от 3 до 9
int countSamples = random(3, 10);
// Генерируем случайную частоту на базе FREQUENCY
int frequency = random(MIN_FREQUENCY, MAX_FREQUENCY);
for (int i = 0; i <= countSamples; i++) {
// Активируем глаза
drawIcon(ICON_EYE_STRAIGHT, ICON_EYE_STRAIGHT);
// Генерируем случайный интервал на базе FREQUENCY
int range = random(-FREQUENCY, FREQUENCY);
tone(BUZZER_PIN, frequency + range);
// Выполняем случайную задержку от 70 до 170 мс
delay(random(70, 170));
// Деактивируем глаза
drawIcon(ICON_EYE_OFF, ICON_EYE_OFF);
// Выключаем звук
noTone(BUZZER_PIN);
// Выполняем случайную задержку от 0 до 30 мс
delay(random(0, 30));
}
}
// Функция генерации звуковой последовательности
// Сначала медленный убывающий тон со случайными задержками
// Затем быстрый возрастающий тон со случайными задержками
void toneSlowDownFastUp() {
// Генерируем случайную частоту на базе FREQUENCY
int frequency = random(MIN_FREQUENCY, MAX_FREQUENCY);
// Активируем глаза
drawIcon(ICON_EYE_STRAIGHT, ICON_EYE_STRAIGHT);
// Генерируем медленный понижающий тон со случайными задержками
for (int range = 0; range <= random(100, 1000); range++) {
tone(BUZZER_PIN, frequency - (range * 2));
delayMicroseconds(random(0, 1000));
}
// Деактивируем глаза
drawIcon(ICON_EYE_OFF, ICON_EYE_OFF);
// Генерируем быстрый повышающий тон со случайными задержками
for (int range = 0; range <= random(100, 1000); range++) {
tone(BUZZER_PIN, frequency + (range * 10));
delayMicroseconds(random(0, 1000));
}
}
// Функция генерации звуковой последовательности
// Сначала медленный возрастающий тон со случайными задержками
// Затем быстрый убывающий тон со случайными задержками
void toneSlowUpFastDown() {
// Генерируем случайную частоту на базе FREQUENCY
int frequency = random(MIN_FREQUENCY, MAX_FREQUENCY);
// Активируем глаза
drawIcon(ICON_EYE_STRAIGHT, ICON_EYE_STRAIGHT);
// Генерируем медленный возрастающий тон со случайными задержками
for (int range = 0; range <= random(100, 1000); range++) {
tone(BUZZER_PIN, frequency + (range * 2));
delayMicroseconds(random(0, 1000));
}
// Деактивируем глаза
drawIcon(ICON_EYE_OFF, ICON_EYE_OFF);
// Генерируем быстрый понижающий тон со случайными задержками
for (int range = 0; range <= random(100, 1000); range++) {
tone(BUZZER_PIN, frequency - (range * 10));
delayMicroseconds(random(0, 1000));
}
}
===== Ресурсы =====
* [[amp>collection/io-kits?utm_source=man&utm_campaign=io-kit-robot-ampie-head&utm_medium=wiki|Наборы IO.KIT]] в магазине
* [[kits:io-kit|Электронные материалы IO.KIT]]
==== Софт ====
* [[amp>page/arduino-ide?utm_source=announce&utm_campaign=arduino&utm_medium=wiki |Страница загрузки Arduino IDE]]
* [[https://amperka.github.io/led-matrix-editor/|Редактор изображений для LED-матрицы]]
==== Библиотеки ====
* [[https://github.com/end2endzone/AnyRtttl|AnyRtttl]]
* [[https://github.com/Arduino-IRremote/Arduino-IRremote|IRremote]]
* [[https://www.arduino.cc/reference/en/libraries/servo/|Servo]]
* [[https://github.com/GyverLibs/TimerMs|TimerMs]]
* [[https://github.com/amperka/TroykaButton|TroykaButton]]
* [[https://github.com/amperka/TroykaLedMatrix/|TroykaLedMatrix]]