DIY ウィザード杖キット - Arduino 101、LEDストリップ、電圧レギュレーター等
部品と材料
| 部品名 | 数量 | 備考 |
|---|---|---|
| Arduino 101 | 1 | IMU (慣性計測装置) 内蔵 |
| Pololu 電圧レギュレーター ($4) | 1 | 5V 出力、高効率 |
| 単3電池ボックス (スイッチ付き) ×3本用 | 1 | |
| 単3電池 | 3本 | 種類は問わない |
| M バレルジャックアダプター (オス) | 1 | $1.95 電源着脱が容易に |
| F バレルジャックアダプター (メス) | 1 | $1.95 電源着脱が容易に |
| アドレサブル LED ストリップ (NeoPixel 互換) | 1 | 60 LED 版、例題コードはこれに対応 |
| スポーツテープ (布テープ) | 1巻 | 電子部品固定用、グリップにもなる |
| ゴムバンド | 1袋 | テープ固定までの仮止め用 |
| 杖の芯材 (ほうきの柄、スプリンクラーパイプ、天然木の杖等) | 1 | お好みで |
使用アプリとオンラインサービス
- Arduino IDE
プロジェクト概要
このプロジェクトでは、Arduino 101、電池、LED ストリップを使って、ジェスチャーで操作する「ウィザードの杖」を作ります。杖を特定の動きで振ることで、3種類の異なる発光パターン「呪文」を呼び出せます。

技術的仕組み
コードは Arduino 101 内蔵の慣性計測装置 (IMU) を使って Gフォースを読み取りますが、パターンマッチングエンジン (PME) は使用していません (PME 使用例は Kitty Yeung 氏の PME プロジェクトを参照)。代わりに、X、Y、Z 方向の急激な衝撃 (インパルス) をカウントし、短時間内に特定の組み合わせが検出されたときに「呪文」を発動させます。
呪文の唱え方
杖を地面に軽く 2回縦にタップ して呪文を開始し、次に杖を 鋭く前方へ突き出す、左右に傾ける、または さらに縦に1回タップ することで 3 種類の呪文から 1 つを選択、最後に 縦にタップ して呪文を発動します。
視覚フィードバック
ジェスチャーのどの段階にいるかを理解しやすくするため、LED ストリップで視覚フィードバックを行います。最初の 2 回タップで白色フラッシュし、「呪文開始」を通知。以降の各ステップでも視覚フィードバックがあるため、どの呪文が選択されるかがわかります。
呪文の詳細な唱え方
Arduino 101 の USB ポートが下向き、ピンヘッダーが自分側を向くように杖を握ります。
- 杖を地面に軽く 2回タップ (空中で 2 回鋭く縦に振っても OK)。白色フラッシュしたら呪文開始。
- 以下のいずれかの動作で呪文を選択:
- 前方/後方に鋭く振る → 呪文 1 (緑色点灯)
- 左右に傾ける → 呪文 2 (青色点灯)
- さらに縦に 1 回タップ → 呪文 3 (赤色点灯)
- LED が単色で点灯したら、最後に縦に 1 回タップ して呪文を発動。選択された色で輝きとフラッシュを繰り返します。
重要: ジェスチャーは時間制限あり! 各ステップ間は 1 秒以内 に次の動作をしないとリセットされます。失敗しても 1 秒待てば最初からやり直せます。この仕様により、杖を持って歩いているだけで誤って呪文が発動することはありません。正確に唱えるには少しコツがいりますが、練習すればマスターできます。
電子部品の組み立て
基本的には Arduino 101、電池、NeoPixel LED ストリップの 3 要素だけです。
電源構成にはいくつか選択肢があります。最も手軽なのは 9V 電池で Arduino 101 から直接 LED を駆動する方法ですが、電池寿命は非常に短いです。LiPo 電池を使えば電力/重量比を最大化でき「プロっぽい」仕上がりになりますが、安全な充電環境が必要です。筆者は 単3電池 3本 + 4ドルの電圧レギュレーター という、電池寿命とシンプルさのバランスが取れた構成を採用しました。これは「シャドーシアター」と「カレイドスコープ無限鏡」プロジェクトと同じ電源構成です。
ステップ 1: LED ストリップ端子の準備
LED ストリップの端から約 1cm の位置でカットし、ハーネスを取り出します。被覆を剥いて芯線を露出させます。


ステップ 2: 電圧レギュレーターへのヘッダーピン半田付け
電圧レギュレーター付属の曲がったヘッダーピンを分解し、緑と黄色のワイヤー先端に 2 本半田付けします。これで Arduino 101 のピンに差し込みやすくなります。

ステップ 3: 電圧レギュレーター配線
赤色ワイヤーを電圧レギュレーターの VIN 端子に半田付け。GND と VOUT にはストレートヘッダーピンを半田付けします。

ステップ 4: バレルジャックアダプター接続
電池ボックスのワイヤー先端を剥き、オスバレルジャックの端子に接続してネジで固定。赤 = (+)、黒 = (-) です。

次に、LED ストリップ側のワイヤーをメスバレルジャックに接続。黄色 = (-)、赤 = (+) です。

ステップ 5: Arduino 101 への接続
ハーネスを Arduino 101 に接続します:
- 緑色ワイヤー → PIN 6
- 黄色ワイヤー → PIN 13 付近の GND
- 電圧レギュレーターのヘッダーピン: GND → GND、VOUT → Arduino 101 の「VIN」ピン
注意: VIN と GND を逆にすると発煙・発火の危険があります。筆者は経験済みです。


サンプルコードの書き込み
ステップ 1
電池ボックスのスイッチを OFF にした状態で、USB ケーブルで Arduino 101 を PC に接続し、Arduino IDE でサンプルコードを書き込みます。
Arduino 101 初めての方は 公式ガイド を参照してください。
書き込み成功後、USB を外し、電池ボックスのスイッチを ON にして、USB ポートを下向き・ピンを自分側にして杖を振り、縦方向に 4 回動かして「赤の呪文」が出るかテストしてみてください。
コード解説 (コードをそのまま使うだけなら飛ばして「杖の組み立て」へ)
サンプルコードは Arduino 101 の衝撃検出機能を使って衝撃をカウントし、新しい衝撃がない状態が短時間続くとカウントをリセットするタイマーを併用しています。タイマーが切れる前に正しい組み合わせが検出されると、「呪文」関数が呼ばれます。
地面をしっかりタップすると、初期衝撃に加えて杖の振動による「ノイズ衝撃」が複数検出されます。ループ冒頭のコードは、各衝撃バッチから主要なインパルスを抽出し、縦方向、左右方向、前後方向のいずれかに分類します。gesture 変数で各主要インパルスを保持: 1 = 縦方向、2 = 前後方向、3 = 左右方向。
組み合わせ追跡には switch 文を使い、ジェスチャーが蓄積されるにつれてステップを進めます。「次のインパルスが左右ならステップ 2 へ、タイマー切れならステップ 0 へ戻る」といった「ゲームブック」のようなロジックです。
呪文の定義
ここを変えれば各呪文の演出を簡単に変更できます。現在は Adafruit NeoPixel ライブラリの theaterChase サンプルを色違いで実行するだけです。呪文を増やしたり、色を変えたり、独自の発光関数を書いたりも容易です。theaterChase の最初の 3 引数が R, G, B 値、最後の引数が速度 (大きいほど遅い) です。
///////////////////////////////////////////////// spells //////////////////////
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: 杖の芯材を用意
ほうきの柄、スプリンクラーパイプ、天然木の杖などなんでも OK。Michael's などのクラフトストアでも良い杖が売っています (杖コーナーにあります)。スポーツテープを巻いて電子部品の作業エリアをきれいにすると作業しやすいです (任意)。


ゴムバンドをいくつか杖に掛けて、部品を取り付けやすくします。

ステップ 2: Arduino 101 を杖に取り付け (USB ポート下向き)
ゴムバンドで Arduino 101 を固定します。

ステップ 3: LED ストリップを巻き付け
両端をゴムバンドで仮止めしながら LED ストリップを巻き付けます。


ステップ 4: スポーツテープで Arduino 101 と電池ボックスを固定
電池ボックスの ON/OFF スイッチにアクセスできるよう注意しながら、スポーツテープでしっかり巻き固定します。杖を振っても外れないよう固定しますが、電池交換のことも考えておきましょう。


ステップ 5: LED 光拡散とデコレーション



ソースコード
ウィザード杖 サンプルコード (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 <Adafruit_NeoPixel.h>
#define PIN 6 // NeoPixel 接続ピン
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800); // 60 LED
int tr = 0, tg = 0, tb = 0; // 目標色 (スムージング用)
int r = 0, g = 0, b = 0; // 現在色
long globaltimer = 0; // ジェスチャータイムアウト用
long gesturetimer = 0; // ジェスチャー段階管理用
long ledtimer = 0; // LED 更新タイミング用
int fade = 10; // 色変化スピード (スムージング用)
int tap = 0; // 縦方向インパルスカウンタ
int lr = 0; // 左右方向インパルスカウンタ
int fb = 0; // 前後方向インパルスカウンタ
int gesture = 0; // 分類されたジェスチャー: 1=縦, 2=左右, 3=前後
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); // 50ms 以上のスパイクで割り込み
CurieIMU.interrupts(CURIE_IMU_SHOCK);
strip.begin();
strip.show(); // 全 LED 消灯で初期化
}
void loop() {
// 基本フィルター: IMU は反動・打ち消しで複数の衝撃を検知する。
// 各ジェスチャーの主要な衝撃軸を特定しようと試みる。
if (millis() - globaltimer > 170) {
// tap, lr, fb の合計を比較し、最大のものを採用
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;
}
}
// 1秒でグローバルタイムアウト・リセット
if (millis() - globaltimer > 1000) {
globaltimer = millis() - 170;
tr = 0; tg = 0; tb = 0;
state = 0;
}
// 1秒でジェスチャータイムアウト・リセット
if (millis() - gesturetimer > 1000) {
gesturetimer = millis() - 350;
tr = 0; tg = 0; tb = 0;
state = 0;
}
// ステートマシン: ジェスチャーシーケンス追跡
switch (state) {
case 0: { // 初期状態: 最初のタップ待ち
if (millis() - gesturetimer > 350) {
if (gesture == 1) {
state = 1; gesture = 0; gesturetimer = millis();
}
}
break;
}
case 1: { // 1回タップ検出済み: 2回目のタップ待ち
if (millis() - gesturetimer > 350) {
if (gesture == 1) {
r = 10; g = 10; b = 10; // 白フラッシュ フィードバック
state = 2; gesture = 0; gesturetimer = millis();
}
}
break;
}
case 2: { // 2回タップ検出済み: ここで 3 つの呪文に分岐
if (millis() - gesturetimer > 350) {
if (gesture == 1) { state = 3; gesture = 0; gesturetimer = millis(); } // タップ → 呪文3
if (gesture == 2) { state = 4; gesture = 0; gesturetimer = millis(); } // 左右 → 呪文2
if (gesture == 3) { state = 5; gesture = 0; gesturetimer = millis(); } // 前後 → 呪文1
}
break;
}
case 3: { // 3回タップ: 呪文3 (赤) 選択中、最終タップ待ち
tr = 20; tg = 0; tb = 0; globaltimer = millis() - 250;
if (millis() - gesturetimer > 350) {
if (gesture == 1) { state = 0; tapspell(); }
}
break;
}
case 4: { // 2タップ+左右: 呪文2 (青) 選択中、最終タップ待ち
tr = 0; tg = 0; tb = 20; globaltimer = millis() - 250;
if (millis() - gesturetimer > 350) {
if (gesture == 1) { state = 0; leftspell(); }
}
break;
}
case 5: { // 2タップ+前後: 呪文1 (緑) 選択中、最終タップ待ち
tr = 0; tg = 20; tb = 0; globaltimer = millis() - 250;
if (millis() - gesturetimer > 350) {
if (gesture == 1) { state = 0; forwardspell(); }
}
break;
}
default: break;
}
// LED 色スムージング (非ブロッキング)
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();
}
}
// 衝撃検出時の割り込みコールバック
static void eventCallback(void) {
if (CurieIMU.getInterruptStatus(CURIE_IMU_SHOCK)) {
if (CurieIMU.shockDetected(X_AXIS, POSITIVE) || CurieIMU.shockDetected(X_AXIS, NEGATIVE)) {
tap++; globaltimer = millis();
}
if (CurieIMU.shockDetected(Y_AXIS, POSITIVE) || CurieIMU.shockDetected(Y_AXIS, NEGATIVE)) {
lr++; globaltimer = millis();
}
if (CurieIMU.shockDetected(Z_AXIS, POSITIVE) || 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();
}
// Adafruit NeoPixel 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);
}
}
}
}
/* このライブラリはフリーソフトウェアです。GNU LGPL v2.1 またはそれ以降の条件で再配布・変更可能です。
詳細は GNU LGPL を参照してください。 */
製造プロセス
- Raspberry Pi用DIY Google Voice HATの自作ガイド
- Raspberry Pi と Arduino を連携させてロボットを自作:ステップバイステップガイド
- SolidWorksでプログレッシブカットアウェイアニメーションを作成: ステップバイステップチュートリアル
- 積層造形の後処理を守る7つの実証済みヒント
- AC電界の検出:静電結合とシールドのハンズオンガイド
- 抵抗回路での電力消費計算
- 製造業の日を祝おう:自分に合った日を選んで参加しよう
- 機能するIoTビジネスモデルの構築と検証――実践ガイド
- 射出成形金型の設計・製造エキスパート・ガイド
- ガラス繊維製造の完全ガイド
- 食品の色:鮮やかな色相が食欲とプレゼンテーションを高める仕組み