ชื่อโปรเจกต์: On Air Sign with Oplà kit #CloudGames2022
ไอเดียโปรเจกต์นี้มันเริ่มมาจากช่วงกักตัวโควิดตอนที่ต้อง Work From Home กันจนเปื่อยนั่นแหละน้อง จนกลายเป็นความคุ้นชินใหม่ไปเลย โปรเจกต์นี้พี่จัดทำขึ้นเพื่อร่วมสนุกใน [Arduino](https://s.shopee.co.th/7fUgFAWSki) Cloud Games โดยเฉพาะ
ในระบบนี้จะมีอุปกรณ์อยู่ 2 ตัวหลักๆ:
- Arduino Oplà IOT Kit: ตัวนี้เป็นตัวแม่ (Main device) ทำหน้าที่ไปดึงข้อมูลการนัดหมายมาจาก Google Calendar พอถึงเวลามีประชุมปุ๊บ มันก็จะสั่งเปิดป้ายไฟทันที หรือถ้าเราเลิกประชุมก่อนกำหนด ก็สั่งปิดจากตรงนี้ได้เลย
- on Air Sign: ตัวนี้เป็นตัวลูก (Secondary device) เอาไปติดไว้หน้าประตูห้องทำงานเรานั่นแหละ พอมีประชุมปุ๊บไฟก็ติดพรึ่บ บอกให้คนข้างนอกรู้ว่าห้ามเข้า! (จริงๆ จะใช้แค่ Oplà Kit อย่างเดียวก็ได้นะ แต่แยกตัวลูกออกมาแบบนี้มันหล่อเท่กว่าเยอะ หรือจะใช้อุปกรณ์ที่รองรับ IOT ตัวไหนมาโมดิฟายก็ได้ จัดไปวัยรุ่น)
ส่วนเรื่องการเชื่อมต่อ พี่ใช้ Arduino IOT Cloud และทำระบบ Thing to Thing communication เพื่อให้มันคุยกันเองได้ (อยากรู้ลึกกว่านี้ไปหาอ่านเอาเองนะเรื่อง Thing to Thing)
พี่สร้าง Thing สำหรับ ESP8266 แล้วประกาศ Variable เป็น boolean ไว้ตัวหนึ่ง เอาไว้สั่ง toggle เปิด-ปิดป้ายไฟ แล้วก็ทำ Dashboard ไว้คุมผ่านมือถือด้วย เวลาจะสั่งงานก็แค่จิ้มๆ เอา สะดวกจัด

ตัวเคสพี่ใช้เครื่อง 3D Print ปริ้นออกมาด้วยเส้น PLA สีขาว ส่วนตัวอักษรพี่ใช้เทคนิคสลับสีเส้นฟิลาเมนต์เอา พอเครื่องปริ้นเลเยอร์แรกเสร็จก็กดหยุดแล้วเปลี่ยนเส้นด้วยมือ เพราะเครื่องพี่มันมีหัวฉีดเดียว (สายโมดิฟายต้องเข้าใจ) หรือใครจะลองใช้กล่องไม้แล้วเอามาพ่นสีบนกระจกแบบที่พี่เคยทดลองดูก็ได้นะ ออกมาดูดีเหมือนกัน
เสร็จแล้วก็มาทำวงจร พี่ไปรื้อเอา Led panel จากไฟฉายเก่าๆ มาใช้ใหม่ (Recycle ไงน้อง) ดูตาม Schematics แล้วทำตามได้เลย หรือจะดีไซน์วงจรเองก็จัดไป โค้ดสำหรับ ESP8266 พี่แนบไว้ให้แล้ว มันจะใช้ Pin 2 คอยรับค่าจาก boolean variable ที่ส่งมาจาก IOT Cloud

ตอนโปรแกรม ESP-01 พี่ก็โมฯ ตัว USB-TTL เพิ่มปุ่ม Reset กับปุ่มเข้าโหมด Programming เข้าไปเอง หรือจะต่อบน Breadboard ก็ได้นะ โค้ดมีให้พร้อม จัดไปอย่าให้เสีย

รูปนี้คือไส้ในของป้ายไฟที่พี่ทำขึ้นมาเอง มีของตามนี้เลย:
- แบตเตอรี่ 18650 พร้อมรางถ่าน
- บอร์ดชาร์จและวงจรป้องกันแบต 18650
- ESP-01 ต่อกับ BS170 สวิตช์ที่ Pin 2
- Led Panel ที่รื้อมาจากไฟฉาย
- สวิตช์ On-Off

สำหรับฝั่ง Oplà kit พี่ก็สร้าง Thing ตัวที่สองขึ้นมา โดยใช้ Variable ชื่อเดียวกับตัวแรกแล้วตั้งค่าให้มัน Synchronized กัน
พอ Oplà kit ต่อ WiFi และ Arduino Cloud ได้แล้ว มันจะไปดึง Google calendar feed ในรูปแบบ iCal โดยใช้ secret url (หรือที่เขาเรียกกันว่า magic cookie url นั่นแหละ) พี่ต้องลองผิดลองถูกอยู่หลายรอบกว่าจะทำ HTTPS requests เพื่อดาวน์โหลดไฟล์ขนาด 100kb+ โดยที่ไม่มี SD card ได้ แถมยังต้อง Parse ข้อมูลใน Memory ที่มีอยู่อย่างจำกัดอีก สู้งานนะน้องงานนี้! (จริงๆ จะใช้ Google Script หรือเขียนโค้ดบน [Raspberry Pi](https://s.shopee.co.th/6pvZFdZdRf) ช่วยก็ได้นะถ้าไม่ไหว)
#include <SPI.h>
#include <WiFiNINA.h>
#include <ArduinoHttpClient.h>
char server[] = "calendar.google.com";
WiFiClient client;
int port = 443;
void sendRequestICalendarFeed() {
if (client.connectSSL(server, port)) {
String get("GET /calendar/ical/");
get += SECRET_ICALENDAR;
get += "/basic.ics HTTP/1.1";
client.println(get);
client.println("Host: calendar.google.com");
client.println("Connection: close");
client.println();
}
}
void readICalendarFeed() {
while (client.connected()) {
String line = client.readStringUntil('\
');
if (line == "0\\r") {
// end of response
break;
}
// parsing response...
}
}
ตรรกะการ Parsing ก็จะประมาณนี้:
struct Event {
public:
String dtstart;
String dtend;
String dtstamp;
String uid;
String created;
String description;
String lastModified;
String location;
int sequence;
String status;
String summary;
String transp;
};
Event events[20];
int event_index = -1;
int event_length = event_index + 1;
void load(WiFiClient client) {
String line;
String buffer = "";
String key;
String value;
bool insideEvent = false;
bool today = false;
int idx = -1;
while (client.connected()) {
String line = client.readStringUntil('\
');
if (line == "0\\r") {
break;
}
if (line.charAt(0) == ' ') {
// collecting multiline prop
if (buffer.length() < 100)
buffer += line.substring(1);
} else {
if (buffer != "") {
// process previous prop
auto m = buffer.indexOf(':');
if (m == -1)
m = buffer.indexOf(';');
key = buffer.substring(0, m);
value = buffer.substring(m + 1);
if (key.compareTo("BEGIN") == 0 && value.compareTo("VEVENT\\r") == 0) {
if (idx < 19){
idx++;
}
insideEvent = true;
} else if (key.compareTo("END") == 0 && value.compareTo("VEVENT\\r") == 0) {
insideEvent = false;
if (!is_today(events[idx].dtstart.c_str(), ArduinoCloud.getLocalTime())) {
idx--;
}
} else if (key.compareTo("DTSTART") == 0 && insideEvent) {
events[idx].dtstart = value;
} else if (key.compareTo("DTEND") == 0 && insideEvent) {
events[idx].dtend = value;
} else if (key.compareTo("DTSTAMP") == 0 && insideEvent) {
events[idx].dtstamp = value;
} else if (key.compareTo("CREATED") == 0 && insideEvent) {
events[idx].created = value;
} else if (key.compareTo("DESCRIPTION") == 0 && insideEvent) {
events[idx].description = value;
} else if (key.compareTo("SUMMARY") == 0 && insideEvent) {
events[idx].summary = value;
}
buffer = "";
}
buffer = line;
}
}
event_length = idx + 1;
if (!client.connected()) {
client.stop();
}
}
ตอนที่ดึงข้อมูลไฟล์มา พี่จะกรองเอาเฉพาะการนัดหมายที่มีในวันนี้เท่านั้น ส่วนอันอื่นก็เมินมันไปซะ เพราะ Memory เรามีจำกัด ต้องประหยัดหน่อย

พอได้ลิสต์นัดหมายของวันนี้มาแล้ว ระบบก็จะเช็คดูว่าตอนนี้มีประชุมอันไหนที่กำลังดำเนินอยู่ (หรืออันถัดไปคืออะไร) แล้วก็เอามาโชว์บนหน้าจอ จังหวะนั้นแหละ Oplà kit จะสั่งให้ Variable "onAir" เป็น true แล้วป้ายหน้าห้องเราก็จะไฟสว่างพรึ่บทันที!

ปุ่มข้างบนเอาไว้คุมป้ายไฟ On Air ได้โดยตรงเลยนะ ถ้าเห็นมันเป็นสีแดงแสดงว่ากำลัง On Air อยู่ ห้ามช็อตนะตัวนี้!

เราสามารถกดปุ่มเพื่อจบการประชุมก่อนเวลาได้ด้วย พอจบแล้วป้ายไฟก็จะดับ และไฟ LED ก็จะเปลี่ยนเป็นสีเขียวแทน นอกจากนี้ยังกดเลื่อนดูลิสต์การนัดหมายอื่นๆ ได้อีก หล่อเท่เลยงานนี้

ในอนาคตพี่กะว่าจะอัปเกรดให้ Oplà kit มันแก้ไขการนัดหมายบน Google Calendar ได้ด้วย เช่น กดจองคิวประชุมใหม่ได้เลย และว่าจะทำ Oplà kit อีกชุดให้แฟนใช้ด้วย จะได้ไม่ต้องเดินมาขัดจังหวะกันตอนประชุม
รายละเอียดทางเทคนิคเพิ่มเติม (ฉบับจัดเต็ม)
Professional Broadcasting Status Hub
โปรเจกต์นี้สร้างขึ้นเพื่อ #CloudGames2022 โดยเฉพาะ โดยใช้ Arduino Oplà Kit มาทำเป็นตัวบ่งบอกสถานะ "On Air" ผ่านระบบ Cloud สำหรับสายสตรีมเมอร์หรือคนทำงานทางไกล
- Arduino IoT Cloud Synchronization: ตัว Oplà kit จะต่อ WiFi และคอยเฝ้าดู Variable "Live" ใน Cloud ตลอดเวลา พอเรากดสวิตช์ใน Dashboard (หรือจะใช้สคริปต์ในคอมสั่งเอา) หน้าจอ Oplà กับวงแหวน LED ก็จะเปลี่ยนเป็นสีแดงเข้มทันที
- Capacitive Touch Interactions: ใช้เซนเซอร์สัมผัสแบบวงกลมของ Oplà ให้เป็นประโยชน์ แค่แตะที่ตัวเครื่องครั้งเดียวก็สั่ง "Mute" ไมโครโฟนในคอมพิวเตอร์ผ่าน IoT webhook ได้ด้วยนะน้อง โคตรล้ำ!
การแสดงผล (Visualization)
- OLED Graphical Status: แสดงโลโก้ "ON AIR" แบบ High-contrast บนหน้าจอทรงกลมของเครื่อง ตัว Firmware พี่เขียนระบบ "Ghosting" animation ไว้ด้วยนะ เพื่อป้องกันจอ OLED เบิร์นเวลาเปิดสตรีมยาวๆ ถนอมของกันหน่อยวัยรุ่น!