บทความนี้จะสาธิตการสร้าง state machine อย่างง่ายแต่ใช้งานได้จริงบน Arduino กันครับ
State machine คืออะไร ?
State machine ในความเข้าใจของผมคือ การที่เรามี สถานะของเหตุการณ์บางอย่างที่เราต้องการเปลี่ยนไปเป็นอีกอย่าง มีความสัมพันธ์กับสถานะก่อนหน้า, ระหว่างเปลี่ยน และหลังเปลี่ยนอย่างไร จากเงื่อนไขอะไร รายละเอียดสามารถอ่านได้ในเว็ปไซต์ด้านล่างนี้ ซึ่งเขียนไว้ดีอยู่แล้วครับ
https://www.glurgeek.com/education/state-machine-diagram/
ในบทความนี้จะสาธิตการเขียนโปรแกรมแบบ finite State machine บน Arduinoโดยใช้อุปกรณ์ดังนี้
โดย Library ที่ต้องติดตั้งมีดังนี้ (คลิ๊กเพื่อดูวิธีการติดตั้ง Library)
1. Arduino-fsm (Jon Black)
2. LiquidCrystal (F Malpartida)
ทดลองใช้ Shield LCD พร้อมปุ่มกด
เริ่มแรกจะอธิบายส่วนประกอบของ shield หน้าจอพร้อมปุ่มกดที่เราจะเอามาใช้เป็น Input และ Output ของบทความนี้กันก่อน shield นี้ประกอบไปด้วย หน้าจอ LCD 1602 แบบ parallel และปุ่มกดจำนวน 6 ปุ่มต่อกันแบบ parallel คล้ายวงจรดังรูป เพื่อลดจำนวน input จาก 6 ตัวเหลือแค่ analog 1 ตัว จะเห็นว่าระดับแรงดันเมื่อกดแต่ละสวิตซ์จะไม่เท่ากันตามจำนวนตัวต้านทานที่กระแสไหลผ่าน
โดยขาสัญญาณของหน้าจอทั้งหมดถูก define ไว้ดังนี้
Pin |
Function |
Analog 0 |
Button (select, up, right, down and left) |
Digital 4 |
DB4 |
Digital 5 |
DB5 |
Digital 6 |
DB6 |
Digital 7 |
DB7 |
Digital 8 |
RS (Data or Signal Display Selection) |
Digital 9 |
Enable |
Digital 10 |
Backlit Control |
จากนั้นให้เราลองมาสร้าง Sketch ตัวอย่างของการใช้จอกันก่อน โดยใช้ Code ด้านล่างนี้
เมื่ออัพโหลดแล้วอาจจะเห็นจอมืดไปหรือสว่างไป ให้เราหมุนปรับ potentiometer ด้านบนซ้ายมือหลายๆรอบเพื่อหาความสว่างของตัวอักษรที่พอดี จากนั้นให้ลองกดสวิทซ์ต่างๆจะได้ผลลัพธ์ประมาณนี้
ในโค้ดการทดสอบเบื้องต้นนี้จะตั้งค่าจอ และอ่านค่า analog จากขาA0 เพื่อระบุว่าปุ่มไหนกำลังถูกกดอยู่ และแสดงออกทางหน้าจอ เมื่อวัตถุดิบและการทดสอบเบื้องต้นผ่านแล้ว ต่อไปจะเป็นตัวอย่างการใช้ Library Arduino-fsm ครับ
ตัวอย่างการใช้ Arduino-fsm เบื้องต้น
ตัวอย่างการเปิดปิด LED โดยหากเรากดปุ่ม ไฟก็จะติดเป็นระยะเวลา 3 วินาที หลังจากนั้นกลับไปดับเหมือนเดิม เราสามารถเขียนผังสถานะและการเปลี่ยนผ่านได้ดังรูป
เราจะเห็นว่าสถานะมี 2 สถานะ คือ state_led_off และ state_led_on ซึ่งหมายถึงสถานะที่ led ดับหรือติด ตามลำดับ ซึ่งการจะเปลี่ยนสถานะจาก off เป็น on จะเรียกว่า transition โดยเป็นการ transition ที่ถูกจากการกระทำ (Action) ชื่อว่า BUTTON_EVENT เมื่อสถานะมีการเปลี่ยนเป็น state_led_on แล้วจะถูกเปลี่ยนสถานะอีกรอบหลังจาก 3 วินาทีตามเส้นทางด้านบน
ด้วยความช่วยเหลือของไลบรารี่ Arduino-fsm เราจะสามารถเขียนให้อยู่ในรูปแบบ Code ได้ดังนี้
ตัวโค้ดจะประกอบไปด้วย 3 ส่วนหลักๆ คือ
1. 1. กำหนดสถานะทั้งหมด
2. 2. กำหนดฟังก์ชันที่จะถูกเรียกแต่ละสถานะ
3. 3. กำหนดเส้นทางจากสถานะหนึ่งไปอีกสถานะหนึ่ง โดยการกระทำอย่างหนึ่ง
เราลองมาดูทีละส่วน ส่วนแรกเป็นการสร้างสถานะ State ขึ้นมา ซึ่งประกอบไปด้วย state_led_off และ state_led_on โดยแต่ละสถานะประกอบไปด้วยฟังก์ชันที่จะถูกเรียกเมื่อมีการ
1. เข้าสถานะนั้น
2. อยู่ในสถานะนั้น
3. กำลังออกจากสถานะนั้น
ดังรูป
State state_led_off(&led_off, &check_button, NULL); |
State state_led_on(&led_on, &check_button, NULL); |
Fsm fsm(&state_led_off); |
เมื่อดูตามโค้ดด้านบนจะเห็นว่า state_led_off ตอนเริ่มเข้าสถานะจะเรียกฟังก์ชัน led_off
และเมื่อเข้าสถานะแล้วจะเรียกฟังก์ชัน check_button จนกว่าจะมีการออกจากสถานะ จะเรียกฟังก์ชัน NULL ( ไม่กำหนดฟังก์ชัน )
จากนั้นสร้าง class สำหรับจัดการ State machine ชื่อ fsm โดยกำหนดสถานะเริ่มต้นเป็น state_led_off
Fsm fsm(&state_led_off);
เมื่อเราได้สถานะและกำหนดฟังก์ชันสำหรับการใช้ในสถานะแล้ว เราต้องสร้างเส้นทาง จากสถานะแต่ละสถานะให้ครบ โดยฟังก์ชันสำหรับการใช้สร้างสถานะมีสองแบบคือ add_transition และ add_timed_transition ในโค้ดเราจะเห็นว่าจะถูกกำหนดในฟังก์ชัน setup คือ
fsm.add_transition(&state_led_off, &state_led_on, BUTTON_EVENT, NULL); |
fsm.add_timed_transition(&state_led_on, &state_led_off, 3000, NULL); |
เมื่อดูจากโค้ดด้านบนจะได้ว่า
1. ตั้ง transition จากสถานะ state_led_off เป็น state_led_on เมื่อมีการเรียก Action BUTTON_EVENT
2. ตั้ง timed transition จากสถานะ state_led_on เป็น state_led_off เมื่อระยะเวลาผ่านไป 3000 ms (3 วินาที)
โดย BUTTON_EVENT จะถูกเรียกผ่านการสั่งคำสั่ง trigger เช่นปรากฏในฟังก์ชัน check_button() ดังโค้ดด้านล่าง
void check_button() |
{ |
int buttonState = digitalRead(BUTTON_PIN); |
if (buttonState == LOW) { |
Serial.println("button_pressed"); |
fsm.trigger(BUTTON_EVENT); |
} |
} |
หลังจากพอเห็นภาพองค์ประกอบรวมๆแล้ว ลองมาดูการใช้งานกับจอ LCD พร้อมปุ่มกดกันครับ
ทำหน้าต่าง LCD เลื่อนไปกลับด้วย Arduino-fsm
การทำหน้าต่างเลื่อนไปมา สมมติเราออกแบบให้เมื่อกดปุ่มซ้ายขวา จะเป็นการเลื่อนหน้า lcd ไปซ้ายหรือขวา จะสามารถเขียนออกเป็น state-machine ได้ดังรูป
เราจะเห็นว่าในตัวอย่างนี้การเปลี่ยนสถานะจะมีสองการกระทำคือ BUTTON_LEFT และ BUTTON_RIGHT ซึ่งหากแปลงเป็นโค้ดจะได้ดังนี้
อัพโหลดโค้ดลงบอร์ดจะได้ผลตามวิดิโอด้านล่าง
ยังไม่สะใจ ลองสร้างสถานะเพิ่มขึ้นอีกหน่อยเพื่อให้เห็นภาพมากขึ้น เช่น ถ้าเป็นไปที่ page2 ให้เลือกระยะเวลา count down และกด select เพื่อเริ่มจับเวลา เมื่อครบตามเวลาให้แสดงข้อความ finished จากนั้น 3 วินาที ให้กลับไปที่หน้า page2 เราสามารถเขียนเพิ่มสถานะได้ดังรูป
โดยมีสถานะ Countdown และ Countdown Finish เพิ่มเข้ามา และมี transition เพิ่มเข้ามาอีก เพิ่มเข้ามาอีก 3 เส้น สามารถเขียนโค้ดได้เป็น
เท่านี้เราก็จะได้ระบบการทำงานแบบ State-machine โดยที่โค้ดไม่ดูยุ่งเหยิงแล้วล่ะครับลองไปปรับใช้กันดู
ธนบดี บุหลันศรีชาติ
ผู้เขียน