ชื่อโปรเจกต์: PuzzleBox with MKR WiFi 1010
เฮ้ยน้อง! บทความนี้อาจจะเก่าไปหน่อยนะ ถ้าอยากได้เวอร์ชันล่าสุดแบบกริ๊บๆ ให้ไปดูที่นี่เลย
ไอ้พวกของมีค่าเนี่ย บางทีจะเก็บให้พ้นหูพ้นตาก็ยากอยู่นะ ยกเว้นน้องจะมีตู้นิรภัยยักษ์... แต่ใครมันจะไปมีที่วางขนาดนั้นล่ะ? มานี่มา รุ่นพี่จะพาทำ PuzzleBox ใช้เองแบบหล่อเท่ ด้วยอุปกรณ์จาก MKR IoT Bundle กับลังกระดาษที่เหลือใช้นี่แหละ! พี่ไม่รับประกันนะว่าของข้างในจะปลอดภัยร้อยเปอร์เซ็นต์ไหม แต่มันช่วยดึงความสนใจจากพวกขโมยขโจรได้ดีเลยล่ะ เอาไว้เก็บขนมก็พอ อย่าเอาไปเก็บทองจริงล่ะน้อง เดี๋ยวจะหาว่าพี่ไม่เตือน!
สรุปสั้นๆ ให้ฟัง
กล่องนี้จะถูกล็อคไว้ด้วย [Servo](https://s.shopee.co.th/7fUgFAWSki) motor และวิธีเปิดคือ น้องต้องหมุน Potentiometer ให้ตรงตามรหัสที่ตั้งไว้ รหัสพวกนี้ตั้งค่าผ่านแอป Blynk ได้เลย ส่วน RGB LED จะคอยช่วยบอกใบ้ ถ้าสีเริ่มอุ่นขึ้น (จากฟ้าไปแดง) แสดงว่าใกล้ความจริงแล้ว พอรหัสถูกต้องปุ๊บ [Buzzer](https://s.shopee.co.th/6pvZFdZdRf) จะบรรเลงเพลง ส่วน Servo ก็จะเปิดฝากล่องออกให้เอง จัดไปวัยรุ่น! อุปกรณ์ที่ต้องใช้มีตามนี้:
- Buzzer
- RGB LED
- Potentiometer 3 ตัว
- หน้าจอ [LCD](https://s.shopee.co.th/6AfsSPcAnb)
- Servo motor

จบงานนี้ได้อะไรบ้าง?
- รู้จักกับแพลตฟอร์ม Blynk (สาย IoT ต้องมี!)
- การต่อวงจรและใช้งานหน้าจอ LCD
- ใช้ Buzzer บรรเลงเพลง Star Wars ให้ดูโปร!
อยากเทพกว่านี้ไหม?
บทความนี้เป็นส่วนหนึ่งของซีรีส์การเรียนรู้เรื่อง MKR WiFi 1010 และ IoT อุปกรณ์ในเซ็ต MKR IoT Bundle เอาไปทำอะไรได้อีกเพียบ เช่น:
- I Love You Pillow with MKR WiFi 1010
- Puzzle Box with MKR WiFi 1010
- Pavlov's Cat with MKR WiFi 1010
- The Nerd with MKR WiFi 1010
- Plant communicator with MKR WiFi 1010
รู้จักกับ Blynk
Blynk คือแอปมือถือยอดฮิตสำหรับงาน IoT ที่จะทำให้เราควบคุม [Arduino](https://s.shopee.co.th/7fUgFAWSki) ผ่านเน็ตได้จากทุกที่ทุกเวลา ตัวแอปเน้นความง่ายและมีคู่มือดีมาก น้องมือใหม่เล่นได้สบายๆ
เริ่มต้นกับ Blynk
การสร้างโปรเจกต์ใหม่มันง่ายมาก พี่เตรียม QR code ไว้ให้แล้ว จะได้ไม่ต้องเสียเวลาไปลากเอง เปิดแอป Blynk แล้วสแกนที่ไอคอน QR code มุมขวาบนได้เลย เครื่องมือ (Widgets) ที่พี่ตั้งค่าไว้จะโผล่มาบนจอทันที!


พอสร้างโปรเจกต์เสร็จ อย่าลืมเอา Auth Token ส่งเข้าอีเมลตัวเองนะ มันคือรหัสสำคัญที่เอาไว้เชื่อมต่อ Hardware กับมือถือ ทุกโปรเจกต์จะมีรหัสไม่ซ้ำกัน และที่สำคัญน้องต้องลง Blynk library ในเครื่องด้วยนะ (ถ้าใช้ Arduino Web Editor มันจะเรียกมาให้เอง) ถ้าพร้อมแล้วก็ Upload sketch นี้ แล้วลองเลื่อน Slider บนแอปดู ผลลัพธ์จะโผล่ที่ Serial Monitor:
#include <WiFiNINA.h>
#include <BlynkSimpleWiFiNINA.h>
const char* ssid = SECRET_SSID; // ชื่อ WiFi บ้านน้อง
const char* password = SECRET_PSWD; // รหัสผ่าน WiFi
char auth[] = SECRET_TOKEN; // รหัส Blynk API token
// ตัวแปรเก็บค่ารหัสลับ
// ตั้งค่ารหัสเริ่มต้นเป็น ( 1 1 1 )
int SliderValueOne = 1;
int SliderValueTwo = 1;
int SliderValueThree = 1;
// ฟังก์ชัน Blynk สำหรับดึงค่าจากแอป
BLYNK_WRITE(V1) {
SliderValueOne = param.asInt(); // รับค่าจาก Virtual Pin V1 มาเก็บในตัวแปร
}
BLYNK_WRITE(V2) {
SliderValueTwo = param.asInt();
} BLYNK_WRITE(V3) {
SliderValueThree = param.asInt();
}
void setup() {
Serial.begin(9600);
Blynk.begin(auth, ssid, password); // เชื่อมต่อ Blynk และ WiFi
}
void loop() {
int Temp_Slider_One_value = SliderValueOne;
int Temp_Slider_Two_value = SliderValueTwo;
int Temp_Slider_Three_value = SliderValueThree;
Blynk.run();
// เช็คว่ามีการเปลี่ยนรหัสจากแอปไหม ถ้าเปลี่ยนให้โชว์ใน Console
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 และตัวต้านทาน (Resistor) ขนาด 220 Ohm ส่วนความสว่างจอปรับได้ผ่าน Analog pin 3 (A3) โดยใส่ค่า 0 คือสว่างสุด และ 255 คือมืดสุด
analogWrite(A3, 0);
ลอง Upload sketch ตัวอย่างดูว่าหน้าจอแสดงผลได้ปกติไหม ห้ามช็อตนะตัวนี้!
// include code library:
#include <LiquidCrystal.h>
// กำหนดขา Pins ที่เชื่อมต่อกับหน้าจอ LCD
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); // กำหนดขนาดจอ (16 คอลัมน์ 2 แถว)
lcd.print("hello, world!");
}
void loop() {
lcd.setCursor(0, 1);
lcd.print(millis() / 1000); // โชว์วินาทีที่ผ่านไป
}
เพิ่ม Potentiometer เข้าไป
เราจะอ่านค่าจาก Potentiometer ด้วยคำสั่ง analogRead() โดยต่อเข้ากับ Analog Pins 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 เป็นตัวบอกใบ้ ยิ่งหมุนเข้าใกล้รหัสที่ถูกต้อง สีจะเปลี่ยนจากโทนเย็น (Blue) ไปเป็นโทนร้อน (Red) ตามลำดับ: น้ำเงิน -> ฟ้าน้ำทะเล -> เหลือง -> แดง


ลองเช็คสีด้วยโค้ดนี้ดู หล่อเท่แน่นอน!
// ขา Pins สำหรับ RGB LED
int redPin = 6;
int greenPin = 8;
int bluePin = 7;
void setup() {
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
Serial.begin(9600);
}
void loop() {
setColor(0, 0, 255); // น้ำเงิน
delay(1000);
setColor(0, 255, 255); // ฟ้าน้ำทะเล
delay(1000);
setColor(255, 255, 0); // เหลือง
delay(1000);
setColor(255, 0, 0); // แดง
delay(1000);
}
// ฟังก์ชันส่งค่าสีไปที่ Pins
void setColor(int red, int green, int blue){
analogWrite(redPin, red);
analogWrite(greenPin, green);
analogWrite(bluePin, blue);
}
เชื่อมทุกอย่างเข้ากับ Blynk
เอาล่ะวัยรุ่น ถึงเวลารวมร่าง! ต่อ Board เข้ากับ Blynk, เชื่อม Potentiometer เข้ากับ LCD และตั้งค่าให้ LED กะพริบสีเขียวเมื่อรหัสถูกต้อง
- เราจะใช้ฟังก์ชัน
giveColorFeedback()คอยเปลี่ยนสี LED ตามความใกล้เคียงของรหัสที่น้องหมุน
void giveColorFeedback(int PotOne, int PotTwo, int PotThree){...}
- และใช้ตัวแปรพวกนี้เก็บรหัสที่ส่งมาจากแอป
int SliderValueOne = 1;
int SliderValueTwo = 1;
int SliderValueThree = 1;
รหัสเริ่มต้นคือ 1-1-1 นะ ถ้าอยากเปลี่ยนก็ไปเลื่อนในแอปเอา แต่อย่าลืมนะว่าถ้ากด Reset บอร์ด รหัสจะกลับไปเป็นค่าเริ่มต้นทันที!
- มีตัวแปร
bool start = true;เอาไว้เช็คว่ากล่องเปิดไปหรือยัง จะได้ไม่เปิดค้างรัวๆ ใน Loop
จัด Sketch ชุดใหญ่ไปลงเครื่องซะ:
#include <LiquidCrystal.h>
#include <SPI.h>
#include <WiFiNINA.h>
#include <BlynkSimpleWiFiNINA.h>
int redPin = 6;
int greenPin = 8;
int 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;
int SliderValueTwo = 1;
int 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); // แดง (ใกล้มาก)
}
else if (abs(PotOne - SliderValueOne) <= 3 && abs(PotTwo - SliderValueTwo) <= 3 && abs(PotThree - SliderValueThree) <= 3 ) {
setColor(255, 255, 0); // เหลือง
}
else if (abs(PotOne - SliderValueOne) <= 4 && abs(PotTwo - SliderValueTwo) <= 4 && abs(PotThree - SliderValueThree) <= 4 ) {
setColor(0, 255, 255); // ฟ้า
}
else {
setColor(0, 0, 255); // น้ำเงิน (ยังห่างไกล)
}
}
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); }
}
}
void setColor(int red, int green, int blue) {
analogWrite(redPin, red);
analogWrite(greenPin, green);
analogWrite(bluePin, blue);
}
ใส่ Buzzer เพิ่มเสียงเพลง
เราจะใช้ Buzzer บรรเลง เพลงธีม Star Wars ตอนเปิดกล่องให้มันอลังการไปเลย! การต่อ Buzzer ก็ง่ายๆ ตามนี้:

ลองเบิร์นโค้ดนี้แล้วฟังเสียงพลังแห่งจักรวาลดู:
// กำหนดความถี่ตัวโน้ต
const int a = 440;
const int f = 350;
const int cH = 523;
const int eH = 659;
const int gS = 415;
const int fH = 698;
#define buzzerPin 1
void setup() { pinMode(buzzerPin, OUTPUT); }
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);
}
void beep(int note, int duration) {
tone(buzzerPin, note, duration);
noTone(buzzerPin);
delay(50);
}
ติดตั้ง Servo Motor
Servo motor คือพระเอกที่เป็น "ตัวล็อค" ของเรา มันจะหมุนไป 90 องศาเมื่อรหัสถูก เพื่อเปิดฝากล่อง ต่อแค่ 3 สายก็จบงานได้

ใช้ฟังก์ชันตามนี้เพื่อควบคุมการหมุน:
#include <Servo.h>
Servo myservo;
void setup() {
myservo.attach(9); // ต่อ Servo ที่ Pin 9
myservo.write(0); // เริ่มต้นที่ 0 องศา
}
void loop() {
open_the_box(); delay(2000);
close_the_box(); delay(2000);
}
void open_the_box() {
for (int pos = 0; pos <= 90; pos += 1) {
myservo.write(pos); delay(15);
}
}
void close_the_box() {
for (int pos = 90; pos >= 0; pos -= 1) {
myservo.write(pos); delay(15);
}
}
จำไว้นะน้อง ถ้าอยากจะปิดกล่องอีกครั้ง ก็แค่หมุน Potentiometer ทุกตัวกลับไปที่ 0 ก็เรียบร้อย!
ประกอบร่าง Puzzle Box ของน้อง
มันจะเป็นกล่องไม่ได้ถ้าไม่มีตัวกล่อง! ไปดาวน์โหลดไฟล์แบบร่าง Case ด้านล่างนี้มาดูเป็นแนวทางนะ พี่ใช้กระดาษลูกฟูกหนา 2 มม. กำลังดีเลย
รายละเอียดทางเทคนิคเพิ่มเติม
การทำงานร่วมกับฮาร์ดแวร์เข้ารหัส
PuzzleBox สุดล้ำตัวนี้รวมเอาระบบล็อคเชิงกลเข้ากับความท้าทายแบบดิจิทัล เหมาะมากสำหรับทำ Escape Room หรือกล่องของขวัญที่ต้องใช้ความพยายามหน่อย
- Multi-Sensor Riddle Matrix: กล่องจะไม่ยอมเปิดจนกว่าผู้ใช้จะผ่านด่านทดสอบ 3 อย่าง เช่น การเอียงกล่องให้ได้องศาที่กำหนด (ใช้ IMU ของบอร์ด MKR), การหมุนรหัส และการจ่อไฟให้ได้ความสว่างที่พอเหมาะ
- Blynk Remote Overlook: คนสร้างกล่องสามารถแอบดู "ความคืบหน้าการแก้ปริศนา" ได้แบบเรียลไทม์ผ่านมือถือด้วย MKR WiFi 1010 แถมยังกดสั่งเปิดกล่องจากระยะไกลได้ด้วยถ้าคนเล่นเริ่มจะท้อจนจะพังกล่องทิ้งซะก่อน
ความปลอดภัย
- Secure Opening Pulse: เมื่อไขปริศนาครบแล้ว Arduino จะส่งสัญญาณ Pulse ไปที่ 12V Solenoid Bolt เพื่อดึงสลักล็อคฝากล่องออกจริงๆ หลังจากตรวจสอบความถูกต้องเรียบร้อยแล้ว หล่อเท่เลยงานนี้!