Устали от своих старых аркадных игр, которые выглядят и работают одинаково? Хотите создать быструю веселую игру, которой вы сможете наслаждаться с друзьями и семьей, не задумываясь? Тогда вы находитесь в правильном месте. В этой статье мы рассмотрим как создать интерактивную аркадную игру на 2-х человек с использованием платы Arduino и светодиодной ленты WS2811.
Принцип работы нашего проекта игры можно посмотреть в следующем видео.
Также недавно на нашем сайте мы рассматривали создание игры Tetris на основе Arduino.
Необходимые компоненты
- Светодиодная лента WS2811 (купить на AliExpress).
- Плоская кнопка AHF X2.
- Плата Arduino Nano (купить на AliExpress).
- Зуммер (купить на AliExpress).
- Литий-ионный аккумулятор.
- Выключатель.
- Шайбы М3 х 50 мм.
- Шайбы М3 х 20 мм.
- Болты М3 х 10 мм.
Распиновка светодиодной ленты WS2811
Светодиодная лента WS2811 получает питание через свои контакты Vcc и Gnd. Светодиодная лента состоит из одного разъема «папа» и разъема «мама», центральный контакт которого используется в качестве контакта для передачи данных.
Проектирование акрилового корпуса в SolidWorks
Итак, первое, что нам нужно было сделать, это создать файл dxf с помощью любого программного обеспечения для твердотельного моделирования. Мы выбрали SolidWorks, но вы можете выбрать любое программное обеспечение, которое вам нравится. Нам нужно было сделать дизайн таким, чтобы он был достаточно большим, чтобы вместить все 50 светодиодов и две кнопки. Также необходимо было иметь слот для кнопки переключения, чтобы мы могли включать и выключать игру, когда это необходимо. Итак, после нескольких часов проб и испытаний мы наконец пришли к дизайну нашей игры, показанном на следующем рисунке.
Скачать stl-файлы для изготовления этих элементов корпуса игры вы можете по следующей ссылке.
Схема проекта
Схема аркадной игры на основе платы Arduino и светодиодной ленты WS2811 показана на следующем рисунке.
Открытые провода vcc и gnd светодиодной ленты WS2811 подключите к 5 В и земле платы Arduino. Средний контакт разъема «мама» — это контакт данных. Подключите его к цифровому выходу данных Arduino (в нашем случае это контакт № 5).
Подключите один контакт плоской кнопки к контакту 2 платы Arduino, а другой — к земле через перемычки. Таким образом нужно вывести провода из кнопки.
Подключите одну клемму второй плоской кнопки к контакту 3 Arduino, а другую — к земле через перемычки.
Аналогичным образом вы можете подключить (+) зуммера к контакту 7 Arduino, а другой — к земле через перемычки.
И, наконец, вы можете подключить переключатель между контактом Vin платы Arduino и литий-ионным аккумулятором 7,4 В.
Объяснение кода программы для Arduino
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
Первым делом в коде программы подключим библиотеку FastLED, позволяющую управлять адресными светодиодными лентами.
1 |
#include <FastLED.h> |
Затем зададим контакты, с помощью которых мы будем взаимодействовать со светодиодной лентой, зуммером, кнопками и светодиодом.
1 2 3 4 5 6 7 8 |
#define LED_PIN 5 #define BUZZER_PIN 7 #define NUM_LEDS 50 #define BUTTON_PIN1 2 #define BUTTON_PIN2 3 #define DELAY_MS 50 #define RED_BUTTON_FREQ 100 // Frequency for the red button sound #define GREEN_BUTTON_FREQ 200 // Frequency for the green button sound |
После этого создадим CRGB объект для управления светодиодной лентой. Он будет иметь длину NUM_LEDS (в нашем случае это 50).
1 |
CRGB leds[NUM_LEDS]; |
В следующих строках программы объявляются переменные, используемые для хранения текущего и предыдущего состояний кнопок, количества загорающихся красных и зеленых светодиодов и флагов, указывающих, была ли нажата красная или зеленая кнопка.
1 2 3 4 5 6 7 8 |
int ledCount1 = 0; // Number of red LEDs to light up from one end int ledCount2 = 0; // Number of green LEDs to light up from the other end bool buttonState1 = false; bool lastButtonState1 = false; bool buttonState2 = false; bool lastButtonState2 = false; bool isRedButtonPressed = false; bool isGreenButtonPressed = false; |
В функции setup() мы инициализируем светодиодную ленту и зададим режимы работы используемых контактов (на ввод или вывод данных).
1 2 3 4 5 6 |
void setup() { FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS); pinMode(BUTTON_PIN1, INPUT_PULLUP); pinMode(BUTTON_PIN2, INPUT_PULLUP); pinMode(BUZZER_PIN, OUTPUT); } |
В функции loop() мы сначала будем считывать состояния кнопок подключенных к контактам BUTTON_PIN1 и BUTTON_PIN2 с помощью функции digitalRead() записывать их в переменные buttonState1 и buttonState2.
1 2 3 |
void loop() { buttonState1 = digitalRead(BUTTON_PIN1) == LOW; buttonState2 = digitalRead(BUTTON_PIN2) == LOW; |
Следующий фрагмент кода проверяет, изменилось ли состояние buttonState1 (первой кнопки) с момента последней итерации. Если кнопка нажата (buttonState1 имеет значение true), значение LEDCount1 увеличивается и проверяется не превысило ли оно общее количество светодиодов. Также мы устанавливаем для isRedButtonPressed значение true и воспроизводим звуковой сигнал с помощью функции tone() с частотой, определяемой RED_BUTTON_FREQ.
1 2 3 4 5 6 7 8 9 10 11 12 |
if (buttonState1 != lastButtonState1) { if (buttonState1) { // Button 1 is pressed ledCount1++; if (ledCount1 > NUM_LEDS) { ledCount1 = NUM_LEDS; } isRedButtonPressed = true; tone(BUZZER_PIN, RED_BUTTON_FREQ); // Play the red button sound } lastButtonState1 = buttonState1; } |
Этот блок кода аналогичен предыдущему, но обрабатывает вторую кнопку (buttonState2), увеличивает значение параметра ledCount2 и проверяет, что оно не превышает общее количество светодиодов ленты, устанавливает для isGreenButtonPressed значение true и воспроизводит звуковой сигнал зуммера с частотой, определяемой GREEN_BUTTON_FREQ.
1 2 3 4 5 6 7 8 9 10 11 12 |
if (buttonState2 != lastButtonState2) { if (buttonState2) { // Button 2 is pressed ledCount2++; if (ledCount2 > NUM_LEDS) { ledCount2 = NUM_LEDS; } isGreenButtonPressed = true; tone(BUZZER_PIN, GREEN_BUTTON_FREQ); // Play the green button sound } lastButtonState2 = buttonState2; } |
Следующая команда выключает все светодиоды в ленте.
1 |
FastLED.clear(); |
Следующий цикл зажигает первые ledCount1 светодиодов ленты зеленым цветом.
1 2 3 |
for (int i = 0; i < ledCount1; i++) { leds[i] = CRGB::Green; } |
Следующий цикл зажигает последние ledCount2 светодиодов ленты красным цветом, начиная с ее конца.
1 2 3 |
for (int i = NUM_LEDS - 1; i >= NUM_LEDS - ledCount2; i--) { leds[i] = CRGB::Red; } |
Следующий фрагмент кода проверяет, равно ли общее количество горящих светодиодов (ledCount1 + ledCount2) общему количеству светодиодов (NUM_LEDS) или превышает его. Если это так, он определяет горящих светодиодов какого цвета больше вызывает соответствующую функцию (greenChaserEffect(), redChaserEffect() или tieChaserEffect()) для отображения эффекта преследования на светодиодной ленте. После этого он сбрасывает значения LEDCount1 и LEDCount2 в ноль.
1 2 3 4 5 6 7 8 9 10 11 12 |
if ((ledCount1 + ledCount2) >= NUM_LEDS) { if (ledCount1 > ledCount2) { greenChaserEffect(); } else if (ledCount2 > ledCount1) { redChaserEffect(); } else { // It's a tie tieChaserEffect(); } ledCount1 = 0; ledCount2 = 0; } |
Следующая строка кода обновляет светодиодную ленту новыми цветами, установленными на предыдущих шагах.
1 |
FastLED.show(); |
Если была нажата красная или зеленая кнопка (isRedButtonPressed или isGreenButtonPressed имеет значение true), это вводит задержку для управления продолжительностью звука зуммера, останавливает звук зуммера с помощью noTone() и сбрасывает флаги нажатия кнопок.
1 2 3 4 5 6 |
if (isRedButtonPressed || isGreenButtonPressed) { delay(50); noTone(BUZZER_PIN); isRedButtonPressed = false; isGreenButtonPressed = false; } |
В следующей строке вводим задержку для устранения дребезга кнопок и управления скоростью отклика кода.
1 |
delay(50); |
Определение функции TieChaserEffect(), которая отображает эффект преследования, при котором красные и зеленые светодиоды движутся к центру светодиодной ленты.
1 2 3 |
void tieChaserEffect() { // ... } |
Определение функции greenChaserEffect(), которая отображает эффект преследования, при котором зеленые светодиоды перемещаются от одного конца к другому концу светодиодной ленты.
1 2 3 |
void greenChaserEffect() { // ... } |
Определение функции redChaserEffect(), которая отображает эффект преследования, при котором красные светодиоды перемещаются от одного конца к другому концу светодиодной ленты.
Тестирование работы проекта
После сборки электронной части проекта вы должны упаковать ее в изготовленный корпус. У вас должна получиться конструкция следующего вида.
После этого вы можете приступить к тестированию работы проекта как показано на следующем видео.
Исходный код программы (скетча)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
#include <FastLED.h> #define LED_PIN 5 #define BUZZER_PIN 7 #define NUM_LEDS 50 #define BUTTON_PIN1 2 #define BUTTON_PIN2 3 #define DELAY_MS 50 #define RED_BUTTON_FREQ 100 // Frequency for the red button sound #define GREEN_BUTTON_FREQ 200 // Frequency for the green button sound CRGB leds[NUM_LEDS]; int ledCount1 = 0; // Number of red LEDs to light up from one end int ledCount2 = 0; // Number of green LEDs to light up from the other end bool buttonState1 = false; bool lastButtonState1 = false; bool buttonState2 = false; bool lastButtonState2 = false; bool isRedButtonPressed = false; bool isGreenButtonPressed = false; void setup() { FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS); pinMode(BUTTON_PIN1, INPUT_PULLUP); pinMode(BUTTON_PIN2, INPUT_PULLUP); pinMode(BUZZER_PIN, OUTPUT); } void loop() { buttonState1 = digitalRead(BUTTON_PIN1) == LOW; buttonState2 = digitalRead(BUTTON_PIN2) == LOW; if (buttonState1 != lastButtonState1) { if (buttonState1) { // Button 1 is pressed ledCount1++; if (ledCount1 > NUM_LEDS) { ledCount1 = NUM_LEDS; } isRedButtonPressed = true; tone(BUZZER_PIN, RED_BUTTON_FREQ); // Play the red button sound } lastButtonState1 = buttonState1; } if (buttonState2 != lastButtonState2) { if (buttonState2) { // Button 2 is pressed ledCount2++; if (ledCount2 > NUM_LEDS) { ledCount2 = NUM_LEDS; } isGreenButtonPressed = true; tone(BUZZER_PIN, GREEN_BUTTON_FREQ); // Play the green button sound } lastButtonState2 = buttonState2; } // Turn off all LEDs FastLED.clear(); // Light up the specified number of red LEDs from one end for (int i = 0; i < ledCount1; i++) { leds[i] = CRGB::Green; } // Light up the specified number of green LEDs from the other end for (int i = NUM_LEDS - 1; i >= NUM_LEDS - ledCount2; i--) { leds[i] = CRGB::Red; } if ((ledCount1 + ledCount2) >= NUM_LEDS) { if (ledCount1 > ledCount2) { greenChaserEffect(); } else if (ledCount2 > ledCount1) { redChaserEffect(); } else { // It's a tie tieChaserEffect(); } // Reset the LED counts ledCount1 = 0; ledCount2 = 0; } // Show the updated LED strip FastLED.show(); if (isRedButtonPressed || isGreenButtonPressed) { delay(50); // Adjust this delay to control the duration of the buzzer sound noTone(BUZZER_PIN); // Stop the buzzer sound isRedButtonPressed = false; isGreenButtonPressed = false; } delay(50); // Adjust this delay to debounce the buttons and control the response speed } void tieChaserEffect() { for (int i = 0; i < NUM_LEDS / 2; i++) { leds[i] = CRGB::Red; // Set the color of the current LED to red FastLED.show(); // Update the LED strip delay(DELAY_MS); // Wait for a short period } for (int i = NUM_LEDS; i >= NUM_LEDS / 2; i--) { leds[i] = CRGB::Green; // Set the color of the current LED to red FastLED.show(); // Update the LED strip delay(DELAY_MS); // Wait for a short period } } void greenChaserEffect() { // Set all LEDs to green initially for (int i = 0; i < ledCount1; i++) { leds[i] = CRGB::Green; } FastLED.show(); // Update the LED strip with the initial green color for (int i = ledCount1; i < NUM_LEDS; i++) { leds[i] = CRGB::Green; // Set the color of the current LED to red FastLED.show(); // Update the LED strip delay(DELAY_MS); // Wait for a short period } } void redChaserEffect() { // Set all LEDs to green initially for (int i = NUM_LEDS - 1; i >= NUM_LEDS - ledCount2; i--) { leds[i] = CRGB::Red; } FastLED.show(); // Update the LED strip with the initial green color for (int i = ledCount2; i > 0; i--) { leds[i] = CRGB::Red; // Set the color of the current LED to red FastLED.show(); // Update the LED strip delay(DELAY_MS); // Wait for a short period } } |