====== Электронное приложение к набору «IO.KIT Электронные часы» ====== На этой странице ты найдёшь все нужные материалы для сборки мини-проектов набора [[amp>product/io-kit-clock?utm_source=man&utm_campaign=io-kit-clock&utm_medium=wiki|Электронные часы]] из серии [[amp>collection/io-kits?utm_source=man&utm_campaign=io-kit-clock&utm_medium=wiki|IO.KIT]]: * Схемы проектов в электронном виде. * Исходный код программ (копируй его в редактор Arduino IDE). * Дополнительные материалы: программные библиотеки, даташиты и т. п. **Обрати внимание** Для сборки и функционирования электронных часов тебе понадобится [[amp>product/io-kit-basic?utm_source=man&utm_campaign=io-kit-clock&utm_medium=wiki|IO.KIT Базовый]]! ===== Проекты ===== Прежде чем приступать к экспериментам, нужно подготовить свой компьютер: * Установи среду программирования [[https://amperka.ru/page/arduino-ide?utm_source=announce&utm_campaign=io-kit-clock&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:clock:010-drawing-wiring-cmyk.png?nolink&550 |}} // Подключаем библиотеку для работы с четырёхразрядным дисплеем #include // Создаём объект дисплея на шине SPI и пине 10 QuadDisplay qd(10); void setup() { // Инициализируем дисплей qd.begin(); } void loop() { // Выводим на дисплей целое число qd.displayInt(1234); // Ждём 1 секунду delay(1000); // Выводим на дисплей рациональное число // с заданной точностью 2 знака после запятой qd.displayFloat(3.14, 2); // Ждём 1 секунду delay(1000); // Очищаем дисплей qd.displayClear(); // Ждём 1 секунду delay(1000); } ==== №2. Индикация символов ==== {{ :kits:io-kit:clock:020-drawing-wiring-cmyk.png?nolink&550 |}} // Подключаем библиотеку для работы с четырёхразрядным дисплеем #include // Создаём объект дисплея на шине SPI и пине 10 QuadDisplay qd(10); void setup() { // Инициализируем дисплей qd.begin(); // Выводим на дисплей надпись «PLAY» qd.displayDigits(QD_P, QD_L, QD_A, QD_Y); } void loop() { } ==== №3. Кнопочный детектор ==== {{ :kits:io-kit:clock:030-drawing-wiring-cmyk.png?nolink&550 |}} // Подключаем библиотеку для работы с четырёхразрядным дисплеем #include // Подключаем библиотеку для работы с кнопками #include // Создаём объект дисплея на шине SPI и пине 10 QuadDisplay qd(10); // Создаём объекты кнопок на пинах: 2, 3, 4 и 5 TroykaButton buttonS1(5); TroykaButton buttonS2(2); TroykaButton buttonS3(3); TroykaButton buttonS4(4); void setup() { // Инициализируем дисплей qd.begin(); // Инициализируем кнопки buttonS1.begin(); buttonS2.begin(); buttonS3.begin(); buttonS4.begin(); } void loop() { // Считываем состояния с кнопок buttonS1.read(); buttonS2.read(); buttonS3.read(); buttonS4.read(); // Выводим нажатую кнопку на индикатор if (buttonS1.justPressed()) { qd.displayDigits(QD_NONE, QD_NONE, QD_S, QD_1); // Ждём 1000 мс delay(1000); } else if (buttonS2.justPressed()) { qd.displayDigits(QD_NONE, QD_NONE, QD_S, QD_2); // Ждём 1000 мс delay(1000); } else if (buttonS3.justPressed()) { qd.displayDigits(QD_NONE, QD_NONE, QD_S, QD_3); // Ждём 1000 мс delay(1000); } else if (buttonS4.justPressed()) { qd.displayDigits(QD_NONE, QD_NONE, QD_S, QD_4); // Ждём 1000 мс delay(1000); } else { // Если нажатой кнопки нет, выводим символы «----» qd.displayDigits(QD_MINUS, QD_MINUS, QD_MINUS, QD_MINUS); } } ==== №4. Диммер с индикацией ==== {{ :kits:io-kit:clock:040-drawing-wiring-cmyk.png?nolink&700 |}} // Подключаем библиотеку для работы с четырёхразрядным дисплеем #include // Создаём объект дисплея на шине SPI и пине 10 QuadDisplay qd(10); // Даём понятное имя пину 5 со светодиодом constexpr uint8_t LED_PIN = 5; // Даём понятное имя пину A0 с потенциометром constexpr uint8_t POT_PIN = A0; void setup() { // Настраиваем пин со светодиодом в режим выхода pinMode(LED_PIN, OUTPUT); // Настраиваем пин с потенциометром в режим входа pinMode(POT_PIN, INPUT); // Инициализируем дисплей qd.begin(); } void loop() { // Считываем аналоговый сигнал с потенциометра int rotation = analogRead(POT_PIN); // Преобразуем диапазон значений с потенциометра [0;1023] // в диапазон значений для светодиода [0;255] int brightness = map(rotation, 0, 1023, 0, 255); // Выдаём результат на светодиод analogWrite(LED_PIN, brightness); // Преобразуем диапазон значений с потенциометра [0;1023] // в процентный диапазон значений [0;100] int percent = map(rotation, 0, 1023, 0, 100); // Выводим результат на дисплей qd.displayInt(percent); // Ждём 25 мс delay(25); } ==== №5. Кухонный таймер ==== {{ :kits:io-kit:clock:050-drawing-wiring-cmyk.png?nolink&700 |}} // Подключаем библиотеку для работы с четырёхразрядным дисплеем #include // Подключаем библиотеку для работы с кнопками #include // Создаём объект дисплея на шине SPI и пине 10 QuadDisplay qd(10); // Создаём объекты кнопок на пинах: 2, 3, 4 и 5 TroykaButton buttonS1(5); TroykaButton buttonS2(2); TroykaButton buttonS3(3); TroykaButton buttonS4(4); // Даём понятное имя пину A2 с пищалкой constexpr uint8_t BUZZER_PIN = A2; // Даём понятное имя пину A0 с потенциометром constexpr uint8_t POT_PIN = A0; // Создаём переменную для хранения счетчика таймера int timerCount; // Создаём переменную для работы таймера unsigned long timeLastUpdate = 0; // Создаём перечисление состояний таймера с соответствующей переменной enum { TIMER_SETUP, // «Установка таймера» TIMER_TICK, // «Таймер тикает» TIMER_DONE // «Таймер завершен» } timerState; void setup() { // Инициализируем дисплей qd.begin(); // Инициализируем кнопки buttonS1.begin(); buttonS2.begin(); buttonS3.begin(); buttonS4.begin(); // Настраиваем пин с пищалкой в режим выхода pinMode(BUZZER_PIN, OUTPUT); // Настраиваем пин с потенциометром в режим входа pinMode(POT_PIN, INPUT); // Устанавливаем режим «Установка таймера» timerState = TIMER_SETUP; } void loop() { // Если установлен режим «Установка таймера» if (timerState == TIMER_SETUP) { // Переходим в функцию обработки режима «Установка таймера» handleTimerSetup(); } // Если установлен режим «Таймер тикает» if (timerState == TIMER_TICK) { // Переходим в функцию обработки режима «Таймер тикает» handleTimerTick(); } // Если установлен режим «Таймер завершен» if (timerState == TIMER_DONE) { // Переходим в функцию обработки режима «Таймер завершен» handleTimerDone(); } } // Функция обработки режима «Установка таймера» void handleTimerSetup() { // Выводим на индикатор выводим символы «----» qd.displayDigits(QD_MINUS, QD_MINUS, QD_MINUS, QD_MINUS); // Подаём звуковой сигнал про установку таймера playMelodyTimerSetup(); // Считываем состояние с кнопки S1 buttonS1.read(); // Пока на нажата кнопка S1 while (!buttonS1.isClick()) { // Считываем показания с потенциометра timerCount = readPot(POT_PIN); // Выводим полученные показания на дисплей qd.displayInt(timerCount); // Обновляем состояние с кнопки S1 buttonS1.read(); } // Подаём звуковой сигнал о старте таймера playMelodyTimerBegin(); // Устанавливаем режим «Таймер тикает» timerState = TIMER_TICK; } // Функция обработки режима «Таймер тикает» void handleTimerTick() { // Передаем константе состояние таймера const auto updateDone = timer(); // Если таймер обновил данные, т.е. прошла 1 секунда if (updateDone) { // Выводим счётчик таймера на дисплей qd.displayInt(timerCount); // Если счётчик дошёл до ноля if (timerCount == 0) { // Отображаем на индикаторе строку про завершения таймера qd.displayDigits(QD_NONE, QD_E, QD_n, QD_d); // Подаём звуковой сигнал на пищалку про завершения таймера playMelodyTimerEnd(); // Устанавливаем режим «Таймер завершен» timerState = TIMER_DONE; } } } // Функция обработки режима «Таймер завершен» void handleTimerDone() { do { // Считываем состояние с кнопки S1 buttonS1.read(); // Пока не нажата кнопка S1 } while (!buttonS1.isClick()); // Устанавливаем режим «Установка таймера» timerState = TIMER_SETUP; } // Функция для отсчёта времени посекундно bool timer() { // Запоминаем текущее время long timeNow = millis(); // Если прошла одна секунда if (timeNow - timeLastUpdate > 1000) { // Обновляем текущее время timeLastUpdate = timeNow; // Уменьшаем текущий счётчик на единицу timerCount--; // Да, таймер досчитал return true; } // Нет, таймер не досчитал return false; } // Функция считывания показаний с потенциометра int readPot(int pin) { // Создаём переменную для хранения // аналогового сигнала с потенциометра в отчётах АЦП int sensorADC = 0; // Создаём переменную для хранения // преобразованных показаний с потенциометра int sensorValue = 0; // Считываем усреднённый аналоговый сигнал с потенциометра for (int i = 0; i < 16; i++) { sensorADC += analogRead(pin); delay(5); } sensorADC = sensorADC / 16; // Преобразуем диапазон значений с потенциометра [0;1023] // в диапазон значений для таймера [0;360] sensorValue = map(sensorADC, 0, 1023, 360, 0); // Возвращаем полученное значение return sensorValue; } // Функция мелодии «Установки таймера» void playMelodyTimerSetup() { tone(BUZZER_PIN, 330, 100); delay(150); tone(BUZZER_PIN, 330, 100); delay(300); tone(BUZZER_PIN, 330, 100); delay(300); tone(BUZZER_PIN, 262, 100); delay(100); tone(BUZZER_PIN, 330, 100); delay(300); tone(BUZZER_PIN, 392, 100); delay(550); tone(BUZZER_PIN, 523, 100); delay(575); } // Функция мелодии «Старт таймера» void playMelodyTimerBegin() { tone(BUZZER_PIN, 2000, 200); delay(300); tone(BUZZER_PIN, 3000, 200); delay(300); tone(BUZZER_PIN, 4000, 200); } // Функция мелодии «Завершение таймера» void playMelodyTimerEnd() { for (int i = 0; i <= 5; i++) { tone(BUZZER_PIN, 1000, 200); delay(400); } } ==== №6. Консольный хронометр ==== {{ :kits:io-kit:clock:060-drawing-wiring-cmyk.png?nolink&480 |}} // Подключаем библиотеку для работы с часами реального времени RTC #include // Создаём объект часов реального времени на шине I²C RTC clock; void setup() { // Открываем монитор Serial-порта Serial.begin(9600); // Инициализируем часы реального времени clock.begin(); // Устанавливаем временную отметку в модуль // Выберите метод установки времени: ручной или автоматический // После установки времени в часы, закомментируйте метод clock.set // Ручная установка временной отметки в модуль: 14:25:45 1 августа 2020 года // 14 час, 25 мин, 45 сек, 1 число, август, 2020 год, суббота clock.set(14, 25, 45, 1, 8, 2020, SATURDAY); // Автоматическая установка временной отметки в модуль // Время берётся из ПК при компиляции // clock.set(__TIMESTAMP__); } void loop() { // Создаём переменные для хранения времени, даты и дня недели String timeStr; String dateStr; String weekDayStr; if (millis() % 1000 == 0) { // Если прошла одна секунда, запрашиваем данные с часов clock.read(); // Получаем текущее время, дату и день недели в переменные clock.getTimeStamp(timeStr, dateStr, weekDayStr); // Выводим в консоль текущее время, дату и день недели Serial.print(timeStr); Serial.print("\t"); Serial.print(dateStr); Serial.print("\t"); Serial.println(weekDayStr); } } ==== №7. Часы ==== {{ :kits:io-kit:clock:070-drawing-wiring-cmyk.png?nolink&700 |}} // Подключаем библиотеку для работы с четырёхразрядным дисплеем #include // Подключаем библиотеку для работы с кнопками #include // Подключаем библиотеку для работы с часами реального времени RTC #include // Создаём объект дисплея на шине SPI и пине 10 QuadDisplay qd(10); // Создаём объекты кнопок на пинах: 2, 3, 4 и 5 TroykaButton buttonS1(5); TroykaButton buttonS2(2); TroykaButton buttonS3(3); TroykaButton buttonS4(4); // Создаём объект часов реального времени на шине I²C RTC clock; // Создаём переменные для хранения часов и минут uint8_t hours; uint8_t minutes; void setup() { // Инициализируем дисплей qd.begin(); // Инициализируем кнопки buttonS1.begin(); buttonS2.begin(); buttonS3.begin(); buttonS4.begin(); // Инициализируем часы реального времени clock.begin(); // Устанавливаем временную отметку в модуль // Выберите метод установки времени: ручной или автоматический // После установки времени в часы, закомментируйте метод clock.set // Ручная установка временной отметки в модуль: 14:25:45 1 августа 2020 года // 14 час, 25 мин, 45 сек, 1 число, август, 2020 год, суббота // clock.set(14, 25, 45, 1, 8, 2020, SATURDAY); // Автоматическая установка временной отметки в модуль // Время берётся из ПК при компиляции clock.set(__TIMESTAMP__); } void loop() { // Запрашиваем данные с часов clock.read(); // Получаем текущие показания часов и минут в переменные hours = clock.getHour(); minutes = clock.getMinute(); // Выводим время на дисплей qd.displayScore(hours, minutes, true); // Обновляем время через функцию обработки кнопок updateTimeButtons(); } // Функция обновления времени через кнопки void updateTimeButtons() { // Считываем состояния с кнопок buttonS1.read(); buttonS2.read(); buttonS3.read(); buttonS4.read(); // Настраиваем время на часах с помощью кнопок if (buttonS1.isClick()) { clock.setHour(hours - 1); } else if (buttonS2.isClick()) { clock.setMinute(minutes - 1); } else if (buttonS3.isClick()) { clock.setMinute(minutes + 1); } else if (buttonS4.isClick()) { clock.setHour(hours + 1); } } ==== №8. Часы с будильником ==== {{ :kits:io-kit:clock:080-drawing-wiring-cmyk.png?nolink&700 |}} // Подключаем библиотеку для работы с четырёхразрядным дисплеем #include // Подключаем библиотеку для работы с кнопками #include // Подключаем библиотеку для работы с часами реального времени RTC #include // Создаём объект дисплея на шине SPI и пине 10 QuadDisplay qd(10); // Создаём объекты кнопок на пинах: 2, 3, 4 и 5 TroykaButton buttonS1(5); TroykaButton buttonS2(2); TroykaButton buttonS3(3); TroykaButton buttonS4(4); // Создаём объект часов реального времени на шине I²C RTC clock; // Даём понятное имя пищалке на пине A2 constexpr uint8_t BUZZER_PIN = A2; // Создаём переменные для хранения часов и минут uint8_t hours; uint8_t minutes; // Создаём константы для хранения часов и минут будильника constexpr uint8_t alarmHours = 7; constexpr uint8_t alarmMinutes = 30; void setup() { // Инициализируем дисплей qd.begin(); // Инициализируем кнопки buttonS1.begin(); buttonS2.begin(); buttonS3.begin(); buttonS4.begin(); // Инициализируем часы реального времени clock.begin(); // Настраиваем пин с пищалкой в режим выхода pinMode(BUZZER_PIN, OUTPUT); // Устанавливаем временную отметку в модуль // Выберите метод установки времени: ручной или автоматический // После установки времени в часы, закомментируйте метод clock.set // Ручная установка временной отметки в модуль: 14:25:45 1 августа 2020 года // 14 час, 25 мин, 45 сек, 1 число, август, 2020 год, суббота // clock.set(14, 25, 45, 1, 8, 2020, SATURDAY); // Автоматическая установка временной отметки в модуль // Время берётся из ПК при компиляции clock.set(__TIMESTAMP__); } void loop() { // Запрашиваем данные с часов clock.read(); // Получаем текущие показания часов и минут в переменные hours = clock.getHour(); minutes = clock.getMinute(); // Выводим время на дисплей qd.displayScore(hours, minutes, true); // Обновляем время через функцию обработки кнопок updateTimeButtons(); // Обрабатываем времени будильника alarmRing(); } // Функция обновления времени через кнопки void updateTimeButtons() { // Считываем состояния с кнопок buttonS1.read(); buttonS2.read(); buttonS3.read(); buttonS4.read(); // Настраиваем время на часах с помощью кнопок if (buttonS1.isClick()) { clock.setHour(hours - 1); } else if (buttonS2.isClick()) { clock.setMinute(minutes - 1); } else if (buttonS3.isClick()) { clock.setMinute(minutes + 1); } else if (buttonS4.isClick()) { clock.setHour(hours + 1); } } // Функция обработки времени будильника bool alarmRing() { // Если текущее время совпадает со временем будильника if (hours == alarmHours && minutes == alarmMinutes) { // Мигаем дисплеем и пищим зуммером qd.displayClear(); tone(BUZZER_PIN, 1000, 200); delay(200); qd.displayScore(hours, minutes, false); delay(300); } } ==== №9. Плеер с выбором трека ==== {{ :kits:io-kit:clock:090-drawing-wiring-cmyk.png?nolink&700 |}} // Подключаем библиотеку для работы с мелодиями в формате RTTTL #include // Подключаем библиотеку для работы с четырёхразрядным дисплеем #include // Подключаем библиотеку для работы с кнопками #include // Создаём объект дисплея на шине SPI и пине 10 QuadDisplay qd(10); // Создаём объекты кнопок на пинах: 2, 3, 4 и 5 TroykaButton buttonS1(5); TroykaButton buttonS2(2); TroykaButton buttonS3(3); TroykaButton buttonS4(4); // Даём понятное имя пину A2 с пищалкой constexpr uint8_t BUZZER_PIN = A2; // Мелодия «Имперский марш» в формате RTTTL const char imperM[] PROGMEM = "imperMarch:d=8,o=5,b=95:" "4a4,4a4,4a4,f.4,16c,4a4,f.4,16c," "2a4,4e,4e,4e,f.,16c,4g#4,f.4," "16c,2a4,4a,a.4,16a4,4a,g#.,16g," "16f#,16e,f,p,a#4,4d#,d.,16c#,16c," "16b4,c,p,f4,4g#4,f.4,16a4,4c," "a.4,16c,2e,4a,a.4,16a4,4a,g#.," "16g,16f#,16e,f,p,a#4,4d#,d.," "16c#,16c,16b4,c,p,f4,4g#4,f.4," "16c,4a4,f.4,16c,2a4"; // Мелодия «Марио» в формате RTTTL const char marioB[] PROGMEM = "marioBroth:d=4,o=5,b=100:" "16e6,16e6,32p,8e6,16c6,8e6,8g6,8p," "8g,8p,8c6,16p,8g,16p,8e,16p," "8a,8b,16a#,8a,16g.,16e6,16g6,8a6," "16f6,8g6,8e6,16c6,16d6,8b,16p,8c6," "16p,8g,16p,8e,16p,8a,8b,16a#," "8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6," "16c6,16d6,8b,8p,16g6,16f#6,16f6,16d#6," "16p,16e6,16p,16g#,16a,16c6,16p,16a," "16c6,16d6,8p,16g6,16f#6,16f6,16d#6,16p," "16e6,16p,16c7,16p,16c7,16c7,p,16g6," "16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a," "16c6,16p,16a,16c6,16d6,8p,16d#6,8p," "16d6,8p,16c6"; // Мелодия «Миссия невыполнима» в формате RTTTL const char misImp[] PROGMEM = "missionImp:d=16,o=6,b=95:" "32d,32d#,32d,32d#,32d,32d#,32d,32d#," "32d,32d,32d#,32e,32f,32f#,32g,g,8p," "g,8p,a#,p,c7,p,g,8p," "g,8p,f,p,f#,p,g,8p," "g,8p,a#,p,c7,p,g,8p," "g,8p,f,p,f#,p,a#,g," "2d,32p,a#,g,2c#,32p,a#,g," "2c,a#5,8c,2p,32p,a#5,g5,2f#," "32p,a#5,g5,2f,32p,a#5,g5,2e," "d#,8d"; // Мелодия «Тетрис» в формате RTTTL const char tetris[] PROGMEM = "tetris:d=4,o=5,b=160:" "e6,8b,8c6,8d6,16e6,16d6,8c6,8b," "a,8a,8c6,e6,8d6,8c6,b,8b," "8c6,d6,e6,c6,a,2a,8p,d6," "8f6,a6,8g6,8f6,e6,8e6,8c6,e6," "8d6,8c6,b,8b,8c6,d6,e6,c6," "a,a"; // Мелодия «Европа» в формате RTTTL const char europe[] PROGMEM = "Europe:d=4,o=5,b=140:" "16c#6,32b,32p,8c#.6,16p,f#,p.,32d6,32p," "32c#6,32p,32d6,16p.,32c#6,16p.,b.,p," "32d6,32p,32c#6,32p,d6,f#,p.,32b," "32p,32a,32p,32b,16p.,32a,16p.,32g#," "16p.,32b,16p.,a.,32c#6,32p,32b,32p," "c#6,2f#,p,16p,32d6,32p,32c#6,32p,32d6," "16p.,32c#6,16p.,b.,p,32d6,32p,32c#6,32p," "d6,f#,p.,32b,32p,32a,32p,32b," "16p.,32a,16p.,32g#,16p.,32b,16p.,2a," "16p,32g#,32p,32a,32p,b.,16a,16b," "8c#6,8b,8a,8g#,f#,d6,1c#6,8p," "16c#6,16d6,16c#6,16b,2c#.6,16p"; // Создаём переменную для хранения номера трека uint8_t songID; // Задаём константы минимального и максимального номеров трека constexpr uint8_t minSongID = 1; constexpr uint8_t maxSongID = 5; // Создаём переменную для хранения паузы bool pause = false; // Создаём перечисление состояний плеера с соответствующей переменной enum { PLAY_OFF, // «Плеер не играет» PLAY_ON, // «Плеер играет» } playState; void setup() { // Инициализируем дисплей qd.begin(); // Инициализируем кнопки buttonS1.begin(); buttonS2.begin(); buttonS3.begin(); buttonS4.begin(); // Настраиваем пин с пищалкой в режим выхода pinMode(BUZZER_PIN, OUTPUT); // Устанавливаем режим «Плеер не играет» playState = PLAY_OFF; // Устанавливаем трек №1 songID = 1; } void loop() { // Считываем состояния с кнопок readButtons(); // Обновляем трек с помощью кнопок updateSongIDButtons(); // Выводим выбранный трек на индикатор printDisplaySongID(); // Проверяем состояние плеера switch (playState) { // Если установлен режим «Плеер не играет» case PLAY_OFF: // Переходим в функцию обработки режима «Плеер не играет» handlePlayOff(); break; // Если установлен режим «Плеер играет» case PLAY_ON: // Переходим в функцию обработки режима «Плеер играет» handlePlayOn(); break; } } // Функция обработки режима «Плеер не играет» void handlePlayOff() { // Если был клик по кнопке S1 if (buttonS1.isClick()) { // Выбираем трек к воспроизведению setSongID(); // Устанавливаем режим «Плеер играет» playState = PLAY_ON; } } // Функция обработки режима «Плеер играет» void handlePlayOn() { // Включаем трек playSongID(); // Если был клик по кнопке S1 if (buttonS1.isClick()) { // Останавливаем трек stopSongID(); // Устанавливаем режим «Плеер не играет» playState = PLAY_OFF; } // Если был клик по кнопке S4 if (buttonS4.isClick()) { // Ставим на паузу текущий трек pause = !pause; } } // Функция считывания состояний с кнопок void readButtons() { buttonS1.read(); buttonS2.read(); buttonS3.read(); buttonS4.read(); } // Функция обновления текущего трека с помощью кнопок void updateSongIDButtons() { if (buttonS3.isClick()) { songID++; } if (buttonS2.isClick()) { songID--; } // Корректируем номер трека в рамки диапазона [min;max] wrapSongID(); } // Функция корректировки номеров трека в диапазон [min;max] void wrapSongID() { if (songID > maxSongID) { songID = minSongID; } if (songID < minSongID) { songID = maxSongID; } } // Функция вывода текущего трека на индикатор void printDisplaySongID() { if (songID == 1) { qd.displayDigits(QD_P, QD_0, QD_0, QD_1); } else if (songID == 2) { qd.displayDigits(QD_P, QD_0, QD_0, QD_2); } else if (songID == 3) { qd.displayDigits(QD_P, QD_0, QD_0, QD_3); } else if (songID == 4) { qd.displayDigits(QD_P, QD_0, QD_0, QD_4); } else if (songID == 5) { qd.displayDigits(QD_P, QD_0, QD_0, QD_5); } else { qd.displayDigits(QD_E, QD_r, QD_r, QD_NONE); } } // Функция выбора трека void setSongID() { if (songID == 1) { anyrtttl::nonblocking::begin_P(BUZZER_PIN, imperM); } else if (songID == 2) { anyrtttl::nonblocking::begin_P(BUZZER_PIN, marioB); } else if (songID == 3) { anyrtttl::nonblocking::begin_P(BUZZER_PIN, misImp); } else if (songID == 4) { anyrtttl::nonblocking::begin_P(BUZZER_PIN, tetris); } else if (songID == 5) { anyrtttl::nonblocking::begin_P(BUZZER_PIN, europe); } } // Функция воспроизведения трека void playSongID() { // Если не включена пауза if (pause == false) { // Продолжаем играть трек anyrtttl::nonblocking::play(); } // Если трек завершился if (anyrtttl::nonblocking::done()) { // Устанавливаем режим «Плеер не играет» playState = PLAY_OFF; } } // Функция остановки трека void stopSongID() { // Останавливаем трек anyrtttl::nonblocking::stop(); } ==== №10. Troyka Slot-машина ==== {{ :kits:io-kit:clock:100-drawing-wiring-cmyk.png?nolink&700 |}} // Подключаем библиотеку для работы с мелодиями в формате RTTTL #include // Подключаем библиотеку для работы с четырёхразрядным дисплеем #include // Подключаем библиотеку для работы с кнопками #include // Создаём объект дисплея на шине SPI и пине 10 QuadDisplay qd(10); // Создаём объект кнопки на пине 2 TroykaButton button(2); // Даём понятное имя пину A2 с пищалкой constexpr uint8_t BUZZER_PIN = A2; // Даём понятное имя пину 5 со светодиодом constexpr uint8_t LED_PIN = 5; // Мелодия «Старт» в формате RTTTL const char* gameStart = "gameStart:d=4,o=5,b=100:" "16e6,16e6,32p,8e6,16c6,8e6,8g6,8p," "8g,8p"; // Мелодия «Игра проиграна» в формате RTTTL const char* gameOver = "gameOver:d=4,o=5,b=90:" "32c6,32c6,32c6,8p,16b,16f6,16p,16f6," "16f.6,16e.6,16d6,16c6,16p,16e,16p,16c"; // Мелодия «Игра выиграна» в формате RTTTL const char* gameWin = "gameWin:d=4,o=5,b=180:" "8g3,8c4,8e4,8g4,8c,8e,3g,3e," "8e3,8c4,8e#4,8A#4,8c,8e#,3a#,3e#," "8b#3,8d4,8f4,8b#4,8d,8f,3b#,32p," "8b#,32p,8b#,32p,8b#,1c6"; // Создаём перечисление состояний игры с соответствующей переменной enum { GAME_START, // «Начало игры» GAME_PLAY, // «Процесс игры» GAME_END, // «Конец игры» GAME_OVER, // «Игра проиграна» GAME_WIN // «Игра выиграна» } gameState; // Создаём перечисление разрядов дисплея enum { D1 = 0, // Первый разряд D2 = 1, // Второй разряд D3 = 2, // Третий разряд D4 = 3 // Четвёртый разряд }; // Создаём переменную для хранения значения на разряде дисплея uint8_t digitCounter; // Создаём переменную для хранения значения на разряде дисплея в битовом виде uint8_t digitMaskQD; // Создаём массив для хранения значений на разрядах дисплея в битовом виде uint8_t digitMasksQD[4]; // Создаём массив для хранения статуса установки значений на разрядах дисплея // true: разряд установлен, false: разряд не установлен bool digitStates[4]; // Создаём переменную для работы таймера обновления значений на разрядах дисплея long timeLastUpdateDigit = 0; // Создаём переменную для работы таймера остановки значений на разрядах дисплея long timeLastStopDigit = 0; // Создаём константу для хранения времени обновления значений счётчика на разрядах дисплея constexpr int timeUpdateDigit = 100; // Создаем переменную для хранения времени остановки значений счётчика на разрядах дисплея int timeStopDigit; void setup() { // Инициализируем дисплей qd.begin(); // Инициализируем кнопку button.begin(); // Настраиваем пин с пищалкой в режим выхода pinMode(BUZZER_PIN, OUTPUT); // Настраиваем пин со светодиодом в режим выхода pinMode(LED_PIN, OUTPUT); // Задаём зерно генерации случайных чисел randomSeed(analogRead(A6)); // Устанавливаем режим «Начало игры» gameState = GAME_START; } void loop() { // Считываем состояние с кнопки button.read(); // Выбираем действие в зависимости от текущего режима игры switch (gameState) { // Если установлен режим «Начало игры» case GAME_START: { // Переходим в функцию обработки режима «Начало игры» handleGameStart(); break; } // Если установлен режим «Процесс игры» case GAME_PLAY: { // Переходим в функцию обработки режима «Процесс игры» handleGamePlay(); break; } // Если установлен режим «Конец игры» case GAME_END: { // Переходим в функцию обработки режима «Конец игры» handleGameEnd(); break; } // Если установлен режим «Игра проиграна» case GAME_OVER: { // Переходим в функцию обработки режима «Игры проиграна» handleGameOver(); break; } // Если установлен режим «Игра выиграна» case GAME_WIN: { // Переходим в функцию обработки режима «Игры выиграна» handleGameWin(); break; } } } // Функция обработки режима «Начало игры» void handleGameStart() { // Зажигаем светодиод digitalWrite(LED_PIN, HIGH); // Выводим приветствие на дисплей qd.displayDigits(QD_P, QD_L, QD_A, QD_Y); // Играем приветственную мелодию anyrtttl::blocking::play(BUZZER_PIN, gameStart); // Ожидаем нажатие на кнопку do { // Считываем состояние с кнопки button.read(); } while (!button.isClick()); // Гасим светодиод digitalWrite(LED_PIN, LOW); // Сбрасываем настройки по умолчанию resetDefaults(); // Генерируем случайное число timeStopDigit = timeRandom(); // Устанавливаем режим «Процесс игры» gameState = GAME_PLAY; } // Функция обработки режима «Процесс игры» void handleGamePlay() { // В режиме «Процесс игры» работают два таймера: // timerUpdateDigit(): таймер обновления значений символов, // который отвечает за время поочередного обновления цифр от 0 до 9 // timerStopDigit(): таймер остановки значений символов, // который отвечает за время остановки обновления цифр от 0 до 9 // Если таймер обновления символов досчитал, // получаем последующее число от 0 до 9 // Передаем константе состояние таймера обновления символов const auto timerUpdateDone = timerUpdateDigit(); // Если таймер досчитал, т.е. прошла 1 секунда if (timerUpdateDone) { // Увеличиваем цифру на один digitCounter++; // Если цифра стала более 9 if (digitCounter > 9) { // Обнуляем цифру до ноля digitCounter = 0; } // Конвертируем число в QD-символов в битовом виде digitMaskQD = intToQD(digitCounter); } // Передаем константе состояние таймера остановки значений const auto timerStopDone = timerStopDigit(); // Если таймер остановки значений счётчика досчитал, // проверяем какой разряд стоит на очередь установки if (timerStopDone) { // Если очередь второго разряда if (digitStates[D2] == false) { // Фиксируем разряд статусом true: установлен digitStates[D2] = true; // Присваиваем текущий символ на счётчике в массив хранения значений результата digitMasksQD[D2] = digitMaskQD; // Генерируем случайное число timeStopDigit = timeRandom(); // Если очередь третьего разряда } else if (digitStates[D3] == false) { // Фиксируем разряд статусом true: установлен digitStates[D3] = true; // Присваиваем текущий символ на счётчике в массив хранения значений результата digitMasksQD[D3] = digitMaskQD; // Генерируем случайное число timeStopDigit = timeRandom(); // Если очередь четвёртого разряда } else if (digitStates[D4] == false) { // Фиксируем разряд статусом true: установлен digitStates[D4] = true; // Присваиваем текущий символ на счётчике в массив хранения значений результата digitMasksQD[D4] = digitMaskQD; } } // Присваиваем текущий символ на счётчике всем не установленным разрядам for (int i = D2; i <= D4; i++) { if (!digitStates[i]) { digitMasksQD[i] = digitMaskQD; } } // Если все разряды установлены if (checkDigitStates()) { // Устанавливаем режим «Конец игры» gameState = GAME_END; } // Выводим текущие символы разрядов на дисплей printDigits(); } // Функция обработки режима «Конец игры» void handleGameEnd() { // Выводим текущие символы разрядов на дисплей printDigits(); // Ждём 1000 мс для наглядности комбинации на дисплее delay(1000); // Проверяем значения разрядов на совпадения if (checkDigitsWin()) { // Устанавливаем режим «Игра выиграна» gameState = GAME_WIN; // Если вдруг совпала выигрышная комбинация, // устанавливаем читерскую инструкцию digitMasksQD[D4] = intToQD(--digitCounter); // Устанавливаем режим «Игра проиграна» gameState = GAME_OVER; } else { // Устанавливаем режим «Игра проиграна» gameState = GAME_OVER; } // Выводим текущие символы разрядов на дисплей после проверки на совпадения printDigits(); // Ждём 1000 мс для наглядности комбинации на дисплее delay(1000); } // Функция обработки режима «Игра проиграна» void handleGameOver() { // Печатаем на дисплей про проигрыш qd.displayDigits(QD_F, QD_A, QD_I, QD_L); // Играем мелодию проигрыша anyrtttl::blocking::play(BUZZER_PIN, gameOver); // Ожидаем нажатие на кнопку do { button.read(); } while (!button.isClick()); // Устанавливаем режим «Начало игры» gameState = GAME_START; } // Функция обработки режима «Игра выиграна» void handleGameWin() { // Печатаем на дисплей про выигрыш qd.displayDigits(QD_C, QD_O, QD_O, QD_L); // Играем мелодию выигрыша anyrtttl::blocking::play(BUZZER_PIN, gameWin); // Ожидаем нажатие на кнопку do { button.read(); } while (!button.isClick()); // Устанавливаем режим «Начало игры» gameState = GAME_START; } // Функция-таймер обновления значений счётчика на разрядах дисплея bool timerUpdateDigit() { // Запоминаем текущее время long timeNow = millis(); // Если прошёл интервал времени по обновлению значений счётчика if (timeNow - timeLastUpdateDigit > timeUpdateDigit) { // Обновляем текущее время timeLastUpdateDigit = timeNow; // Да, таймер досчитал return true; } // Нет, таймер не досчитал return false; } // Функция-таймер остановки значений счётчика на разрядах дисплея bool timerStopDigit() { // Запоминаем текущее время long timeNow = millis(); // Если прошёл интервал времени по остановки значений счётчика if (timeNow - timeLastStopDigit > timeStopDigit) { timeLastStopDigit = timeNow; // Да, таймер досчитал return true; } // Нет, таймер не досчитал return false; } // Функция конвертации целого числа int в QD-символ дисплея int intToQD(int digitInt) { // Создаём переменную для хранения QD-символа int digitQD; // Конвертируем целое число int в QD-символ дисплея switch (digitInt) { case 0: digitQD = QD_0; break; case 1: digitQD = QD_1; break; case 2: digitQD = QD_2; break; case 3: digitQD = QD_3; break; case 4: digitQD = QD_4; break; case 5: digitQD = QD_5; break; case 6: digitQD = QD_6; break; case 7: digitQD = QD_7; break; case 8: digitQD = QD_8; break; case 9: digitQD = QD_9; break; } // Возвращаем QD-символ дисплея return digitQD; } // Функция вывода символов на дисплей void printDigits() { qd.displayDigits(QD_NONE, digitMasksQD[D2], digitMasksQD[D3], digitMasksQD[D4]); } // Функция проверки выигрышной комбинации bool checkDigitsWin() { if (digitMasksQD[D2] == digitMasksQD[D3] && digitMasksQD[D3] == digitMasksQD[D4]) { return true; } else { return false; } } // Функция проверки статуса установки разрядов на дисплее bool checkDigitStates() { for (int i = D2; i <= D4; i++) { if (digitStates[i] == false) { return false; } } return true; } // Функция сброса статуса установки разрядов на дисплее void resetDigitStates() { for (int i = D2; i <= D4; i++) { digitStates[i] = false; } } // Функция сброса настроек по умолчанию void resetDefaults() { // Засекаем текущее время для таймеров timeLastUpdateDigit = millis(); timeLastStopDigit = millis(); // Сбрасываем состояние разрядов resetDigitStates(); // Присваиваем переменной для хранения значения разряда число 0 digitCounter = 0; // Присваиваем переменной для хранения // значения разряда в символьном представление QD знак «-» digitMaskQD = QD_MINUS; } // Функция генерации случайно числа от 1000 до 3000 int timeRandom() { return random(1000, 3000); } ===== Ресурсы ===== * [[amp>collection/io-kits?utm_source=man&utm_campaign=io-kit-clock&utm_medium=wiki|Наборы IO.KIT]] в магазине * [[kits:io-kit|Электронные материалы IO.KIT]] ==== Софт ==== * [[amp>page/arduino-ide?utm_source=announce&utm_campaign=io-kit-clock&utm_medium=wiki |Страница загрузки Arduino IDE]] ==== Библиотеки ==== * [[https://github.com/end2endzone/AnyRtttl|AnyRtttl]] * [[https://github.com/amperka/TroykaButton|TroykaButton]] * [[https://github.com/amperka/TroykaRTC|TroykaRTC]] * [[https://github.com/amperka/QuadDisplay2|QuadDisplay2]]