Электронное приложение к набору «Arduino. Восьмибитная академия»
Привет, дружище!
Добро пожаловать в уютную Восьмибитную академию.
На этой странице мы собрали всё, чтобы тебе было удобнее проходить набор:
- Схемы проектов в электронном виде.
- Исходный код учебных программ (копируй его в редактор Arduino IDE).
- Дополнительные материалы: программные библиотеки, даташиты и т. п.
Обрати внимание
Именно здесь находятся самые актуальные версии кода с исправлениями. Написанному верить!
Обрати внимание
Для успешной загрузки скетчей понадобиться драйвер CH340. Прочитайте наши статьи по установке драйвера для Windows или для Linux
Эксперименты
В начале книги идёт вступительная часть с экспериментами №1–№6, где тебе не потребуется никакое программирование.
Поэтому в электронной версии мы приводим материалы, начиная с эксперимента №7.
№7. Цифролампа
- Lamp.ino
void setup() { // Настраиваем пин 2 в режим выхода pinMode(2, OUTPUT); // Подаем на пин 2 высокий уровень digitalWrite(2, HIGH); } void loop() { }
№8. Маячок
- Blink.ino
void setup() { // Настраиваем пин 2 в режим выхода pinMode(2, OUTPUT); } void loop() { // Подаем на пин 2 высокий уровень digitalWrite(2, HIGH); // Ждём 200 мс delay(200); // Подаем на пин 2 низкий уровень digitalWrite(2, LOW); // Ждём 800 мс delay(800); }
№9. Светофор
- TrafficLight.ino
// Даём пинам понятные имена constexpr int LED_RED_PIN = 2; constexpr int LED_YELLOW_PIN = 3; constexpr int LED_GREEN_PIN = 4; // Константа для хранения паузы в миллисекундах constexpr int PAUSE = 2000; void setup() { // Настраиваем пины светодиодов в режим выхода pinMode(LED_RED_PIN, OUTPUT); pinMode(LED_YELLOW_PIN, OUTPUT); pinMode(LED_GREEN_PIN, OUTPUT); } void loop() { // Зажигаем красный светодиод digitalWrite(LED_RED_PIN, HIGH); // Ждём 2 секунды delay(PAUSE); // Гасим красный светодиод digitalWrite(LED_RED_PIN, LOW); // Зажигаем жёлтый светодиод digitalWrite(LED_YELLOW_PIN, HIGH); // Ждём 2 секунды delay(PAUSE); // Гасим жёлтый светодиод digitalWrite(LED_YELLOW_PIN, LOW); // Зажигаем зелёный digitalWrite(LED_GREEN_PIN, HIGH); // Ждём 2 секунды delay(PAUSE); // Гасим зелёный светодиод digitalWrite(LED_GREEN_PIN, LOW); }
№10. SOS
- SOS.ino
// Даём пину со светодиодом понятное имя constexpr int LED_RED_PIN = 2; void setup() { // Настраиваем пин со светодиодом в режим выхода pinMode(LED_RED_PIN, OUTPUT); } void loop() { // Переменная для хранения паузы в миллисекундах int pause = 200; // Мигаем красным светодиодом с интервалом из переменной pause digitalWrite(LED_RED_PIN, HIGH); delay(pause); digitalWrite(LED_RED_PIN, LOW); delay(pause); digitalWrite(LED_RED_PIN, HIGH); delay(pause); digitalWrite(LED_RED_PIN, LOW); delay(pause); digitalWrite(LED_RED_PIN, HIGH); delay(pause); digitalWrite(LED_RED_PIN, LOW); // Меняем значение паузы pause = 500; // Мигаем красным светодиодом с интервалом из переменной pause delay(pause); digitalWrite(LED_RED_PIN, HIGH); delay(pause); digitalWrite(LED_RED_PIN, LOW); delay(pause); digitalWrite(LED_RED_PIN, HIGH); delay(pause); digitalWrite(LED_RED_PIN, LOW); delay(pause); digitalWrite(LED_RED_PIN, HIGH); delay(pause); digitalWrite(LED_RED_PIN, LOW); delay(pause); // Меняем значение паузы pause = 200; // Мигаем красным светодиодом с интервалом из переменной pause digitalWrite(LED_RED_PIN, HIGH); delay(pause); digitalWrite(LED_RED_PIN, LOW); delay(pause); digitalWrite(LED_RED_PIN, HIGH); delay(pause); digitalWrite(LED_RED_PIN, LOW); delay(pause); digitalWrite(LED_RED_PIN, HIGH); delay(pause); digitalWrite(LED_RED_PIN, LOW); // Меняем значение паузы pause = 2000; // Ставим задержку перед следующим циклом мигания delay(pause); }
№11. Охотники за приведениями
Схема №1
Схема №2
- Ghostbusters.ino
// Даём пинам понятные имена constexpr int LED_PIN = 2; constexpr int WIRE_PIN = 4; void setup() { // Настраиваем пин со светодиодом в режим выхода pinMode(LED_PIN, OUTPUT); // Настраиваем пин с подключённым проводом в режим входа pinMode(WIRE_PIN, INPUT); } void loop() { // Создаём переменную для хранения сигнала с провода bool wireState; // Считываем значение уровня сигнала сигнала с провода // Если провод соединён с линией питания, получим логическую единицу // Если провод соединён с линией GND, получим логический ноль wireState = digitalRead(WIRE_PIN); // Применим полученное значение для светодиода // Логическая единица — светодиод горит // Логический ноль — светодиод не горит digitalWrite(LED_PIN, wireState); }
№12. Фонарик
Программный код №1
- Flashlight-v1.ino
// Даём пинам понятные имена constexpr int LED_PIN = 2; constexpr int BUTTON_PIN = 4; void setup() { // Настраиваем пин со светодиодом в режим выхода pinMode(LED_PIN, OUTPUT); // Настраиваем пин с кнопкой в режим входа pinMode(BUTTON_PIN, INPUT); } void loop() { // Создадим переменную для хранения состояния с кнопки // И сразу же запишем значение уровня сигнала // Если кнопка нажата — логический ноль // Если кнопка не нажата — логическая единица bool buttonState = digitalRead(BUTTON_PIN); // Применим полученное значение для управления светодиодом digitalWrite(LED_PIN, buttonState); }
Программный код №2
- Flashlight-v2.ino
// Даём пинам понятные имена constexpr int LED_PIN = 2; constexpr int BUTTON_PIN = 4; void setup() { // Настраиваем пин со светодиодом в режим выхода pinMode(LED_PIN, OUTPUT); // Настраиваем пин с кнопкой в режим входа pinMode(BUTTON_PIN, INPUT); } void loop() { // Создадим переменную для хранения состояния с кнопки // Считываем инвертированное состояние с кнопки // Если кнопка нажата — логическая единица // Если кнопка не нажата — логический ноль bool buttonState = !digitalRead(BUTTON_PIN); // Применим полученное значение для управления светодиодом digitalWrite(LED_PIN, buttonState); }
№13. Фонарик без посторонних
- FlashlightPullUP.ino
// Даём пинам понятные имена constexpr int LED_PIN = 2; constexpr int BUTTON_PIN = 4; void setup() { // Настраиваем пин со светодиодом в режим выхода pinMode(LED_PIN, OUTPUT); // Настраиваем пин с кнопкой в режим входа с подтяжкой к питанию pinMode(BUTTON_PIN, INPUT_PULLUP); } void loop() { // Создадим переменную для хранения состояния кнопки // Считываем инвертированное состояние с кнопки // Если кнопка нажата — логическая единица // Если кнопка не нажата — логический ноль bool buttonState = !digitalRead(BUTTON_PIN); // Применим полученное значение для управления светодиодом digitalWrite(LED_PIN, buttonState); }
№14. Выключатель
Программный код №1
- Switch-v1.ino
// Даём пинам понятные имена constexpr int LED_PIN = 2; constexpr int BUTTON_PIN = 4; // Создаём переменную для хранения состояние светодиода // В начале программы светодиод должен быть потушен, // поэтому присваиваем переменной значение LOW bool ledState = LOW; void setup() { // Настраиваем пин со светодиодом в режим выхода pinMode(LED_PIN, OUTPUT); // Настраиваем пин с кнопкой в режим входа с подтяжкой к питанию pinMode(BUTTON_PIN, INPUT_PULLUP); } void loop() { // Создаём переменную для хранения состояние кнопки // Считываем инвертированное состояние с кнопки bool buttonState = !digitalRead(BUTTON_PIN); // Проверяем нажата ли кнопка сейчас if (buttonState == HIGH) { // Если да, значит это клик // Меняем состояние переменной для светодиода ledState = !ledState; // Применяем новое значение переменной для управления светодиодом digitalWrite(LED_PIN, ledState); } }
Программный код №2
- Switch-v2.ino
// Даём пинам понятные имена constexpr int LED_PIN = 2; constexpr int BUTTON_PIN = 4; // Создаём переменную для хранения состояние светодиода // В начале программы светодиод должен быть потушен, // поэтому присваиваем переменной значение LOW bool ledState = LOW; // Создаём переменную для хранения последнего состояние кнопки // В начале программы кнопка должна быть отпущена, // поэтому присваиваем переменной значение LOW bool lastButtonState = LOW; void setup() { // Настраиваем пин со светодиодом в режим выхода pinMode(LED_PIN, OUTPUT); // Настраиваем пин с кнопкой в режим входа с подтяжкой к питанию pinMode(BUTTON_PIN, INPUT_PULLUP); } void loop() { // Создаём переменную для хранения состояние кнопки // Считываем инвертированное состояние с кнопки bool buttonState = !digitalRead(BUTTON_PIN); // Проверяем два условия // Условие 1: нажата ли кнопка сейчас // Условие 2: была ли кнопка отжата до фиксирования нового нажатия if (buttonState == HIGH && lastButtonState == LOW) { // Если оба условия прошли, значит это клик // Меняем состояние переменной для светодиода ledState = !ledState; // Применяем новое значение переменной для управления светодиодом digitalWrite(LED_PIN, ledState); } // Запишем в переменную новое значение состояния кнопки lastButtonState = buttonState; }
№15. Выключатель без глюков
- SwitchDebounce.ino
// Даём пинам понятные имена constexpr int LED_PIN = 2; constexpr int BUTTON_PIN = 4; // Создаём переменную для хранения состояние светодиода // В начале программы светодиод должен быть потушен, // поэтому присваиваем переменной значение LOW bool ledState = LOW; // Создаём переменную для хранения последнего состояние кнопки // В начале программы кнопка должна быть отпущена, // поэтому присваиваем переменной значение LOW bool lastButtonState = LOW; void setup() { // Настраиваем пин со светодиодом в режим выхода pinMode(LED_PIN, OUTPUT); // Настраиваем пины с кнопкой в режим входа с подтяжкой к питанию pinMode(BUTTON_PIN, INPUT_PULLUP); } void loop() { // Создаём переменную для хранения состояние кнопки // Считываем инвертированное состояние с кнопки bool buttonState = !digitalRead(BUTTON_PIN); // Проверяем два условия // Условие 1: нажата ли кнопка сейчас // Условие 2: была ли она отжата кнопка до фиксирования нового нажатия if (buttonState && !lastButtonState) { // Подождём пока дребезг кнопки затихнет delay(10); // Снова считываем инвертированное состояние с кнопки buttonState = !digitalRead(BUTTON_PIN); // Проверяем нажата ли ещё кнопка сейчас if (buttonState) { // Если кнопка всё ещё нажата // Меняем состояние переменной для светодиода ledState = !ledState; // Применяем новое значение переменной для управления светодиодом digitalWrite(LED_PIN, ledState); } } // Запишем в переменную новое значение состояния кнопки lastButtonState = buttonState; }
№16. Рулим яркостью
- LedBrightness.ino
// Даём пину со светодиодом понятное имя constexpr int LED_PIN = 3; // Константа для хранения паузы в миллисекундах constexpr int PAUSE = 500; void setup() { // Настраиваем пин со светодиодом в режим выхода pinMode(LED_PIN, OUTPUT); } void loop() { // Выдаём на светодиод ШИМ-сигнал // Для начала зажжём светодиод на 25% яркости // 25% от числа 255 будет примерно 64 // 25% от напряжения 5В будет примерно 1,25 В analogWrite(LED_PIN, 64); // Ждём 250 мс delay(PAUSE); // Зажжём светодиод на 50% яркости // 50% от числа 255 будет примерно 128 // 50% от напряжения 5В будет примерно 2,5 В analogWrite(LED_PIN, 128); // Ждём 250 мс delay(PAUSE); // Зажжём светодиод на 75% яркости // 75% от числа 255 будет примерно 192 // 75% от напряжения 5В будет примерно 3,75 В analogWrite(LED_PIN, 192); // Ждём 250 мс delay(PAUSE); // Зажжём светодиод на 100% яркости // 100% от числа 255 будет 255 // 100% от напряжения 5В будет 5 В analogWrite(LED_PIN, 255); // Ждём 250 мс delay(PAUSE); // В завершении потушим светодиод, т.е. дадим 0% яркости // 0% от числа 255 будет 0 // 0% от напряжения 5В будет 0 В analogWrite(LED_PIN, 0); // Ждём 250 мс delay(PAUSE); }
№17. Дыхание света
- LedBrightnessSmooth.ino
// Даём пину со светодиодом понятное имя constexpr int LED_PIN = 3; void setup() { // Настраиваем пин со светодиодом в режим выхода pinMode(LED_PIN, OUTPUT); } void loop() { // Создаём цикл для перебора всех значений от 0 до 255 // Выдаём на светодиод ШИМ-сигнал for (int brightness = 0; brightness <= 255; brightness++) { analogWrite(LED_PIN, brightness); // Ждём 10 мс delay(10); } // Создаём цикл для перебора всех значений от 255 до 0 // Выдаём на светодиод ШИМ-сигнал for (int brightness = 255; brightness >= 0; brightness--) { analogWrite(LED_PIN, brightness); // Ждём 10 мс delay(10); } }
№18. Охотники за приведениями 2.0
- Ghostbusters2.0.ino
// Даём пинам понятные имена constexpr int LED_PIN = 3; constexpr int WIRE_PIN = A0; void setup() { // Настраиваем пин со светодиодом в режим выхода pinMode(LED_PIN, OUTPUT); // Настраиваем пин с подключённым проводом в режим входа pinMode(WIRE_PIN, INPUT); } void loop() { // Создаём переменную для хранения сигнала с провода // Считываем значение уровня сигнала сигнала с провода // Функция analogRead считывает напряжение на пине от 0 до 5 В // и преобразует его к числу от 0 до 1023 int wireState = analogRead(WIRE_PIN); // Создаём переменную для хранения состояние светодиода // Разрядность АЦП В Iskra Nano — 10 бит // т.е. «analogRead» возвращает числовой диапазон от 0 до 1023 // Разрядность ШИМ В Iskra Nano — 8 бит // т.е. «analogWrite» ожидает диапазон от 0 до 255 // Преобразуем значения из одного диапазона [0; 1023] в другой [0; 255], // самый простой способ разделить на четыре int brightness = wireState / 4; // Выдаём результат на светодиод analogWrite(LED_PIN, brightness); }
№19. Диммер
Программный код №1
- Dimmer-v1.ino
// Даём пинам понятные имена constexpr int LED_PIN = 3; constexpr int POT_PIN = A0; void setup() { // Настраиваем пин со светодиодом в режим выхода pinMode(LED_PIN, OUTPUT); // Настраиваем пин с потенциометром в режим входа pinMode(POT_PIN, INPUT); } void loop() { // Считываем аналоговый сигнал с потенциометра int rotation = analogRead(POT_PIN); // Преобразуем значение из шкалы 10 бит в 8 бит, // самый простой способ разделить на четыре int brightness = rotation / 4; // Выдаём результат на светодиод analogWrite(LED_PIN, brightness); }
Программный код №2
- Dimmer-v2.ino
// Даём пинам понятные имена constexpr int LED_PIN = 3; constexpr int POT_PIN = A0; void setup() { // Настраиваем пин со светодиодом в режим выхода pinMode(LED_PIN, OUTPUT); // Настраиваем пин с потенциометром в режим входа pinMode(POT_PIN, INPUT); } void loop() { // Считываем аналоговый сигнал с потенциометра int rotation = analogRead(POT_PIN); // Преобразуем значение из шкалы 10 бит в 8 бит, // используем функцию map int brightness = map(rotation, 0, 1023, 0, 255); // Выдаём результат на светодиод analogWrite(LED_PIN, brightness); }
№20. Цветовая рулетка
Программный код №1
- ColorGame-v1.ino
// Даём понятные имена пинам к которым подключены выводы RGB-светодиода constexpr int LED_RED_PIN = 9; constexpr int LED_GREEN_PIN = 10; constexpr int LED_BLUE_PIN = 11; // Даём понятное имя пину с кнопкой constexpr int BUTTON_PIN = A0; // Создаём три переменные для хранения яркости каждого цвета RGB-светодиода int red, green, blue; void setup() { // Настраиваем пины RGB-светодиода в режим выхода pinMode(LED_RED_PIN, OUTPUT); pinMode(LED_GREEN_PIN, OUTPUT); pinMode(LED_BLUE_PIN, OUTPUT); // Настраиваем пин с кнопкой в режим входа с подтяжкой к питанию pinMode(BUTTON_PIN, INPUT_PULLUP); } void loop() { // Считываем нажатие с кнопки bool buttonState = !digitalRead(BUTTON_PIN); if (buttonState) { // Генерируем случайное число от 1 до 7 int number = random(1, 8); // В зависимости от сгенерированного числа, // подаём разные сигнала на ножки RGB-светодиода if (number == 1) { red = HIGH; green = LOW; blue = LOW; } else if (number == 2) { red = LOW; green = HIGH; blue = LOW; } else if (number == 3) { red = LOW; green = LOW; blue = HIGH; } else if (number == 4) { red = HIGH; green = HIGH; blue = LOW; } else if (number == 5) { red = LOW; green = HIGH; blue = HIGH; } else if (number == 6) { red = HIGH; green = LOW; blue = HIGH; } // Выводим полученные значения на ножки RGB-светодиода digitalWrite(LED_RED_PIN, red); digitalWrite(LED_GREEN_PIN, green); digitalWrite(LED_BLUE_PIN, blue); // Ждём одну секунду delay(1000); // Подаём на все ножки RGB-светодиода логический ноль digitalWrite(LED_RED_PIN, LOW); digitalWrite(LED_GREEN_PIN, LOW); digitalWrite(LED_BLUE_PIN, LOW); } }
Программный код №2
- ColorGame-v2.ino
// Даём понятные имена пинам к которым подключены выводы RGB-светодиода constexpr int LED_RED_PIN = 9; constexpr int LED_GREEN_PIN = 10; constexpr int LED_BLUE_PIN = 11; // Даём понятное имя пину с кнопкой constexpr int BUTTON_PIN = A0; // Создаём три переменные для хранения яркости каждого цвета RGB-светодиода int red, green, blue; void setup() { // Настраиваем пины RGB-светодиода в режим выхода pinMode(LED_RED_PIN, OUTPUT); pinMode(LED_GREEN_PIN, OUTPUT); pinMode(LED_BLUE_PIN, OUTPUT); // Настраиваем пин с кнопкой в режим входа с подтяжкой к питанию pinMode(BUTTON_PIN, INPUT_PULLUP); // Передаём случайное число с пина A5 // для последующей генерации случайных чисел randomSeed(analogRead(A5)); } void loop() { // Считываем нажатие с кнопки bool buttonState = !digitalRead(BUTTON_PIN); if (buttonState) { // Генерируем случайное число от 1 до 7 int number = random(1, 8); // В зависимости от сгенерированного числа, // подаём разные сигнала на ножки RGB-светодиода if (number == 1) { red = HIGH; green = LOW; blue = LOW; } else if (number == 2) { red = LOW; green = HIGH; blue = LOW; } else if (number == 3) { red = LOW; green = LOW; blue = HIGH; } else if (number == 4) { red = HIGH; green = HIGH; blue = LOW; } else if (number == 5) { red = LOW; green = HIGH; blue = HIGH; } else if (number == 6) { red = HIGH; green = LOW; blue = HIGH; } // Выводим полученные значения на ножки RGB-светодиода digitalWrite(LED_RED_PIN, red); digitalWrite(LED_GREEN_PIN, green); digitalWrite(LED_BLUE_PIN, blue); // Ждём одну секунду delay(1000); // Подаём на все ножки RGB-светодиода логический ноль digitalWrite(LED_RED_PIN, LOW); digitalWrite(LED_GREEN_PIN, LOW); digitalWrite(LED_BLUE_PIN, LOW); } }
№21. Лампа настроения
- MoodLamp.ino
// Даём понятные имена пинам к которым подключены выводы RGB-светодиода constexpr int LED_RED_PIN = 9; constexpr int LED_GREEN_PIN = 10; constexpr int LED_BLUE_PIN = 11; // Даём понятное имя пину с потенциометром constexpr int POT_PIN = A0; // Создаём три переменные для хранения яркости каждого цвета RGB-светодиода int red, green, blue; void setup() { // Настраиваем пины RGB-светодиода в режим выхода pinMode(LED_RED_PIN, OUTPUT); pinMode(LED_GREEN_PIN, OUTPUT); pinMode(LED_BLUE_PIN, OUTPUT); // Настраиваем пин с потенциометром в режим входа pinMode(POT_PIN, INPUT); } void loop() { // Считываем аналоговый сигнал с потенциометра int rotation = analogRead(A0); // В зависимости от поворота ручки потенциометра, // генерируем разными методами цвета Red, Green и Blue if (rotation <= 170) { red = 255; green = map(rotation, 0, 170, 0, 255); blue = 0; } else if (rotation <= 340) { red = map(rotation, 170, 340, 255, 0); green = 255; blue = 0; } else if (rotation <= 512) { red = 0; green = 255; blue = map(rotation, 340, 512, 0, 255); } else if (rotation <= 682) { red = 0; green = map(rotation, 512, 682, 255, 0); blue = 255; } else if (rotation <= 852) { red = map(rotation, 682, 852, 0, 255); green = 0; blue = 255; } else { red = 255; green = 0; blue = map(rotation, 852, 1023, 255, 0); } // Выводим полученные значения на ножки RGB-светодиода analogWrite(LED_RED_PIN, red); analogWrite(LED_GREEN_PIN, green); analogWrite(LED_BLUE_PIN, blue); }
№22. О частота, ты звук!
- Beep.ino
// Даём пинам понятные имена constexpr int BUZZER_PIN = 3; constexpr int BUTTON_PIN = 4; void setup() { // Настраиваем пин с пищалкой в режим выхода pinMode(BUZZER_PIN, OUTPUT); // Настраиваем пин с кнопкой в режим входа с подтяжкой к питанию pinMode(BUTTON_PIN, INPUT_PULLUP); } void loop() { // Считываем нажатие с кнопки bool buttonState = !digitalRead(BUTTON_PIN); // Применяем полученное значение к пищалке digitalWrite(BUZZER_PIN, buttonState); }
№23. На старт, внимание, бзз!
- Frequency.ino
// Даём пину с пищалкой понятное имя constexpr int BUZZER_PIN = 3; void setup() { // Настраиваем пин с пищалкой в режим выхода pinMode(BUZZER_PIN, OUTPUT); // Выдаём на пищалку ШИМ-сигнал с коэффициентом заполнения 50% analogWrite(BUZZER_PIN, 127); } void loop() { }
№24. Синтезатор
- FrequencyRange.ino
// Даём понятное имя пину с пищалкой constexpr int BUZZER_PIN = 3; // Даём понятное имя пину с потенциометром constexpr int POT_PIN = A0; void setup() { // Настраиваем пин с пищалкой в режим выхода pinMode(BUZZER_PIN, OUTPUT); // Настраиваем пин с потенциометром в режим входа pinMode(POT_PIN, INPUT); } void loop() { // Считываем аналоговый сигнал с потенциометра int rotation = analogRead(POT_PIN); // Преобразуем значение с потенциометра в частотный диапазон для пищалки int frequency = map(rotation, 0, 1023, 20, 20000); // Заставляем пин с пищалкой звучать на высчитанной частоте tone(BUZZER_PIN, frequency); }
№25. Я вам спою
- Melody.ino
// Даём понятное имя пину с пищалкой constexpr int BUZZER_PIN = 7; // Даём понятное имя пину с кнопкой constexpr int BUTTON_PIN = 4; // Создаём звуковой шрифт из нот со своими частотами constexpr int NOTE_B3 = 247; constexpr int NOTE_C4 = 262; constexpr int NOTE_D4 = 294; constexpr int NOTE_E4 = 330; constexpr int NOTE_F4 = 349; constexpr int NOTE_G4 = 392; constexpr int NOTE_A4 = 440; constexpr int NOTE_B4 = 494; constexpr int NOTE_C5 = 523; void setup() { // Настраиваем пин с пищалкой в режим выхода pinMode(BUZZER_PIN, OUTPUT); // Настраиваем пин с кнопкой в режим входа с подтяжкой к питанию pinMode(BUTTON_PIN, INPUT_PULLUP); } void loop() { // Считываем нажатие с кнопки bool buttonState = !digitalRead(BUTTON_PIN); // По нажатию кнопки проигрываем мелодию из последовательности нот if (buttonState) { tone(BUZZER_PIN, NOTE_E4, 100); delay(150); tone(BUZZER_PIN, NOTE_E4, 100); delay(300); tone(BUZZER_PIN, NOTE_E4, 100); delay(300); tone(BUZZER_PIN, NOTE_C4, 100); delay(100); tone(BUZZER_PIN, NOTE_E4, 100); delay(300); tone(BUZZER_PIN, NOTE_G4, 100); delay(550); tone(BUZZER_PIN, NOTE_C5, 100); delay(575); } }
№26. Кнопочные ковбои
- ButtonCowboys.ino
// Даём пинам понятные имена constexpr int BUZZER_PIN = 4; constexpr int LED_ONE_PIN = 3; constexpr int LED_TWO_PIN = 5; constexpr int BUTTON_ONE_PIN = 2; constexpr int BUTTON_TWO_PIN = 6; void setup() { // Настраиваем пин с пищалкой в режим выхода pinMode(BUZZER_PIN, OUTPUT); // Настраиваем пины со светодиодами в режим выхода pinMode(LED_ONE_PIN, OUTPUT); pinMode(LED_TWO_PIN, OUTPUT); // Настраиваем пины с кнопкой в режим входа с подтяжкой к питанию pinMode(BUTTON_ONE_PIN, INPUT_PULLUP); pinMode(BUTTON_TWO_PIN, INPUT_PULLUP); // Передаём случайное число с пина A0 // для последующей генерации случайных чисел randomSeed(analogRead(A0)); } void loop() { // Выжидаем случайное время от 2 до 10 секунд delay(random(2000, 10000)); // Даём сигнал «пли!» // Пищим 250 миллисекунд с частотой 3 килогерца tone(BUZZER_PIN, 3000, 250); // Запускаем бесконечный цикл while while (true) { // Считываем состояния кнопок двух игроков bool buttonPlayerOne = !digitalRead(BUTTON_ONE_PIN); bool buttonPlayerTwo = !digitalRead(BUTTON_TWO_PIN); // Проверяем, нажата ли одна из кнопок. if (buttonPlayerOne || buttonPlayerTwo) { if (buttonPlayerOne) { // Если на кнопку нажал первый игрок // Зажигаем первый светодиод на 1000 мс // Выводим звуковой сигнал 4000 Гц / 1000 мс digitalWrite(LED_ONE_PIN, HIGH); tone(BUZZER_PIN, 4000, 1000); delay(1000); digitalWrite(LED_ONE_PIN, LOW); // Если на кнопку нажал второй игрок // Зажигаем первый светодиод на 1000 мс // Выводим звуковой сигнал 4000 Гц / 1000 мс } else if (buttonPlayerTwo) { digitalWrite(LED_TWO_PIN, HIGH); tone(BUZZER_PIN, 4000, 1000); delay(1000); digitalWrite(LED_TWO_PIN, LOW); } // Выходим из бесконечного цикла break; } } }
№27. Терминал
- Terminal.ino
void setup() { // Открываем соединения с последовательным портом (терминалом) Serial.begin(9600); // Выводим приветственную строку в терминал Serial.println("Hello, world"); } void loop() { // Выводим каждую секунду новую строку Serial.println("This is a new line"); delay(1000); }
№28. Время жизни
- Lifetime.ino
void setup() { // Открываем соединения с последовательным портом (терминалом) Serial.begin(9600); // Выводим приветственную строку в терминал Serial.println("Hello, world"); } void loop() { // Создаём переменную time // и присваиваем ей количество секунд с начала работы программы int time = millis() / 1000; // Выводим полученные данные Serial.print("Time life: "); Serial.print(time); Serial.println(" seconds"); delay(1000); }
№29. Бегущий огонь
- RunningFire.ino
// Создаём массив с контактами светодиодов constexpr int PIN_LEDS[] = {2, 3, 4, 5, 6, 7, 8, 9}; // Количество светодиодов в массиве constexpr int COUNT_LEDS = 8; void setup() { // Настраиваем пины со светодиодами в режим выхода for (int i = 0; i < COUNT_LEDS ; i++) { pinMode(PIN_LEDS[i], OUTPUT); } } void loop() { // Мигаем каждым светодиодом в цикле по очереди for (int i = 0; i < COUNT_LEDS; i++) { digitalWrite(PIN_LEDS[i], HIGH); delay(100); digitalWrite(PIN_LEDS[i], LOW); delay(100); } }
№30. Многопоточность
- MultipleBlink.ino
// Даём пинам понятные имена constexpr int LED_RED_PIN = 2; constexpr int LED_GREEN_PIN = 3; // Переменные для счётчиков красного и зелёного светодиода long millisLastRed = 0; long millisLastGreen = 0; // Переменные для хранения состояний светодиодов bool ledRedState = false; bool ledGreenState = false; void setup() { // Настраиваем пины со светодиодами в режим выхода pinMode(LED_RED_PIN, OUTPUT); pinMode(LED_GREEN_PIN, OUTPUT); } void loop() { // Засекаем текущее время long millisNow = millis(); if (millisNow - millisLastRed > 100) { // Если интервал красного LED превысил 100 мс // Инвертируем его состояние millisLastRed = millisNow; ledRedState = !ledRedState; digitalWrite(LED_RED_PIN, ledRedState); } if (millisNow - millisLastGreen > 1000) { // Если интервал зелёного LED превысил 1000 мс // Инвертируем его состояние millisLastGreen = millisNow; ledGreenState = !ledGreenState; digitalWrite(LED_GREEN_PIN, ledGreenState); } }
№31. Выкручивание напряжения
Программный код №1
- VoltageRegulator-v1.ino
// Даём понятное имя пину с потенциометром constexpr int POT_PIN = A0; void setup() { // Открываем соединения с последовательным портом Serial.begin(9600); // Настраиваем пин с потенциометром в режим входа pinMode(POT_PIN, INPUT); } void loop() { // Считываем аналоговый сигнал с потенциометра int sensorADC = analogRead(POT_PIN); // Преобразуем отсчёты АЦП в напряжение float sensorVoltage = (sensorADC * 5) / 1024; // Выводим в терминал показания с потенциометра // в отсчётах АЦП и в напряжении Serial.print(sensorADC); Serial.print(" ADC"); Serial.print("\t\t"); Serial.print(sensorVoltage); Serial.println(" Volts"); delay(1000); }
Программный код №2
- VoltageRegulator-v2.ino
// Даём понятное имя пину с потенциометром constexpr int POT_PIN = A0; void setup() { // Открываем соединения с последовательным портом Serial.begin(9600); // Настраиваем пин с потенциометром в режим входа pinMode(POT_PIN, INPUT); } void loop() { // Считываем аналоговый сигнал с потенциометра int sensorADC = analogRead(POT_PIN); // Преобразуем отсчёты АЦП в напряжение float sensorVoltage = (sensorADC * 5.0) / 1024.0; // Выводим в терминал показания с потенциометра // в отсчётах АЦП и в напряжении Serial.print(sensorADC); Serial.print(" ADC"); Serial.print("\t\t"); Serial.print(sensorVoltage); Serial.println(" Volts"); delay(1000); }
№32. Термометр
- Thermometer.ino
// Даём понятное имя пину с датчиком температуры TMP36 constexpr int TMP36_PIN = A0; void setup() { // Открываем соединения с последовательным портом Serial.begin(9600); } void loop() { // Считываем аналоговый сигнал с TMP36 int tmp36ADC = analogRead(TMP36_PIN); // Преобразуем отсчёты АЦП в напряжение float tmp36Voltage = (tmp36ADC * 5.0) / 1024.0; // Преобразуем напряжение с датчика в значение температуры float tmp36Temperature = (tmp36Voltage - 0.5) * 100; // Выводим результаты измерений в терминал Serial.print(tmp36ADC); Serial.print(" ADC"); Serial.print("\t\t"); Serial.print(tmp36Voltage); Serial.print(" Volts"); Serial.print("\t"); Serial.print(tmp36Temperature); Serial.println(" Degrees C"); delay(1000); }
№33. Функциональный термометр
- ThermometerFunc.ino
// Даём понятное имя пину с датчиком температуры TMP36 constexpr int TMP36_PIN = A0; float readTemperature() { // Считываем аналоговый сигнал с TMP36 int tmp36ADC = analogRead(TMP36_PIN); // Преобразуем отсчёты АЦП в напряжение float tmp36Voltage = (tmp36ADC * 5.0) / 1024.0; // Преобразуем напряжение с датчика в значение температуры float tmp36Temperature = (tmp36Voltage - 0.5) * 100.0; // Возвращаем результат return tmp36Temperature; } void setup() { // Открываем соединения с последовательным портом Serial.begin(9600); } void loop() { // Запрашиваем температуру у датчика TMP36 float tmp36Temp = readTemperature(); // Выводим результат измерений в терминал Serial.print(tmp36Temp); Serial.println(" Degrees C"); delay(1000); }
№34. Библиотечный термометр
- ThermometerLib.ino
// Библиотека для работы с датчиком температуры TMP36 #include <TroykaThermometer.h> // Даём понятное имя пину с датчиком температуры TMP36 constexpr int TMP36_PIN = A0; // Создаём объект для работы с датчиком температуры TMP36 // В параметре передаём пин, к которому подключен сенсор TroykaThermometer tmp36(TMP36_PIN); void setup() { // Открываем соединения с последовательным портом Serial.begin(9600); } void loop() { // Считываем данные с датчика TMP36 tmp36.read(); // Запрашиваем температуру у датчика TMP36 float tmp36Temp = tmp36.getTemperatureC(); // Выводим результат измерений в терминал Serial.print(tmp36Temp); Serial.println(" Degrees C"); delay(1000); }
№35. Заводим дисплей
- DisplayBegin.ino
// Библиотека для работы с текстовым дисплеем #include <LiquidCrystal.h> // Назначаем контакты для подключения дисплея // Используется 4-битный параллельный интерфейс constexpr int RS_PIN = 2; constexpr int EN_PIN = 3; constexpr int DB4_PIN = 4; constexpr int DB5_PIN = 5; constexpr int DB6_PIN = 6; constexpr int DB7_PIN = 7; // Создаём объект для работы с дисплеем // В параметрах передаём используемые пины LiquidCrystal lcd(RS_PIN, EN_PIN, DB4_PIN, DB5_PIN, DB6_PIN, DB7_PIN); void setup() { // Инициализируем дисплей на 2 строки по 16 символов lcd.begin(16, 2); // Устанавливаем курсор на первом знакоместе в первой строке lcd.setCursor(0, 0); // Печатем первую строку lcd.print("Hello, world!"); // Устанавливаем курсор на первом знакоместе во второй строке lcd.setCursor(0, 1); // Печатем вторую строку lcd.print("www.amperka.ru"); } void loop() { }
№36. Бегущая строка
- Ticker.ino
// Библиотека для работы с текстовым дисплеем #include <LiquidCrystal.h> // Назначаем контакты для подключения дисплея // Используется 4-битный параллельный интерфейс constexpr int RS_PIN = 2; constexpr int EN_PIN = 3; constexpr int DB4_PIN = 4; constexpr int DB5_PIN = 5; constexpr int DB6_PIN = 6; constexpr int DB7_PIN = 7; // Создаём объект для работы с дисплеем // В параметрах передаём используемые пины LiquidCrystal lcd(RS_PIN, EN_PIN, DB4_PIN, DB5_PIN, DB6_PIN, DB7_PIN); void setup() { // Инициализируем дисплей на 2 строки по 16 символов lcd.begin(16, 2); // Открываем соединения с последовательным портом Serial.begin(9600); } void loop() { // Создаём объект динамической строки String data; if (Serial.available()) { // Если пришли данные с последовательного порта // Считываем их в строку data = Serial.readString(); // Выводим данные из строки на дисплей в виде бегущей строки lcdPrintScroll(data); } } // Функция для вывода эффекта бегущей строки void lcdPrintScroll(String str) { // Очищаем дисплей lcd.clear(); // Устанавливаем курсор на шестнадцатом знакоместе во второй строке lcd.setCursor(16, 1); // Печатаем строку на дисплей lcd.print(str); // Сдвигаем видимую область дисплея влево на шестнадцать символов for(int i = 0; i < str.length() + 16; i++) { lcd.scrollDisplayLeft(); delay(200); } }
№37. Бегущая строка без правил
- TickerUnlimited.ino
// Библиотека для работы с текстовым дисплеем #include <LiquidCrystal.h> // Назначаем контакты для подключения дисплея // Используется 4-битный параллельный интерфейс constexpr int RS_PIN = 2; constexpr int EN_PIN = 3; constexpr int DB4_PIN = 4; constexpr int DB5_PIN = 5; constexpr int DB6_PIN = 6; constexpr int DB7_PIN = 7; // Создаём объект для работы с дисплеем // В параметрах передаём используемые пины LiquidCrystal lcd(RS_PIN, EN_PIN, DB4_PIN, DB5_PIN, DB6_PIN, DB7_PIN); void setup() { // Инициализируем дисплей на 2 строки по 16 символов lcd.begin(16, 2); // Открываем соединение с последовательным портом Serial.begin(9600); } void loop() { // Создаём объект динамической строки String data; if (Serial.available()) { // Если пришли данные с последовательного порта // Считываем их в строку data = Serial.readString(); // Выводим данные из строки на дисплей в виде бегущей строки lcdPrintScroll(data); } } // Функция для вывода эффекта бегущей строки void lcdPrintScroll(String str) { // Создаём строку и заполняем её 16-ю пробелами String strSpace; for (int i = 0; i <= 15; i++) { strSpace.concat(" "); } // Создаём строку и заполняем её: String str16; str = strSpace + str + strSpace; for (int i = 0; i < str.length(); i++) { str16 = str.substring(i, i + 16); lcd.setCursor(0, 1); lcd.print(str16); delay(200); } }
№38. I²C-сканер
- ScannerI2C.ino
// Библиотека для работы с I²C-устройствами #include <Wire.h> void setup() { // Инициализируем шину I²C Wire.begin(); // Открываем соединение с последовательным портом Serial.begin(9600); } void loop() { // Выводим сообщение о начале работы сканера Serial.println("I2C Scanner begin..."); int address; int countDevices = 0; // Сканируем адреса шины I²C от 0 до 127 for (address = 1; address < 127; address++ ) { Wire.beginTransmission(address); if (Wire.endTransmission() == 0) { // Если найдено устройство I²C, выводим его адрес в терминал Serial.println("I2C device found at address:"); Serial.print("DEC:"); Serial.println(address); Serial.print("HEX:0x"); Serial.println(address, HEX); countDevices++; } } // Если устройства I²C не обнаружены, выводим сообщение в терминал if (countDevices == 0) { Serial.println("No I2C devices found!"); } // Выводим сообщение о завершении работы сканера Serial.println("I2C Scanner end!\n"); delay(5000); }
№39. Заводим дисплей по I²C
- DisplayBeginI2C.ino
// Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Устанавливаем курсор на первом знакоместе в первой строке lcd.setCursor(0, 0); // Печатаем первую строку lcd.print("Hello, world"); // Устанавливаем курсор на первом знакоместе во второй строке lcd.setCursor(0, 1); // Печатаем вторую строку lcd.print("www.amperka.ru"); } void loop() { }
№40. Системы счисления
- NumberSystem.ino
// Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Даём понятное имя пину с потенциометром constexpr int POT_PIN = A0; // Переменная для хранения переводимого числа int value = 0; void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Печатаем данные на дисплей printDataLCD(value); } void loop() { // Считываем сигнал с потенциометра int valueNow = readPot(POT_PIN); // Если текущее показание потенциометра отличается от предыдущего if (value != valueNow) { // Обновляем показатель value = valueNow; // Печатаем данные на дисплей printDataLCD(value); } } // Функция для считывания аналогового сигнала с потенциометра // с преобразованием в диапазон значений от 0 до 16 int readPot(int pin) { int sensorADC = 0; int sensorValue = 0; // Фильтруем данные а затем делим результат for (int i = 0; i < 32; i++) { sensorADC += analogRead(pin); delay(10); } sensorADC = sensorADC / 32; // Преобразуем выходной сигнал с потенциометра в диапазон значений от 0 до 16 sensorValue = map(sensorADC, 0, 1023, 0, 15); // Возвращаем результат return sensorValue; } // Функция для вывода числа на дисплей в различных системах счисления void printDataLCD(int numberNow) { lcd.clear(); lcd.setCursor(0, 0); lcd.print("DEC:"); lcd.print(numberNow, DEC); lcd.setCursor(8, 0); lcd.print("BIN:"); lcd.print(numberNow, BIN); lcd.setCursor(0, 1); lcd.print("OCT:"); lcd.print(numberNow, OCT); lcd.setCursor(8, 1); lcd.print("HEX:"); lcd.print(numberNow, HEX); }
№41. Термометр на ЖК
- ThermometerLCD.ino
// Библиотека для работы с датчиком температуры TMP36 #include <TroykaThermometer.h> // Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Даём понятное имя пину с датчиком температуры TMP36 constexpr int TMP36_PIN = A0; // Создаём объект для работы с датчиком температуры TMP36 // В параметре передаём пин, к которому подключен сенсор TroykaThermometer tmp36(TMP36_PIN); // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Устанавливаем курсор на первом знакоместе в первой строке lcd.setCursor(0, 0); // Выводим строку на дисплей lcd.print("<Thermometer>"); } void loop() { // Считываем данные с датчика TMP36 tmp36.read(); // Устанавливаем курсор на первом знакоместе во второй строке lcd.setCursor(0, 1); // Выводим данные температуры на дисплей lcd.print("T = "); lcd.print(tmp36.getTemperatureC()); lcd.print(" C"); delay(1000); }
№42. Настольные часы
- ClockLCD.ino
// Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Библиотека для работы с кнопкой #include <TroykaButton.h> // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Создаём объекты кнопок для управления TroykaButton buttonMinute(2); TroykaButton buttonHour(3); int hours, minutes, seconds; long millisLastTime = 0; void setup() { // Инициализируем кнопки buttonMinute.begin(); buttonHour.begin(); // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Устанавливаем курсор на первом знакоместе в первой строке lcd.setCursor(0, 0); // Выводим строку на дисплей lcd.print("<Amperka Clock>"); setTime(12, 45, 30); } void loop() { // Обновляем время посекундно updateTime(); // Обновляем время через кнопки updateTimeButtons(); // Преобразуем время wrapTime(); // Выводим на дисплее время в формате «чч:мм:сс» printTimeLCD(); } // Функция для установки времени void setTime(int h, int m, int s) { hours = h; minutes = m; seconds = s; } // Функция для отсчёта времени посекундно void updateTime() { long millisNowTime = millis(); if (millisNowTime - millisLastTime > 1000) { millisLastTime = millisNowTime; seconds++; } } // Функция для обновления времени через кнопки void updateTimeButtons() { buttonHour.read(); buttonMinute.read(); if (buttonHour.isClick()) { hours++; } if (buttonMinute.isClick()) { minutes++; } } // Функция для форматирования времени // При достижении лимита в 60 секунд, 60 минут и 24 часа отсчёт начинается заново void wrapTime() { if (seconds >= 60) { minutes++; seconds -= 60; } if (minutes >= 60) { hours++; minutes -= 60; } if (hours >= 24) { hours = 0; } } // Функция для вывода на дисплее времени в формате «чч:мм:сс» void printTimeLCD() { lcd.setCursor(0, 1); lcd.print("TIME: "); printTwoDigits(hours); lcd.print(":"); printTwoDigits(minutes); lcd.print(":"); printTwoDigits(seconds); } // Функция для вывода на дисплей двух цифр: // на первом знакоместе 0, если цифра меньше 10 void printTwoDigits(int digit) { if (digit < 10) { lcd.print("0"); } lcd.print(digit); }
№43. Часы с термометром
- ClockThermometerLCD.ino
// Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Библиотека для работы с кнопкой #include <TroykaButton.h> // Библиотека для работы с датчиком температуры TMP36 #include <TroykaThermometer.h> // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Создаём объекты кнопок для управления TroykaButton buttonMinute(2); TroykaButton buttonHour(3); // Создаём объект для работы с датчиком температуры TMP36 // В параметре передаём пин, к которому подключен сенсор TroykaThermometer tmp36(A0); // Переменные для хранения часов, минут и секунд int hours, minutes, seconds; // Переменные для хранения температуры float temparature; long millisLastTime = 0; long millisLastTemp = 0; void setup() { // Инициализируем кнопки buttonMinute.begin(); buttonHour.begin(); // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Устанавливаем курсор на первом знакоместе в первой строке lcd.setCursor(0, 0); // Устанавливаем тестовое время 12:45:30 setTime(12, 45, 30); } void loop() { // Обновляем время посекундно updateTime(); // Обновляем время через кнопки updateTimeButtons(); // Обновляем температуру updateTemperature(); // Преобразуем время wrapTime(); // Выводим на дисплее время в формате «чч:мм:сс» printTimeLCD(); } // Функция для установки времени void setTime(int h, int m, int s) { hours = h; minutes = m; seconds = s; } // Функция для отсчёта времени void updateTime() { long millisNowTime = millis(); if (millisNowTime - millisLastTime > 1000) { millisLastTime = millisNowTime; seconds++; } } // Функция для обновления температуры void updateTemperature() { long millisNowTemp = millis(); if (millisNowTemp - millisLastTemp > 500) { millisLastTemp = millisNowTemp; tmp36.read(); temparature = tmp36.getTemperatureC(); } } // Функция для обновления времени через кнопки void updateTimeButtons() { buttonHour.read(); buttonMinute.read(); if (buttonHour.isClick()) { hours++; } if (buttonMinute.isClick()) { minutes++; } } // Функция для форматирования времени // При достижении лимита в 60 секунд, 60 минут и 24 часа отсчёт начинается заново void wrapTime() { if (seconds >= 60) { minutes++; seconds -= 60; } if (minutes >= 60) { hours++; minutes -= 60; } if (hours >= 24) { hours = 0; } } // Функция для вывода на дисплее времени в формате «чч:мм:сс» и температуры void printDataLCD() { lcd.setCursor(0, 0); lcd.print("TIME: "); printTwoDigits(hours); lcd.print(":"); printTwoDigits(minutes); lcd.print(":"); printTwoDigits(seconds); lcd.setCursor(0, 1); lcd.print("TEMP: "); lcd.print(temparature); lcd.print(" C"); } // Функция для вывода на дисплей двух цифр: // на первом знакоместе 0, если цифра меньше 10 void printTwoDigits(int digit) { if (digit < 10) { lcd.print("0"); } lcd.print(digit); }
№44. Карманный девайс
- ClockThermometerLCD.ino
// Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Библиотека для работы с кнопкой #include <TroykaButton.h> // Библиотека для работы с датчиком температуры TMP36 #include <TroykaThermometer.h> // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Создаём объекты кнопок для управления TroykaButton buttonMinute(2); TroykaButton buttonHour(3); // Создаём объект для работы с датчиком температуры TMP36 // В параметре передаём пин, к которому подключен сенсор TroykaThermometer tmp36(A0); // Переменные для хранения часов, минут и секунд int hours, minutes, seconds; // Переменные для хранения температуры float temparature; long millisLastTime = 0; long millisLastTemp = 0; void setup() { // Инициализируем кнопки buttonMinute.begin(); buttonHour.begin(); // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Устанавливаем курсор на первом знакоместе в первой строке lcd.setCursor(0, 0); // Устанавливаем тестовое время 12:45:30 setTime(12, 45, 30); } void loop() { // Обновляем время посекундно updateTime(); // Обновляем время через кнопки updateTimeButtons(); // Обновляем температуру updateTemperature(); // Преобразуем время wrapTime(); // Выводим на дисплее время в формате «чч:мм:сс» printTimeLCD(); } // Функция для установки времени void setTime(int h, int m, int s) { hours = h; minutes = m; seconds = s; } // Функция для отсчёта времени void updateTime() { long millisNowTime = millis(); if (millisNowTime - millisLastTime > 1000) { millisLastTime = millisNowTime; seconds++; } } // Функция для обновления температуры void updateTemperature() { long millisNowTemp = millis(); if (millisNowTemp - millisLastTemp > 500) { millisLastTemp = millisNowTemp; tmp36.read(); temparature = tmp36.getTemperatureC(); } } // Функция для обновления времени через кнопки void updateTimeButtons() { buttonHour.read(); buttonMinute.read(); if (buttonHour.isClick()) { hours++; } if (buttonMinute.isClick()) { minutes++; } } // Функция для форматирования времени // При достижении лимита в 60 секунд, 60 минут и 24 часа отсчёт начинается заново void wrapTime() { if (seconds >= 60) { minutes++; seconds -= 60; } if (minutes >= 60) { hours++; minutes -= 60; } if (hours >= 24) { hours = 0; } } // Функция для вывода на дисплее времени в формате «чч:мм:сс» и температуры void printDataLCD() { lcd.setCursor(0, 0); lcd.print("TIME: "); printTwoDigits(hours); lcd.print(":"); printTwoDigits(minutes); lcd.print(":"); printTwoDigits(seconds); lcd.setCursor(0, 1); lcd.print("TEMP: "); lcd.print(temparature); lcd.print(" C"); } // Функция для вывода на дисплей двух цифр: // на первом знакоместе 0, если цифра меньше 10 void printTwoDigits(int digit) { if (digit < 10) { lcd.print("0"); } lcd.print(digit); }
№45. Биты и байты
- BitsBytes.ino
// Библиотека для работы с кнопкой #include <TroykaButton.h> // Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Создаём переменную для хранения тестового числа byte value = 127; // Максимальныое знакоместо на дисплее constexpr int COL_MAX = 11; // Индекс положения курсора int digitIndex = 0; // Создаём объекты кнопок для управления TroykaButton buttonLeft(3); TroykaButton buttonRight(2); TroykaButton buttonSet(A3); void setup() { // Инициализируем кнопки buttonLeft.begin(); buttonRight.begin(); buttonSet.begin(); // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Отображаем курсор lcd.cursor(); // Включаем мигание курсора lcd.blink(); // Печатаем данные на дисплей printDataLCD(); lcd.setCursor(COL_MAX - digitIndex, 0); } void loop() { // Считываем состояние кнопок buttonLeft.read(); buttonRight.read(); buttonSet.read(); // Если нажата кнопка «вправо», смещаем курсор на одно знакоместо «вправо» if (buttonRight.isClick() && digitIndex > 0) { digitIndex--; lcd.setCursor(COL_MAX - digitIndex, 0); } // Если нажата кнопка «влево», смещаем курсор на одно знакоместо «влево» if (buttonLeft.isClick() && digitIndex < 7) { digitIndex++; lcd.setCursor(COL_MAX - digitIndex, 0); } // Если нажата кнопка «Установка» // «переворачиваем» значение выбранного курсором бита if (buttonSet.isClick()) { bitInvert(digitIndex); printDataLCD(); lcd.setCursor(COL_MAX - digitIndex, 0); } } // Функция для вывода числа на дисплей в системах счисления BIN и DEC void printDataLCD() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("BIN:"); printByteLCD(); lcd.setCursor(0, 1); lcd.print("DEC:"); lcd.print(value); } // Функция для вывода числа по байтам void printByteLCD() { for (int i = 7; i >= 0; i--) { lcd.print(bitRead(value, i)); } } // Функция для инвертирования значение бита void bitInvert(int bitNumber) { bool bitValue = bitRead(value, bitNumber); bitValue = !bitValue; bitWrite(value, bitNumber, bitValue); }
№46. Тайны знакогенератора
- СharacterList.ino
// Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); } void loop() { // Устанавливаем курсор на третьем знакоместе в первой строке lcd.setCursor(2, 0); lcd.print("ASCII Table"); // Выводим в цикле все символы знакогенератора с кодом от 0 до 255 for (int i = 0 ; i <= 255 ; i++) { lcd.setCursor(0, 1); if (i < 16) { lcd.print("0"); } lcd.print(i, HEX); lcd.print(" = "); lcd.write(i); delay(500); } lcd.clear(); }
№47. Я календарь переключу…
- СharacterSwitchPage.ino
// Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Константа с адресом нулевой страницы знакогенератора constexpr int LCD_PAGE_0 = 0b101000; // Константа с адресом первой страницы знакогенератора constexpr int LCD_PAGE_1 = 0b101010; void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Устанавливаем курсор на четвёртом знакоместе в первой строке lcd.setCursor(3, 0); // Выводим строку на дисплей lcd.print("Switch Page"); // Устанавливаем курсор на шестом знакоместе во второй строке lcd.setCursor(5, 1); // Выводим пять символов из таблицы знакогенератора lcd.print("\x9f\x9e\x9d\x9c\x9b"); } void loop() { // Переключаем две страницы знакогенератора с интервалом каждую секунду lcd.command(LCD_PAGE_0); delay(1000); lcd.command(LCD_PAGE_1); delay(1000); }
№48. Индикатор заряда
- ChargeIndicator.ino
// Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Даём понятное имя пину, который подключен // к батарейному отсеку через резистивный делитель constexpr int VOLTAGE_PIN = A3; // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Создаём константы резисторного делителя: // номиналы резисторов и коэффициент делителя constexpr float R1 = 10; constexpr float R2 = 1; constexpr float k = R2/(R2+R1); // Создаём константы с адресами иконок заряда батареи из таблицы знакогенератора constexpr int LCD_ICON_BATTERY_1 = 0x9B; constexpr int LCD_ICON_BATTERY_2 = 0x9C; constexpr int LCD_ICON_BATTERY_3 = 0x9D; constexpr int LCD_ICON_BATTERY_4 = 0x9E; constexpr int LCD_ICON_BATTERY_5 = 0x9F; void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Устанавливаем курсор на первом знакоместе в первой строке lcd.setCursor(0, 0); // Выводим строку на дисплей lcd.print("Charge indicator"); } void loop() { // Измеряем напряжение на батарейном отсеке float batVoltage = readVoltage(); // Устанавливаем курсор на первом знакоместе в второй строке lcd.setCursor(0, 1); // Выводим напряжение на батарейном отсеке lcd.print(batVoltage); lcd.print(" V / "); // Выводим иконку, которая соответствует остаточному уровню заряда батареи if (batVoltage >= 9) { lcd.write(LCD_ICON_BATTERY_1); } else if (batVoltage >= 8) { lcd.write(LCD_ICON_BATTERY_2); } else if (batVoltage >= 7) { lcd.write(LCD_ICON_BATTERY_3); } else if (batVoltage >= 6) { lcd.write(LCD_ICON_BATTERY_4); } else { lcd.write(LCD_ICON_BATTERY_5); } delay(1000); } // Функция для считывания напряжения c батарейного отсека float readVoltage() { // Считываем аналоговый сигнал с резисторного делителя int batADC = analogRead(VOLTAGE_PIN); // Преобразуем отсчёты АЦП в напряжение с учётом коэффициента делителя float batVoltage = ((batADC * 5.0) / 1024.0) / k; return batVoltage; }
№49. Люксметр
Программный код №1
- Luxmeter-v1.ino
// Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Библиотека для работы с фоторезистором #include <TroykaLight.h> // Даём понятное имя пину с фоторезистором constexpr int LDR_PIN = A3; // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Создаём объект для работы с фоторезистором TroykaLight ldr(LDR_PIN); void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Устанавливаем курсор на первом знакоместе в первой строке lcd.setCursor(0, 0); // Выводим строку на дисплей lcd.print("<Luxmeter>"); } void loop() { // Считываем данные с фоторезистора ldr.read(); // Запрашиваем освещенность у фоторезистора long ldrLight = ldr.getLightLux(); // Устанавливаем курсор на первом знакоместе во второй строке lcd.setCursor(0, 1); // Выводим показания освещенности на дисплей lcd.print("L = "); lcd.print(ldrLight); lcd.print(" Lx"); delay(1000); }
Программный код №2
- Luxmeter-v2.ino
// Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Библиотека для работы с фоторезистором #include <TroykaLight.h> // Даём понятное имя пину с фоторезистором constexpr int LDR_PIN = A3; // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Создаём объект для работы с фоторезистором TroykaLight ldr(LDR_PIN); void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Устанавливаем курсор на первом знакоместе в первой строке lcd.setCursor(0, 0); // Выводим строку на дисплей lcd.print("<Luxmeter>"); } void loop() { // Считываем данные с фоторезистора ldr.read(); // Запрашиваем освещенность у фоторезистора long ldrLight = ldr.getLightLux(); // Создаём динамическую строку класса String, которая содержит шесть символов: // показание освещённости с ведущими нулями String ldrLightStr = longToStr(ldrLight, 6); // Устанавливаем курсор на первом знакоместе во второй строке lcd.setCursor(0, 1); // Выводим показания освещенности на дисплей lcd.print("L = "); lcd.print(ldrLightStr); lcd.print(" Lx"); delay(1000); } // Функция для преобразования переменной типа long в переменную String, // оставляя при этом заданное количество знаков. String longToStr(long value, int countDigits) { String strValue; int lenValue = String(value).length(); for (int i = countDigits; i > lenValue; i--) { strValue += "0"; } strValue += String(value); return strValue; }
№50. Терменвокс
- Theremin.ino
// Даём понятное имя пину с фоторезистором constexpr int LDR_PIN = A3; // Даём понятное имя пину с пищалкой constexpr int BUZZER_PIN = 7; void setup() { // Настраиваем пин с фоторезистором в режим входа pinMode(LDR_PIN, INPUT); // Настраиваем пин с пищалкой в режим выхода pinMode(BUZZER_PIN, OUTPUT); } void loop() { // Считываем аналоговый сигнал с фоторезистора int ldrLight = analogRead(LDR_PIN); // Преобразуем значение с фоторезистора в частотный диапазон для пищалки int frequency = map(ldrLight, 0, 1023, 500, 5000); // Заставляем пин с пищалкой звучать на высчитанной частоте tone(BUZZER_PIN, frequency); }
№51. Умная подсветка
- SmartBacklight.ino
// Библиотека для работы с фоторезистором #include <TroykaLight.h> // Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Даём понятное имя пину с потенциометром constexpr int POT_PIN = A2; // Даём понятное имя пину с фоторезистором constexpr int LDR_PIN = A3; // Создаём объект для работы с фоторезистором TroykaLight ldr(LDR_PIN); // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); } void loop() { // Считываем аналоговый сигнал с потенциометра int potADC = analogRead(POT_PIN); // Преобразуем значение с потенциометра в диапазон освещённости // для порог срабатывания подсветки long limitLight = map(potADC, 0, 1023, 0, 100000); // Считываем данные с фоторезистора ldr.read(); // Запрашиваем освещенность у фоторезистора long ldrLight = ldr.getLightLux(); // Выводим на дисплей // порог освещенности срабатывания подсветки и текущую освещенность printDataLCD(limitLight, ldrLight); if (ldrLight < limitLight) { // Если текущее показание фоторезистора ниже порога, включаем подсветку lcd.backlight(); } else if (ldrLight > limitLight) { // Если текущее показание фоторезистора выше порога, выключаем подсветку lcd.noBacklight(); } delay(1000); } // Функция для вывода на дисплей // порога освещенности срабатывания подсветки и текущую освещенность void printDataLCD(long limitLight, long ldrLight) { String limitLightStr = longToStr(limitLight, 6); String ldrLightStr = longToStr(ldrLight, 6); lcd.setCursor(0, 0); lcd.print("P = "); lcd.print(limitLightStr); lcd.print(" Lx"); lcd.setCursor(0, 1); lcd.print("L = "); lcd.print(ldrLightStr); lcd.print(" Lx"); } // Функция для преобразования переменной типа long в переменную String, // оставляя при этом заданное количество знаков. String longToStr(long value, int countDigits) { String strValue; int lenValue = String(value).length(); for (int i = countDigits; i > lenValue; i--) { strValue += "0"; } strValue += String(value); return strValue; }
№52. Умная подсветка без глюков
- SmartBacklightHyst.ino
// Библиотека для работы с фоторезистором #include <TroykaLight.h> // Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Даём понятное имя пину с потенциометром constexpr int POT_PIN = A2; // Даём понятное имя пину с фоторезистором constexpr int LDR_PIN = A3; // Создаём объект для работы с фоторезистором TroykaLight ldr(LDR_PIN); // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); } void loop() { // Считываем аналоговый сигнал с потенциометра int potADC = analogRead(POT_PIN); // Преобразуем значение с потенциометра в диапазон освещённости // для порог срабатывания подсветки long limitLight = map(potADC, 0, 1023, 0, 100000); // Создаём переменную для хранения гистерезиса int hyster = limitLight * 0.1 + 50; // Считываем данные с фоторезистора ldr.read(); // Запрашиваем освещенность у фоторезистора long ldrLight = ldr.getLightLux(); // Выводим на дисплей // порог освещенности срабатывания подсветки и текущую освещенность printDataLCD(limitLight, ldrLight); if (ldrLight < limitLight - hyster) { // Если текущее показание фоторезистора ниже нижней границы гистерезиса, // включаем подсветку lcd.backlight(); } else if (ldrLight > limitLight + hyster) { // Если текущее показание фоторезистора выше верхней границы гистерезиса, // выключаем подсветку lcd.noBacklight(); } delay(1000); } // Функция для вывода на дисплей // порога освещенности срабатывания подсветки и текущую освещенность void printDataLCD(long limitLight, long ldrLight) { String limitLightStr = longToStr(limitLight, 6); String ldrLightStr = longToStr(ldrLight, 6); lcd.setCursor(0, 0); lcd.print("P = "); lcd.print(limitLightStr); lcd.print(" Lx"); lcd.setCursor(0, 1); lcd.print("L = "); lcd.print(ldrLightStr); lcd.print(" Lx"); } // Функция для преобразования переменной типа long в переменную String, // оставляя при этом заданное количество знаков. String longToStr(long value, int countDigits) { String strValue; int lenValue = String(value).length(); for (int i = countDigits; i > lenValue; i--) { strValue += "0"; } strValue += String(value); return strValue; }
№53. Оптопрерыватель
- SecuritySystem.ino
// Библиотека для работы с фоторезистором #include <TroykaLight.h> // Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Даём понятное имя пину с потенциометром constexpr int POT_PIN = A2; // Даём понятное имя пину с фоторезистором constexpr int LDR_PIN = A3; // Создаём объект для работы с фоторезистором TroykaLight ldr(LDR_PIN); // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); long ldrLight; long lightNormal; int hyster; // Переменная для хранения кол-ва проходящих нарушителей int passCount = 0; void setup() { // Настраиваем пин с пищалкой в режим выхода pinMode(BUZZER_PIN, OUTPUT); // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Устанавливаем курсор на первом знакоместе в первой строке lcd.setCursor(0, 0); // Выводим строку на дисплей lcd.print("Security system"); // Устанавливаем курсор на первом знакоместе во второй строке lcd.setCursor(0, 1); // Выводим строку на дисплей lcd.print("Pass count: "); // Печатаем кол-во проходящих нарушителей lcd.print(passCount); // Считываем данные с фоторезистора ldr.read(); // Запрашиваем освещенность у фоторезистора ldrLight = ldr.getLightLux(); // Корректируем значение штатного освещения и гистерезиса updateLightNormal(ldrLight); } void loop() { // Считываем данные с фоторезистора ldr.read(); // Запрашиваем освещенность у фоторезистора ldrLight = ldr.getLightLux(); if (ldrLight < lightNormal - hyster) { // Если уровень освещённости падает ниже нормального с учётом гистерезиса lcd.setCursor(12, 1); // Обновляем кол-во нарушителей lcd.print(++passCount); // Пищим зуммеров playMelodyAlarm(); } else { // Корректируем значение штатного освещения и гистерезиса updateLightNormal(ldrLight); } delay(100); } // Функция для корректировки значений штатного освещения и гистерезиса void updateLightNormal(long ldrLight) { lightNormal = ldrLight; hyster = lightNormal * 0.1 + 50; } // Функция для воспроизведения сигнала тревоги void playMelodyAlarm() { for (int i = 0; i <= 2; i++) { tone(BUZZER_PIN, 200, 300); delay(200); tone(BUZZER_PIN, 120, 300); delay(200); } }
№54. Заметки мейкера
- MakerNotes.ino
// Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Константа для хранения паузы в миллисекундах constexpr int PAUSE = 5000; // Создаём три строки в виде массива символов char str1[] = "1. Read the manual carefully."; char str2[] = "2. Wire all breadboard contents."; char str3[] = "3. Flash the Iskra Nano board."; void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); } void loop() { // Поочерёдно выводим строки на дисплей // с автопереносом символов на следующую строку printLCDAutoLine(str1); delay(PAUSE); printLCDAutoLine(str2); delay(PAUSE); printLCDAutoLine(str3); delay(PAUSE); } // Функция для вывода строки на дисплей // с автопереносом символов на следующую строку void printLCDAutoLine(char *str) { lcd.clear(); lcd.setCursor(0, 0); for (int i = 0; i < 16 && str[i] != '\0'; i++) { lcd.print(str[i]); } lcd.setCursor(0, 1); for (int i = 16; i < 32 && str[i] != '\0'; i++) { lcd.print(str[i]); } }
№55. Кастомные символы
- CustomSymbols.ino
// Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Создаём символ динозаврика в двоичной системе BIN byte dino[8] = { 0b00000, 0b00111, 0b00111, 0b10110, 0b11111, 0b01010, 0b01010, 0b00000 }; // Создаём символ кактуса в двоичной системе BIN byte cactus[8] = { 0b00100, 0b00101, 0b10101, 0b10101, 0b10111, 0b11100, 0b00100, 0b00000 }; // Создаём символ сердца в двоичной системе BIN byte heart[8] = { 0b00000, 0b01010, 0b11111, 0b11111, 0b11111, 0b01110, 0b00100, 0b00000 }; // Константы для хранения адресов символов из таблицы знакогенератора constexpr byte LCD_ICON_DINO = 0x00; constexpr byte LCD_ICON_CACTUS = 0x01; constexpr byte LCD_ICON_HEART = 0x02; void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Добавляем собственный символы в ячейки знакогенератора lcd.createChar(LCD_ICON_DINO, dino); lcd.createChar(LCD_ICON_CACTUS, cactus); lcd.createChar(LCD_ICON_HEART, heart); // Выводим на дисплей строки и созданные символы lcd.setCursor(0, 0); lcd.print("I "); lcd.write(LCD_ICON_HEART); lcd.print(" Arduino! "); lcd.setCursor(0, 1); lcd.write(LCD_ICON_DINO); lcd.setCursor(5, 1); lcd.write(LCD_ICON_CACTUS); lcd.setCursor(8, 1); lcd.write(LCD_ICON_CACTUS); lcd.setCursor(13, 1); lcd.print("\x02"); } void loop() { }
№56. Бегущий динозаврик
- RunningDino.ino
// Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Библиотека для работы с кнопкой #include <TroykaButton.h> // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Даём понятное имя пину с пищалкой constexpr int BUZZER_PIN = 7; // Создаём звуковой шрифт из нот со своими частотами constexpr int NOTE_B3 = 247; constexpr int NOTE_C4 = 262; constexpr int NOTE_D4 = 294; constexpr int NOTE_E4 = 330; constexpr int NOTE_F4 = 349; constexpr int NOTE_G4 = 392; constexpr int NOTE_A4 = 440; constexpr int NOTE_B4 = 494; constexpr int NOTE_C5 = 523; // Создаём символ динозаврика в двоичной системе BIN byte dino[8] = { 0b00000, 0b00111, 0b00111, 0b10110, 0b11111, 0b01010, 0b01010, 0b00000 }; // Создаём символ кактуса в двоичной системе BIN byte cactus[8] = { 0b00100, 0b00101, 0b10101, 0b10101, 0b10111, 0b11100, 0b00100, 0b00000 }; // Назначаем константы с адресами кастомных символов в таблице знакогенератора constexpr byte LCD_ICON_DINO = 0x00; constexpr byte LCD_ICON_CACTUS = 0x01; // Назначаем константы с адресами штатных символов в таблице знакогенератора constexpr byte LCD_ICON_BLOCK = 0xFF; constexpr byte LCD_ICON_EMPTY = 0x20; // Переменные для подсчёта набранных очков и хранения рекордов int score = 0; int bestScore = 0; // Переменная для блокировки очков bool stateFreezeScore = false; // Массив игровой области // Нижняя строка дисплея char gameArea[16]; // Ячейка дисплея // динозаврик в прыжке или нет char gameDinoJump; long millisLastTime = 0; long millisLastJump = 0; // Время зависания динозавра в воздухе int jumpInterval = 50; // Скорость прокрутки дисплея int speedScroller = 300; // Создаём перечисления состояний игры enum { GAME_BEGIN, GAME_PLAY, GAME_OVER } stateGame; // Создаём объект для работы с кнопкой TroykaButton button(5); void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Добавляем собственные символы в ячейки знакогенератора lcd.createChar(LCD_ICON_DINO, dino); lcd.createChar(LCD_ICON_CACTUS, cactus); // Инициализируем кнопку button.begin(); // Задаём зерно генерации случайных чисел randomSeed(analogRead(A0)); // Устанавливаем режим «Начало игры» stateGame = GAME_BEGIN; } void loop() { // Если установлен режим «Начало игры» if (stateGame == GAME_BEGIN) { // Обнуляем текущий результат score = 0; // Очищаем массив игровой зоны for (int i = 0; i <= 15; i++) { gameArea[i] = LCD_ICON_EMPTY; } // Выводим начальную заставку на дисплей printGameBegin(); // Выводим приветственную музыку melodyGameBegin(); // Ожидаем нажатие кнопки для старта while (!button.isClick()) { button.read(); } // Устанавливаем режим «Процесс игры» stateGame = GAME_PLAY; // Очищаем дисплей lcd.clear(); delay(1000); } // Если установлен режим «Процесс игры» if (stateGame == GAME_PLAY) { // и функция playGame выдаёт ложное значение, // переходим в режим «Конец игры». if (playGame() == false) { stateGame = GAME_OVER; } } // Если установлен режим «Конец игры» if (stateGame == GAME_OVER) { // Выводим конечную заставку на дисплей printGameOver(); // Выводим завершающую музыку melodyGameOver(); // Ждём нажатия кнопки для повторной игры while (!button.isClick()) { button.read(); } // Заново переходим в режим «Начало игры» stateGame = GAME_BEGIN; delay(1000); } } // Функция вывода начальной заставки на экран void printGameBegin() { // Очищаем дисплей lcd.clear(); lcd.setCursor(4, 0); lcd.print("Dino Game"); lcd.setCursor(3, 1); lcd.print("Press Start"); // Выводим два барьера по бокам lcd.setCursor(0, 1); lcd.write(LCD_ICON_BLOCK); lcd.setCursor(15, 1); lcd.write(LCD_ICON_BLOCK); // Выводим динозаврика lcd.setCursor(1, 1); lcd.write(LCD_ICON_DINO); } // Функция вывода конечной заставки на экран void printGameOver() { // Выводим сообщение «Игра окончена» lcd.setCursor(4, 1); lcd.print("Game Over"); delay(1000); lcd.clear(); // Выводим набранный счёт lcd.setCursor(4, 0); lcd.print("Score: "); lcd.print(score); // Если счёт превзошёл предыдущий рекорд, обновляем значение рекорда if (score > bestScore) { bestScore = score; } // Выводим лучший результат игры за все попытки lcd.setCursor(5, 1); lcd.print("Best: "); lcd.print(bestScore); } // Функция обработки игрового процесса bool playGame() { button.read(); // Запоминаем текущее время unsigned long millisNowTime = millis(); // Если прошёл заданный интервал if (millisNowTime - millisLastTime >= speedScroller) { millisLastTime = millisNowTime; // Генерируем случайно число от 0 до 10 int randomNumber = random(0, 10); // Если выпало число 0 if (randomNumber == 0) { // Выводим препятствие gameArea[15] = LCD_ICON_CACTUS; } else { // В остальных случаях выводим пустоту gameArea[15] = LCD_ICON_EMPTY; } // Сдвигаем игровую область дисплея на шаг влево for (int i = 0; i <= 15; i++) { gameArea[i] = gameArea[i + 1]; } // Если блокировка очков выключена, увеличиваем очки if (!stateFreezeScore) { score++; } } // Выводим барьеры gameArea[0] = LCD_ICON_BLOCK; gameArea[15] = LCD_ICON_BLOCK; // Если нажата кнопка if (button.isPressed()) { tone(BUZZER_PIN, 1000, 100); // Включаем динозаврика в прыжке gameDinoJump = LCD_ICON_DINO; // Включаем заморозку очков stateFreezeScore = true; // Запоминаем текущее время прыжка millisLastJump = millis(); } // Если время прыжка истекло и внизу нет препятствия if (millis() - millisLastJump >= jumpInterval) { if (gameArea[1] == LCD_ICON_EMPTY || gameArea[1] == LCD_ICON_DINO) { // Возвращаем динозаврика на землю gameArea[1] = LCD_ICON_DINO; gameDinoJump = LCD_ICON_EMPTY; // Выключаем заморозку очков stateFreezeScore = false; } else { return false; } } // Обновляем содержимое на дисплее updateDataLCD(); return true; } // Функция вывода данных на дисплей void updateDataLCD() { // Печатаем игровое поле for (int i = 0; i <= 15; i++) { lcd.setCursor(i, 1); lcd.write(gameArea[i]); } // Выводим ячейку для прыжка и текущее количество очков lcd.setCursor(1, 0); lcd.write(gameDinoJump); lcd.setCursor(4, 0); lcd.print("Score: "); lcd.print(score); } // Функция вступительной мелодии void melodyGameBegin() { tone(BUZZER_PIN, NOTE_E4, 100); delay(150); tone(BUZZER_PIN, NOTE_E4,100); delay(300); tone(BUZZER_PIN, NOTE_E4,100); delay(300); tone(BUZZER_PIN, NOTE_C4,100); delay(100); tone(BUZZER_PIN, NOTE_E4,100); delay(300); tone(BUZZER_PIN, NOTE_G4, 100); delay(550); tone(BUZZER_PIN, NOTE_C5, 100); delay(575); } // Функция завершающей мелодии void melodyGameOver() { for (int i = 0; i <= 2; i++) { tone(BUZZER_PIN, NOTE_G4, 300); delay(200); tone(BUZZER_PIN, NOTE_C4, 300); delay(200); } }
№57. Светодиодная лента своими руками
№58. Сдаём вождение по току
- BlinkBJT.ino
// Даём понятное имя пину с транзистором, // который управляет светодиодной лентой constexpr int PIN_TRANSISTOR_BJT = 2; void setup() { // Настраиваем пин с транзистором в режим выхода pinMode(PIN_TRANSISTOR_BJT, OUTPUT); } void loop() { // Зажигаем ленту digitalWrite(PIN_TRANSISTOR_BJT, HIGH); // Ждём 200 мс delay(200); // Гасим ленту digitalWrite(PIN_TRANSISTOR_BJT, LOW); // Ждём 800 мс delay(800); }
№59. Волшебное прикосновение
№60. Сдаём вождение по напряжению
- BlinkMOSFET.ino
// Даём понятное имя пину с транзистором, // который управляет светодиодной лентой constexpr int PIN_MOSFET = 2; void setup() { // Настраиваем пин с транзистором в режим выхода pinMode(PIN_MOSFET, OUTPUT); } void loop() { // Зажигаем ленту digitalWrite(PIN_MOSFET, HIGH); // Ждём 200 мс delay(200); // Гасим ленту digitalWrite(PIN_MOSFET, LOW); // Ждём 800 мс delay(800); }
№61. Саймон говорит
- SimonSays.ino
// Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Создаём звуковой шрифт из нот со своими частотами constexpr int NOTE_C3 = 131; constexpr int NOTE_D3 = 147; constexpr int NOTE_E3 = 165; constexpr int NOTE_F3 = 175; constexpr int NOTE_G3 = 196; constexpr int NOTE_A3 = 220; constexpr int NOTE_B3 = 247; constexpr int NOTE_C4 = 262; constexpr int NOTE_D4 = 294; constexpr int NOTE_E4 = 330; constexpr int NOTE_F4 = 349; constexpr int NOTE_G4 = 392; constexpr int NOTE_A4 = 440; constexpr int NOTE_B4 = 494; constexpr int NOTE_C5 = 523; // Даём понятное имя пину с пищалкой constexpr int BUZZER_PIN = 2; // Даём понятные имена пинам со светодиодами constexpr int LED_1_PIN = 3; constexpr int LED_2_PIN = 4; constexpr int LED_3_PIN = 5; constexpr int LED_4_PIN = 6; // Даём понятные имена пинам с кнопками constexpr int BUTTON_1_PIN = A3; constexpr int BUTTON_2_PIN = A2; constexpr int BUTTON_3_PIN = A1; constexpr int BUTTON_4_PIN = A0; // Массив нот для мелодии «Начало игры» constexpr int NOTE_GAME_BEGIN[] = { NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_F4, NOTE_G4 }; // Массив длительности нот для мелодии «Начало игры constexpr int NOTE_DURATION_GAME_BEGIN[] = { 8, 4, 8, 4, 8, 4, 8, 8, 8, 8, 4, 8, 2 }; // Количество нот в мелодии «Начало игры constexpr int NOTE_COUNT_GAME_BEGIN = 13; // Массив нот для мелодии «Выигрыш» constexpr int NOTE_GAME_WIN[] = { NOTE_C4, NOTE_C4, NOTE_G4, NOTE_C5, NOTE_G4, NOTE_C5 }; // Массив длительности нот для мелодии «Выигрыш» constexpr int NOTE_DURATION_GAME_WIN[] = { 8, 8, 8, 4, 8, 4 }; // Количество нот в мелодии «Выигрыш» constexpr int NOTE_COUNT_GAME_WIN = 6; // Массив пинов со светодиодами constexpr int LED_NUMBER[] = { LED_1_PIN, LED_2_PIN, LED_3_PIN, LED_4_PIN }; // Массив нот для светодиодов constexpr int LED_NOTE[] = { NOTE_F3, NOTE_G3, NOTE_A3, NOTE_B3 }; // Массив пинов с кнопками constexpr int BUTTON_NUMBER[] = { BUTTON_1_PIN, BUTTON_2_PIN, BUTTON_3_PIN, BUTTON_4_PIN }; // Количество светодиодов и кнопок в массиве constexpr int LED_BUTTON_COUNTS = 4; // Переменные для текущего и лучшего счёта int score = 0; int scoreBest = 0; // Константа максимального счёта constexpr int SCORE_MAX = 128; // Массив последовательности случайно сгенерированных комбинаций int randomArray[SCORE_MAX]; // Массив последовательности нажатия кнопок int inputArray[SCORE_MAX]; // Создаём перечисления состояний игры enum { GAME_BEGIN, GAME_PLAY, GAME_OVER } stateGame; void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Назначаем пины светодиодов в режим выхода // А пины кнопок в режим входа с подтяжкой к питанию for (int i = 0; i < LED_BUTTON_COUNTS; i++) { pinMode(LED_NUMBER[i], OUTPUT); pinMode(BUTTON_NUMBER[i], INPUT_PULLUP); } // Задаём зерно генерации случайных чисел randomSeed(analogRead(A6)); // Устанавливаем режим «Начало игры» stateGame = GAME_BEGIN; } void loop() { // Если установлен режим «Начало игры» if (stateGame == GAME_BEGIN) { // Обнуляем текущий результат score = 0; // Выводим приветственную заставку на дисплей printLCDGameBegin(); // Выводим приветственную музыку playMelodyGameBegin(); // Даём пользователю время на просмотр заставки delay(1000); // Очищаем дисплей lcd.clear(); // Устанавливаем режим «Процесс игры» stateGame = GAME_PLAY; } // Если установлен режим «Процесс игры» if (stateGame == GAME_PLAY) { // и функция playGame выдаёт ложное значение, // переходим в режим «Конец игры». if (playGame() == false) { stateGame = GAME_OVER; } } // Если установлен режим «Конец игры» if (stateGame == GAME_OVER) { // Выводим конечную заставку на дисплей printLCDGameOver(); // Выводим завершающую музыку playMelodyGameOver(); stateGame = GAME_BEGIN; delay(1000); } } // Функция обработки игрового процесса bool playGame() { lcd.setCursor(2, 0); lcd.print("<Simon Says>"); lcd.setCursor(0, 1); lcd.print("Score:"); while (true) { // Отображаем на дисплее текущее количество очков lcd.setCursor(6, 1); lcd.print(score); // Зажигаем все светодиоды ledsSetAll(HIGH); // Включаем победную мелодию playMelodyGameWin(); // Гасим все светодиоды ledsSetAll(LOW); // Ждём delay(1000); // Демонстрируем случайную последовательность светодиодов generateRandomArray(); // Игра продолжается и счёт увеличивается, // пока функция readInputArray истинна. if (readInputArray()) { score++; } else { return false; } } } // Функция вывода начальной заставки на экран void printLCDGameBegin() { // Очищаем дисплей lcd.clear(); // Выводим текст начальной заставки lcd.setCursor(2, 0); lcd.print("<Simon Says>"); lcd.setCursor(4, 1); lcd.print("DIY Game"); } // Функция вывода конечной заставки на экран void printLCDGameOver() { lcd.clear(); lcd.setCursor(0, 1); lcd.print("Score:"); lcd.print(score); // Если счёт превзошёл предыдущий рекорд, обновляем значение рекорда if (score > scoreBest) { scoreBest = score; } // Выводим лучший результат игры за все попытки lcd.setCursor(0, 0); lcd.print("Best: "); lcd.print(scoreBest); } // Функция вывода мелодии «Начало игры void playMelodyGameBegin() { // Выполняем перебор нот for (int i = 0; i < NOTE_COUNT_GAME_BEGIN; i++) { int led = random(0, LED_BUTTON_COUNTS); // Зажигаем случайно выбранный светодиод digitalWrite(LED_NUMBER[led], HIGH); // Играем ноту с определённой длительностью tone(BUZZER_PIN, NOTE_GAME_BEGIN[i], 1000 / NOTE_DURATION_GAME_BEGIN[i]); // Делаем задержку для выделения нот в мелодии: // Длительность текущей ноты плюс 30% delay((1000 / NOTE_DURATION_GAME_BEGIN[i]) * 1.3); // Гасим случайно выбранный светодиод digitalWrite(LED_NUMBER[led], LOW); } } // Функция вывода мелодии «Выигрыш» void playMelodyGameWin() { // Выполняем перебор нот for (int i = 0; i < NOTE_COUNT_GAME_WIN; i++) { // Играем ноту с определённой длительностью tone(BUZZER_PIN, NOTE_GAME_WIN[i], 1000 / NOTE_DURATION_GAME_WIN[i]); // Делаем задержку для выделения нот в мелодии: // Длительность текущей ноты плюс 30% delay((1000 / NOTE_DURATION_GAME_WIN[i]) * 1.3); } } // Функция вывода мелодии «Проигрыш» void playMelodyGameOver() { for (int i = 0; i <= 2; i++) { // Зажигаем все светодиоды ledsSetAll(HIGH); // Издаём звуковой сигнал tone(BUZZER_PIN, NOTE_G3, 300); delay(200); // Гасим все светодиоды ledsSetAll(LOW); // Издаём звуковой сигнал tone(BUZZER_PIN, NOTE_C3, 300); delay(200); } } // Функция вывода произвольной комбинации светодиодов void generateRandomArray() { // Генерируем случайное число в диапазоне от 0 до 3 randomArray[score] = random(0, LED_BUTTON_COUNTS); // Зажигаем по очереди всю последовательность светодиодов for (int scoreNow = 0; scoreNow <= score; scoreNow++) { ledBeep(randomArray[scoreNow]); } } // Функция считывания угадываемой комбинации кнопок bool readInputArray() { int scoreNow = 0; // Пока количество нажатий на кнопки не превысило количество очков while (scoreNow <= score) { // Проверяем каждую кнопку for (int i = 0; i < LED_BUTTON_COUNTS; i++) { if (!digitalRead(BUTTON_NUMBER[i])) { // Подаём светодиодный звуковой сигнал нажатой кнопки ledBeep(i); // Присваиваем массиву кода кнопок, текущий номер кнопки inputArray[scoreNow] = i; delay(250); // Если текущая нажатая клавиша не совпадает со случайно сгенерированной if (inputArray[scoreNow] != randomArray[scoreNow]) { // Функция ложна return false; } scoreNow++; } } } return true; } // Функция включение или отключение всех светодиодов void ledsSetAll(bool state) { for (int i = 0; i < LED_BUTTON_COUNTS; i++) { digitalWrite(LED_NUMBER[i], state); } } // Функция светодиодного звукового сигнала void ledBeep(int led) { // Зажигаем выбранный светодиод digitalWrite(LED_NUMBER[led], HIGH); // Подаём звуковой сигнал зажжённого светодиода tone(BUZZER_PIN, LED_NOTE[led], 100); delay(400); // Гасим выбранный светодиод digitalWrite(LED_NUMBER[led], LOW); delay(100); }
№62. Саймон говорит с сохранением
- SimonSaysEEPROM.ino
// Библиотека для доступа к энергонезависимой памяти EEPROM #include <EEPROM.h> // Библиотека для работы с текстовым дисплеем по шине I²C #include <LiquidCrystal_I2C.h> // Создаём объект для работы с дисплеем // В параметрах передаём I²C-адрес дисплея, кол-во строк и символов LiquidCrystal_I2C lcd(0x38, 16, 2); // Константа для хранения адреса ячейки в памяти EEPROM // для сохранения и считывания данных игры constexpr int EEPROM_ADDRESS = 200; // Создаём звуковой шрифт из нот со своими частотами constexpr int NOTE_C3 = 131; constexpr int NOTE_D3 = 147; constexpr int NOTE_E3 = 165; constexpr int NOTE_F3 = 175; constexpr int NOTE_G3 = 196; constexpr int NOTE_A3 = 220; constexpr int NOTE_B3 = 247; constexpr int NOTE_C4 = 262; constexpr int NOTE_D4 = 294; constexpr int NOTE_E4 = 330; constexpr int NOTE_F4 = 349; constexpr int NOTE_G4 = 392; constexpr int NOTE_A4 = 440; constexpr int NOTE_B4 = 494; constexpr int NOTE_C5 = 523; // Даём понятное имя пину с пищалкой constexpr int BUZZER_PIN = 2; // Даём понятные имена пинам со светодиодами constexpr int LED_1_PIN = 3; constexpr int LED_2_PIN = 4; constexpr int LED_3_PIN = 5; constexpr int LED_4_PIN = 6; // Даём понятные имена пинам с кнопками constexpr int BUTTON_1_PIN = A3; constexpr int BUTTON_2_PIN = A2; constexpr int BUTTON_3_PIN = A1; constexpr int BUTTON_4_PIN = A0; // Массив нот для мелодии «Начало игры» constexpr int NOTE_GAME_BEGIN[] = { NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_F4, NOTE_G4 }; // Массив длительности нот для мелодии «Начало игры constexpr int NOTE_DURATION_GAME_BEGIN[] = { 8, 4, 8, 4, 8, 4, 8, 8, 8, 8, 4, 8, 2 }; // Количество нот в мелодии «Начало игры constexpr int NOTE_COUNT_GAME_BEGIN = 13; // Массив нот для мелодии «Выигрыш» constexpr int NOTE_GAME_WIN[] = { NOTE_C4, NOTE_C4, NOTE_G4, NOTE_C5, NOTE_G4, NOTE_C5 }; // Массив длительности нот для мелодии «Выигрыш» constexpr int NOTE_DURATION_GAME_WIN[] = { 8, 8, 8, 4, 8, 4 }; // Количество нот в мелодии «Выигрыш» constexpr int NOTE_COUNT_GAME_WIN = 6; // Массив пинов со светодиодами constexpr int LED_NUMBER[] = { LED_1_PIN, LED_2_PIN, LED_3_PIN, LED_4_PIN }; // Массив нот для светодиодов constexpr int LED_NOTE[] = { NOTE_F3, NOTE_G3, NOTE_A3, NOTE_B3 }; // Массив пинов с кнопками constexpr int BUTTON_NUMBER[] = { BUTTON_1_PIN, BUTTON_2_PIN, BUTTON_3_PIN, BUTTON_4_PIN }; // Количество светодиодов и кнопок в массиве constexpr int LED_BUTTON_COUNTS = 4; // Переменные для текущего и лучшего счёта int score = 0; int scoreBest = 0; // Константа максимального счёта constexpr int SCORE_MAX = 128; // Массив последовательности случайно сгенерированных комбинаций int randomArray[SCORE_MAX]; // Массив последовательности нажатия кнопок int inputArray[SCORE_MAX]; // Создаём перечисления состояний игры enum { GAME_BEGIN, GAME_PLAY, GAME_OVER } stateGame; void setup() { // Инициализируем дисплей lcd.init(); // Включаем подсветку lcd.backlight(); // Назначаем пины светодиодов в режим выхода // А пины кнопок в режим входа с подтяжкой к питанию for (int i = 0; i < LED_BUTTON_COUNTS; i++) { pinMode(LED_NUMBER[i], OUTPUT); pinMode(BUTTON_NUMBER[i], INPUT_PULLUP); } // Задаём зерно генерации случайных чисел randomSeed(analogRead(A6)); // Считываем лучший результат игры из EEPROM EEPROM.get(EEPROM_ADDRESS, scoreBest); // Устанавливаем режим «Начало игры» stateGame = GAME_BEGIN; } void loop() { // Если установлен режим «Начало игры» if (stateGame == GAME_BEGIN) { // Обнуляем текущий результат score = 0; // Выводим приветственную заставку на дисплей printLCDGameBegin(); // Выводим приветственную музыку playMelodyGameBegin(); // Даём пользователю время на просмотр заставки delay(1000); // Очищаем дисплей lcd.clear(); // Устанавливаем режим «Процесс игры» stateGame = GAME_PLAY; } // Если установлен режим «Процесс игры» if (stateGame == GAME_PLAY) { // и функция playGame выдаёт ложное значение, // переходим в режим «Конец игры». if (playGame() == false) { stateGame = GAME_OVER; } } // Если установлен режим «Конец игры» if (stateGame == GAME_OVER) { // Выводим конечную заставку на дисплей printLCDGameOver(); // Выводим завершающую музыку playMelodyGameOver(); stateGame = GAME_BEGIN; delay(1000); } } // Функция обработки игрового процесса bool playGame() { lcd.setCursor(2, 0); lcd.print("<Simon Says>"); lcd.setCursor(0, 1); lcd.print("Score:"); while (true) { // Отображаем на дисплее текущее количество очков lcd.setCursor(6, 1); lcd.print(score); // Зажигаем все светодиоды ledsSetAll(HIGH); // Включаем победную мелодию playMelodyGameWin(); // Гасим все светодиоды ledsSetAll(LOW); // Ждём delay(1000); // Демонстрируем случайную последовательность светодиодов generateRandomArray(); // Игра продолжается и счёт увеличивается, // пока функция readInputArray истинна. if (readInputArray()) { score++; } else { return false; } } } // Функция вывода начальной заставки на экран void printLCDGameBegin() { // Очищаем дисплей lcd.clear(); // Выводим текст начальной заставки lcd.setCursor(2, 0); lcd.print("<Simon Says>"); lcd.setCursor(4, 1); lcd.print("DIY Game"); } // Функция вывода конечной заставки на экран void printLCDGameOver() { lcd.clear(); lcd.setCursor(0, 1); lcd.print("Score:"); lcd.print(score); // Если счёт превзошёл предыдущий рекорд, обновляем значение рекорда if (score > scoreBest) { scoreBest = score; // Сохраняем лучший результат игры в EEPROM EEPROM.put(EEPROM_ADDRESS, scoreBest); } // Выводим лучший результат игры за все попытки lcd.setCursor(0, 0); lcd.print("Best: "); lcd.print(scoreBest); } // Функция вывода мелодии «Начало игры void playMelodyGameBegin() { // Выполняем перебор нот for (int i = 0; i < NOTE_COUNT_GAME_BEGIN; i++) { int led = random(0, LED_BUTTON_COUNTS); // Зажигаем случайно выбранный светодиод digitalWrite(LED_NUMBER[led], HIGH); // Играем ноту с определённой длительностью tone(BUZZER_PIN, NOTE_GAME_BEGIN[i], 1000 / NOTE_DURATION_GAME_BEGIN[i]); // Делаем задержку для выделения нот в мелодии: // Длительность текущей ноты плюс 30% delay((1000 / NOTE_DURATION_GAME_BEGIN[i]) * 1.3); // Гасим случайно выбранный светодиод digitalWrite(LED_NUMBER[led], LOW); } } // Функция вывода мелодии «Выигрыш» void playMelodyGameWin() { // Выполняем перебор нот for (int i = 0; i < NOTE_COUNT_GAME_WIN; i++) { // Играем ноту с определённой длительностью tone(BUZZER_PIN, NOTE_GAME_WIN[i], 1000 / NOTE_DURATION_GAME_WIN[i]); // Делаем задержку для выделения нот в мелодии: // Длительность текущей ноты плюс 30% delay((1000 / NOTE_DURATION_GAME_WIN[i]) * 1.3); } } // Функция вывода мелодии «Проигрыш» void playMelodyGameOver() { for (int i = 0; i <= 2; i++) { // Зажигаем все светодиоды ledsSetAll(HIGH); // Издаём звуковой сигнал tone(BUZZER_PIN, NOTE_G3, 300); delay(200); // Гасим все светодиоды ledsSetAll(LOW); // Издаём звуковой сигнал tone(BUZZER_PIN, NOTE_C3, 300); delay(200); } } // Функция вывода произвольной комбинации светодиодов void generateRandomArray() { // Генерируем случайное число в диапазоне от 0 до 3 randomArray[score] = random(0, LED_BUTTON_COUNTS); // Зажигаем по очереди всю последовательность светодиодов for (int scoreNow = 0; scoreNow <= score; scoreNow++) { ledBeep(randomArray[scoreNow]); } } // Функция считывания угадываемой комбинации кнопок bool readInputArray() { int scoreNow = 0; // Пока количество нажатий на кнопки не превысило количество очков while (scoreNow <= score) { // Проверяем каждую кнопку for (int i = 0; i < LED_BUTTON_COUNTS; i++) { if (!digitalRead(BUTTON_NUMBER[i])) { // Подаём светодиодный звуковой сигнал нажатой кнопки ledBeep(i); // Присваиваем массиву кода кнопок, текущий номер кнопки inputArray[scoreNow] = i; delay(250); // Если текущая нажатая клавиша не совпадает со случайно сгенерированной if (inputArray[scoreNow] != randomArray[scoreNow]) { // Функция ложна return false; } scoreNow++; } } } return true; } // Функция включение или отключение всех светодиодов void ledsSetAll(bool state) { for (int i = 0; i < LED_BUTTON_COUNTS; i++) { digitalWrite(LED_NUMBER[i], state); } } // Функция светодиодного звукового сигнала void ledBeep(int led) { // Зажигаем выбранный светодиод digitalWrite(LED_NUMBER[led], HIGH); // Подаём звуковой сигнал зажжённого светодиода tone(BUZZER_PIN, LED_NOTE[led], 100); delay(400); // Гасим выбранный светодиод digitalWrite(LED_NUMBER[led], LOW); delay(100); }
№63. Бегущая тень
- SoftwareSPI.ino
// Назначаем контакты для связи с выходным сдвиговым регистром constexpr int LATCH_PIN = 10; constexpr int DATA_PIN = 11; constexpr int CLOCK_PIN = 13; // Переменная для хранения значений восьми светодиодов в битовом виде byte leds = 0b00000000; void setup() { // Настраиваем пины для связи с выходным сдвиговым регистром в режим выхода pinMode(LATCH_PIN, OUTPUT); pinMode(DATA_PIN, OUTPUT); pinMode(CLOCK_PIN, OUTPUT); } void loop() { // Последовательно зажигаем светодиоды через сдвиговый регистр for (int i = 0; i < 8; i++) { bitWrite(leds, i, HIGH); sendData(); delay(100); } // Последовательно гасим светодиоды через сдвиговый регистр for (int i = 0; i < 8; i++) { bitWrite(leds, i, LOW); sendData(); delay(100); } } // Функция для переди байта в сдвиговый регистр // через программный SPI начиная с младшего бита void sendData() { digitalWrite(LATCH_PIN, LOW); shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, leds); digitalWrite(LATCH_PIN, HIGH); }
№64. Бегущая тень по SPI
- HardwareSPI.ino
// Библиотека для работы с SPI-устройствами #include <SPI.h> // Назначаем контакт для защёлки сдвигового регистра constexpr int LATCH_PIN = 10; // Переменная для хранения значений восьми светодиодов в битовом виде byte leds = 0b00000000; void setup() { // Инициализируем шину SPI SPI.begin(); // Инициируем передачу данных по SPI младшим битом вперёд SPI.setBitOrder(LSBFIRST); // Назначаем контакт защёлки сдвигового регистра в режим выхода pinMode(LATCH_PIN, OUTPUT); } void loop() { // Последовательно зажигаем светодиоды через сдвиговый регистр for (int i = 0; i < 8; i++) { bitWrite(leds, i, HIGH); sendData(); delay(100); } // Последовательно гасим светодиоды через сдвиговый регистр for (int i = 0; i < 8; i++) { bitWrite(leds, i, LOW); sendData(); delay(100); } } // Функция для переди байта в сдвиговый регистр // через аппаратный SPI начиная с младшего бита void sendData() { digitalWrite(LATCH_PIN, LOW); SPI.transfer(leds); digitalWrite(LATCH_PIN, HIGH); }
Полезные скетчи
Очистка EEPROM
- EEPROMClear.ino
// Библиотека для работы с EEPROM-памятью #include <EEPROM.h> // Даём понятное имя встроенном светодиоду constexpr int LED_PIN = 13; void setup() { // Выставляем светодиод в редим выхода pinMode(LED_PIN, OUTPUT); // Шасим светодиод digitalWrite(LED_PIN, LOW); // прогогняем по очереди все ячейки EEPROM-памяти for (int i = 0; i < EEPROM.length(); i++) { // Заполняем каждую ячейку значением 0xFF EEPROM.write(i, 0xFF); } // После очистки памяти, зажигаем светодиод digitalWrite(LED_PIN, HIGH); } void loop() { }
Чтение EEPROM
- EEPROMRead.ino
// Библиотека для работы с EEPROM-памятью #include <EEPROM.h> void setup() { // Открываем Serial-порт Serial.begin(9600); // Прогогняем по очереди все ячейки EEPROM-памяти for (int i = 0; i < EEPROM.length(); i++) { // Считываем каждую ячейку памяти Serial.print(i); Serial.print("\t"); Serial.println(EEPROM.read(i)); } } void loop() { }
Ресурсы
- Набор «Arduino. Восьмибитная академия» в магазине.