Arduino自定义通信协议解析

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

协议首部

指令长度

控制指令

校验和

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

设备类型

设备号

端口号


比如说上位机发送如下的格式的数据:AA0305020106,意思就是协议的首部为AA,指令长度为03,控制指令为050201:实际意义就是设备类型为05,设备号为02,端口为01,这三个数据的校验和为06.现在需要写一个类库,称为ProtocolParser,来解析上述格式的数据。思路也很简单,就是将串口发送的数据存起来,再解析该数据各个字段的含义即可。类库采用C++写的,面向对象的风格。
ProtocolParser.h的源码如下:
#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());
  }
}
编译运行上传到板子上以后,在串口调试助手中调试结果如下图:
Arduino自定义通信协议解析_第1张图片


你可能感兴趣的:(瞎搞,C++,Linux,C)