上一篇文章给出了通信协议的设计。通信协议的格式如下:
协议首部 |
指令长度 |
控制指令 |
校验和 |
“控制指令”设计成如下格式:
设备类型 |
设备号 |
端口号 |
#ifndef PROTOCOLPARSER_H
#define PROTOCOLPARSER_H
/*
自定义的库函数
协议解析器 V1.0
解析的数据格式:
协议首部-指令长度-控制指令-校验和
"控制指令"格式:
设备类型-设备号-端口号
*/
#include
#include
//#include
//#pragma warning(disable:4996)
#define BUFFER_SIZE 128//假定接收数据的最大长度为128
class 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()
{
}
#endif
ProtocolParser.cpp的源码如下:
/*
ProtocolParser类的各个函数的实现
*/
#include "ProtocolParser.h"
//#include
using namespace std;
//获取协议首部指定索引的字符,这里默认首部为一个字节,比如说为0xAA
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())//判断实际接收到的校验和跟计算出来的校验和是否相等
{
//解析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
#include
//#pragma warning(disable:4996)
#define BUFFER_SIZE 128//假定接收数据的最大长度为128
class 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());
}
}
编译运行上传到板子上以后,在串口调试助手中调试结果如下图: