นวัตกรรมกล่องเตือนความจำอัจฉริยะอัตโนมัติ ช่วยแก้อาการพฤติกรรมเนือยนิ่ง (Sedentary Behavior) ของคนทำงานออฟฟิศและนักเรียน อุปกรณ์จะสุ่มเล่นเพลงพร้อมแสงไฟจังหวะสามช่าสุดมันส์ และเปลี่ยนเป็นไฟสีแดงนับเวลาถอยหลังทุก 30 นาที!
* รูปแบบการประกอบอุปกรณ์จำลองในเคสอะคริลิค/พลาสติก 3D Print
อุปกรณ์อิเล็กทรอนิกส์พื้นฐาน หาง่าย และราคาประหยัด สามารถสร้างใช้งานได้จริง
ไมโครคอนโทรลเลอร์ประมวลผลหลัก สั่งการตามเวลาและรับสัญญาณสถานะ
Main MCU Boardโมดูลเล่นไฟล์เพลง MP3 ผ่าน MicroSD Card เช็คสถานะการจบเพลงด้วย Serial Feedback
Software Serial (TX/RX)หลอดไฟ RGB แสดงไฟจังหวะสามช่าตอนเล่นเพลง และไฟแดงนับเวลาดวงละ ~3.75 นาที
Digital Pin 6สวิตช์อิเล็กทรอนิกส์ควบคุมการเปิด-ปิดอุปกรณ์ภายนอก เช่น มอเตอร์เต้น หรือพัดลม
Digital Pin 3ลำโพงกำลังขับเหมาะสม เสียงดังฟังชัด ต่อตรงขนานเข้ากับขา SPK1/SPK2
Speaker Direct OutFormat เป็น FAT32 เก็บไฟล์เพลง MP3 0001 - 0003 และตัวต้านทาน 1kΩ สำหรับสายสื่อสาร
FAT32 / Resistor 1kΩผังการเชื่อมต่อขาพินระหว่าง Arduino กับโมดูลต่างๆ อย่างถูกต้องและปลอดภัย
| โมดูล / อุปกรณ์ | ขาอุปกรณ์ (Component Pin) | ขาบอร์ด Arduino (Pin Target) | หมายเหตุสำคัญ |
|---|---|---|---|
| DFPlayer Mini | TX | Pin 10 (Software RX) | ต่อตรง หรือผ่านตัวต้านทาน 1kΩ |
| DFPlayer Mini | RX | Pin 11 (Software TX) | ต่อผ่านตัวต้านทาน 1kΩ ป้องกันไฟเกิน |
| DFPlayer Mini | BUSY | Unused (ไม่ได้ต่อ) | ยกเลิกการใช้ - เปลี่ยนไปใช้ Serial Feedback ตรวจเพลงจบแทน |
| DFPlayer Mini | VCC / GND | 5V / GND | ใช้ GND ร่วมกับระบบทั้งหมด |
| NeoPixel LED (8 ดวง) | DIN (Data In) | Pin 6 | ควบคุมสีไฟรายดวง 8 LEDs |
| NeoPixel LED (8 ดวง) | VCC / GND | 5V / GND | แนะนำต่อกับไฟ 5V ที่เสถียร |
| Relay / Driver Pin | IN / SIG | Pin 3 | สวิตช์เปิดอุปกรณ์ภายนอกพร้อมเพลง |
ขั้นตอนการทำงานอัตโนมัติที่ไม่ขัดจังหวะผู้ใช้ ตรวจเช็คสถานะสมบูรณ์ผ่านซอฟต์แวร์
ระบบสุ่มเล่นเพลงแทร็ก 1 - 3 ทันที สั่งเปิดสวิตช์รีเลย์ และแสดงไฟ NeoPixel จังหวะสามช่าสุดสนุกสนาน
บอร์ด Arduino ตรวจสอบสัญญาณ Serial Feedback (DFPlayerPlayFinished) จากโมดูล DFPlayer Mini โดยตรงเมื่อเล่นเพลงจบ
หยุดเพลงและแสดงไฟสีแดงแทนเวลาดวงละ ~3.75 นาที โดยดวงที่กำลังเดินอยู่จะกระพริบ และดวงที่ผ่านมาแล้วจะติดค้าง
เมื่อครบกำหนด 30 นาที ระบบจะวนกลับไปสุ่มเพลงใหม่และเล่นไฟสามช่าเตือนให้ผู้ใช้นั่งทำงานลุกขยับร่างกายทันที!
ไฟจังหวะสามช่าตอนเล่นเพลง + ไฟแดงนับเวลาดวงละ ~3.75 นาทีตอนรอ + เช็คเพลงจบด้วย Serial Feedback
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
#include <Adafruit_NeoPixel.h>
// ---------------- Pins Configuration ----------------
#define LED_PIN 6 // พินต่อ NeoPixel (8 ดวง)
#define NUM_LEDS 8
#define ACTION_PIN 3 // พินสั่งเปิด-ปิดอุปกรณ์ (เช่น รีเลย์/มอเตอร์)
// *ยกเลิกการใช้ BUSY_PIN (ขา 4) ปล่อยว่างไว้ได้เลยครับ*
// ---------------- Timing Configuration ----------------
// ตั้งเวลาพักรอนับถอยหลัง 30 นาที = 30 * 60 * 1000 = 1,800,000 มิลลิวินาที
const unsigned long INTERVAL = 1800000UL;
// ---------------- States Definition ----------------
enum SystemState {
STATE_PLAYING, // กำลังเล่นเพลงและโชว์ไฟจังหวะสามช่า
STATE_WAITING // กำลังรอนับเวลา 30 นาที (ไฟแดงนับถอยหลังดวงละ 4 นาที)
};
SystemState currentState = STATE_PLAYING;
unsigned long timerStartMillis = 0;
// ---------------- Objects Setup ----------------
SoftwareSerial mySoftwareSerial(10, 11); // RX, TX สำหรับเชื่อมต่อ DFPlayer
DFRobotDFPlayerMini myDFPlayer;
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
void setup() {
Serial.begin(115200);
mySoftwareSerial.begin(9600);
// สุ่ม Seed จาก Analog pin
randomSeed(analogRead(0));
// Initialize Pins
pinMode(ACTION_PIN, OUTPUT);
digitalWrite(ACTION_PIN, LOW); // ปิดอุปกรณ์ไว้ก่อน
// Initialize NeoPixel
strip.begin();
strip.show();
strip.setBrightness(100); // ความสว่างปานกลาง
// Initialize DFPlayer Mini
Serial.println(F("Initializing DFPlayer..."));
if (!myDFPlayer.begin(mySoftwareSerial)) {
Serial.println(F("DFPlayer Error! Check wiring & SD Card."));
setAllLEDs(255, 0, 0); // ไฟสีแดงเตือนถ้าพบข้อผิดพลาด
while (true);
}
Serial.println(F("DFPlayer Mini Ready."));
myDFPlayer.volume(25); // ระดับเสียง (0 - 30)
// เริ่มต้นสั่งเล่นเพลงรอบแรกทันทีเมื่อเปิดเครื่อง
triggerPlayRoutine();
}
void loop() {
switch (currentState) {
case STATE_PLAYING:
// กำลังเล่นเพลงอยู่: แสดงไฟจังหวะสามช่าสุดมันส์
samChaLightEffect();
// ตรวจสอบสัญญาณ Serial Feedback จาก DFPlayer ว่าเพลงเล่นจบหรือยัง
if (myDFPlayer.available()) {
// ถ้าประเภทของอีเวนต์คือ เล่นเพลงจบ (Play Finished)
if (myDFPlayer.readType() == DFPlayerPlayFinished) {
Serial.println(F("Song finished automatically via Serial Feedback!"));
stopPlayRoutine();
// สลับเข้าสู่โหมดรอนับเวลา 30 นาที
currentState = STATE_WAITING;
timerStartMillis = millis();
}
}
break;
case STATE_WAITING:
{
unsigned long currentMillis = millis();
unsigned long elapsed = currentMillis - timerStartMillis;
// ตรวจสอบว่าครบ 30 นาทีหรือยัง
if (elapsed >= INTERVAL) {
Serial.println(F("30 minutes elapsed. Starting next song..."));
triggerPlayRoutine(); // วนกลับมาเปิดเพลงใหม่
currentState = STATE_PLAYING;
} else {
// ระหว่างรอ: แสดงไฟสีแดงนับเวลาดวงละ ~3.75 นาที ดวงปัจจุบันกะพริบ ดวงที่ผ่านมาติดค้าง
showRedProgressWithBlink(elapsed, INTERVAL);
}
}
break;
}
delay(50);
}
// ----------------- ฟังก์ชันเริ่มเล่นเพลงและอุปกรณ์ -----------------
void triggerPlayRoutine() {
int songNumber = random(1, 4); // สุ่มเพลง 1 ถึง 3
Serial.print(F("Playing Random Track: "));
Serial.println(songNumber);
myDFPlayer.play(songNumber);
digitalWrite(ACTION_PIN, HIGH); // เปิดอุปกรณ์ทำงานพร้อมเพลง
}
// ----------------- ฟังก์ชันหยุดเพลงและปิดอุปกรณ์ -----------------
void stopPlayRoutine() {
myDFPlayer.stop();
digitalWrite(ACTION_PIN, LOW); // ปิดอุปกรณ์
clearLEDs();
}
// ----------------- เอฟเฟกต์ไฟจังหวะสามช่า (ตอนเปิดเพลง) -----------------
void samChaLightEffect() {
// แบ่งรอบเวลา 1 วินาที (1000 มิลลิวินาที) เป็นจังหวะสนุกสนานแนวสามช่า
unsigned long beatTime = millis() % 1000;
if (beatTime < 150) {
setAllLEDs(255, 20, 100); // บีทที่ 1: สีชมพู/ส้ม
}
else if (beatTime < 250) {
clearLEDs(); // พักสั้นๆ
}
else if (beatTime < 400) {
setAllLEDs(0, 255, 100); // บีทที่ 2: สีเขียวสดใส
}
else if (beatTime < 500) {
clearLEDs(); // พักสั้นๆ
}
else {
// ช่วงรัวจังหวะท้าย: วิ่งสีรุ้งหมุนวนเร็วๆ
static byte wheelPos = 0;
for (int i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + wheelPos) & 255));
}
strip.show();
wheelPos += 30;
}
}
// ----------------- ฟังก์ชันแสดงไฟสีแดง ดวงละ ~3.75 นาที และดวงปัจจุบันกะพริบ -----------------
void showRedProgressWithBlink(unsigned long elapsed, unsigned long total) {
// คำนวณว่าเวลาที่ผ่านไปตกอยู่ที่หลอดไฟดวงที่เท่าไหร่ (0 ถึง 7)
int activeLedIndex = (elapsed * NUM_LEDS) / total;
if (activeLedIndex >= NUM_LEDS) activeLedIndex = NUM_LEDS - 1;
// สร้างจังหวะกะพริบสำหรับหลอดปัจจุบัน (กระพริบทุกๆ 500ms)
bool isBlinkOn = (millis() / 500) % 2 == 0;
strip.clear();
for (int i = 0; i < NUM_LEDS; i++) {
if (i < activeLedIndex) {
// ดวงที่ผ่านมาแล้ว: ติดไฟสีแดงค้างไว้
strip.setPixelColor(i, strip.Color(100, 0, 0));
} else if (i == activeLedIndex) {
// ดวงปัจจุบันกำลังนับเวลาอยู่: ให้กะพริบติด-ดับ
if (isBlinkOn) {
strip.setPixelColor(i, strip.Color(100, 0, 0)); // สีแดงสว่าง
} else {
strip.setPixelColor(i, strip.Color(0, 0, 0)); // ดับ
}
} else {
// ดวงที่ยังมาไม่ถึง: ปิดไว้
strip.setPixelColor(i, strip.Color(0, 0, 0));
}
}
strip.show();
}
// ----------------- ฟังก์ชันช่วยคำนวณสีรุ้ง -----------------
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if (WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if (WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
void setAllLEDs(byte r, byte g, byte b) {
for (int i = 0; i < NUM_LEDS; i++) {
strip.setPixelColor(i, strip.Color(r, g, b));
}
strip.show();
}
void clearLEDs() {
strip.clear();
strip.show();
}
ทดสอบจำลองเอฟเฟกต์ไฟ LED บนหน้าจอบราวเซอร์