本帖最后由 太空虫虫 于 2016-7-13 22:47 编辑
写在最前面:本帖是介绍与EV3进行蓝牙通信的基本原理,所以可能需要一点计算机及编程语言方法的知识来帮助理解。另外,本帖不涉及如何编写手机APP等方面的知识,也没有现成的软件供下载,如果是有这方面需求的小伙伴,可以去搜搜论坛的其他帖子。谢谢~-----------------------------------------------------------------------------------------------------------------------
最近,想实现通过手机蓝牙控制EV3。在论坛找了一圈,发现掌握这方面知识的前辈蛮多了,但是无一例外都是在介绍自己编写的手机APP,对于如何与EV3进行蓝牙通信均没有介绍,虽然有些前辈愿意开源自己的APP源码,但如果没有一定的Java编程经验以及相应的开发软件的话,要解读起来还挺麻烦,所以,小弟通过官方的文档和反编译其他前辈的APP,了解了一点EV3进行蓝牙通信的基本原理。和大家分享一下,希望对大家有用。
直接上图,我们先拿EV3间的通信为对象来进行研究。下图就是EV3间通信的基本框图 通过图大家可以发现一个叫mailbox的东西,MailBox是EV3自身底层操作系统虚拟出来的东西。 根据我的研究EV3上跑的用户程序要和外界通信,无论发送或接收都是通过这个MailBox来完成的。 写过EV3之间蓝牙通信的小伙伴都应该用过上面这个指令,这个命令就是对MailBox进行读或写操作的指令。
而一个比较麻烦的问题是,在EV3的眼中,外界的其他设备也都是EV3。也就是说,当它想发送信息给目标设备时,也只会把这个数据往自己的“MailBox”里面丢,当目标设备想发送信息给EV3时,也必须让信息能进入到它的MailBox里才行。 那怎么样实现非EV3设备与EV3设备的通信呢? 我们先根据EV3系统的结构对第一幅图进行展开,可以得到下面这幅图。 为了更具体的说明上副图的含义,我们现在假设EV3甲的用户程序需要发送一个数据到EV3乙的用户程序中,那么在EV3甲中数据会先被丢入MailBox中,但是通过图我们可以看到,实际上MailBox是没办法直接把数据发送出去,而是把数据交给了一个叫“EV3协议处理程序”的东西。它是底层操作系统的一个后台程序,作用就是对需要发送出去的信息进行“打包“,对接收到的信息进行“拆包”。“EV3协议“是与EV3通信的关键,本帖后面会重点介绍。 那如上所说,EV3协议处理程序对数据进行打包完成后,就会通过CPU的数据接口把数据发送给EV3上的蓝牙模块(具体是什么接口我没有研究过,理论上是串口)。从这里开始,数据的发送就交由蓝牙模块负责了。 EV3蓝牙模块内的蓝牙芯片会对要发送的数据再按照一个叫SPP的协议进行打包。SPP协议是蓝牙的子协议之一,有兴趣的小伙伴可以自己去百度一下,这里不做介绍,大家只需要知道EV3的蓝牙模块支持这个协议就好(好像也只支持这一个)。打包完成后数据终于能通过蓝牙模块的射频电路真正的被发送出去了。 当数据到达EV3乙后,经历的就是在EV3甲中的逆过程。数据在蓝牙模块按照SPP拆包后,又在CPU里被按照“EV3协议“再拆一次包后,数据最终到达了EV3乙的MailBox里。 讲了这么一大段EV3与EV3间的通信,那EV3如何与非EV3的设备进行通信呢?总结起来就是一句话:找一个带蓝牙且支持SPP的设备,按照EV3协议收发数据即可。而支持SPP的蓝牙设备满地都是,所以说掌握EV3协议就成为了与EV3通信的关键。
-------------------------------------------------------------------------------------------------------------- EV3协议,乐高官方学名为direct commands,也就是“直接命令”,在乐高的官方文档“LEGO MINDSTORMS EV3 Communication Developer Kit”中有介绍。(有兴趣的小伙伴可以去官网http://www.lego.com/zh-cn/mindstorms/downloads下来看看)。“直接命令”类似与Windows里面的命令控制台命令,是内建在EV3底层操作系统里的,所以可以直接执行这些命令而无需编写任何用户程序。“直接命令”十分强大,可以实现命令EV3执行某个程序、直接读取EV3的输入口、直接控制输出口、下载、上传和删除文件、写MailBox等等的功能, 其中写MailBox就是我们所要介绍的。下达命令的实质就是按照官方文档里规定的协议格式,把数据打包后发送给EV3,EV3拿到数据交由协议处理程序拆包后,按照数据的内容执行相应的动作。下达“直接命令”有3个渠道,WIFI、USB和蓝牙,乐高的编程软件就是通过USB口给EV3下达“直接命令”来实现程序的下载上传等功能的,我们这里只需要通过蓝牙向EV3发送写MailBox命令就可以实现我们的目标了。
******************************************************* 下面就是重点了,关于“写MailBox命令”协议格式。 **************************************************************** 官方的文档中定义的格式如下: 它由8个部分组成: 代号 长度 含义 bbbb: 2字节 整个包从mmmm到ppp的数据长度, (小端模式保存) mmmm 2字节 包计数器,目前还不知道作用,直接填0就好。 tt 1字节 包类型,直接填0x81就好。 ss 1字节 命令类型, 0x9e代表写mailbox ll 1字节 mailbox名称字符串的长度 + 1,比如“abc” 的长度为4. aa… ll字节 mailbox名称+0, 内容为字符的ASCII码 + 0x00 LLLL 2字节 包有效数据长度,(小端模式保存) ppp… LLLL字节 包有效数据,我们要丢到EV3 mailbox里的东西就放在这里 (小端模式保存:就是在保存超过一个字节的数据时把低位数据放在低地址,高位放高地址的保存方法,相应的还有大端模式) 额。。看完上面这堆东西大家还一头雾水,搞不懂是什么意思? 那就对了,要不然大家直接看文档就行了,我也用不着发这个帖子了。下面我尽量用简单的语音说明一下上诉内容的意思。 首先说一下Mailbox支持放3种数据,逻辑、数值、字符串。
我们先拿逻辑类型的数据来举例。逻辑只有真和假两种状态,所以EV3协议规定用1字节来表示,真是1,假是0。 那么我们要给EV3的’abc’ mailbox发一个逻辑真的话,数据按如下步骤打包 1. tt、ss、mmmm的内容按照开头的介绍填入规定值即可 2. mailbox名称是用字符串表示的,字符串是由字符和结尾的0终止标识组成的,所以 ’abc’ 字符串的长度为 3+1 = 4,即ll 为4,aaa部分的内容为 0x61,0x62,0x63,0x00 (0x61为’a’的ASCII码,依次类推,字符最后加0x00为终止标识) 3. 如上所说逻辑的数据使用一个字节表示,所以LLLL有效数据长度为1, 所以内容为 0x01,0x00 4. 因为要发送的是逻辑真,所以ppp的部分内容是0x01。 5. 最后,通过计算,整个包的长度为 1(ppp) + 2(LLLL) + 4(aaa.) + 1(II) + 1(tt) + 1(ss) + 2(mmmm) = 12. 所以bbbb 填入 0x0c, 0x00
所以,整个包是这样的(16进制表示), 0x0c, 0x00, 0x00, 0x00, 0x81, 0x9e, 0x04, 0x61, 0x62, 0x63, 0x00, 0x01, 0x00, 0x01 bbbb mmmm tt ss ll aaa.. LLLL ppp
数值、字符串和逻辑只是LLLL和ppp两个部分不一样,其余都相同。 字符串的规则比较简单。比如要给“abc” mailbox发送 “1234” 这个字符串的话。 LLLL内容为0x05, 0x00, ppp的内容就是 0x31, 0x32, 0x33, 0x34,0x00. 数值EV3协议是用32位浮点数表示的,所以LLLL为4,ppp采用小端模式存放32位浮点数的4个字节。 具体比如50.5这样的数字怎么转换为4个字节的数据,是有一套规则的,但是我也不清楚,也没有必要搞清楚,因为使用C语言里的union的数据类型可以很方便的得到,这个后续实际应用篇再讲。
不好意思,由于不可描述的原因Delay一天了,现在一楼先把前天的知识补充完整 (接上文) 以上说的是我们自己的蓝牙设备发送数据给EV3,如果是EV3发送数据给我们的设备的话,从我们自己的蓝牙模块数据口输出的也同样是按照上诉规则打包的数据,我们只需要按照规则拆包就好。
看完上诉的内容,可能会有小伙伴有这样的疑问,在解析包时EV3是如何区分这3种数据类型的? 如果发送的是由3个字符组成的字符串数据,那么就和发送数值类型数据的包的效数据长度一样都是4了,那EV3如何区分,答案是EV3不会区分,对于采用何种数据类型来解析包,是由用户程序决定的,比如用户程序里有如下一个命令,那么对于Mailbox名称为“abc”的包,EV3就会用数值类型来解析,哪怕发送方的本意是要发送3个字符的字符串,当然,最后解析出来的数值肯定是错误的。
另外,小弟做过如下这样的实验,本来是想让同一个mailbox名称能同时接收两种数据类型,结果,无论用何种数据类型往“abc”里发送,都收不到任何的东西。具体原因可能和EV3底层操作系统对于包解析的运作规则有关,所以请保证一个mailbox名称在程序里只接收一种数据类型。
|