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