智能小车6:串口协议
在智能小车四《串口通信》中讲解了串口的通信原理,它就是一个直接把信息转为电信号的工具,透明传输。接着这篇文章我们来解决一下没有协议而发生信息错乱的情况。比如在我们的小车里,收到字符u表示要前进。我们用实际手机给小车发一条蓝牙串口命令。
从上面你拼出什么了么?CONNECT ...... 这些是蓝牙协议的内容,他可能会与我们的命令重合,使我们的小车发生错乱。于是我想自己定义一个协议,我参考TCP协议的结构来定义的。关于TCP协议也是大学里网络课程有的,我简单描述一下。
TCP包结构:
TCP协议是基于端口的,所以它有源端口、目的端口,而串口协议不存在这个。其它的字段的含义可以网上查到,我这里面不再赘述了。最后我把协议定义成如下结构:
如何实现这个协议呢?需要分别在小车(arduino c语言)和控制端(android java)各实现一套数据包的解析和生成程序。
首先是小车端:
一、发送数据包:
二、接收数据包:
里面的包头识别函数(readHead)与检查包函数(checkFullPackage)我要用伪代码了。因为代码太多了,再粘下去,文章不能看了。
包头识别函数(readHead):
一个字符一个字符的读取,只到读到首部标识字符。第二位是长度,所以用一个循环while读取,直到读取到长度或超时退出。如果读到长度,则可以计算出还需多少空间来存储包。当然第二位也有可能是首部标识,这种情况,就舍弃第一个字符,重新从头计算。之后就可以开始读取数据了,但数据里还有可能是首部字符,这时又舍弃前面的字符,重新执行本函数。
检查包函数(checkFullPackage)比较简单:
最后得到这个命令,可以看得出,getCmd的返回值是个int,并不是一个字符串。嗯,这是因为我的小车功能还比较简单,一个int就能表示所有的命令了,这样也方便调试。另外还有一个问题就是int占用空间少,比较适合arduino这样的硬件受限的设备,字符串可能效率非常低。不过只要我一直玩下去,这就能变成一个字符串。
好累,android部分的协议我还是放到讲android的时候再讲吧。
文/程忠 浏览次数:0次 2017-08-12 09:22:20
从上面你拼出什么了么?CONNECT ...... 这些是蓝牙协议的内容,他可能会与我们的命令重合,使我们的小车发生错乱。于是我想自己定义一个协议,我参考TCP协议的结构来定义的。关于TCP协议也是大学里网络课程有的,我简单描述一下。
TCP包结构:
TCP协议是基于端口的,所以它有源端口、目的端口,而串口协议不存在这个。其它的字段的含义可以网上查到,我这里面不再赘述了。最后我把协议定义成如下结构:
如何实现这个协议呢?需要分别在小车(arduino c语言)和控制端(android java)各实现一套数据包的解析和生成程序。
首先是小车端:
一、发送数据包:
ZZProtocol zzp; /** cmd是要发送的命令 */ void sendCmd(String cmd){ int len=cmd.length(); char data[len+3]; char cmdArray[len]; for(int i=0;i<len;i++){ cmdArray[i]=cmd.charAt(i); } zzp.sendMsg(cmdArray,len,data); for(int i=0;i<len+3;i++){ Serial.print(data[i]); } Serial.println(); } /* msg:要发送的内容 len:数据长度 data:最后发送数据包 */ void ZZProtocol::sendMsg(char msg[], int& len, char data[]) { if (len <= 0) { return; } data[0] = STARTFLAG; data[1] = (char) len; data[3] = msg[0]; char tmpCode = data[3]; for (int i = 1; i < len; i++) { data[i + 3] = msg[i]; tmpCode = tmpCode^data[i + 3]; } data[2] = tmpCode; }
二、接收数据包:
/** 得到命令 */ int getCmd(){ int receiveData[64]; int rstData[64]; int rstNum=0; receiveMsg(receiveData,rstData,rstNum); if(rstNum>0){ if(rstNum==1){ int cmd=(int)rstData[0]; return cmd; } } return 0; } /** 接收命令 */ void receiveMsg(int receiveData[],int rstData[],int &rstNum){ int readNum=Serial.available(); if(readNum>0){ int startIndex=0; readHead(receiveData,startIndex); int rstFlag=zzp.checkFullPackage(receiveData,startIndex,rstData,rstNum); if(rstNum>0){ //正常命令 /* Serial.println("cmd:"); printMsg(rstData,rstNum); */ }else{ //错误信息,调试时用,Serial.print会再次传给蓝牙,造成arduino死机 Serial.print("error:"); Serial.println(rstFlag); if(rstFlag==-1){ char str[100]=""; sprintf(str, "[(head error) byte0 btye1 char0 char1 length]:%d,%d,%c,%c,%d",receiveData[0],receiveData[1],receiveData[0],receiveData[1],readNum); Serial.println(str); }else if(rstFlag==-2){ for(int i=1;i<startIndex;i++){ Serial.print((byte)receiveData[i]); Serial.print(" "); } Serial.println(); } } }else{ //读到的数,调试用 /* if(readNum>0){ Serial.print("readNum:"); Serial.println(readNum); } */ } }
里面的包头识别函数(readHead)与检查包函数(checkFullPackage)我要用伪代码了。因为代码太多了,再粘下去,文章不能看了。
包头识别函数(readHead):
一个字符一个字符的读取,只到读到首部标识字符。第二位是长度,所以用一个循环while读取,直到读取到长度或超时退出。如果读到长度,则可以计算出还需多少空间来存储包。当然第二位也有可能是首部标识,这种情况,就舍弃第一个字符,重新从头计算。之后就可以开始读取数据了,但数据里还有可能是首部字符,这时又舍弃前面的字符,重新执行本函数。
检查包函数(checkFullPackage)比较简单:
/** * * @param receiveData 接收字符串 * @param dataLen 接收字符串长度 * @param rstData 返回字符串 * @param rstLen 返回字符串长度 * @return -1:head或len验证没通过。-2 checkCode验证没通过 。1正常返回 */ int ZZProtocol::checkFullPackage(int receiveData[],int dataLen,int rstData[],int &rstLen) { int head= receiveData[0]; int len = receiveData[1]; int checkCode = receiveData[2]; int dataCheck=-1; if(head==STARTFLAG&&dataLen==len+3){ for (int i=3; i < dataLen ; i++) { if (dataCheck == -1) { dataCheck = receiveData[i]; } else { dataCheck = dataCheck^receiveData[i]; } } }else{ return -1; } if(checkCode==dataCheck){ int index = 0; while (index < len ) { rstData[index]=receiveData[index+3]; index++; } rstLen=len; return 1; }else{ return -2; } }
最后得到这个命令,可以看得出,getCmd的返回值是个int,并不是一个字符串。嗯,这是因为我的小车功能还比较简单,一个int就能表示所有的命令了,这样也方便调试。另外还有一个问题就是int占用空间少,比较适合arduino这样的硬件受限的设备,字符串可能效率非常低。不过只要我一直玩下去,这就能变成一个字符串。
好累,android部分的协议我还是放到讲android的时候再讲吧。
相关阅读
评论:
↓ 广告开始-头部带绿为生活 ↓
↑ 广告结束-尾部支持多点击 ↑