กลับไปหน้ารวมไฟล์
six-sided-die-sketch-part-1-f33152.md

ทอยเต๋า Part 1

รุ่นพี่เพิ่งทำโปรเจคทอยเต๋าง่ายๆ ให้น้องชายเอาไปใช้เล่นบอร์ดเกม ตัวน้องเค้าดีใจมาก แต่พี่คิดว่ามันน่าจะเป็นประโยชน์กับคนอื่นด้วย เลยจะมาแชร์สเก็ตช์ 'ทอยเต๋า' นี้ พร้อมกับอีกเวอร์ชันที่ทอยพร้อมกันสองลูก (เดี๋ยวไปดูในบทความ Rolling the Dice, part 2 กันต่อนะ)

ในบทความนี้ พี่จะพาน้องไปดู Part 1 ของสเก็ตช์ 'ทอยเต๋า' ทั้งสองส่วน นั่นคือการทอยเต๋าแค่หนึ่งลูก การออกแบบสเก็ตช์และส่วนประกอบของ Part 1 นั้นตรงไปตรงมา – มันใช้ LED 7 ดวงเพื่อแสดงลวดลายจุด (pip) มาตรฐานทั้งหกหน้าของลูกเต๋า และใช้ปุ่มกดธรรมดาเชื่อมต่อแบบง่ายๆ แค่ปุ่มเดียว

ถ้าน้องสงสัยว่าทำไมต้องใช้ LED 7 ดวง ไม่ใช่ 6 ดวงล่ะ? นั่นเพราะว่าเราต้องใช้ 7 ดวงถึงจะสามารถจัดเรียงเลียนแบบรูปแบบจุดที่เป็นไปได้ทั้งหมดของลูกเต๋าหกหน้าทุกด้านได้ (ดูด้านล่าง) LED แต่ละดวงจะถูกแมปและเชื่อมต่อกับพอร์ตดิจิทัล I/O ของไมโครคอนโทรลเลอร์ สำหรับเต๋าหนึ่งลูกนี่โอเค แต่ถ้าเราจะเริ่มเพิ่มเป็นสองลูกหรือมากกว่านั้นล่ะ? ไม่ได้แล้วจ้า ต้องใช้พอร์ตเยอะเกินไป แต่ไม่ต้องห่วง นี่คือจุดที่บทความ 'Rolling the Dice, part 2' จะมาต่อยอดและแสดงให้ดูว่าเราจะทำได้ยังไงโดยใช้ IC อนุกรม-ขนานอินพุต/เอาต์พุตราคาถูก เช่น IC 74HC595 เพื่อเพิ่มลูกเต๋าได้ตามที่เราต้องการ อย่างไรก็ตาม สำหรับ Part 1 นี้ เรามาเริ่มต้นด้วยการเชื่อมต่อลูกเต๋าและ LED ของมันโดยตรงกับไมโครคอนโทรลเลอร์กันก่อน ไปลุยดู Part 1 กันเลยดีกว่า...

ออกแบบ - ฮาร์ดแวร์

LEDs

อย่างที่บอกไปข้างต้น การออกแบบวงจรมี LED 7 ดวง และปุ่มกด 1 ปุ่ม LED เจ็ดดวงถูกใช้ในการออกแบบนี้เพื่อให้เราสามารถจัดวางมันบนเบรดบอร์ดให้สะท้อนถึงรูปแบบการจัดเรียงจุด (pip) ของแต่ละหน้าของลูกเต๋าหกหน้าดั้งเดิมได้ นั่นคือ:

รูปแบบการจัดวาง LED สำหรับแต่ละหน้าของเต๋า - X คือติด, 0 คือดับ

การจัดวางบนเบรดบอร์ดที่พี่ทำสุดท้ายออกมาเป็นแบบนี้:

ใช้เบรดบอร์ดไซส์ครึ่งกำลังดี

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

สำหรับการอ้างอิง การกำหนดขา Digital Pin ของไมโครคอนโทรลเลอร์ให้กับ LED แต่ละดวงมีดังนี้ (สังเกตว่าการวางตัวของ LED บนเบรดบอร์ดเป็นไปตามภาพด้านบน):

  • LED มุมขวาล่าง (สีแดง) - Digital Pin 2
  • LED กลางล่าง (สีเขียว) - Digital Pin 3
  • LED มุมซ้ายล่าง (สีน้ำเงิน) - Digital Pin 4
  • LED ตรงกลาง (สีเหลือง) - Digital Pin 5
  • LED มุมขวาบน (สีแดง) - Digital Pin 6
  • LED กลางบน (สีเขียว) - Digital Pin 7
  • LED มุมซ้ายบน (สีน้ำเงิน) - Digital Pin 8
  • และสุดท้าย ปุ่มกดเชื่อมต่อกับ Digital Pin 9

ปุ่มกด (Button Switch)

ปุ่มกดเดี่ยวถูกใช้ในการออกแบบนี้ โดยเมื่อกดแล้วปล่อย มันจะกระทำการทอยเต๋าใหม่ สเก็ตช์ใช้ไลบรารี ez_switch_lib ซึ่งทำให้การออกแบบง่ายขึ้น อินสแตนซ์ของ ez_switch_lib ถูกกำหนดค่าเริ่มต้นสำหรับสวิตช์เดียว และถูกกำหนดเพิ่มเติมในฟังก์ชัน setup (ฟังก์ชัน add_switch) ให้เป็นปุ่มกดที่เชื่อมต่อแบบง่ายๆ โดยไม่ใช้ตัวต้านทานดึงลง (pull down resistor) ระวังไว้นะว่าพารามิเตอร์ของฟังก์ชัน add_switch รวมถึงมาโครของไลบรารีสองตัวคือ 'button_switch' และ 'circuit_C2' พวกนี้ถูกกำหนดไว้ในไฟล์เฮดเดอร์ (.h) ของ ez_switch_lib แล้ว แค่ต้องอ้างอิงมันก็พอ

หลังจากที่ประกาศใช้ไลบรารี่ สร้างอินสแตนซ์ และเพิ่ม/สร้างปุ่มสวิตช์แล้ว สิ่งที่ต้องทำต่อมาก็คือการตรวจสอบสถานะของสวิตช์อย่างต่อเนื่อง ซึ่งจะทำในลูปหลัก (main loop) นั่นเอง ถ้าน้องกดและปล่อยปุ่มสวิตช์ มันจะคืนค่าสถานะเป็น 'switched' ออกมา (นี่คือมาโครที่กำหนดไว้ในไลบรารี่ ez_switch_lib)

ปุ่มสวิตช์สามารถวางไว้ตรงไหนบนเบรดบอร์ดก็ได้ที่สะดวก แล้วก็ต่อสายตามวงจรที่ออกแบบไว้ พี่วางมันไว้ริมซ้ายของบอร์ด ให้กดง่ายหน่อย ดูแผนภาพการจัดวางคอมโพเนนต์ใน Schematic นะ

ออกแบบ - Sketch

ก่อนจะอธิบายส่วนหลักของสเก็ตช์ พี่ขอพูดถึง Heart Beat Monitor ที่พี่แถมมาให้ในโค้ดด้วย มันถูกออกแบบมาให้กะพริบ LED ที่ติดมากับบอร์ด (LED_BUILTIN ซึ่งปกติจะอยู่ที่พิน 13) ด้วยความถี่ 1 เฮิรตซ์ (1 รอบต่อวินาที) ไอ้นี่แหละที่บอกเราว่าสเก็ตช์ยังรันอยู่หรือเปล่า ถ้าน้องไม่อยากได้ฟีเจอร์นี้ ก็ปิดมันซะโดยตั้งค่ามาโครในส่วนประกาศ heart beat ให้เป็น 'false' ง่ายๆ คือเปลี่ยนบรรทัดนี้เป็น '#define heart_beat_on false' ข้อมูลที่เกี่ยวข้องกับ heart beat monitor มีดังนี้:

// Define heart beat data...
//
#define heart_beat_pin LED_BUILTIN // digital pin for heart beat LED
#define heart_beat_on true // determines if the implementation uses the heartbeat
long unsigned heart_beat_freq = 1000; // time(milliseconds) of heart beat frequency
long unsigned heart_beat_on_off_time; // the time the LED is on and off - 1/2 frequency
long unsigned last_heart_beat_time; // time in milliseconds of last heart beat status change
bool heart_beat_status = HIGH; // current status of heart beat, start high

พี่บอกไปแล้วว่าโค้ดใช้ LED บนบอร์ด (LEDBUILTIN) และวิธีปิด monitor ถ้าไม่ต้องการ แต่ถ้าน้องอยากเปลี่ยนความถี่การกะพริบ ก็แค่ไปแก้ค่าตัวแปร 'heart_beat_freq' ให้เป็นจำนวนมิลลิวินาทีต่อรอบที่ต้องการ จำไว้ว่า 1 รอบครบคือ เปิดแล้วปิด ดังนั้นอัตราการกะพริบจริงจะถูกคำนวณให้เป็นครึ่งหนึ่งของความถี่ที่ตั้งไว้อัตโนมัติ

ฟังก์ชัน heart_beat ทำงานตรงไปตรงมา แต่มันจะทำงานได้ก็ต่อเมื่อถูกเรียกอย่างสม่ำเสมอเท่านั้น ดังนั้นต้องเรียกมันตลอดทั้งโค้ด ตามที่เห็นในสเก็ตช์เลย

ต่อไป สเก็ตช์หลักทำงานโดยการตรวจสอบว่าปุ่มสวิตช์ที่ต่ออยู่ถูกกดหรือไม่ ปุ่มสวิตช์ถูกกำหนดค่าโดยใช้ไลบรารี่ ez_switch_lib ซึ่งช่วยจัดการปัญหาเด้งของสวิตช์ (debounce) ให้อัตโนมัติ เลยไม่ต้องมานั่งเขียนโค้ดจัดการเองให้วุ่นวาย

วิธีการนำไลบรารี่มาใช้ คือให้สร้างโฟลเดอร์ชื่อ 'ez_switch_lib' ไว้ในไดเรกทอรี Arduino/libraries ของเครื่องน้อง แล้วดาวน์โหลดไฟล์ทั้งสามจากบทความ Project Hub มาลงในโฟลเดอร์นี้:

1. ez_switch_lib.h

2. ez_switch_lib.cpp

3. keywords.txt

มาไล่ดูส่วนสำคัญอื่นๆ ของสเก็ตช์กันต่อดีกว่า:

การประกาศและเริ่มต้นใช้งานไลบรารี่

ประกาศใช้ไลบรารี่สวิตช์แบบนี้เลย:

#include 

และเริ่มต้นใช้งานสำหรับสวิตช์ตัวเดียวด้วยโค้ดนี้:

Switches my_switches(1); 

ตรงนี้เราสร้างอินสแตนซ์ของสวิตช์ให้รองรับแค่สวิตช์เดียว และใช้ชื่อว่า 'my_switches' เป็นชื่ออินสแตนซ์ ยังไม่ต้องกำหนดประเภทของมันตอนนี้เดี๋ยวไปเซ็ตใน setup ละกัน

setup - ฟังก์ชันนี้ใช้เซ็ตอัพทุกอย่างที่สเก็ตช์ต้องการ:

  • เราตั้งค่าปุ่มสวิตช์ด้วยฟังก์ชัน add_switch() ฟังก์ชันนี้รับพารามิเตอร์สามตัว - ประเภทสวิตช์, พินสวิตช์ และประเภทวงจร โดยประเภทสวิตช์กำหนดด้วยมาโครสำเร็จรูปจากไลบรารี่ 'button_switch', พินสวิตช์กำหนดเป็น 'button_switch_pin' ซึ่งคือพินดิจิตอล (12) ที่เราตัดสินใจใช้ในสเก็ตช์ของเรา และประเภทวงจรที่กำหนดด้วยมาโครสำเร็จรูปจากไลบรารี่ 'circuit_C2' นี่บอกไลบรารี่สวิตช์ว่าให้ต่อสวิตช์แบบง่ายๆ โดยไม่ต้องใช้ตัวต้านทาน (Resistor) 10k โอห์มแบบ pull-down
  • LED ที่ประกาศไว้ และ
  • ตัวตรวจสอบการเต้นของหัวใจ (heart beat monitor)

ที่ต้องสังเกตคือ กระบวนการกำหนดค่าปุ่มสวิตช์จะถูกตรวจสอบความสำเร็จ ถ้ากำหนดค่าไม่ได้ด้วยเหตุผลอะไรก็ตาม สเก็ตช์จะหยุดทำงานทันที จะสังเกตเห็นได้ถ้าตัวตรวจสอบการเต้นของหัวใจถูกตั้งค่าไว้แล้วแต่ไม่ทำงาน (ไม่กระพริบ) ค่าของพารามิเตอร์ 'button_switch_pin' ถูกกำหนดตามมาโครของสเก็ตช์

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

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

void announce_throw() {
uint8_t led;
// เริ่มต้นด้วยการล้างค่าจุด/แต้มของลูกเต๋าที่มีอยู่
clear_pips();
for (uint8_t cycle = 1; cycle <= 2; cycle++) {// ทำ 2 รอบ
for (uint8_t led = 0; led < max_leds; led++) {
digitalWrite(pip_pins[led], HIGH);
digitalWrite(pip_pins[max_leds - led - 1], HIGH);
delay(60);
heart_beat(); // ให้ตัวจับเวลาการเต้นของหัวใจทำงานไปด้วยระหว่างที่ประกาศการทอย
digitalWrite(pip_pins[led], LOW);
digitalWrite(pip_pins[max_leds - led - 1], LOW);
delay(20);
heart_beat(); // ให้ตัวจับเวลาการเต้นของหัวใจทำงานไปด้วยระหว่างที่ประกาศการทอย
}
}
}

throw the die - ฟังก์ชัน throw_die() ขั้นแรกจะ 'ประกาศ' ว่ากำลังจะเริ่มทอยลูกเต๋า โดยให้ LED ทั้งเจ็ดดวงกระพริบติด-ดับ สองรอบ ผ่านการเรียกฟังก์ชัน announce_throw()

หลังจากนั้น ฟังก์ชันจะรีเซ็ตค่า seed สำหรับ random ทุกครั้งที่ถูกเรียก แล้วจึงกำหนดค่าที่ทอยได้ (หน้าลูกเต๋าแบบสุ่ม) ในช่วง 0-5 (หรือ 1-6 ในโลกความเป็นจริง)

จากค่านี้ ฟังก์ชันจะตรวจสอบว่า LED แต่ละดวงถูกกำหนดให้เป็นจุด (pip) ของหน้าลูกเต๋าหน้านั้นหรือไม่ ถ้าใช่ ก็จะจุด LED นั้นขึ้นมา

...
#define faces_per_die 6
#define max_pips_per_face 6
uint8_t pip_patterns[faces_per_die][max_pips_per_face] = {
// LED ที่แสดงรูปแบบจุดบนลูกเต๋า, หน้า/ด้านที่ 1-6 (ดัชนีอาร์เรย์ 0-5) บน LED 7 ดวง
5, 0, 0, 0, 0, 0, // 1 จุด, แค่ LED ตรงกลาง
3, 7, 0, 0, 0, 0, // 2 จุด, LED ช่องนอกตรงกลางสองดวง
2, 5, 8, 0, 0, 0, // 3 จุด, LED แนวทแยง
2, 4, 6, 8, 0, 0, // 4 จุด, LED แต่ละมุม
2, 4, 5, 6, 8, 0, // 5 จุด, LED ทั้งหมด
2, 3, 4, 6, 7, 8, // 6 จุด, LED แต่ละคอลัมน์ด้านนอก 3 ดวง
};
...
void throw_die() {
announce_throw(); // 'ประกาศ' การทอยลูกเต๋า
randomSeed(analogRead(A0) * 31 +
analogRead(A1) * 37 +
random(1023, 10000)); // เปลี่ยนค่า seed ไปเรื่อยๆ
uint8_t die_face = (random(1, 104640) % faces_per_die); // ช่วง 0-(faces_per_die-1)
// แสดงจุดบนลูกเต๋า
for (uint8_t column = 0; column < max_pips_per_face; column++) {
uint8_t led = pip_patterns[die_face][column];
if (led != 0) {
// มี LED สำหรับจุดนี้อยู่ ก็จุดมันซะ
digitalWrite(led, HIGH);
}
}
}

main loop - ส่วนหลักของสเก็ตช์นี้เรียบง่ายมาก มันวนลูปไปเรื่อยๆ เพื่อตรวจสอบสถานะของปุ่มกดด้วยฟังก์ชัน read_switch (myswitches.read_switch(switch_id)) จริงจังอยู่นะ ถ้ามันตรวจจับการกดและปล่อยปุ่มได้ (ฟังก์ชันอ่านปุ่มคืนค่ามาเป็น 'switched') เท่านั้น มันถึงจะสั่งทอยลูกเต๋าโดยเรียกใช้ฟังก์ชัน throw_die():

void loop() {
do {
heart_beat(); // keep pumping the heart beat timer every cycle
if (my_switches.read_switch(switch_id) == switched) {
// the value 'switched' is defined by ez_switch_lib.h
// Switch has been pressed and released, so throw the die...
throw_die();
}
} while (true);
}

Randomized Logic Visualizer

โปรเจกต์นี้จำลองการทำงานของลูกเต๋าหกหน้าจริงๆ ด้วยการแมป

ข้อมูล Frontmatter ดั้งเดิม

title: "งานง่ายแต่หล่อ: สุ่มเต๋า 6 หน้าแบบสายช่าง (Part 1)"
description: "มาเขียนโค้ดกวนๆ กัน! ใช้ LED จัดเป็นเมทริกซ์ 3x3 แล้วสั่งงานตรงจากไมโครคอนโทรลเลอร์ให้มันสุ่มเลขเต๋า 6 หน้าได้แบบตึงๆ งานนี้ทั้งฝึกโค้ดและฝึกต่อวงจร วัยรุ่นสายฮาร์ดแวร์ห้ามพลาด!"
author: "ronbentley1"
category: "Gadgets, Games & Toys"
tags:
  - "die"
  - "dice"
  - "ez_switch_lib"
  - "games"
views: 3023
likes: 0
price: 1499
difficulty: "Easy"
components:
  - "7x Resistor 220 ohm"
  - "1x Arduino UNO"
  - "7x 5 mm LED: Red"
  - "1x Development Kit Accessory, Jumper Wire Kit"
  - "1x Tactile Switch, Top Actuated"
  - "1x Solderless Breadboard Half Size"
  - "8x Jumper wires (generic)"
tools: []
apps:
  - "1x Arduino IDE"
downloadableFiles:
  - "https://projects.arduinocontent.cc/f127ed1d-e127-4a86-915e-a9da0e1080dd.ino"
  - "https://projects.arduinocontent.cc/f127ed1d-e127-4a86-915e-a9da0e1080dd.ino"
documentationLinks: []
passwordHash: "e82e52297d49a65bfb388538bfcc53b413bfef3ddeb4568e92e285e38d381f19"
encryptedPayload: "U2FsdGVkX1/E3HA6fWzETcYy2IY2+HBnkFZlFMEtAqI5h2jlcehwOSsP6iDuS68AhDeqeVbs6orJgiey7t9fmo3NmbWDW8DGqfRsJNDr1hg="
seoDescription: "Create a Six sided die sketch using a 3 x 3 LED Matrix and Microcontroller in this Arduino project."
videoLinks: []
heroImage: "https://cdn.jsdelivr.net/gh/bigboxthailand/arduino-assets@main/images/projects/six-sided-die-sketch-part-1-f33152_cover.jpg"
lang: "en"