Arduino 自定义通讯协议的实现(键值型,不定长数据,传输稳定,提供多机通讯模块调度demo)
参考仁泉之子老哥给的思路上实现拓展 :他的Blog链接
本文示例代码下载链接
数据帧示例:
{{
Data@Name|verify}}
↑值和键都可以为任意长度
| 数据帧 | |
|---|---|
| {{ | 开始标志 |
| Data | 数据段(值),可以替换为你想要发送的数据 |
| Name | 数据名称(键),可以替换为你要给哪个(从机/模块)(接收/发送)数据 |
| Verify | 校验码,对内容 Data@Name|verify 进行校验 |
| }} | 结束标志 |
校验方法:获取校验码
char GetVertifyValue(char* buf)
{int sum = 0;for (int i = 0; i < strlen(buf); i++){sum += *(buf + i);}return sum % ('z' - ' ') + ' ';
}
通讯协议内容
#include "EleCom.h"ELeCom::ELeCom()
{setBufSize(BUFSIZE);
}ELeCom::~ELeCom()
{if (buf != NULL){free(buf);buf = NULL;}
}inline bool ELeCom::setBufSize(unsigned int size)
{buf = (byte *)malloc(sizeof(byte) * size);if (NULL == buf){return false;}bufSize = size;memset(buf, 0, size);
}void ELeCom::addData(byte data)
{//Err: 数据头 {{ 确认if (data == '{'){if (cnt == 1 || cnt == 2){*(buf + 1) = data;cnt = 2;//serDict = NULL;return;}*buf = data;cnt = 1;return;}//Err: 未确认数据头if (cnt < 2){cnt = 0;return;}//Err: 接收数据大于 接收缓冲区大小if (cnt >= bufSize){cnt = 0;return;}//Err: 过滤掉其他非字符if (data < ' ' || data > '}'){cnt = 0;return;}//添加数据*(buf + cnt) = data;cnt++;//判断数据尾if (data == '}'){if (*(buf + cnt - 2) == '}'){resolveCommand(cnt);cnt = 0;posSepa = 0;posSepl = 0;}}
}void ELeCom::resolveCommand(int length)
{if (length < 7){return;}posSepa = GetChPos(buf, '@');posSepl = GetChPos(buf, '|');if (posSepa == 0){return;}//提取键()int keyLen = posSepl - posSepa - 1;char thisKey[keyLen + 1] = {0};memset(thisKey, 0, keyLen + 1);strncpy(thisKey, buf + posSepa + 1, keyLen);thisKey[keyLen] = '\0';// 提取校验码char verify = *(buf + posSepl + 1);if (verify > 0){int sum = 0;for (int i = 2; i < posSepl; i++){sum += *(buf + i);// DEBUG("*(buf + i):");// DEBUGln(*(buf + i));}// DEBUG("sum:");// DEBUGln(sum);char thisValueVerify = sum % ('z' - ' ') + ' ';DEBUG("thisValueVerify:");DEBUGln(thisValueVerify);DEBUG("verify:");DEBUGln(verify);if (thisValueVerify != verify){return;}}//提取值int valueLen = posSepa - 2;char thisValue[valueLen + 1] = {0};memset(thisValue, 0, valueLen + 1);strncpy(thisValue, buf + 2, valueLen);thisValue[valueLen] = '\0';flushSerDict(&serDict.dicKey, thisKey, keyLen);flushSerDict(&serDict.dicValue, thisValue, valueLen);revFlag = true;
}void ELeCom::flushSerDict(char** tmp, char* value, int len)
{flushSerDictMemory(tmp, len + 1);strcpy(*tmp, value);
}void ELeCom::flushSerDictMemory(char** tmp, int len) {free(*tmp);*tmp = (char*)malloc(len);memset(*tmp, 0, len);
}SerDict* ELeCom::getSerialDict()
{if (revFlag){revFlag = false;return &(serDict);}return NULL;
}//void ELeCom::setResolveCallback(ResolveCallback fun) {// this->resolveCallback = fun;//}
/*@Author:ou@Date: 2021-08-06 13:24:53@LastEditTime: 2021-08-09 10:49:15@LastEditors: Please set LastEditors@Description: 自定义通信指令解析库, 专门解析固定格式的字符串 例: {{Data@Name|verify}}@FilePath: \undefinede:\project\72 UNO导入py\EleCommunication\EleCom.h
*/
#ifndef ELECOM_H
#define ELECOM_H#include //接收数组缓冲区大小
#define BUFSIZE 250typedef void (*ResolveCallback)(char* key, char* value);typedef struct SerDict
{char* dicKey;char* dicValue;
};class ELeCom
{private://接收缓冲区byte *buf = NULL;int bufSize = 0;//已接收数据的总长度int cnt = 0;//@分隔位置int posSepa = 0;//|分隔位置int posSepl = 0;ResolveCallback resolveCallback = NULL;public://完整接收标志位bool revFlag = false;SerDict serDict;ELeCom(/* args */);~ELeCom();bool setBufSize(unsigned int size);void flushSerDictMemory(char** tmp, int len);void flushSerDict(char** tmp, char* value, int len);void addData(byte data);void resolveCommand(int length);//void setResolveCallback(ResolveCallback fun);SerDict* getSerialDict();friend void MySub(char** dest,char* src,int len);
};static ELeCom eleCom;#define GetChPos(thisBuf,ch) (int)strchr(thisBuf, ch) - (int)thisBuf#define Debug 1
#if Debug#define DEBUG(s) Serial.print(s)#define DEBUGln(s) Serial.println(s)
#else#define DEBUG(s)#define DEBUGln(s)
#endif
#endif
main
#include "EleCommunication.h"void setup() {Serial.begin(9600);
}void loop() {//SendUltValue(Ult_RevPinsValues, GetStructNum(Ult_RevPinsValues));Task_Process();
}void Task_DataProcessing()
{SerDict* EData = eleCom.getSerialDict();if (EData){char* moudleName = NULL;moudleName = GetModuleName(moudleName, EData);DEBUG("moudleName: ");DEBUGln(moudleName);ModuleScheduling(moudleName, EData);free(moudleName);}
}void GetModuleName(char* moudleName, int pos, SerDict* EData)
{Serial.println(EData->dicKey);Serial.println(EData->dicValue);memset(moudleName, 0, pos + 1);strncpy(moudleName, EData->dicKey, pos);moudleName[pos] = '\0';
}char* GetModuleName(char* moudleName, SerDict* EData)
{int posSep_ = GetChPos(EData->dicKey, '_');moudleName = (char*)malloc(posSep_ + 1);memset(moudleName, 0, posSep_ + 1);strncpy(moudleName, EData->dicKey, posSep_);moudleName[posSep_] = '\0';return moudleName;
}void ModuleScheduling(char* moudleName, SerDict* EData)
{int modulesNum = GetStructNum(moduleNames);for (int i = 0; i < modulesNum; i++){if (!strcmp(moduleNames[i], moudleName)){modsControls[i](EData);}}
}void ModuleSetting(char** moduleKeys, int** moduleValues, int settingNum, SerDict* EData)
{for (int i = 0; i < settingNum; i++){if (!strcmp(moduleKeys[i], EData->dicKey)){moduleValues[i] = atoi(EData->dicValue);}}
}void serialEvent()
{if (Serial.available()){eleCom.addData(Serial.read());}//while(Serial.read() >= 0){}
}/**@description: 定时器中断服务函数@param {*}@return {*}
*/
void TASK_TIMER_IRQ()
{for (int i = 0; i < TaskNum; i++){if (tasks[i].curTime){tasks[i].curTime--;}else{tasks[i].timeIsOK = true;tasks[i].curTime = tasks[i].endTime;}}
}/**@description: 任务调度函数@param {*}@return {*}
*/
void Task_Process()
{static u32 thisTime = 0;if (millis() - thisTime > FlushTime_Task){thisTime = millis();TASK_TIMER_IRQ();}u8 i = 0;for (int i = 0; i < TaskNum; i++){if (tasks[i].timeIsOK){tasks[i].task();tasks[i].timeIsOK = false;}}
}int isArraySame(int* buf1, int* buf2, int num)
{for (int i = 0; i < num; i++){if (buf1[i] != buf2[i]){return 0;}}return -1;
}char GetVertifyValue(char* buf)
{int sum = 0;for (int i = 0; i < strlen(buf); i++){sum += *(buf + i);}return sum % ('z' - ' ') + ' ';
}
main.h
#ifndef ELECOMMUNICATION_H
#define ELECOMMUNICATION_H
#include "EleCom.h"
#include
#include "MsTimer1.h"
#include "EleRGB.h"
#include "ElePBuz.h"
#include "EleUlt.h"/**************************模块调度**********************************/
typedef void(*ModsControl)(SerDict* EData);
void RGB_Control(SerDict* EData);
void PBuz_Control(SerDict* EData);
void Ult_Control(SerDict* EData);char* moduleNames[]={"RGB","PBuz","EleUlt"};
ModsControl modsControls[]={RGB_Control,PBuz_Control,Ult_Control};/******************************************************************/enum Tasks
{DataProcessing,
};typedef void(*Task)();typedef struct
{bool timeIsOK;bool doIsOK;u32 curTime;u32 endTime;Task task;
}MyTaskList;void Task_DataProcessing();MyTaskList tasks[] =
{{0,0, 1, 1, Task_DataProcessing},
};#define FlushTime_Task 1 //ms
#define GetStructNum(a) (sizeof(a)/sizeof(a[0]))#define u16 unsigned int
#define u32 unsigned long
#define TaskNum (GetStructNum(tasks))#define Debug 1
#if Debug#define DEBUG(s) Serial.print(s)#define DEBUGln(s) Serial.println(s)
#else#define DEBUG(s)#define DEBUGln(s)
#endif#endif
使用示例
- 编写从机模块文件,这里以点个RGB彩灯为例
void RGB_Control(SerDict* EData)
{int RGB_SetNum = GetStructNum(RGB_RevPinsKeys);ModuleSetting((char**)RGB_RevPinsKeys, (int**)&RGB_RevPinsValues, RGB_SetNum , EData);ModuleSetting((char**)RGB_RevDataKeys, (int**)&RGB_RevDataValues, RGB_SetNum, EData);RGB_Init(RGB_RevPinsValues, GetStructNum(RGB_RevPinsValues));RGB_Set(RGB_RevPinsValues, RGB_RevDataValues, RGB_SetNum);
}void RGB_Init(int* pins, int num)
{static int* lastPins = (int*)malloc(sizeof(int) * num);if ((RGB_Status && isArraySame(pins, lastPins, num)) != 0){return;}for (int i = 0; i < num; i++){if (pins[i] == -1){return;}}for (int i = 0; i < num; i++){pinMode(pins[i], OUTPUT);}memcpy(lastPins, pins, sizeof(int)*num);DEBUG("lastPins: ");DEBUGln(pins[0]);DEBUGln(pins[1]);DEBUGln(pins[2]);DEBUGln(lastPins[0]);DEBUGln(lastPins[1]);DEBUGln(lastPins[2]);RGB_Status = 1;
}void RGB_Set(int* pins, int* values, int num)
{if (0 == RGB_Status){return;}for (int i = 0; i < num; i++){if (values[i] == -1){continue;}analogWrite(pins[i], values[i]);DEBUG("pins[i]: ");DEBUGln(pins[i]);DEBUG("values[i]: ");DEBUGln(values[i]);}
}//int isArraySame(int* buf1, int* buf2, int num)
//{
// for (int i = 0; i < num; i++)
// {
// if (buf1[i] != buf2[i])
// {
// return 0;
// }
// }
// return -1;
//}
/*@Author: ou@Date: 2021-08-13 15:52:18@LastEditTime: 2021-08-13 15:53:38@LastEditors: Please set LastEditors@Description: In User Settings Edit@FilePath: \EleCommunication\EleRGB.h
*/
#ifndef ELERGB_H
#define ELERGB_Hint RGB_Status = 0;
enum {R,G,B
};char* RGB_RevPinsKeys[] = {"RGB_PinR", "RGB_PinG", "RGB_PinB"};
int RGB_RevPinsValues[] = { -1, -1, -1};char* RGB_RevDataKeys[] = {"RGB_RData", "RGB_GData", "RGB_BData"};
int RGB_RevDataValues[] = { -1, -1, -1};void RGB_Control(SerDict* EData);//class EleRGB
//{
// public:
// int RGB_Status = 0;
// enum {
// R,
// G,
// B
// };
//
// //{{9@RGB_PinR|"}}
// //{{10@RGB_PinG|?}}
// //{{11@RGB_PinB|;}}
// char* RGB_RevPinsKeys[] = {"RGB_PinR", "RGB_PinG", "RGB_PinB"};
// int RGB_RevPinsValues[] = { -1, -1, -1};
//
// char* RGB_RevDataKeys[] = {"RGB_RData", "RGB_GData", "RGB_BData"};
// int RGB_RevDataValues[] = { -1, -1, -1};
//
// void RGB_Control(SerDict* EData);
//};#endif
- 在通讯协议头文件中进行模块配置即可
/**************************模块调度**********************************/
typedef void(*ModsControl)(SerDict* EData);
void RGB_Control(SerDict* EData); /*111111111111111111111*/
void PBuz_Control(SerDict* EData);
void Ult_Control(SerDict* EData);char* moduleNames[]={"RGB","PBuz","EleUlt"}; /*111111111111111111111*/
ModsControl modsControls[]={RGB_Control,PBuz_Control,Ult_Control}; /*111111111111111111111*//******************************************************************/
3.编写对应上位机指令发送,以python为例:
import serial
import time
from threading import Threadclass EleComunicaton():cnt: int = 0buf = ""posSepa = 0posSepl = 0revFlag = 0thisKey = ''thisValue = ''def __init__(self, com, baud):self.com = comself.baud = baudself.eleSerial = serial.Serial(self.com, self.baud)time.sleep(3)if not self.eleSerial.isOpen():print(self.StrOpenErr)# Thread(target=self.SerialRevData).start()def SerialSendData(self, data):self.eleSerial.write(data.encode())time.sleep(0.1)def SerialRevData(self):while True:while self.eleSerial.inWaiting() > 0:strTmp = self.eleSerial.read(1)self.addData(str(strTmp,'utf-8'))def addData(self, data):global cntglobal bufglobal posSepaglobal posSeplif data == '{':if self.cnt == 1 or self.cnt == 2:self.buf+=dataself.cnt = 2return# print(self.buf)self.cnt = 1returnif self.cnt < 2:self.cnt = 0returnif self.cnt >= 250:self.cnt = 0returnif data < ' ' or data > '}':self.cnt = 0return# print(self.buf)self.buf+=dataself.cnt += 1if data == '}':#print(self.buf)if self.buf[self.cnt-2]=='}':self.resolveCommand(self.cnt)self.cnt=0self.posSepa=0self.posSepl=0def resolveCommand(self,length):global posSepaglobal posSeplglobal thisKeyglobal thisValueglobal bufif length<7:returnself.posSepa = self.buf.index('@')+1self.posSepl = self.buf.index('|')print("self.posSepa:",end='')print(self.posSepa)print("self.posSepl:", end='')print(self.posSepl)if(self.posSepa==0):returnself.thisKey= self.buf[self.posSepa:self.posSepl]verify = self.buf[self.posSepl+1:self.posSepl+2]print(self.thisKey)if verify>'0':sum=0for i in range(1,self.posSepl):sum+= ord(self.buf[i])print(self.buf[i])thisValueVerify = sum % (ord('z') - ord(' ')) + ord(' ')print("thisValueVerify:", end='')print(chr(thisValueVerify))print("verify:", end='')print(verify)print("self.thisKey:", end='')print(self.thisKey)if chr(thisValueVerify) != verify:print('1111')returnvalueLen=self.posSepa-2self.thisValue = self.buf[1:self.posSepa-1]self.buf = ""print("self.thisValue:", end='')print(self.thisValue)def getSerialDict(self):if self.revFlag:self.revFlag=0return [self.thisKey,self.thisValue]return 0def EleRGB_PinInit(self, pinR, pinG, pinB):pinsValue = [pinR, pinG, pinB]pinKey = ["RGB_PinR", "RGB_PinG", "RGB_PinB"]cmd = '';for i in range(len(pinsValue)):# for i in range(1):cmd = str(pinsValue[i]) + '@' + pinKey[i]print(('{{' + cmd + '|' + self.GetVerify(cmd) + '}}').encode())self.SerialSendData(('{{' + cmd + '|' + self.GetVerify(cmd) + '}}'))def EleRGB_ColorShow(self, colR, colG, colB):colorsData = [colR, colG, colB]dataKey = ["RGB_RData", "RGB_GData", "RGB_BData"]cmd = ''for i in range(len(colorsData)):cmd = str(colorsData[i]) + '@' + dataKey[i]print(('{{' + cmd + '|' + self.GetVerify(cmd) + '}}').encode())self.SerialSendData(('{{' + cmd + '|' + self.GetVerify(cmd) + '}}'))def ElePbuz_PinInit(self, pin):pinsValue = [pin]pinKey = ["PBuz_Pin"]cmd = '';for i in range(len(pinsValue)):# for i in range(1):cmd = str(pinsValue[i]) + '@' + pinKey[i]print(('{{' + cmd + '|' + self.GetVerify(cmd) + '}}').encode())self.SerialSendData(('{{' + cmd + '|' + self.GetVerify(cmd) + '}}'))def EleUlt_PinInit(self, pinTRIG,pinECHO):pinsValue = [pinTRIG,pinECHO]pinKey = ["Ult_PinTRIG","Ult_PinECHO"]cmd = '';for i in range(len(pinsValue)):# for i in range(1):cmd = str(pinsValue[i]) + '@' + pinKey[i]print(('{{' + cmd + '|' + self.GetVerify(cmd) + '}}').encode())self.SerialSendData(('{{' + cmd + '|' + self.GetVerify(cmd) + '}}'))def ElePbuz_Tone(self, fre):freData = [fre]dataKey = ["PBuz_Data"]cmd = ''for i in range(len(freData)):cmd = str(freData[i]) + '@' + dataKey[i]print(('{{' + cmd + '|' + self.GetVerify(cmd) + '}}').encode())self.SerialSendData(('{{' + cmd + '|' + self.GetVerify(cmd) + '}}'))def GetVerify(self, content):sum = 0for ele in content:sum = sum + ord(ele)# print( ord(ele))sum = sum % (ord('z') - ord(' ')) + ord(' ')return chr(sum)StrOpenErr = '串口打开失败'StrCodeType = 'utf-8'if __name__ == '__main__':eleCom = EleComunicaton('com40', 9600)eleCom.EleUlt_PinInit(3,9);while (True):revData = eleCom.getSerialDict()eleCom.SerialRevData()if revData:print(str(revData[0]))print(str(revData[1]))# if eleCom.revData != '':# eleCom.EleRGB_PinInit(9, 10, 11)## eleCom.EleRGB_ColorShow(0, 50,0)## eleCom.ElePbuz_PinInit(6)## voice=[523,587,659,698,784,880,988,1047]# for i in range(0,8):# eleCom.ElePbuz_Tone(voice[i])# time.sleep(1)# eleCom.ElePbuz_Tone(0)
示例:可如图编辑发送数据

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
