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(){}#endifProtocolParser.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
- Arduino自定义通信协议解析
- Arduino通信协议设计
- 自定义通信协议
- 自定义通信协议
- wireshark解析自定义通信协议插件之Lua实现
- Arduino解析
- 自定义通信协议举例
- 自定义应用层通信协议
- 一种自定义网络通信协议
- 一种自定义网络通信协议
- 自定义应用层通信协议
- 自定义应用层通信协议
- 自定义应用层通信协议
- 自定义通信协议(实用)
- 自定义应用层通信协议
- 自定义通信协议设计基础
- 自定义应用层通信协议
- 自定义Socket通信协议
- Core Animation - 寄宿图<一>
- pull解析XML文件
- Java Exception——Java中的异常处理流程
- flash debug版本
- 杭电2063,最大配对
- Arduino自定义通信协议解析
- 多语言适配,让text自动改变大小
- XAML学习笔记1
- iOS 下如何设置全局字体?
- 提高php代码效率的若干写法(真的很实用)
- Spark pipe 实例
- 测试人员的绩效考核应该如何开展?
- AppDelegate详解
- MySQL 5.6 一主多从的 半同步复制搭建(已纠正)