====== Электронное приложение к набору «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]]