Проекты на Arduino и Slot Shield
Дети девяностых и их родители могут не читать описание, а сразу переходить к сборке девайса.
Тамагочи — это электронная игрушка, суть которой сводится к выращиванию виртуального питомца. Его нужно вовремя кормить, поить, развлекать — как настоящего котёнка или щеночка. Не уследишь, электронный зверёк заболеет или даже умрёт.
В порыве ностальгии мы решили сделать своего тамагочи, а в качестве виртуального друга выбрали милых космических захватчиков из классической аркады Space Invaders.
Наш питомец будет жить на монохромном LED-дисплее. Мы начнём с простейшего двухклеточного организма, к концу игры он займёт собой почти все 64 пикселя матрицы. На втором экране будут появляться его просьбы: пришельца нужно кормить, поить, говорить приятные слова и вовремя укладывать спать. Каждому из этих действий соответствует кнопка на четырёхкнопочной клавиатуре. Не забывайте о питомце, иначе он может погибнуть.
Полный сет компонентов проекта. В сет входят:
Установите Troyka Slot Shield на Iskra Neo
Вставьте две LED матрицы 8×8 в левый и средний слоты верхнего ряда. Чтобы у матриц были разные адреса, капните припой на любую контактную площадку любого из модулей. Как это сделать — смотрите на странице описания матриц.
Поверните четырёхкнопочную клавиатуру на 90 градусов против часовой стрелки и вставьте в оставшийся слот верхнего ряда.
Переверните пьезопищалку вверх ногами и вставьте в нижний правый слот.
Прошейте контроллер скетчем через Arduino IDE.
// библиотека для работы I²C #include "Wire.h" // библиотека для работы со светодиодной матрицей #include "TroykaLedMatrix.h" // библиотека для работы с кнопками #include "TroykaButton.h" // создаём объекты matrix для работы с матрицами // для каждой матрицы передаём свой адрес // подробнее читайте на: // http://wiki.amperka.ru/продукты:troyka-led-matrix TroykaLedMatrix matrix1; TroykaLedMatrix matrix2(0b01100001); // создаём объекты для работы с кнопками и передаём им номера пинов TroykaButton button1(0); TroykaButton button2(1); TroykaButton button3(4); TroykaButton button4(5); // пин пищалки #define BUZZER_PIN 10 // интервал времени до сметри // если инопланетянин не получил потребность #define INTERVAL_DEATH 10000 // первое состояние инопланиянина const uint8_t stateAlienOne[][8] { { 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00}, { 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00}, { 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00}, { 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00}, }; // второе состояние инопланиянина const uint8_t stateAlienTwo[][8] { { 0x00, 0x00, 0x00, 0x18, 0x3C, 0x00, 0x00, 0x00}, { 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0x00, 0x00}, { 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x00, 0x00}, { 0x00, 0x00, 0x00, 0x00, 0x0C, 0x1E, 0x00, 0x00} }; // третье состояние инопланиянина const uint8_t stateAlienThree[][8] { { 0x00, 0x00, 0x00, 0x38, 0x54, 0x7C, 0x00, 0x00}, { 0x00, 0x00, 0x1C, 0x2A, 0x3E, 0x00, 0x00, 0x00}, { 0x00, 0x00, 0x38, 0x54, 0x7C, 0x00, 0x00, 0x00}, { 0x00, 0x00, 0x00, 0x1C, 0x2A, 0x3E, 0x00, 0x00} }; // четвёртое состояние инопланиянина const uint8_t stateAlienFour[][8] { { 0x00, 0x00, 0x1C, 0x2A, 0x3E, 0x2A, 0x00, 0x00}, { 0x00, 0x00, 0x38, 0x54, 0x7C, 0x2A, 0x00, 0x00}, { 0x00, 0x00, 0x38, 0x54, 0x7C, 0x54, 0x00, 0x00}, { 0x00, 0x00, 0x1C, 0x2A, 0x3E, 0x54, 0x00, 0x00}, }; // пятое состояние инопланиянина const uint8_t stateAlienFive[][8] { { 0x00, 0x18, 0x3C, 0x5A, 0x7E, 0x3C, 0x00, 0x00}, { 0x00, 0x00, 0x18, 0x3C, 0x5A, 0x7E, 0x5A, 0x00}, { 0x00, 0x00, 0x18, 0x3C, 0x5A, 0x7E, 0x99, 0x00}, { 0x00, 0x18, 0x3C, 0x5A, 0x7E, 0x5A, 0x00, 0x00} }; // шестое состояние инопланиянина const uint8_t stateAlienSix[][8] { { 0x00, 0x38, 0x7C, 0xD6, 0xFE, 0xBA, 0x92, 0x10}, { 0x00, 0x1C, 0x3E, 0x6B, 0x7F, 0x59, 0x92, 0x20}, { 0x00, 0x1C, 0x3E, 0x6B, 0x7F, 0x5D, 0x49, 0x08}, { 0x00, 0x38, 0x7C, 0xD6, 0xFE, 0x9A, 0x49, 0x04} }; // отображение «поесть» const uint8_t food[][8] { { 0x00, 0x20, 0x44, 0x28, 0x04, 0x00, 0x7E, 0x3C}, { 0x40, 0x28, 0x44, 0x08, 0x00, 0x00, 0x7E, 0x3C}, }; // отображение «попить» const uint8_t drink[][8] { { 0x00, 0x24, 0x24, 0x3C, 0x34, 0x2C, 0x34, 0x3C}, { 0x00, 0x24, 0x24, 0x3C, 0x2C, 0x34, 0x2C, 0x3C}, }; // отображение «приласкать» const uint8_t love[][8] { { 0x00, 0x66, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, 0x00}, { 0x00, 0x24, 0x7E, 0x7E, 0x3C, 0x18, 0x00, 0x00}, }; // отображение «поспать» const uint8_t sleep[][8] { { 0x00, 0x00, 0x00, 0x80, 0x80, 0xFE, 0xFF, 0x81}, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; // отображение «OK» const uint8_t ok[] { 0x00, 0x64, 0x94, 0x95, 0x96, 0x95, 0x65, 0x00 }; // отображение «смерти» const uint8_t death[] { 0x00, 0x40, 0xE0, 0x40, 0x40, 0x7C, 0xFE, 0xFF }; // перечесления enum {ONE, TWO, THREE, FOUR, FIVE, SIX}; enum {NOT, FOOD, DRINK, LOVE, SLEEP}; // размер инопланетянина int alienSize; // желание инопланетянина int alienDesire; // номер кадра анимации инопланитянина int frameAnimationAlien = 0; // номер кадра анимации желания инопланитянина int frameAnimationDesire = 0; // переменные запоминания времени unsigned long desireMillis; unsigned long animationMillis; unsigned long deathMillis; // переменные включения таймеров состояния bool timerEnableDesire = false; bool timerEnableDeath = false; // случайный диапазон времени // через который у инопланитянина появится новая потребность int intervalDesire; void setup() { // начало работы с матрицами matrix1.begin(); matrix2.begin(); // очищаем матрицы matrix1.clear(); matrix2.clear(); // начало работы с кнопками button1.begin(); button2.begin(); button3.begin(); button4.begin(); // начальный размер инопланитянина alienSize = ONE; // желаний нет alienDesire = NOT; // запоминаем текущее время animationMillis = millis(); // для генерации разных значений randomSeed(analogRead(A0)); } void loop() { // считываем данные с кнопок button1.read(); button2.read(); button3.read(); button4.read(); // отображение элементов на матрице if (millis() - animationMillis > 500) { animationMillis = millis(); updateBitmapAnimationAlien(); updateBitmapAnimationDesire(); } // если у инопланитянина нет желаний и таймер не запущен if (alienDesire == NOT && !timerEnableDesire) { // выбираем случайный период времени // через который инопланетянин выявит в чём то потребность intervalDesire = random(1000, 5000); // запоминаем текущее время desireMillis = millis(); // включаем таймер ожидание появление потребности timerEnableDesire = true; } // если таймер включён и период выявлеения желания прошёл if (timerEnableDesire && millis() - desireMillis > intervalDesire) { // выбираем случайное желание alienDesire = random(1, 5); // выключаем таймер ожидание появление потребности timerEnableDesire = false; // запоминаем текущее время deathMillis = millis(); // выключаем таймер ожидания смерти timerEnableDeath = true; } // если таймер смерти включён и прошёл свой интервал if(timerEnableDeath && millis() - deathMillis > INTERVAL_DEATH) { // отображаем могилку matrix1.drawBitmap(death); // стираем потребность в чём то // так как умершей в ней уже не нуждается matrix2.clear(); for (int y = 0; y <= 2; y++) { // издаём звуковой сигнал tone(BUZZER_PIN, 200, 300); delay(200); // издаём звуковой сигнал tone(BUZZER_PIN, 131, 300); delay(200); } // котроллер отправляем тоже в вечный сон while(1); } // проверяем состояние потребности инопланетянин switch(alienDesire) { case FOOD: // если нажата соответсвующая кнопка if (button1.justPressed()) { // выводим «ОК» на матрицу matrix2.drawBitmap(ok); // подаём звуковой сигнал tone(BUZZER_PIN, 2000, 300); delay(1000); // увеличиваем размер инопланетянина alienSize++; // убираем потребность alienDesire = NOT; } else if (button2.justPressed() || button3.justPressed() || button4.justPressed()) { // если нажата не соответсвующая кнопка // подаём соответсвующий звуковой сигнал tone(BUZZER_PIN, 500, 500); } break; case DRINK: // если нажата соответсвующая кнопка if (button2.justPressed()) { // выводим «ОК» на матрицу matrix2.drawBitmap(ok); // подаём звуковой сигнал tone(BUZZER_PIN, 2000, 300); delay(1000); // увеличиваем размер инопланетянина alienSize++; // убираем потребность alienDesire = NOT; } else if (button1.justPressed() || button3.justPressed() || button4.justPressed()) { // если нажата не соответсвующая кнопка // подаём соответсвующий звуковой сигнал tone(BUZZER_PIN, 500, 500); } break; case LOVE: // если нажата соответсвующая кнопка if (button3.justPressed()) { // выводим «ОК» на матрицу matrix2.drawBitmap(ok); // подаём звуковой сигнал tone(BUZZER_PIN, 2000, 300); delay(1000); // увеличиваем размер инопланетянина alienSize++; // убираем потребность alienDesire = NOT; } else if (button1.justPressed() || button2.justPressed() || button4.justPressed()) { // если нажата не соответсвующая кнопка // подаём соответсвующий звуковой сигнал tone(BUZZER_PIN, 500, 500); } break; case SLEEP: if (button4.justPressed()) { // выводим «ОК» на матрицу matrix2.drawBitmap(ok); // подаём звуковой сигнал tone(BUZZER_PIN, 2000, 300); delay(1000); // увеличиваем размер инопланетянина alienSize++; // убираем потребность alienDesire = NOT; } else if (button1.justPressed() || button2.justPressed() || button3.justPressed()) { // если нажата не соответсвующая кнопка // подаём соответсвующий звуковой сигнал tone(BUZZER_PIN, 500, 500); } break; } } // функция обновления анимации инопланитянина void updateBitmapAnimationAlien() { // ощичаем матрицу matrix1.clear(); if (frameAnimationAlien == 4) { frameAnimationAlien = 0; } // отображаем двигающегося инопланетянина по кадрово switch (alienSize) { case ONE: matrix1.drawBitmap(stateAlienOne[frameAnimationAlien++]); break; case TWO: matrix1.drawBitmap(stateAlienTwo[frameAnimationAlien++]); break; case THREE: matrix1.drawBitmap(stateAlienThree[frameAnimationAlien++]); break; case FOUR: matrix1.drawBitmap(stateAlienFour[frameAnimationAlien++]); break; case FIVE: matrix1.drawBitmap(stateAlienFive[frameAnimationAlien++]); break; case SIX: matrix1.drawBitmap(stateAlienSix[frameAnimationAlien++]); break; } } // функция обновления анимации желаний инопланитянина void updateBitmapAnimationDesire() { // ощичаем матрицу matrix2.clear(); if (frameAnimationDesire == 2) { frameAnimationDesire = 0; } // отображаем потребность по кадрово switch (alienDesire) { case NOT: matrix2.clear(); break; case FOOD: matrix2.drawBitmap(food[frameAnimationDesire++]); break; case DRINK: matrix2.drawBitmap(drink[frameAnimationDesire++]); break; case LOVE: matrix2.drawBitmap(love[frameAnimationDesire++]); break; case SLEEP: matrix2.drawBitmap(sleep[frameAnimationDesire++]); break; } }
Хотите собрать другой девайс? Выберите своё будущее устройство из списка проектов на Slot Shield.
Если вы захотите изменить графику, рекомендуем воспользоваться удобным редактором для LED матриц.
Где скачать необходимые библиотеки и как их установить?