Сборка защищённой IoT‑головоломки (Puzzle Box) на базе Arduino MKR Bundle
Компоненты и материалы
| Arduino MKR IoT Bundle | Включает MKR1000, серво, LCD, зуммер, RGB‑LED, 3 потенциометра и прочее | 1 |
Приложения и сервисы
- Blynk (iOS/Android) — для задания секретного кода через слайдеры
О проекте
Примечание: Урок может быть устаревшим. Актуальная версия здесь.
Хранить ценности в сейфе неудобно. Сделаем свой «секретный ящик» из картона и компонентов MKR IoT Bundle. Не гарантируем взломостойкость, но весело и отвлекает от настоящих ценностей. Лучше прячьте туда конфеты!
Суть
Крышка держится сервоприводом. Чтобы открыть, нужно три потенциометра выставить в правильную комбинацию. Код задаётся в приложении Blynk. RGB‑LED подсказывает: чем ближе к правильному значению, тем «теплее» цвет (синий → голубой → жёлтый → красный). При угадывании зуммер играет мелодию (тема «Звёздных войн»), серво открывает крышку.
Используемые компоненты набора: зуммер, RGB‑LED, 3 потенциометра, LCD‑экран, сервопривод.

Цели обучения
- Знакомство с платформой Blynk
- Подключение и работа с 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
- Скачайте приложение, создайте аккаунт.
- New Project → назовите, выберите устройство Arduino MKR1000, тип связи WiFi.
- На почту придёт Auth Token — уникальный ключ проекта.
- В Arduino IDE: Скетч → Подключить библиотеку → Управление библиотеками → найдите Blynk и установите (в Web Editor загрузится автоматически при
#include <BlynkSimpleWiFiShield101.h>).









Тестовый скетч для проверки связи со слайдерами (V1, V2, V3):
#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‑экраном
Экран подключается множеством проводов. Питание 5В через резистор 220 Ом. Яркость регулируется PWM на пине A3 (0 — максимум).

analogWrite(A3, 0); // макс. яркость
Пример вывода значений потенциометров на 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);
lcd.print("hello, world!");
}
void loop() {
lcd.setCursor(0, 1);
lcd.print(millis() / 1000);
}
Подключение потенциометров
Три потенциометра на аналоговые входы A0, A1, A2. Значения 0‑1023映射 на 0‑9 функцией map().

int PotOne = map(analogRead(A0), 0, 1023, 0, 9);
RGB‑LED как индикатор близости
Цвет меняется от синего (далеко) к красному (близко). Пороги: разница ≤1 — красный, ≤3 — жёлтый, ≤4 — голубой, иначе синий.


// RGB LED pins
int redPin = 6, greenPin = 8, bluePin = 7;
void setColor(int r, int g, int b) {
analogWrite(redPin, r);
analogWrite(greenPin, g);
analogWrite(bluePin, b);
}
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
}
Интеграция с Blynk
Собираем всё вместе: Blynk задаёт комбинацию (слайдеры V1‑V3), LCD показывает текущие значения потенциометров, RGB‑LED даёт подсказку. Переменная start предотвращает повторное открытие. Сброс комбинации — все потенциометры в 0.
#include <LiquidCrystal.h>
#include <SPI.h>
#include <WiFi101.h>
#include <BlynkSimpleWiFiShield101.h>
// RGB LED
int redPin = 6, greenPin = 8, bluePin = 7;
const char* ssid = SECRET_SSID;
const char* password = SECRET_PSWD;
char auth[] = SECRET_TOKEN;
// LCD
const int rs = 12, en = 11, d4 = 2, d5 = 3, d6 = 4, d7 = 5;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
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(); }
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 p1, int p2, int p3) {
if (abs(p1 - SliderValueOne) <= 1 && abs(p2 - SliderValueTwo) <= 1 && abs(p3 - SliderValueThree) <= 1)
setColor(255, 0, 0); // red
else if (abs(p1 - SliderValueOne) <= 3 && abs(p2 - SliderValueTwo) <= 3 && abs(p3 - SliderValueThree) <= 3)
setColor(255, 255, 0); // yellow
else if (abs(p1 - SliderValueOne) <= 4 && abs(p2 - SliderValueTwo) <= 4 && abs(p3 - 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); }
}
Зуммер: играем «Звёздные войны»
Подключение зуммера простое (пин 1). Код воспроизводит тему.

const int c = 261, d = 294, e = 329, f = 349, g = 391, gS = 415, a = 440, aS = 455, b = 466;
const int cH = 523, cSH = 554, dH = 587, dSH = 622, eH = 659, fH = 698, fSH = 740, gH = 784, gSH = 830, aH = 880;
#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);
}
Сервопривод: замок коробки
Серво на пине 9. Поворот на 90° открывает крышку. Закрытие — возврат в 0°. Сброс комбинации (все потенциометры в 0) закрывает коробку.

#include <Servo.h>
Servo myservo;
int pos = 0;
void setup() { myservo.attach(9); myservo.write(0); }
void loop() { open_the_box(); delay(2000); close_the_box(); delay(2000); }
void open_the_box() { for (pos = 0; pos <= 90; pos++) { myservo.write(pos); delay(15); } }
void close_the_box() { for (pos = 90; pos >= 0; pos--) { myservo.write(pos); delay(15); } }
Сборка коробки
Чертеж коробки из 2‑мм картона в DXF: box_E5j7tnFdNC.dxf. Используйте как шаблон.
Код
Полный скетч объединяет все модули (Blynk, LCD, потенциометры, RGB, зуммер, серво). В разделе кода исходного проекта он приведён целиком; здесь опущен для краткости — см. оригинал на Hackster или GitHub.
3D‑модели и детали
Корпус из 2‑мм картона: box_E5j7tnFdNC.dxf
Схемы

Производственный процесс
- Синтаксис нетлистов SPICE: именование компонентов, пассивные и активные элементы, определение источников
- Интегральные схемы (ИС): компактные мощные чипы, питающие современную электронику
- Интеграция TI SensorTag с Blynk через Node‑RED на Raspberry Pi Zero
- Цифровые кости на Arduino: генератор случайных чисел с LCD-дисплеем и кнопкой
- Многопозиционная система датчиков температуры для интеграции в умный дом
- DIY USB-геймпад на Arduino: собственный высокопроизводительный контроллер
- Набор MOSMusic: MOSFET, Arduino и управление светом по музыке
- Микроэлектроника: наука о сверхмалых электронных компонентах
- Основные компоненты сверлильного станка: подробный обзор
- Ключевые компоненты двигателя внутреннего сгорания