====== Танцевальная битва ====== {{ :projects:duel:duel_overview.png?nolink |}} * Платформы: Iskra JS * Языки программирования: JavaScript * Тэги: танцы, дуэль, прыжки, спорт, спор, Troyka модуль, кто не скачет... ===== Что это? ===== Кто быстрее? Кто сильнее? Кто круче? Мы поможем поставить точку в подобных спорах раз и навсегда. Вставайте на игровые площадки, жмите старт и прыгайте что есть сил. Светодиоды будут загораться от каждого прыжка, а общий уровень очков покажет количество жидкости в пивной башне. Опередивший соперника на 20 прыжков — победитель. Покажи всем друзьям и коллегам, что ты — король вечеринки. ===== Что нам понадобится? ===== {{ :projects:duel:duel_parts.jpg?nolink |}} - [[amp>product/iskra-js?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Iskra JS]] - [[amp>product/motor-shield?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Motor Shield]] - [[amp>product/arduino-troyka-shield?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Troyka Shield]] - [[amp>product/piezo-vibration-sensor?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Сенсор вибрации 2 шт.]] - [[amp>product/troyka-led-module?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Светодиод «Пиранья» (Troyka-модуль) 2 шт.]] - [[amp>product/immersible-water-pump?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Погружная помпа с трубкой 2 шт.]] - [[amp>product/troyka-button?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Кнопка (Troyka-модуль)]] - [[amp>product/troyka-mosfet-n-channel-v3?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Силовой ключ (Troyka-модуль)]] - [[amp>product/usb-cable-micro?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Кабель USB (A — Micro USB)]] - [[amp>product/usb-power-plug?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Импульсный блок питания (1000 мА)]] - [[amp>product/structor-arduino?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Крепления Arduino и Iskra (#Структор)]] - [[amp>product/structor-troyka?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Крепления Troyka (#Структор)]] - [[amp>product/structor-big?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Пластины большие (#Структор)]] - [[amp>product/structor-middle?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Пластины средние (#Структор)]] - [[amp>product/structor-mega?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Пластина мега (#Структор)(4 шт.)]] - [[amp>product/nylon-screw-m3x8?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Нейлоновые винты М3×8 (8 шт.)]] - [[amp>product/nylon-standoff-fm-m3x8?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Нейлоновые стойки «мама-папа» М3×8 (4 шт.)]] - [[amp>product/nylon-nut-m3?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Нейлоновые гайки М3 (8 шт.)]] - Пивная башня (2 шт.) - Истинная синяя изолента - Многожильный монтажный провод с сечением 2×1 мм² (3 метра) - Лист фанеры 50×50 мм (2 шт.) - Поролон - Балончик белой краски - Крепёжные элементы: винты, саморезы, гайки ===== Как собрать? ===== - Закрепите платформу Iskra JS на панели #структора «крепление Arduino 7×8 шипов» с помощью акриловых стоек. Стойки закрепите на панели винтами, сверху установите платформу и зафиксируйте гайками.{{ :projects:duel:duel_build1.jpg?nolink |}} - Установите Motor Shield на плату Iskra JS.{{ :projects:duel:duel_build2.jpg?nolink |}} - Установите «лицевую панель Arduino c microUSB-интерфейсом 4×6 шипов» и через неё подключите входящие в комплектацию помп «разъёмы-переходники на 2 провода» к Motor Shield. Красный провод к плюсу, чёрный — к минусу.{{ :projects:duel:duel_build3.jpg?nolink |}} - Установите Troyka Shield на бутерброд из плат Iskra JS и Motor Shield.{{ :projects:duel:duel_build4.jpg?nolink |}} - Закрепите два модуля сенсора вибрации с помощью саморезов и гаек на боковых панелях #структора «7×7 шипа».{{ :projects:duel:duel_build5.jpg?nolink |}} - Подключите модули сенсора вибрации к аналоговым пинам ''A0'' и ''A5'' Troyka Shield и через клеммник выведите провода «папа-папа» для дальнейшего подключения пьезо-дисков.{{ :projects:duel:duel_build6.jpg?nolink |}} - Закрепите силовой ключ с помощью нейлоновых винтов к детали #структора (крепление Troyka 4×7 шипа), откусите бокорезами неиспользуемую часть и подключите к ''6'' пину Troyka Shield.{{ :projects:duel:duel_build7.jpg?nolink |}} - Подготовьте провода для подключения колонки к Iskra JS через силовой ключ: - «Плюс» колонки подключите к пину ''5V'' - «Минус» колонки — к «плюсу» силового ключа. - Пин ''GND'' — к «минусу» силового ключа.{{ :projects:duel:duel_build8.jpg?nolink |}} - Прикрепите обновлённые боковые панели («пластины 7×6 шипов») и заднюю панель («пластина 6×6 шипов») к основанию корпуса устройства («крепление Arduino 7×8 шипов»).{{ :projects:duel:duel_build9.jpg?nolink |}} - Закрепите клеммник с помощью двухстороннего скотча к задней панели корпуса и подключите к нему выведенные ранее провода от модулей сенсоров вибрации и силового ключа.{{ :projects:duel:duel_build10.jpg?nolink |}} - Возьмите кнопку (Troyka-модуль), прикрутите её к панели для крепления Troyka-модулей. из четырёх панелей конструктора (3x2 шипов) соорудите «башенку» и закрепите на будущей верхней панели корпуса («пластина 7×8 шипов»).{{ :projects:duel:duel_build11.jpg?nolink |}} - Закрепите два светодиода «Пиранья» на панелях крепления Troyka-модулей, затем закрепите их по бокам верхней панели устройства.{{ :projects:duel:duel_build12.jpg?nolink |}} - Подключите два светодиода «Пиранья» к ''8'' и ''9'' пину Troyka Shield, а кнопку (Troyka-модуль) — к ''3'' пину Troyka Shield.{{ :projects:duel:duel_build13.jpg?nolink |}} - Закончите сборку лицевой части корпуса с помощью реек #структора и пластины (2×6 шипов). Прикрепите верхнюю панель («пластина 7×8 шипов») к передней, задней и боковым стенкам корпуса.{{ :projects:duel:duel_build14.jpg?nolink |}} - Отпаяйте от пьезо-диска короткие провода и замените их на провод длинной около 1–2 метров. Повторите это для второго датчика.{{ :projects:duel:duel_build15.jpg?nolink |}} - Подключите пьезо-диски к модулям сенсора вибрации через установленный ранее клеммник.{{ :projects:duel:duel_build16.jpg?nolink |}} - Припаяйте многожильный монтажный провод к выводам динамика.{{ :projects:duel:duel_build17.jpg?nolink |}} - Соберите корпус для динамика из четырёх «мега пластин» #Структора и подключите его к клеммнику на задней части корпуса.{{ :projects:duel:duel_build18.jpg?nolink |}} - Возьмите две «пивные башни», опустите в них погружные помпы, концы выходящих трубок проведите в противоположную «башню». Помпы будут откачивать жидкость из башен соперника.{{ :projects:duel:duel_build19.jpg?nolink |}}В итоге должна получите схема.{{ :projects:duel:duel_scheme.png?nolink |}} - Подключите помпы к Motor Shield через «разъём-переходник на 2 провода»{{ :projects:duel:duel_build20.jpg?nolink |}} - Выпилите из листа фанеры два модуля «Troyka XXL» и покрасьте их белой краской.{{ :projects:duel:duel_build21.jpg?nolink |}} - На задней части модуля «Troyka XXL» закрепите несколько кусков поролона и приклейте пьезо-диск. Повторите действие со вторым «Troyka XXL» и сенсором вибрации.{{ :projects:duel:duel_build22.jpg?nolink |}} - Расположите все модули на расстоянии в 15–20 см друг от друга и подключите источник питания к устройству.{{ :projects:duel:duel_build23.jpg?nolink |}} Теперь осталось залить любимый «лимонад» в пивные башни и можно играть. ===== Алгоритм ===== * После подключения питания ничего не делаем пока не нажата кнопка «старт/сброс»; * Проверяем текущее количество очков. Если значение очков дошло до левой или правой границы интервала: * заканчиваем игру; * показываем светодиодом победителя; * переходим в режим ожидания нажатия кнопки «старт/сброс»; * Считываем данные с сенсоров; * Если значение сенсора первого игрока превысило порог: * отнимаем от общего числа очков единицу; * мигаем светодиодом об удачном прыжке; * если количество очков кратно 3, включаем «помпу 1» на 3 секунды. Если помпа в данный момент работает, добавляем к времени её работы ещё 3 секунды. * Если значение сенсора второго игрока превысило порог: * прибавляем к общему числу очков единицу; * мигаем светодиодом об удачном прыжке; * если количество очков кратно 3, включаем «помпу 2» на 3 секунды. Если помпа в данный момент работает, добавляем к её времени работы ещё 3 секунды. ===== Исходный код ===== // подключаем библиотеку для проигрывание мелодий // в формате Nokia RTTTL var player = require('@amperka/ringtone').create(P11); // мелодия старта var melodyStart = 'smb:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p'; // мелодия окончания var melodyEnd = 'smbdeath:d=4,o=5,b=90:32c6,32c6,32c6,8p,16b,16f6,16p,'+ '16f6,16f.6,16e.6,16d6,16c6,16p,16e,16p,16c'; // подключаем библиотеку для управления кнопкой // подключенной к пину 3 var button = require('@amperka/button') .connect(P3); // подключаем библиотеку для управления моторами var Motor = require('@amperka/motor'); // присваиваем первому игроку мотор канала M2 на Motor Shield var motorPlayer1 = Motor.connect(Motor.MotorShield.M2); // присваиваем второму игроку мотор канала M1 на Motor Shield var motorPlayer2 = Motor.connect(Motor.MotorShield.M1); // подключаем библиотеку для управления светодиодами // подключенные к пинам 8 и 9 var ledPlayer1 = require('@amperka/led').connect(P8); var ledPlayer2 = require('@amperka/led').connect(P9); // время работы помп var timeWorkPump1 = 3000; var timeWorkPump2 = 3000; // новое время работы помп var newTimeWorkPump1 = 0; var newTimeWorkPump2 = 0; // текущее время начало работы помп var startTimeWorkPump1 = 0; var startTimeWorkPump2 = 0; // текущее количество очков var score = 0; // количество очков для победы var maxScore = 20; // состояние работы помп var idPumpPlayer1 = 0; var idPumpPlayer2 = 0; // состояние готовности устройства принять прыжок игрока var statePlayer1 = 0; var statePlayer2 = 0; // состояние игры var idPlayGame = 0; // состояние конца игры var idEndGame = 0; // прерывание по нажатию кнопки button.on('press', function() { ledPlayer1.turnOn(); ledPlayer2.turnOn(); // проигрываем мелодию о начале игры player.play(melodyStart).then(function() { print('Melody completed'); // сбрасываем игру resetGame(); // начинаем игру playGame(); }); }); // функция начало игры function playGame() { print('Start Game'); idPlayGame = setInterval(function() { // Не закончилась ли игра? if (score >= maxScore || score <= -maxScore) { clearInterval(idPlayGame); print('Stop Game'); // заканчиваем игру endGame(); // проигрываем мелодию об окончании игры player.play(melodyEnd).then(function() { print('End Game'); }); } // считывание значений с датчиков вибрации var sensorVal1 = analogRead(A5); var sensorVal2 = analogRead(A0); // если значение сенсора первого игрока превысило порог if (sensorVal1 > 0.03 && statePlayer1 === 0) { ledPlayer1.turnOn(); statePlayer1 = 1; // отнимаем от общего числа очков единицу score--; print(score); if (score % 3 === 0) { try { clearTimeout(idPumpPlayer1); // интервал времени, который прошёл // на данный момент работы помпы 1 var intervalTimeWorkedPump1 = (getTime()*1000-startTimeWorkPump1); // оставшиеся время работы помпы 1 var lastTimeWorkPump1 = timeWorkPump1 - intervalTimeWorkedPump1; // новое время работы помпы 1 newTimeWorkPump1 = newTimeWorkPump1 + lastTimeWorkPump1; print('Yes Timer for player 1'); print('Timer1 = ', newTimeWorkPump1); pump1Off(newTimeWorkPump1); } catch (e) { newTimeWorkPump1 = timeWorkPump1; print('No Timer for player 1'); print('Timer1 = ', newTimeWorkPump1); pump1CycleOnOff(); } } // следующий засчитанный прыжок будет доступен через 100 мс setTimeout(function() { statePlayer1 = 0; ledPlayer1.turnOff(); print('Continue read player 1'); }, 100); } // если значение сенсора второго игрока превысило порог if (sensorVal2 > 0.03 && statePlayer2 === 0) { ledPlayer2.turnOn(); statePlayer2 = 1; // прибавляем к общему числу очков единицу score++; print(score); if (score % 3 === 0) { try { clearTimeout(idPumpPlayer2); // интервал времени, который прошёл // на данный момент работы помпы 2 var intervalTimeWorkedPump2 = (getTime()*1000-startTimeWorkPump2); // оставшиеся время работы помпы 2 var lastTimeWorkPump2 = timeWorkPump2 - intervalTimeWorkedPump2; // новое время работы помпы 2 newTimeWorkPump2 = newTimeWorkPump2 + lastTimeWorkPump2; print('Yes Timer for player 2'); print('Timer2 = ', newTimeWorkPump2); pump2Off(newTimeWorkPump2); } catch (e) { newTimeWorkPump2 = timeWorkPump2; print('No Timer for player 2'); print('Timer2 = ', newTimeWorkPump2); pump2CycleOnOff(); } } // следующий засчитанный прыжок будет доступен через 100 мс setTimeout(function() { statePlayer2 = 0; ledPlayer2.turnOff(); print('Continue read player 2'); }, 100); } }, 10); } // функция включения помпы первого игрока с последующим отключением function pump1CycleOnOff() { print('Pump Player 1 On'); startTimeWorkPump1 = getTime()*1000; motorPlayer1.write(1.0); ledPlayer1.turnOn(); pump1Off(timeWorkPump1); } // функция выключения помпы первого игрока function pump1Off(time) { startTimeWorkPump1 = getTime()*1000; idPumpPlayer1 = setTimeout(function() { print('Pump Player 1 Off'); motorPlayer1.write(0); ledPlayer1.turnOff(); idPumpPlayer1 = 0; }, time); } // функция включения помпы второго игрока с последующим отключением function pump2CycleOnOff() { print('Pump Player 2 On'); startTimeWorkPump2 = getTime()*1000; motorPlayer2.write(1.0); ledPlayer2.turnOn(); pump2Off(timeWorkPump2); } // функция выключения помпы второго игрока function pump2Off(time) { startTimeWorkPump2 = getTime()*1000; idPumpPlayer2 = setTimeout(function() { print('Pump Player 2 Off'); motorPlayer2.write(0); ledPlayer2.turnOff(); idPumpPlayer2 = 0; }, time); } // выключить помпы и светодиоды function allOff() { motorPlayer1.write(0); motorPlayer2.write(0); ledPlayer1.turnOff(); ledPlayer2.turnOff(); } // сброс игры function resetGame() { score = 0; try { clearInterval(); print('Yes Interval'); } catch (e) { print('No Interval'); } try { clearTimeout(); print('Yes Timeout'); } catch (e) { print('No Timeout'); } allOff(); } // конец игры function endGame() { try { clearInterval(); print('Yes Interval'); } catch (e) { print('No Interval'); } try { clearTimeout(); print('Yes Timeout'); } catch (e) { print('No Timeout'); } if (score === -maxScore) { // если выиграл первый игрок motorPlayer1.write(1.0); motorPlayer2.write(0); ledPlayer1.turnOn(); ledPlayer2.turnOff(); print('Player 1 Win'); } else if (score === maxScore) { // если выиграл второй игрок motorPlayer1.write(0); motorPlayer2.write(1.0); ledPlayer1.turnOff(); ledPlayer2.turnOn(); print('Player 2 Win'); } else { allOff(); print('No Win'); } // после победы одного из игроков // через 10 секунд выключаем помпы и светодиоды idEndGame = setTimeout(function() { allOff(); }, 10000); } ===== Демонстрация работы устройства ====== {{youtube>9LEGHUFEet0?large}} ===== Что дальше? ===== Добавьте в проект [[amp>collection/display?utm_source=proj&utm_campaign=duel&utm_medium=wiki | дисплей]] чтобы показывать точное количество очков. Не оказалось платы [[amp>product/iskra-js?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Iskra JS]], не беда. Любой обладатель платы [[amp>collection/arduino?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Arduino]] может собрать подобный проект — в его основе лежит скетч из [[конспект-arduino:перетягивание-каната|20 эксперимента]] [[amp>product/matryoshka-z?utm_source=proj&utm_campaign=duel&utm_medium=wiki | Матрёшки Z]]. Используй больше датчиков вибрации и собери свой ответ Dance Dance Revolution.