嵌入式软件架构设计之七:双机通信及通信协议之字符串协议
关键词:双机通信、通信协议、报文、JSON、私有协议
在嵌入式产品开发中,经常会遇到需要双机通信的场景,比如设备与PC上位机软件之间的RS232串口通信;智能手表与后端云服务器之间基于TCP的无线通信;蓝牙主机设备(如手机蓝牙APP)与蓝牙从机(如蓝牙智能锁)之间的通信;设备内部不同SOC芯片之间或是功能模块之间的串口通信,例如两个MCU之间的通信,MCU与蓝牙芯片间的通信,这些都属于双机通信。我们这里不探讨这些例子中的RS232/TTL串口、TCP、蓝牙协议、I2C、SPI等具体的物理层接口或总线协议,而是讨论基于这些底层协议上的应用协议,探讨怎样的应用层协议设计能够高效、稳定的进行数据交换。
应用层通信协议是通信双方为了完成信息交换协商的规则或约定。在实际项目开发中,经常会遇到需要通信双方按照实际应用场景自定义通信协议,包括选择使用文本协议还是二进制协议,制定数据帧格式,约定大小端模式,设计数据类型以及规定收发策略,约定同步方式,纠错方式以及定义控制字符定义等,通信双方共同遵守这些规定。本章介绍几种在实际开发中应用比较广泛的一些双机通信方式和私有通信协议,主要是协议报文的介绍,使读者在自己的开发中遇到类似的情况可以根据实际需求选择或制定私有通信协议。
字符串格式通信协议
字符串格式的通信协议是指通信的帧消息均为由ASCII码可见字符构成的字符串,通常以\n为一帧结束标识,收发双方约定好字符串的功能含义,使用字符串处理函数对帧数据进行编码和解码,实现数据交互。
一、简单字符串通信协议
字符串格式通信协议又分两种,一种是简单字符串通信协议,一种是格式化字符串通信协议。简单字符串通信协议一般由特定语义的单词或几个单词构成,如定义双机通信握手协议:上位机给设备发送“hello?\r\n”请求握手,设备接收到消息后,判断如果接收数据是“hello?”则回复“hello",见表1。
握手请求 | |
方向 | host->slave |
命令内容 | hello?\r\n |
长度 | 8 Byte |
描述 | 握手指令请求,由主机发给从机,发送后等待从机响应,收到从机响应如果是hello表示握手成功,否则失败。 |
握手响应 | |
方向 | slave ->host |
命令响应 | hello\r\n |
长度 | 7 Byte |
描述 | 响应指令。如果收到主机的握手请求指令,则从机回复此命令响应。 |
表1
再如上位机发送“TurnOnLed\r\n"请求设备打开LED灯,设备接收到后对字符串判断识别,如果是开灯请求则执行开灯动作并回复"Ok"予以响应,上位机收到"Ok"响应就知道是开灯请求成功,否则失败,就此完成一次开灯请求的通信,具体定义见表2。
握手请求 | |
方向 | host->slave |
命令内容 | TurnOnLed\r\n |
长度 | 11Byte |
描述 | 开灯请求,由主机发给从机,发送后等待从机响应,响应Ok表示成功,Error或无响应表示失败。 |
握手响应 | |
方向 | slave ->host |
命令响应 | 成功:Ok\r\n 或 失败:Error\r\n |
长度 | 4字节或7 Byte |
描述 | 开灯成功响应Ok;开灯失败响应Error。 |
表2
需要说明的是通信领域广泛使用的‘AT命令’也属于简单字符串通信协议的范畴,关于‘AT命令’上一章我们已经作了详细介绍,此处不再赘述,需要了解请见上章内容(AT命令介绍)。
这种简单字符串通信协议操作简单,但是携带信息少,适用于比较简单的通信和命令控制等应用场景,如果设备调试或生产测试中。
二、格式化字符串通信协议
格式化的字符串通信协议主要指双方通信的数据有着格式化的数据结构,一般包含了消息头,消息数据、特殊字符分隔符以及校验字符,方便识别、信息提取和消息完整性校验。
NMEA 0183协议
格式化字符串通信协议有个比较常见的例子是NMEA 0183协议,GPS/北斗等定位接收机输出的标准信息就采用该协议,NMEA是National Marine Electronics Association 的缩写,是美国国家海洋电子协会的简称,也是数据传输标准工业协会,其定义的0183协议是目前导航设备统一的标准协议。
协议消息的基本格式为:$MSGNAME,data1,data2,data3,…[*CC]\r\n
每条消息均为ASCII 字符构成的字符串,以‘$’符(ASCII码0x24)为开头,后面紧跟消息名,之后是以逗号(ASCII码0x2C)为分隔符的不定数目的数据或参数,最后一个参数是可选校验和,由分隔符‘*’(ASCII 码0x2A)与前面的参数分隔。最后以\r\n(ASCII码0x0D 0x0A)作为消息结束标记。
如NMEA 0183协议中的推荐定位消息(Recommended Minimum Specific GPS/TRANSIT Data)定义为:$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*<13><CR><LF>
$是起始符,GPRMC为消息ID,其他各字段定义如下:
<1> UTC(Coordinated Universal Time)时间,hhmmss(时分秒)格式
<2> 定位状态,A=有效定位,V=无效定位
<3> Latitude,纬度ddmm.mmmm(度分)格式(导位数不足则补0)
<4> 纬度半球 N(北半球)或S(南半球)
<5> Longitude,经度dddmm.mmmm(度分)格式(前导位数不足则补0)
<6> 经度半球 E(东经)或W(西经)
<7> 地面速率(000.0~999.9节,Knot,前导位数不足则补0)
<8> 地面航向(000.0~359.9度,以真北为参考基准,前导位数不足则补0)
<9> UTC日期,ddmmyy(日月年)格式
<10> Magnetic Variation,磁偏角(000.0~180.0度,前导位数不足则补0)
<11> Declination,磁偏角方向,E(东)或W(西)
<12> Mode Indicator,模式指示(仅NMEA0183 3.00版本输出,A=自主定位,D=差分,E=估算,N=数据无效)
<13> 校验和
举例:某设备中GPS定位模组输出的推荐定位信息:$GPRMC,045830.20,A,3107.22165,N,10419.86560,E,0.049,,131216,,,A*74
依据上述推荐定位信息的个字段定义解析得到:
<1> UTC时间4时58分30s
<2> 定位状态A,为有效定位
<3> 当前纬度31度07.22165分
<4> 当前是N(北半球)
<5> 当前经度104度19.86560分
<6> 当前是E(东经)
<7> 地面速率0.049节
<8> 无
<9> UTC日期 16年12月 13日
<10> 磁偏角无
<11> 磁偏角方向无
<12> 模式指示为A=自主定位
<13> 校验和为0x74
自定义私有协议
上面的NMEA 0183协议是一种标准协议,我们也常常根据实际场景自定义格式化的字符串私有协议。在一些物联网应用场景中联网设备与云服务器之间的通信有时会使用这种方式,比如智能手表和服务器通过TCP连接,应用层自定义私有协议,例如GPS上报帧格式如下:
$<DeviceId>*<ICCID>*<UTC Time>*<GPS>*<Latitude,N/S>*<Longtitude,E/W>*<crc>\r\n
格式说明:
$:帧头标志
DeviceId:设备ID
ICCID:设备使用的SIM或eSIM的ICCID
UTC Time:该帧数据的时间戳信息
GPS:帧类型,本帧数据为GPS 信息上报帧
Latitude,N/S:经度值,北纬/南纬标记;N表示北纬,S表示南纬,例如:
Longtitude,E/W:经度值,东经/西经标记,E表示东经,W表示西经,例如:
crc:从帧头$符到crc前'*'的异或值,用于接收方校验数据的有效性
\r\n:帧结束标记
通过严格定义各个字段的格式和物理含义方便服务器端识别出具体的设备和解析出相应的数据,非常直观、简便。
JSON格式数据
JSON 是一种格式化字符串,它的主要的作用是结构化地表示数据,非常易于阅读、理解和编写,也易于机器解析和生成,所以在很多应用中在应用层进行数据交互时也常使用JSON数据作为报文格式。
例如JSON格式数据:
{"name": "Donald Trump","age": 79,"isStudent": false,"hobbies": ["golf", "boast", "rapping", "bullying people"],"address": {"street": "The White House","city": "Washington, DC","country": "USA"}
}
对于JSON数据包的编解码有很多开源的代码,嵌入式C语言有cJson开源代码可供使用,非常方便。cJson文件已添加到附件,有需要的可下载使用。
以上这些就是一些比较常见的字符串格式的例子,前面章节中我们介绍的命令行也是一种格式化的字符串消息协议。可以看到字符串格式的消息协议比较直观,消息内容通常使用命令定义对应的英文单词或几个单词的缩写,容易理解,抓包可见,易于调试。缺点是携带的信息量少,一个字符占用一个字节数据,数据量大,效率低,通常使用在数据量不大,对带宽要求不高的应用场景中。和字符串格式协议对应的是二进制协议,下一章我们将进行介绍。
结束
返回总目录