State Machine เหมาะกับเอามาใช้กับข้อมูลแบบ Stream ที่เรารู้รูปแบบของข้อมูลที่แน่นอนครับ ซึ่ง Serial Port ก็เป็นข้อมูลแบบ Stream ด้วย
โจทย์คือ เราจะรับข้อมูลแบบ stream ในรูปแบบ <XXXXXX> โดยที่
< คือไบต์เริ่มต้น หรือ Header
> คือไบต์สิ้นสุด หรือ Footer
XXXXX คือข้อความ
การเริ่มตอนออกแบบ State Machine เพื่อให้เห็นภาพง่ายๆ ควรเริ่มจากการวาดรูปแผนภาพ
ภาพที่ 1
จากโจทย์ข้างบนวาดออกมาเป็นแผนภาพ State Machine ได้ตามภาพที่ 1 ครับโดยเริ่มจาก state [1] โปรแกรมจะรอจนกระทั่งได้รับ '<'
เมื่อโปรแกรมได้รับ '<' โปรแกรมจะกระโดดไปยัง state [2] คือรับ text แต่ถ้า state [2] ได้รับ '<' อีกครับโปรแกรมจะไปทำ [3] คือลบข้อมูล text ที่รับมาทั้งหมดเพราะถือว่าไม่ตรงตามโปรโตคอล เมื่ออยู่ที่ state [3] แล้วได้รับ text ทุกตัวที่ไม่ใช่ '<' หรือ '>' จะไปที่ state [2] เมื่ออยู่ที่ state [2] แล้วได้รับ '>' คือจบ โปรแกรมจะ print text ที่ได้รับออกมาแล้วจากนั้นจะไปที่ state [1]
มาลองดูโปรแกรมกันครับ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Simple State Machine to receive message via Serial Port for Arduino | |
======= | |
WEBSITE | |
======= | |
https://www.facebook.com/appstack.cc | |
http://www.appstack.cc | |
<-------------------------------------------------------------------> | |
@Author | |
- ultra_mcu@Piak Studiolo LEGO eiei | |
@Date | |
- 2015/03/04 , Create | |
@Tool | |
- Arduino 1.0.6 on OSX | |
*/ | |
#include <stdint.h> | |
#include <stdbool.h> | |
#define _MAX_TEXT_BUFFER_ 50 | |
enum | |
{ | |
STATE_1 = 1, | |
STATE_2, | |
STATE_3, | |
}; | |
char TEXT_BUEFFER[_MAX_TEXT_BUFFER_]; | |
uint8_t TEXT_BUEFFER_CNT = 0; | |
uint8_t STATE = STATE_1; | |
void setup() | |
{ | |
Serial.begin(115200); | |
memset(TEXT_BUEFFER,0,_MAX_TEXT_BUFFER_); | |
} | |
void process_serial(char data,uint8_t *state,char *buffer,uint8_t *buffer_cnt) | |
{ | |
switch(*state) | |
{ | |
case STATE_1: | |
{ | |
if(data == '<') | |
{ | |
*state = STATE_2; | |
} | |
} | |
break; | |
case STATE_2: | |
{ | |
if(data == '<') | |
{ | |
memset(buffer,0,_MAX_TEXT_BUFFER_); | |
*buffer_cnt = 0; | |
*state = STATE_3; | |
} | |
else if(data == '>') | |
{ | |
if(*buffer_cnt > 0) | |
{ | |
Serial.print("Text : "); | |
Serial.println(buffer); | |
} | |
memset(buffer,0,_MAX_TEXT_BUFFER_); | |
*buffer_cnt = 0; | |
*state = STATE_1; | |
} | |
else | |
{ | |
if(*buffer_cnt < _MAX_TEXT_BUFFER_) | |
{ | |
buffer[(*buffer_cnt)++] = data; | |
} | |
} | |
} | |
break; | |
case STATE_3: | |
{ | |
if(data == '>') | |
{ | |
*state = STATE_1; | |
} | |
else if(data == '<') | |
{ | |
memset(buffer,0,_MAX_TEXT_BUFFER_); | |
*buffer_cnt = 0; | |
} | |
else | |
{ | |
buffer[(*buffer_cnt)++] = data; | |
*state = STATE_2; | |
} | |
} | |
break; | |
} | |
} | |
void loop() | |
{ | |
if(Serial.available()> 0) | |
{ | |
process_serial(Serial.read(),&STATE,TEXT_BUEFFER,&TEXT_BUEFFER_CNT); | |
} | |
} |
โปรแกรมจะมี State แบ่งเป็น 3 State คือ STATE_1, STATE_2, STATE_3 โดยจะเก็บอยู่ในตัวแปลชื่อ STATE เมื่อได้รับข้อมูลจาก Serial Port โปรแกรมจะเข้าไปทำงานที่ function process_serial ใน function จะใช้ Switch ในการจัดการ State ซึ่งแต่ละ State ก็ทำงานตามภาพที่ 1 ครับ (ไม่เชื่อลองไล่ Code ดูครับ :p)
ลองเทสโปรแกรมดูครับเอามือรูดคีบอร์ดได้เลย
ปล.ว่าจะเขียนนานแล้ว เพิ่งมีเวลาว่าง
ปล2.เห็นหลายคนใช้ sub string ดูแล้วมันเหนื่อย แล้วก็กินเมม มากๆ
0 ความคิดเห็น:
แสดงความคิดเห็น