DIY-кит «Посох волшебника»: Arduino 101, LED-лента, стабилизатор напряжения
Компоненты и материалы
| Arduino 101 | × | 1 шт. |
| Стабилизатор напряжения Pololu ($4) | × | 1 шт. |
| Кейс для 3×AA батареек с выключателем | × | 1 шт. |
| AA батарейки (любые, 3 шт.) | × | 1 комплект |
| Адаптер «барел-джек» (мама, $1.95) | × | 1 шт. |
| Адаптер «барел-джек» (папа, $1.95) | × | 1 шт. |
| Адресуемая LED-лента (NeoPixel-совместимая, 60 пикселей) | × | 1 шт. |
| Спортивная лента (хлопковая, для фиксации и рукояти) | × | 1 рулон |
| Резиновые кольца (для временной фиксации) | × | 1 упаковка |
| Посох (метла, труба для полива, натуральная трость и т.д.) | × | 1 шт. |
Программное обеспечение и онлайн-сервисы
- Arduino IDE
О проекте
Техническая часть
Код использует инерциальный модуль (IMU) Arduino 101 для измерения перегрузок, но не использует Pattern Matching Engine (пример с PME см. в проекте Kitty Yeung). Вместо этого код считает быстрые импульсы по осям X, Y, Z и запускает «заклинания» при определённых комбинациях в короткое время.
Как колдовать заклинания
Два быстрых вертикальных удара запускают зарядку заклинания, затем резкое движение вперёд, влево/вправо или третий вертикальный удар выбирают одно из трёх заклинаний, и финальный вертикальный удар его активирует.
Визуальная обратная связь
LED-лента помогает понять, на каком этапе вы находитесь. После двойного удара — былая белая вспышка (заклинание начато). На каждом шаге — индикация выбранного заклинания.
Детальная схема жестов
Держите посох с Arduino 101 лицом к себе. Сначала дважды ударьте посохом о землю (нежно!) или сделайте два быстрых вертикальных импульса в воздухе. При белой вспышке:
- Резкое движение вперёд/назад → Заклинание 1 (зелёное свечение)
- Наклон влево/вправо → Заклинание 2 (синее свечение)
- Третий вертикальный импульс → Заклинание 3 (красное свечение)
Когда цвет стал сплошным — финальный вертикальный удар запускает эффект: лента мерцает и вспыхивает выбранным цветом.
Жесты чувствительны ко времени! У вас 1 секунда между шагами, иначе заклинание сбрасывается. Если ошиблись — подождите секунду и начните заново. Случайные срабатывания при ходьбе исключены.
Сборка электроники
По сути: Arduino 101, батарея, лента NeoPixel.
Варианты питания: 9V-крона (быстро, но мало емкость), LiPo (профессионально, но нужен зарядник), 3×AA + $4 стабилизатор — баланс емкости и простоты (та же схема, что в проектах Shadow Theater и Kaleidoscope Infinity Mirror).
Шаг 1: Подготовка разъёма ленты
Отрежьте концевой разъём ленты с запасом ~1 см. Снимите изоляцию с проводов.
Шаг 2: Паяем пины к сигнальным проводам
Разберите гнутые пины из комплекта стабилизатора, припаяйте два к зелёному и жёлтому проводам ленты — для удобного подключения к пинам Arduino.
Шаг 3: Питание на стабилизаторе
Красный провод ленты → VIN стабилизатора. Два прямых пина в GND и VOUT.
Шаг 4: Бочковые разъёмы (barrel jack)
Провода батарейного отсека → мама-разъём: КРАСНЫЙ → (+), ЧЁРНЫЙ → (−).
Свободные провода от начала ленты → папа-разъём: ЖЁЛТЫЙ → (−), КРАСНЫЙ → (+).
Шаг 5: Подключение к Arduino 101
ЗЕЛЁНЫЙ → PIN 6, ЖЁЛТЫЙ → GND (рядом с #13). На стабилизаторе: GND → GND, VOUT → VIN Arduino. Не перепутайте полярность! Обратное подключение — запах гари и сгоревшие компоненты (проверено на личном опыте).
Загрузка примера кода
- Батарейный отсек ВЫКЛ. Подключите Arduino 101 к ПК через USB, загрузите код через Arduino IDE. Инструкции для Arduino 101: arduino.cc.
- Отключите USB, включите батареи, тестируйте жесты (USB-порт вниз, пины к себе). Четыре вертикальных движения → Красное заклинание.
Разбор кода (можно пропустить, если используете как есть)
Код использует детектор ударов Arduino 101 для подсчёта импульсов с таймером сброса. Правильная комбинация до истечения таймера вызывает функцию «заклинания».
Жёсткий удар даёт основной импульс + шум от вибраций. Код выделяет доминирующий импульс из пакета и классифицирует: 1 = вертикаль, 2 = вперёд/назад, 3 = влево/вправо.
Машина состояний (switch) отслеживает последовательность: «если следующий импульс влево/вправо — переход к шагу 2, если таймер истёк — сброс».
Функции заклинаний (легко менять)
Сейчас — theater-chase в разных цветах. Первые три аргумента theaterChase — R, G, B, последний — скорость (больше = медленнее).
void tapspell() { // Красное
theaterChase(strip.Color(127, 20, 0), 20);
theaterChase(strip.Color(127, 20, 50), 55);
strip.show();
}
void leftspell() { // Синее
theaterChase(strip.Color(0, 0, 100), 20);
theaterChase(strip.Color(0, 30, 120), 75);
strip.show();
}
void forwardspell() { // Зелёное
theaterChase(strip.Color(0, 127, 0), 30);
theaterChase(strip.Color(0, 127, 90), 55);
strip.show();
}
Сборка посоха
Много места для творчества! Базовая техника ниже, но делайте уникальный посох. Диффузоры (тьюль, тиссу, пластиковая бутылка над LED) создают классные эффекты.
Шаг 1: Находим палку
Метла, труба, натуральная трость. В Michael's (раздел с палками) тоже есть хорошие. Опционально: обмотайте участок спортивной лентой — чистая рабочая зона.
Накладьте несколько резинок для удобства крепления.
Шаг 2: Arduino 101 на палке (USB-порт вниз)
Шаг 3: Намотка LED-ленты (резинки на обоих концах)
Шаг 4: Спортивная лента — фиксация Arduino и батарейного отсека
Оставьте доступ к выключателю батарейного отсека. Батареи придётся менять.
Шаг 5: Диффузия и декор
Программный код
Wizard Walking Staff Example Code (Arduino)
Подсчёт импульсов по осям X, Y, Z, определённые комбинации вызывают «заклинания» (световые эффекты) на NeoPixel-ленте.
/* Copyright (c) 2016 Intel Corporation. All rights reserved.
Arduino 101 "Wizard's Walking Staff."
Библиотека Adafruit Neopixel: https://learn.adafruit.com/adafruit-neopixel-uberguide/arduino-library-installation
Требуется актуальный Intel Curie Core: https://www.arduino.cc/en/Guide/Arduino101
*/
#include "CurieIMU.h"
#include
#define PIN 6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);
int tr = 0, tg = 0, tb = 0; // целевые цвета для сглаживания
int r = 0, g = 0, b = 0; // текущие цвета
long globaltimer = 0;
long gesturetimer = 0;
long ledtimer = 0;
int fade = 10; // скорость затухания/сглаживания
int tap = 0; // счётчик вертикальных импульсов
int lr = 0; // счётчик левых/правых
int fb = 0; // счётчик вперёд/назад
int gesture = 0;
int state = 0; // состояние машины состояний
void setup() {
globaltimer = millis();
gesturetimer = millis();
ledtimer = millis();
CurieIMU.begin();
CurieIMU.attachInterrupt(eventCallback);
CurieIMU.setDetectionThreshold(CURIE_IMU_SHOCK, 1500); // 1.5g = 1500 mg
CurieIMU.setDetectionDuration(CURIE_IMU_SHOCK, 50); // мс пика для прерывания
CurieIMU.interrupts(CURIE_IMU_SHOCK);
strip.begin();
strip.show(); // все пиксели выкл.
}
void loop() {
// Базовый фильтр: IMU регистрирует множественные удары от отскока.
// Пытаемся выделить доминирующий импульс в каждом жесте.
if (millis() - globaltimer > 170) {
if ((tap > lr) && (tap > fb)) {
gesture = 1; tap = 0; lr = 0; fb = 0;
} else if ((lr > fb) && (lr > tap)) {
gesture = 2; tap = 0; lr = 0; fb = 0;
} else if ((fb > lr) || (fb > tap)) {
gesture = 3; tap = 0; lr = 0; fb = 0;
}
}
// Таймауты сброса
if (millis() - globaltimer > 1000) {
globaltimer = millis() - 170;
tr = tg = tb = 0;
state = 0;
}
if (millis() - gesturetimer > 1000) {
gesturetimer = millis() - 350;
tr = tg = tb = 0;
state = 0;
}
// Машина состояний жестов
switch (state) {
case 0: // ожидание первого вертикального удара
if (millis() - gesturetimer > 350 && gesture == 1) {
state = 1; gesture = 0; gesturetimer = millis();
}
break;
case 1: // один удар записан, ждём второй
if (millis() - gesturetimer > 350 && gesture == 1) {
r = g = b = 10; // белая вспышка обратной связи
state = 2; gesture = 0; gesturetimer = millis();
}
break;
case 2: // два удара — точка ветвления для трёх заклинаний
if (millis() - gesturetimer > 350) {
if (gesture == 1) { state = 3; gesture = 0; gesturetimer = millis(); }
if (gesture == 2) { state = 4; gesture = 0; gesturetimer = millis(); }
if (gesture == 3) { state = 5; gesture = 0; gesturetimer = millis(); }
}
break;
case 3: // три вертикальных удара — режим tapspell (красное)
tr = 20; tg = 0; tb = 0; globaltimer = millis() - 250;
if (millis() - gesturetimer > 350 && gesture == 1) {
state = 0; tapspell();
}
break;
case 4: // два удара + влево/вправо — leftspell (синее)
tr = 0; tg = 0; tb = 20; globaltimer = millis() - 250;
if (millis() - gesturetimer > 350 && gesture == 1) {
state = 0; leftspell();
}
break;
case 5: // два удара + вперёд/назад — forwardspell (зелёное)
tr = 0; tg = 20; tb = 0; globaltimer = millis() - 250;
if (millis() - gesturetimer > 350 && gesture == 1) {
state = 0; forwardspell();
}
break;
}
// Сглаживание цветов и вывод на ленту (неблокирующее)
if (millis() - ledtimer > fade) {
if (tr > r + 1) r++; if (tg > g + 1) g++; if (tb > b + 1) b++;
if (tr < r) r--; if (tg < g) g--; if (tb < b) b--;
for (int i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, r, g, b);
}
strip.show();
ledtimer = millis();
}
}
// Прерывание от IMU при обнаружении удара
static void eventCallback(void) {
if (CurieIMU.getInterruptStatus(CURIE_IMU_SHOCK)) {
if (CurieIMU.shockDetected(X_AXIS, POSITIVE)) { tap++; globaltimer = millis(); }
if (CurieIMU.shockDetected(X_AXIS, NEGATIVE)) { tap++; globaltimer = millis(); }
if (CurieIMU.shockDetected(Y_AXIS, POSITIVE)) { lr++; globaltimer = millis(); }
if (CurieIMU.shockDetected(Y_AXIS, NEGATIVE)) { lr++; globaltimer = millis(); }
if (CurieIMU.shockDetected(Z_AXIS, POSITIVE)) { fb++; globaltimer = millis(); }
if (CurieIMU.shockDetected(Z_AXIS, NEGATIVE)) { fb++; globaltimer = millis(); }
}
}
///////////////// Функции заклинаний /////////////////
void tapspell() {
theaterChase(strip.Color(127, 20, 0), 20);
theaterChase(strip.Color(127, 20, 50), 55);
strip.show();
}
void leftspell() {
theaterChase(strip.Color(0, 0, 100), 20);
theaterChase(strip.Color(0, 30, 120), 75);
strip.show();
}
void forwardspell() {
theaterChase(strip.Color(0, 127, 0), 30);
theaterChase(strip.Color(0, 127, 90), 55);
strip.show();
}
// Theatre-style crawling lights (из примера Adafruit Strandtest)
void theaterChase(uint32_t c, uint8_t wait) {
for (int j = 0; j < 10; j++) {
for (int q = 0; q < 3; q++) {
for (uint16_t i = 0; i < strip.numPixels(); i += 3) {
strip.setPixelColor(i + q, c);
}
strip.show();
delay(wait);
for (uint16_t i = 0; i < strip.numPixels(); i += 3) {
strip.setPixelColor(i + q, 0);
}
}
}
}
Производственный процесс
- Обнаружение электрических полей переменного тока: Практическое руководство по емкостной связке и экраниров…
- Расчет рассеиваемой мощности в резистивных цепях
- Семь проверенных советов для защиты вашей пост-обработки в аддитивном производстве
- Пищевой цвет: как яркие оттенки усиливают аппетит и презентацию
- Как строить и валидировать работающие IoT бизнес-модели — практический гайд
- Сборка DIY Google Voice HAT для Raspberry Pi
- Создание робота на Arduino и Raspberry Pi: пошаговое руководство
- Полное руководство по производству стекловолокна
- Празднуйте производственное мастерство — выберите свой день для Дня производства
- Создание анимации прогрессивного среза в SolidWorks — Пошаговый урок