Журнал LinuxFormat - перейти на главную

LXF103:Arduino

Материал из Linuxformat
Перейти к: навигация, поиск

Содержание

СОЗДАЙ СВОЙ ГАДЖЕТ. Возвращение

Arduino
По многочисленным просьбам читателей, наш цикл аппаратного хакинга вернулся. Грэм Моррисон собирает игру типа «Саймон сказал», используя Arduino и несколько светодиодов.

Статья об аппаратном хакинге из LXF100/101 оказалась столь популярной, что мы решили продолжить. В статье говорилось об Arduino – небольшой печатной плате, включающей простое окружение ввода/вывода и USB-порт, которыми легко управлять, используя несложный язык программирования и IDE на Java. Самое замечательное то, что в ней все – от дизайна платы до управляющего ПО – открытое (Creative Commons для аппаратной схемы и GPL для ПО). В результате вокруг проекта Arduino образовалось сплоченное сообщество, и это также повлияло на выбор его как платформы для нашей «железной» статьи. Другая причина – дешевизна решения. Примерно за 2000 рублей вы можете приобрести полный набор для новичка, содержащий все, что нужно для начала, включая зуммер, светодиоды, сенсоры и кнопки – и, конечно, сам Arduino.

В статье этого месяца мы собираемся продолжить начатое и, опираясь на основы, раскроем чуть больше потенциала Arduino. После этого у вас будет вполне достаточно информации для уверенной работы над собственными проектами. В следующие несколько месяцев мы будем опираться на этот фундамент, не используя ничего, кроме дешевых компонентов и открытых кодов. Но прежде чем замахнуться на большее, заполним несколько пробелов, оставшихся от первого урока. В стиле Linux Format, мы превратим это в забаву, воссоздав классическую игру 80-х – Simon, простую игру на повторение [«Саймон сказал», гра, в которой все повторяют то, что делает или просит сделать ведущий, – прим. пер.]. Игроки должны нажимать клавиши в соответствии с проигранными Саймоном нотами. Сперва вы слушаете последовательность звуков, а затем пытаетесь воссоздать мелодию с помощью четырех больших кнопок на игровой системе. Последовательность становится все длиннее и сложнее для повторения.

Мы сделаем упрощенную версию этой игры, используя три светодиода (вместо нот) и три кнопки. Идея игры следующая: компьютер выбирает последовательность включения светодиодов, а игрок ее повторяет, нажимая на соответствующие кнопки. Каждый раз, когда игрок вводит ее правильно, мы удлиняем последовательность на единицу, и количество очков игрока будут зависеть от запомненной им последовательности.

Пишем игру

Вот что нам понадобится для построения собственного «Саймона»:

  • Плата Arduino.
  • 3 переключателя.
  • 3 светодиода.
  • 3 резистора по 1 КОм для переключателей.
  • 3 резистора по 1 КОм для светодиодов.

Мы также будем использовать монтажную плату из оригинальной статьи для скрепления всех компонентов и проводов вместе.

Первым делом разместим переключатели. Они имеют четыре контакта, но нам нужны только два, на любой из сторон. Наши три переключателя поместим так, чтобы они выглядели как «мостик» над центральной канавкой макетной платы, а соединять будем только две ножки по одну сторону канавки; другую сторону используем для присоединения светодиодов. Дизайн игры должен учитывать эргономику: люди захотят поиграть с вашим устройством, но не с проводами и резисторами, путающимися у них под руками. Кроме того, продумайте, как разместить светодиоды – они должны быть рядом с кнопками, чтобы было ясно, какой светодиод какой кнопке соответствует. У нас получилась не слишком успешная раскладка, и если у вас тоже отсутствуют навыки дизайна а-ля Джонатан Ив, вы можете обнаружить, что весь процесс лучше начать заново, хорошенько разобравшись в проблемах размещения проводов и резисторов.

Расставим кнопки

Следующая задача – подсоединить переключатели. Соединим выход питания 5 В на Arduino с одной из горизонтальных шин на углу платы. Как и полагается «шине», при соединении с одной точкой на горизонтальной линии сигнал передается в любую точку по всей длине платы. Шина обычно используется для передачи питания и заземления на различные компоненты, присоединяемые к центральной части макетной платы (также известной как терминальная полоса), и это именно наш случай. От горизонтальной линии, несущей теперь 5 В, мы должны «прокинуть» резистор к ножке каждого переключателя, чтобы не подавать на переключатели слишком большое напряжение. Если у вас нет макетной платы с шинами, это не мешает продолжить работу над нашим проектом, но тогда придется выполнять каждое соединение с платой Arduino вручную. Надо также подвести провод от ножки, на которую подано напряжение, к трем отдельным цифровым коннекторам Arduino, чтобы мы могли читать состояние каждой кнопки. Используем контакты 5, 6 и 7.

Еще надо соединить другую ножку каждого переключателя с «землей» на Arduino (мы использовали следующий контакт за выходом 5 В). Простейший способ сделать это – соединить все эти ножки с шиной, соседней с шиной питания, а эту новую шину подключить к контакту «земля» на Arduino. Вот и весь монтаж переключателей; установка светодиодов будет еще проще.

Подключим светодиоды

На другой стороне канавки, или паза, посреди макетной платы, надо теперь прикрепить наши три светодиода – поближе к кнопкам, но оставив достаточно места для присоединения земли (GND) к одной ножке и управляющего провода к другой. Мы использовали шину по другую сторону паза для соединения с GND и воткнули светодиоды отрицательной стороной (короткая ножка, или плоская сторона головки) прямо в нее. Другую ножку надо соединить через резистор проводом с одним из контактов ввода/вывода Arduino – у нас это контакты 10, 11 и 12.

До начала работы приведем список соединений, использованных нами на Arduino.

  • 1-й переключатель на Вход 7, светодиод на 12.
  • 2-й переключатель на Вход 6, светодиод на 11.
  • 3-й переключатель на Вход 5, светодиод на 10.
  • 5 В питания на шину к трем переключателям.
  • GND (Земля) на шину к трем переключателям.
  • Другой GND, соединенный с шиной, используемой светодиодами.

Программирование

LXF103 47 1.jpg IDE Arduino использует Java, и вы можете послать вашу игру прямо на устройство, присоединенное к USB-порту.

Ну вот, мы все собрали – теперь перейдем в программное окружение для написания кода, необходимого для игры. Мы рассмотрели работу в окружении в первой статье, это довольно просто. Клиент использует Java, и если вы испытываете проблемы с его запуском, вероятно, нужно убрать программное обеспечения терминала Брайля: оно конфликтует с установленным ПО Arduino.

Вверху нового файла исходного кода следует добавить строки, инициализирующие все наши соединения и глобальные значения:

 #DEFINE MAXSEQ 20
 int rand_array[MAXSEQ];
 int ledPin1 = 12;
 int ledPin2 = 11;
 int ledPin3 = 10;
 int inPin1 = 7;
 int inPin2 = 6;
 int inPin3 = 5;
 int score = 1;

Первая директива DEFINE задает максимальное значение последовательности мигания светодиодов, и эта последовательность хранится в массиве, создаваемом в следующей строке. Массив – это строка элементов, и каждый элемент в нашем примере говорит плате Arduino, какой светодиод зажечь и какая кнопка должна быть нажата: коро- че, это число от 1 до 3. Строкой ниже мы сообщаем Arduino, к каким входам подключены светодиоды и переключатели (соответственно, ledPin и inPin). Последняя строчка создает глобальную переменную для хранения очков, отмечающую, до какой длины последовательно- сти добрался игрок.

Теперь надо добавить функцию setup, используемую Arduino для настройки различных входов и выходов:

 void setup() {
   long val_rand = 0;
   pinMode(ledPin1, OUTPUT);
   pinMode(ledPin2, OUTPUT);
   pinMode(ledPin3, OUTPUT);
   pinMode(inPin1, INPUT);
   pinMode(inPin2, INPUT);
   pinMode(inPin3, INPUT);
   randomSeed(analogRead(0));
   for (int i=0; i<=MAXSEQ; i++){
     rand_array[i] = random (3);
   }
 }

В этом фрагменте кода мы делаем две вещи. Сперва мы говорим плате Arduino, какие контакты использованы как выходы (для мигания светодиодов), а какие – как входы (для считывания состояния кнопок). Мы также используем функцию setup() для заполнения массива случайными значениями, и делаем это в два приема. Функция randomSeed читает вход с ножки 0. Так как ножка не подсоединена, то с нее, фактически, снимается случайный шум, который мы используем как затравку для генератора случайных чисел, а генератор встроен внутрь цикла ‘for’, заполняющего массив случайными значениями между 1 и 3.

Покончив с конфигурацией, перейдем к логике программы.

Вот как должна протекать игра:

  1. Длина последовательности принимается 1. Мы проигрываем всю последовательность на светодиодах.
  2. Игрок должен в ответ нажать на переключатель, соответствующий светодиоду.
  3. Если игрок полностью повторил последовательность, ее длина увеличивается на 1.
  4. Когда игрок допускает ошибку, игра повторяет последовательность текущей длины.

Мы осуществим это с помощью четырех функций. Первая будет считывать, какая кнопка нажата (мы назовем ее readButtons), вторая – проигрывать последовательность на светодиодах (playSequence), и понадобятся еще две функции для сигнализации об успехе попытки или сбое (flashSuccess и flashFailure). Вот функция readButtons:

 int readButtons()
 {
      int val1 = 0; int val2 = 0; int val3 = 0;
   do {
        val1 = digitalRead(inPin1); val2 = digitalRead(inPin2); val3 = digitalRead(inPin3);
      } while (val1 == HIGH && val2 == HIGH && val3 == HIGH);
      if (val1==LOW){
          return 0;
      } else if (val2==LOW) {
          return 1;
      } else if (val3==LOW) {
          return 2;
      }
 }

Она ожидает нажатия кнопки пользователем, а затем возвращает значение 0, 1 или 2, в зависимости от нажатой кнопки. Мы выбрали эти цифры, потому что они в точности соответствуют номерам, используемым для зажигания каждого светодиода. Важное замечание: переключатель возвращает значение HIGH, когда он отпущен и LOW – когда нажат. Так происходит потому, что в нормальном состоянии переключатель пропускает ток, а его нажатие разрывает цепь.

Далее добавим функцию, проигрывающую последовательность на светодиодах:

 void playSequence(int count)
 {
    for (int i=0; i<=count; i++){
      digitalWrite(ledPin1, LOW); digitalWrite(ledPin2, LOW); digitalWrite(ledPin3, LOW);
      delay (200);
      switch (rand_array[i]) {
          case 0:
           digitalWrite(ledPin1, HIGH); break;
          case 1:
           digitalWrite(ledPin2, HIGH); break;
          case 2:
           digitalWrite(ledPin3, HIGH); break;
        }
        delay (200);
    }
    digitalWrite(ledPin1, LOW); digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);
 }

Эта функция принимает один аргумент, count, зависящий от длины проигрываемой последовательности. Он используется как предел для цикла ‘for’, и внутри цикла мы переключаем светодиоды согласно массиву, несущему последовательность. Мы также должны обеспечить выключение светодиода до и после, а также задержку между вспышками, чтобы игрок мог различать свечение отдельных светодиодов.

LXF103 48 1.jpg Не обязательно цепляться за скверный редактор Arduino. Используйте свой любимый редактор и сохраните код в той же директории эскизов.

Остались две функции, показывающие успех или ошибку нажатия кнопки. Будем выдавать две быстрых вспышки всех светодиодов при каждом правильном нажатии кнопки и длинную паузу, если игрок допустит ошибку. Здесь нет места для печати обоих функций, но вам необходимо сочетать вызовы функций digitalWrite(ledPin1, LOW) и digitalWrite(ledPin1, HIGH) для каждого светодиода, а затем запускать delay (100). Как мы это сделали, показывает исходный код на http://www.linuxformat.co.uk/mag/arduino.

Последняя функция зовется void loop(), и без нее не обходится ни один проект Arduino: она заставляет устройство циклически работать, пока идет игра. Вот наша версия:

 void loop(){
   int input_key;
   int winning = true;
   int i=0;
   playSequence (score-1);
   while (winning && i<score){
     input_key = readButtons();
     if (input_key == rand_array[i]){
        i++;
        flashSuccess();
      } else {
        winning = false;
        flashFailure();
      }
   }
   if (winning) {
   score++;
   }
 }

Эта функция воплощает программную логику, описанную ранее. Мы проигрываем последовательность (сперва это одна вспышка), потом погружаемся в цикл while, который выполняется, пока игрок нажимает правильные кнопки. Если игрок ошибся, играем опять, с последовательностью той же длины; а если он правильно воспроизвел последовательность, наградой будет новая игра с увеличенной длиной последовательности для запоминания (это делает score++). Если вы вставите сюда какие-нибудь классные дополнения, сообщите нам, и мы внесем их в наше собственное устройство! LXF

Персональные инструменты
купить
подписаться
Яндекс.Метрика