Arduino自定义通信协议解析

来源:互联网 发布:mac最小化所有窗口 编辑:程序博客网 时间:2024/06/05 04:57

上一篇文章给出了通信协议的设计。通信协议的格式如下:

协议首部

指令长度

控制指令

校验和

“控制指令”设计成如下格式:

设备类型

设备号

端口号


比如说上位机发送如下的格式的数据:AA0305020106,意思就是协议的首部为AA,指令长度为03,控制指令为050201:实际意义就是设备类型为05,设备号为02,端口为01,这三个数据的校验和为06.现在需要写一个类库,称为ProtocolParser,来解析上述格式的数据。思路也很简单,就是将串口发送的数据存起来,再解析该数据各个字段的含义即可。类库采用C++写的,面向对象的风格。
ProtocolParser.h的源码如下:
#ifndef PROTOCOLPARSER_H#define PROTOCOLPARSER_H/*自定义的库函数协议解析器 V1.0解析的数据格式:协议首部-指令长度-控制指令-校验和"控制指令"格式:设备类型-设备号-端口号*/#include<stdlib.h>#include<string.h>//#include <string>//#pragma warning(disable:4996)#define BUFFER_SIZE 128//假定接收数据的最大长度为128class ProtocolParser{public:ProtocolParser(char *header);~ProtocolParser();void Append(char *data);void AppendChar(char ch);//将从串口接收到的字符存到buffersize_t GetDeviceType();//获取设备类型size_t GetDeviceNumber();//获取设备号size_t GetPort();//获取设备所连接的端口号private:bool m_bInCmd;//标志位,标识一条控制指令是否接收完毕char *m_pHeader;//协议首部size_t m_nCmdLength;//指令长度char *m_pCtrlCmd;//控制指令size_t m_nCheckSum;//校验和char buffer[BUFFER_SIZE];//接收的数据暂时存在buffer中size_t m_nRecvDataIndex;//记录接收数据的索引char GetHeader(size_t index);//获取协议首部指定索引的字符size_t GetCmdLength();//获取控制指令长度size_t GetRecvDataLength();//获取接收到的一条控制指令的长度size_t GetCheckSum();//获取实际接收到的校验和};//构造函数初始化,header为协议首部ProtocolParser::ProtocolParser(char *header){m_bInCmd = false;//strcpy(m_pHeader, header);m_pHeader = header;m_nCmdLength = 0;m_nCheckSum = 0;m_nRecvDataIndex = 0;m_pCtrlCmd = NULL;buffer[0] = '\0';}ProtocolParser::~ProtocolParser(){}#endif
ProtocolParser.cpp的源码如下:
/*ProtocolParser类的各个函数的实现*/#include "ProtocolParser.h"//#include <iostream>using namespace std;//获取协议首部指定索引的字符,这里默认首部为一个字节,比如说为0xAAchar ProtocolParser::GetHeader(size_t index){int headerLength = strlen(m_pHeader);return m_pHeader[index];}//获取“控制指令”字段的长度,通过接收到的数据的第2、3位的值获取size_t ProtocolParser::GetCmdLength(){int len = strlen(buffer);if (len >= 2){m_nCmdLength=(buffer[0] - '0') * 16 + (buffer[1] - '0') * 1;}return m_nCmdLength;}//获取实际接收到的校验和size_t ProtocolParser::GetCheckSum(){//int len = strlen(buffer);m_nCheckSum = (buffer[GetCmdLength() * 2 + 2] - '0') * 16 + (buffer[GetCmdLength() * 2 + 3] - '0') * 1;return m_nCheckSum;}//从buffer中解析出设备类型size_t ProtocolParser::GetDeviceType(){//int len = strlen(buffer);return (buffer[2] - '0') * 16 + (buffer[3] - '0') * 1;}//从buffer中解析出设备号size_t ProtocolParser::GetDeviceNumber(){//int len = strlen(buffer);return (buffer[4] - '0') * 16 + (buffer[5] - '0') * 1;}//从buffer中解析出端口号size_t ProtocolParser::GetPort(){//int len = strlen(buffer);return (buffer[6] - '0') * 16 + (buffer[7] - '0') * 1;}//将从串口接收的字符串存入buffer中void ProtocolParser::AppendChar(char ch){size_t bufferLength = strlen(buffer);switch (m_nRecvDataIndex){case 0:case 1://接收到的数据的索引值为0或者1,表示接收到的是首部m_bInCmd = true;buffer[0] = 0;m_nRecvDataIndex++;return;break;case 2:case 3://接收到的数据的索引值为2或者3,表示接收到的是"指令长度"部分buffer[m_nRecvDataIndex - 2] = ch;buffer[m_nRecvDataIndex - 1] = '\0';m_nRecvDataIndex++;return;break;default:break;}if (m_nRecvDataIndex==(GetCmdLength()*2+5))//达到了索引值{buffer[bufferLength] = ch;buffer[bufferLength + 1] = '\0';size_t chksum = 0;//计算根据接收的数据得到的校验和for (size_t i = 0; i < GetCmdLength()*2;++i){chksum ^= buffer[i + 2];}if (chksum==GetCheckSum())//判断实际接收到的校验和跟计算出来的校验和是否相等{//解析buffer的各个字段的含义cout << "DeviceType:" << GetDeviceType() << endl;cout << "DeviceNumber:" << GetDeviceNumber() << endl;cout << "Port:" << GetPort() << endl;}else//不相等说明出错了{cout << "Error" << endl;}buffer[0] = '\0';m_bInCmd = false;m_nRecvDataIndex = 0;m_nCheckSum=0;}else if (m_bInCmd)//指令未接收完毕{buffer[bufferLength] = ch;buffer[bufferLength + 1] = '\0';m_nRecvDataIndex++;}}void ProtocolParser::Append(char *data){for (size_t i = 0; i < strlen(data);++i){AppendChar(data[i]);}}/*int main(){freopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);ProtocolParser protocolParser("AA");char ch;while (cin>>ch){protocolParser.AppendChar(ch);}return 0;}*/
为了验证代码的正确性,有两种方式,一种是将其导入Arduino IDE中,形成自定义的类库,直接调用相关的接口即可;另一种就是修改一下代码,直接粘贴到Arduino IDE中,编译运行即可。这里采用第二种方式,在Arduino IDE中直接修改的源码如下:
/*自定义的库函数:  协议解析器 V1.0解析的数据格式:  协议首部-指令长度-控制指令-校验和"控制指令"格式:  设备类型-设备号-端口号*/#include<stdlib.h>#include<string.h>//#pragma warning(disable:4996)#define BUFFER_SIZE 128//假定接收数据的最大长度为128class ProtocolParser{public:  ProtocolParser(char *header);  ~ProtocolParser();  void Append(char *data);  void AppendChar(char ch);//将从串口接收到的字符存到buffer  size_t GetDeviceType();//获取设备类型  size_t GetDeviceNumber();//获取设备号  size_t GetPort();//获取设备所连接的端口号private:  bool m_bInCmd;//标志位,标识一条控制指令是否接收完毕  char *m_pHeader;//协议首部  size_t m_nCmdLength;//指令长度  char *m_pCtrlCmd;//控制指令  size_t m_nCheckSum;//校验和  char buffer[BUFFER_SIZE];//接收的数据暂时存在buffer中  size_t m_nRecvDataIndex;//记录接收数据的索引  char GetHeader(size_t index);//获取协议首部指定索引的字符  size_t GetCmdLength();//获取控制指令长度  size_t GetRecvDataLength();//获取接收到的一条控制指令的长度  size_t GetCheckSum();//获取实际接收到的校验和};//构造函数初始化,header为协议首部ProtocolParser::ProtocolParser(char *header){  m_bInCmd = false;  //strcpy(m_pHeader, header);  m_pHeader = header;  m_nCmdLength = 0;  m_nCheckSum = 0;  m_nRecvDataIndex = 0;  m_pCtrlCmd = NULL;  buffer[0] = '\0';}ProtocolParser::~ProtocolParser(){}char ProtocolParser::GetHeader(size_t index){  int headerLength = strlen(m_pHeader);  return m_pHeader[index];}//获取“控制指令”字段的长度,通过接收到的数据的第2、3位的值获取size_t ProtocolParser::GetCmdLength(){  int len = strlen(buffer);  if (len >= 2)  {    m_nCmdLength=(buffer[0] - '0') * 16 + (buffer[1] - '0') * 1;  }  return m_nCmdLength;}//获取实际接收到的校验和size_t ProtocolParser::GetCheckSum(){  //int len = strlen(buffer);  m_nCheckSum = (buffer[GetCmdLength() * 2 + 2] - '0') * 16 + (buffer[GetCmdLength() * 2 + 3] - '0') * 1;  return m_nCheckSum;}//从buffer中解析出设备类型size_t ProtocolParser::GetDeviceType(){  //int len = strlen(buffer);  return (buffer[2] - '0') * 16 + (buffer[3] - '0') * 1;}//从buffer中解析出设备号size_t ProtocolParser::GetDeviceNumber(){  //int len = strlen(buffer);  return (buffer[4] - '0') * 16 + (buffer[5] - '0') * 1;}//从buffer中解析出端口号size_t ProtocolParser::GetPort(){  //int len = strlen(buffer);  return (buffer[6] - '0') * 16 + (buffer[7] - '0') * 1;}//将从串口接收的字符串存入buffer中void ProtocolParser::AppendChar(char ch){  size_t bufferLength = strlen(buffer);  switch (m_nRecvDataIndex)  {  case 0:  case 1://接收到的数据的索引值为0或者1,表示接收到的是首部    m_bInCmd = true;    buffer[0] = 0;    m_nRecvDataIndex++;    return;    break;  case 2:  case 3://接收到的数据的索引值为2或者3,表示接收到的是"指令长度"部分    buffer[m_nRecvDataIndex - 2] = ch;    buffer[m_nRecvDataIndex - 1] = '\0';    m_nRecvDataIndex++;    return;    break;  default:    break;  }  if (m_nRecvDataIndex==(GetCmdLength()*2+5))//达到了索引值  {    buffer[bufferLength] = ch;    buffer[bufferLength + 1] = '\0';    size_t chksum = 0;    //计算根据接收的数据得到的校验和    for (size_t i = 0; i < GetCmdLength()*2;++i)    {      chksum ^= buffer[i + 2];    }    if (chksum==GetCheckSum())//判断实际接收到的校验和跟计算出来的校验和是否相等    {      Serial.println("DeviceType:");      Serial.println(GetDeviceType(),DEC);      Serial.println("DeviceNumber:");      Serial.println(GetDeviceNumber(),DEC);      Serial.println("Port:");      Serial.println(GetPort(),DEC);      //GetDeviceNumber();      //GetPort();      //解析buffer的各个字段的含义      //cout << "DeviceType:" << GetDeviceType() << endl;      //cout << "DeviceNumber:" << GetDeviceNumber() << endl;      //cout << "Port:" << GetPort() << endl;    }    else//不相等说明出错了    {      Serial.println("Error");      //cout << "Error" << endl;    }    buffer[0] = '\0';    m_bInCmd = false;    m_nRecvDataIndex = 0;    m_nCheckSum = 0;  }  else if (m_bInCmd)//指令未接收完毕  {    buffer[bufferLength] = ch;    buffer[bufferLength + 1] = '\0';    m_nRecvDataIndex++;  }}void ProtocolParser::Append(char *data){  for (size_t i = 0; i < strlen(data);++i)  {    AppendChar(data[i]);  }}//int led=13;char value;ProtocolParser protocolParser(170);void setup() {  // put your setup code here, to run once:  Serial.begin(9600);  //Serial.println("Parser:");  //pinMode(led,OUTPUT);}void loop() {  // put your main code here, to run repeatedly:  while(Serial.available()>0){    value=Serial.read();    //Serial.print(value);    protocolParser.AppendChar(value);    //Serial.println(protocolParser.getPort());  }}
编译运行上传到板子上以后,在串口调试助手中调试结果如下图:



2 0
原创粉丝点击