Arduino MKRバンドルで作るセキュアIoTパズルボックス:ステップバイステップガイド
部品と用品
| Arduino MKR IoT Bundle | 1 |
アプリとオンラインサービス
| Blynk |
このプロジェクトについて
注意: このチュートリアルは古い可能性があります。最新版は こちら をご覧ください。
大切なものを人の目から隠すのは難しいものです。大きな金庫でもあれば別ですが、そんなスペースはありません。
そこで、MKR IoT Bundleの部品と段ボールで自分だけのパズルボックスを作りましょう! あなたの持ち物の安全は保証できませんが、泥棒への楽しい抑止力にはなります。もちろん、中に入れるのは貴重品ではなくお菓子にしておくことをお勧めします。
ひとことで言うと
サーボモーターでロックされた箱を開けるには、3つのポテンショメーターを正しい組み合わせに回す必要があります。正解の組み合わせはオンラインアプリ「Blynk」で設定可能。RGB LEDが色でヒントをくれます:正解に近づくほど暖色(青→水色→黄→赤)に変化します。
正しい組み合わせになるとブザーが曲を鳴らし、サーボが箱を開けます。
必要なコンポーネント:
- ブザー
- RGB LED
- ポテンショメーター ×3
- LCD画面
- サーボモーター

学習目標
- Blynk IoTプラットフォームの導入
- LCD画面の配線と使用
- ブザーでスターウォーズのテーマを演奏
もっと知りたい方へ
このチュートリアルは、MKR1000とIoTに慣れるための一連の実験の一部です。すべてMKR IoT Bundleの部品で製作可能。
- I Love You Pillow
- Puzzle Box(本プロジェクト)
- Pavlov's Cat
- The Nerd
- Plant Communicator
Blynkの紹介
BlynkはIoT向けの人気モバイルアプリで、インターネット接続されたArduinoをどこからでもいつでも簡単に制御できます。Kickstarterで資金調達され、優れたドキュメントとシンプルさでこの分野で最も使われるアプリの一つになりました。
Blynkの始め方
新規プロジェクト作成は簡単。以下の手順に従うか、Blynk公式の入門ガイドを参照してください。









プロジェクト作成成功後、メールで Auth Token が届きます。これはハードウェアとスマホを接続するための一意の識別子で、プロジェクトごとに異なります。
Arduinoをアプリと接続するにはBlynkライブラリが必要。Arduino Web Editorを使えばインクルード時に自動ダウンロード、そうでなければライブラリマネージャーからインストールしてください。
では、このスケッチをアップロード してスライダーを動かしてみてください:
#include <WiFi101.h>
#include <BlynkSimpleWiFiShield101.h>
const char* ssid = SECRET_SSID;
const char* password = SECRET_PSWD;
char auth[] = SECRET_TOKEN;
int SliderValueOne = 1;
int SliderValueTwo = 1;
int SliderValueThree = 1;
BLYNK_WRITE(V1) { SliderValueOne = param.asInt(); }
BLYNK_WRITE(V2) { SliderValueTwo = param.asInt(); }
BLYNK_WRITE(V3) { SliderValueThree = param.asInt(); }
void setup() {
Serial.begin(9600);
Blynk.begin(auth, ssid, password);
}
void loop() {
int Temp_Slider_One_value = SliderValueOne;
int Temp_Slider_Two_value = SliderValueTwo;
int Temp_Slider_Three_value = SliderValueThree;
Blynk.run();
if(Temp_Slider_One_value != SliderValueOne ||
Temp_Slider_Two_value != SliderValueTwo ||
Temp_Slider_Three_value != SliderValueThree){
Serial.print("New combination: ");
Serial.print(SliderValueOne);
Serial.print(" ");
Serial.print(SliderValueTwo);
Serial.print(" ");
Serial.println(SliderValueThree);
}
}

LCD画面の使用
画面を接続しましょう! LCDは簡単ですが配線が多いので、忍耐が必要です。

5V電源と220Ω抵抗を使用します。輝度はアナログピン3の出力値(0〜255、0が最大輝度)で調整:
analogWrite(A3, 0);
サンプルスケッチをアップロードして動作確認:
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 2, d5 = 3, d6 = 4, d7 = 5;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
void setup() {
analogWrite(A3, 0);
lcd.begin(16, 2);
lcd.print("hello, world!");
}
void loop() {
lcd.setCursor(0, 1);
lcd.print(millis() / 1000);
}
ポテンショメーターの追加
ポテンショメーターの値を読むには、正しいピンで analogRead() するだけ。アナログピン0、1、2に接続します。

ポテンショメーターの値は0〜1023なので、組み合わせを推測不可能にするため map() 関数で0〜9に変換:
int PotOne = map(analogRead(A0), 0, 1023, 0, 9);
LCDにポテンショメーター値を表示するサンプル:
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 2, d5 = 3, d6 = 4, d7 = 5;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
void setup() {
analogWrite(A3, 0);
Serial.begin(9600);
lcd.begin(16, 2);
}
void loop() {
int PotOne = map(analogRead(A0), 0, 1023, 0, 9);
int PotTwo = map(analogRead(A1), 0, 1023, 0, 9);
int PotThree = map(analogRead(A2), 0, 1023, 0, 9);
lcd.setCursor(0, 0); lcd.print(PotOne);
lcd.setCursor(2, 0); lcd.print(PotTwo);
lcd.setCursor(4, 0); lcd.print(PotThree);
}
RGB LEDの追加
RGB LEDで組み合わせの近さをフィードバック。正解に近いほど暖色(青→水色→黄→赤)に変化。


int redPin = 6, greenPin = 8, bluePin = 7;
void setup() {
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
Serial.begin(9600);
}
void loop() {
setColor(0, 0, 255); delay(1000); // blue
setColor(0, 255, 255); delay(1000); // aqua
setColor(255, 255, 0); delay(1000); // yellow
setColor(255, 0, 0); delay(1000); // red
}
void setColor(int red, int green, int blue) {
analogWrite(redPin, red);
analogWrite(greenPin, green);
analogWrite(bluePin, blue);
}
Blynkと接続
ボードをBlynkに接続し、ポテンショメーターをLCDに表示、正解時にLEDを緑点滅させるところまで統合。
giveColorFeedback()関数で、各ポテンショメーターの絶対値が正解から一定閾値以内ならLED色を変更- アプリから送られる値(正解の組み合わせ)を格納する変数
int SliderValueOne = 1;
int SliderValueTwo = 1;
int SliderValueThree = 1;
初期値は1。アプリのスライダーを動かさない限り変わりません。ボードをリセットするとデフォルト値(1,1,1)に戻ります。
bool start = true;で正解済みかどうかを判定し、ループ毎に箱が開かないように制御
#include <LiquidCrystal.h>
#include <SPI.h>
#include <WiFi101.h>
#include <BlynkSimpleWiFiShield101.h>
int redPin = 6, greenPin = 8, bluePin = 7;
const char* ssid = SECRET_SSID;
const char* password = SECRET_PSWD;
char auth[] = SECRET_TOKEN;
const int rs = 12, en = 11, d4 = 2, d5 = 3, d6 = 4, d7 = 5;
bool start = true;
int SliderValueOne = 1, SliderValueTwo = 1, SliderValueThree = 1;
BLYNK_WRITE(V1) { SliderValueOne = param.asInt(); }
BLYNK_WRITE(V2) { SliderValueTwo = param.asInt(); }
BLYNK_WRITE(V3) { SliderValueThree = param.asInt(); }
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
void setup() {
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
analogWrite(A3, 0);
Serial.begin(9600);
lcd.begin(16, 2);
Blynk.begin(auth, ssid, password);
}
void loop() {
int Temp_Slider_One_value = SliderValueOne;
int Temp_Slider_Two_value = SliderValueTwo;
int Temp_Slider_Three_value = SliderValueThree;
Blynk.run();
if(Temp_Slider_One_value != SliderValueOne ||
Temp_Slider_Two_value != SliderValueTwo ||
Temp_Slider_Three_value != SliderValueThree){
Serial.print("New combination: ");
Serial.print(SliderValueOne); Serial.print(" ");
Serial.print(SliderValueTwo); Serial.print(" ");
Serial.println(SliderValueThree);
}
int PotOne = map(analogRead(A0), 0, 1023, 0, 9);
int PotTwo = map(analogRead(A1), 0, 1023, 0, 9);
int PotThree = map(analogRead(A2), 0, 1023, 0, 9);
lcd.setCursor(0, 0); lcd.print(PotOne);
lcd.setCursor(2, 0); lcd.print(PotTwo);
lcd.setCursor(4, 0); lcd.print(PotThree);
if (start) {
giveColorFeedback(PotOne, PotTwo, PotThree);
if (PotOne == SliderValueOne && PotTwo == SliderValueTwo && PotThree == SliderValueThree) {
blinkGreenLed();
start = false;
}
}
if(!start) {
if(PotOne == 0 && PotTwo == 0 && PotThree == 0) start = true;
}
}
void giveColorFeedback(int PotOne, int PotTwo, int PotThree) {
if (abs(PotOne - SliderValueOne) <= 1 && abs(PotTwo - SliderValueTwo) <= 1 && abs(PotThree - SliderValueThree) <= 1)
setColor(255, 0, 0); // Red
else if (abs(PotOne - SliderValueOne) <= 3 && abs(PotTwo - SliderValueTwo) <= 3 && abs(PotThree - SliderValueThree) <= 3)
setColor(255, 255, 0); // Yellow
else if (abs(PotOne - SliderValueOne) <= 4 && abs(PotTwo - SliderValueTwo) <= 4 && abs(PotThree - SliderValueThree) <= 4)
setColor(0, 255, 255); // Aqua
else
setColor(0, 0, 255); // Blue
}
void blinkGreenLed() {
for (int a = 0; a < 2; a++) {
for (int b = 0; b <= 255; b += 5) { setColor(0, b, 0); delay(5); }
for (int b = 255; b >= 0; b -= 5) { setColor(0, b, 0); delay(5); }
}
for (int b = 0; b <= 255; b += 5) { setColor(0, b, 0); delay(5); }
}
void setColor(int red, int green, int blue) {
analogWrite(redPin, red);
analogWrite(greenPin, green);
analogWrite(bluePin, blue);
}
ブザーの追加
箱が開いたときにメロディを再生。スターウォーズのテーマ曲!

const int c = 261, d = 294, e = 329, f = 349, g = 391, gS = 415;
const int a = 440, aS = 455, b = 466, cH = 523, cSH = 554;
const int dH = 587, dSH = 622, eH = 659, fH = 698, fSH = 740;
const int gH = 784, gSH = 830, aH = 880;
int counter = 0;
#define buzzerPin 1
void setup() {
pinMode(buzzerPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
play_jingle();
delay(3000);
}
void play_jingle() {
beep(a, 500); beep(a, 500); beep(a, 500);
beep(f, 350); beep(cH, 150); beep(a, 500);
beep(f, 350); beep(cH, 150); beep(a, 650);
delay(500);
beep(eH, 500); beep(eH, 500); beep(eH, 500);
beep(fH, 350); beep(cH, 150); beep(gS, 500);
beep(f, 350); beep(cH, 150); beep(a, 650);
delay(500);
}
void beep(int note, int duration) {
tone(buzzerPin, note, duration);
noTone(buzzerPin);
delay(50);
counter++;
}
サーボモーターの追加
サーボが箱のロック。正解時に90度回転して開きます。配線は3本のみ。

#include <Servo.h>
int pos = 0;
Servo myservo;
void setup() {
myservo.attach(9);
myservo.write(pos);
}
void loop() {
open_the_box(); delay(2000);
close_the_box(); delay(2000);
}
void open_the_box() {
for (pos = 0; pos <= 90; pos += 1) {
myservo.write(pos);
delay(15);
}
}
void close_the_box() {
for (pos = 90; pos >= 0; pos -= 1) {
myservo.write(pos);
delay(15);
}
}
※箱を閉じるには、すべてのポテンショメーターを0に戻してください。
パズルボックスを組み立てる
箱がなければ始まりません。下記ケースファイルをダウンロードしてガイドとして使用してください。2mm厚の段ボールを使用しました。
コード
完全スケッチ
(上記の各パーツを統合した完全版スケッチはGitHub等で公開)
カスタムパーツとエンクロージャ
2mm段ボール
box_E5j7tnFdNC.dxf
回路図
製造プロセス
- 集積回路(IC):現代エレクトロニクスを支える微小強力チップ
- TI SensorTagをNode-RED経由でBlynkと統合 – Raspberry Pi Zero編
- Arduinoデジタルダイス: LCD表示の乱数ジェネレーター自作
- スマートホーム連携向けマルチポジション温度センサーシステム
- DIY Arduino USBゲームコントローラー - 高性能ゲームパッドを自作
- MotionSense: ArduinoとESP8266で作るスマート侵入検知システム
- MOSMusicプロジェクトキット: MOSFET、Arduinoで音楽同期照明制御
- ボール盤の主要構成部品と機能:ベースからモーターまで完全解説
- 内燃機関の主要コンポーネント:構造と機能
- SPICEネットリスト構文マスター:コンポーネント命名、受動・能動素子、電源定義
- マイクロエレクトロニクス:微小化電子技術の科学