วันพฤหัสบดีที่ 7 พฤษภาคม พ.ศ. 2558

Arduino : จัดการข้อมูลจาก Serial Port ด้วย State Machine

Standard
     State Machine แปลตรงๆ ก็คือสถานะเครื่องจักร ถ้าใครเคยออกแบบวงจร Digital ด้วย Logic gate ที่มีสถานะ อาจจะพอนึกภาพออกมันคือรูปวาดกลมๆ แล้วมีเส้นลากไปลากมา
     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]


มาลองดูโปรแกรมกันครับ

/*
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 ดูแล้วมันเหนื่อย แล้วก็กินเมม มากๆ

Related Posts:

0 ความคิดเห็น:

แสดงความคิดเห็น