单片机与树莓派蓝牙连接_用树莓派玩转蓝牙

作者:Vamei 出处:http://www.cnblogs.com/vamei 严禁转载。

蓝牙是一个使用广泛的无线通信协议,这两年又随着物联网概念进一步推广。我将介绍蓝牙协议,特别是低功耗蓝牙,并用树莓派来实践。树莓派3中内置了蓝牙模块。树莓派通过UART接口和该模块通信。树莓派1和树莓派2中没有内置的蓝牙模块,不过你可以通过USB安装额外的蓝牙适配器。

蓝牙介绍

蓝牙最初由爱立信创制,旨在实现可不同设备之间的无线连接。蓝牙无线通信的频率在2.4GHz附近,和WiFi一样,都属于特高频。相对于低频信号来说,高频传输的速度比较快,穿透能力强,但传输距离比较受限。在没有遮蔽和干扰的情况下,蓝牙设备的最大通信距离能达到30米。但在大多数情况下,蓝牙的实际通信距离在2到5米。相比之下,低频433MHz设备的通信距离很容易超过百米。因此,蓝牙常用于近距离的无线设备,比如无线鼠标和键盘。

41bb41cb7976f7ec0d9a3c03ca1fa680.png

蓝牙的标志

蓝牙的基本工作流程如下:

广播/扫描:通信的一方向外广播自己的信息。另一方通过扫描知道自己周边有哪些蓝牙设备在广播,这些设备的地址是什么,以及是否可以连接。

连接:通信的一方向另一方发起连接请求。双方通过一系列的数据交换建立连接。

数据通信

根据细节上的差别,蓝牙通信又细分为两种:经典蓝牙和低功耗蓝牙。早期的蓝牙通信方式称为经典蓝牙(classic bluetooth)。经典蓝牙中的数据传输协议是串行仿真协议RFCOMM。RFCOMM仿真了常见的串口连接。数据从一端输入,从另一端取出。经典蓝牙的开发非常简单。基于串口开发的有线键鼠程序,就可以直接用于RFCOMM连接的无线键鼠。此外,经典蓝牙可以快速传输数据。因此,诺基亚N95这样的早期智能手机,也用RFCOMM来互传图片和文件。

0b0f0ffe1d5c9b8860527405f4a6e051.png

RFCOMM通信

经典蓝牙的缺点是比较耗电。后来,诺基亚发明了一种可以降低功耗的蓝牙通信方式。2010年出台的蓝牙4.0把这种通信方式规范为“低功耗蓝牙”(BLE,Bluetooth Low Energy)。BLE把通信双方分为非对称的双方,尽量让其中的一方承担主要的开销,减少另一方的负担。举例来说,手环电量少,而且需要长时间待机。BLE通信的主要负担可以放在电量较充裕且充电方便的手机一侧,从而减少手环的能耗。

04b57e530180dbf5a872259eb5b3e182.png

手环作为外设

BLE通信一般也包含广播/扫描的步骤。主动发起广播的设备称为外设(Peripheral),扫描设备称为中心设备(Central)。BLE连接成功之后,就可以开始数据传输。BLE的数据传输协议是ATT和GATT协议。ATT是GATT的基础。ATT协议把通信双方分为服务器(server)和客户(client)。客户主动向服务器发起读写操作。需要注意的是,ATT中的服务器和客户,与广播阶段的外设和中心设备相互独立。当然,在手环这样的应用场景下,外设通常也是服务器。ATT协议以属性(attribute)为单位进行该数据传输。一个属性的格式如下:

3c3e7f2263797083203f68c406c27baf.png

ATT属性

我们分别来理解属性的不同部分:

handle:属性的唯一编号,长度为16位。

type:属性的类型。每种类型用一个UUID编号。

value:属性的值。

permission:属性的权限,分为无、可读、可写、可读写。

服务器储存了多个属性。当客户向服务器请求时,服务器会把自己的属性列表发给客户。随后,客户可以向服务器读取或写入某一个属性值。用读写的方式,通信双方实现了双向通信。

731bf6bc05da49b4f188bebace9fe171.png

以智能手表为例。智能手表和手机配对后,手机可以用读的方式获得智能手表中某个属性下保存的步数,也可以用写的方式写入另一个属性负责的时间。在读写操作中,都是由客户采取主动,服务器只能被动应答。ATT还提供了通知(notification)的工作方式。当服务器改变了某个属性值时,可以主动通知订阅了该属性值的客户。智能手表中的手势识别,就可以通过通知的方式告知手机。这样的话,手机就可以实时地获知手势改变信息。

c71a228d69886ff404eb3b393776d4b6.png

GATT协议构建在ATT协议之上,为属性提供了组织形式。GATT的最小组织单元是Characteristic,可以由数条属性组成。下图中就是一个Characteristic,用于传输红外测温获得的数据。这个例子来自TI的SensorTag:

aefd33cca1b627dbcfedd54dc31de60c.png

从左到右:handle(16进制),handle(10进制),type(16进制),type(文字说明),value(16进制),permission,备注

Characteristc的第一条属性用于声明属性,其类型总是0x2803。这条声明的value部分又可以细分为三部分。第一个部分是0x12,称为Characteristic Properties,是GATT协议层面上的权限控制。其具体含义可参考资料。第二部分0x0025,是Characteristic值的handle。找到handle为0x0025的属性,就在声明属性的下面一行。0x0025的value部分就是红外温度的真正数值。剩下的部分是该Characteristic的UUID,总共128位:

F000-AA01-0451-4000-B000-000000000000

检查Characteristic值的那一行属性,也就是0x0025属性。它的类型也是该Characteristic UUID。除了128位的UUID,蓝牙官方还提供了16位的UUID可供使用,可参考资料。

可以看到,一个Characterstic至少需要两个属性,一个用于声明,一个用于储存它的数据。除此之外,Characteristic还有称为Descriptor的额外描述信息。每个Decriptor占据一行。比如0x0027这个Descriptor,其属性值是54:65:6D:70:7E:20:44:61:74:61,翻译成ASCII就是:

Temp~ Data

此外,温度单位、测量频率等描述信息也经常会以Descriptor的形式放入到Characteristic中。在下一个Characteristic声明出现前的属性,都是该Characteristic的Descriptor。

我们再来看更高级的组织单位Service。一个Service也有行属性作为声明,其类型UUID是0x2800。声明属性的值就是该Service的128位UUID。蓝牙官方也提供了16位的UUID,预留给特定的Service,可参考资料。在下一个Service声明出现前的属性,都属于该Service,比如下图中从0x0023到0x002D的属性:

1b2053ba4ddf6249b0c4429728d3b69a.png

图中包含了一个与红外温度计相关的Service。Service里又有三个Characteristic,分别0x0024-0x0027、0x0028-0x002A、0x002B-0x002D。我已经介绍过第一个Characteristic。第二个Characteristic用于传输温度计参数,第三个用于设置测温频率。

Service和Characteristic都是属性的组织形式。客户可以向服务器请求Service和Characteristic列表,然后对其进行操作。GATT还提供了Profile,可以包括多个Service。不过,Profile并不像前面两者那样存在于服务器。Profile是一种标准,用于说明一个特型设备应该有哪些Service。比如说,HID(Human Interface Device)这种Profile,就说明了蓝牙输入设备应该提供的Service。蓝牙官方定义的Profile可参考资料。

BlueZ

我们用树莓派来深入实践上面学到的蓝牙知识。首先要在树莓派上安装必要的工具。BlueZ是Linux官方的蓝牙协议栈。你可以通过BlueZ提供的接口,进行丰富的蓝牙操作。Raspbian中已经安装了BlueZ。我使用的版本是5.43。你可以检查自己的BlueZ版本:

bluetoothd -v

低版本的BlueZ对低功耗蓝牙的支持有限。如果你的使用版本低于5.43,那么我建议你升级BlueZ。

你可以用下面的命令检查BlueZ的运行状态:

systemctl status bluetooth

我的返回结果是:

● bluetooth.service - Bluetooth service

Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled)

Active: active (running) since Sun 2017-04-23 19:03:08 CST; 1 day 6h ago

Docs: man:bluetoothd(8)

Main PID: 709 (bluetoothd)

Status: "Running"

CGroup: /system.slice/bluetooth.service

└─709 /usr/lib/bluetooth/bluetoothd -C

可以看到,蓝牙服务已经打开,并在正常运行。

你可以用下面命令手动启动或关闭蓝牙服务:

sudosystemctl start bluetoothsudo systemctl stop bluetooth

此外,你还可以让蓝牙服务随系统启动:

sudo systemctl enable bluetooth

了解树莓派上的蓝牙

在Raspbian中,基本的蓝牙操作可以通过bluez中的bluetoothctl命令进行。该命令运行后,将进入到一个新的Shell。在这个shell中输入:

list

将显示树莓派上可用的蓝牙模块,例如:

Controller B8:27:EB:72:47:5E raspberrypi [default]

运行scan命令,开启扫描:

scan on

扫描启动后,用devices命令,可以打印扫描到蓝牙设备的MAC地址和名称,例如:

Device 00:9E:C8:62:AF:55 MiBOX3

Device 4D:CE:7A:1D:B8:6A vamei

此外,你还可以用help命令获得帮助。使用结束后,你可以用exit命令推出bluetoothctl。

除了bluetoothctl,在Raspbian是shell中可以通过hciconfig来控制蓝牙模块。比如开关蓝牙模块:

sudo hciconfig hci0 up #启动hci设备

sudo hciconfig hci0 down #关闭hci设备

命令中的hci0指的是0号HCI设备,即树莓派的蓝牙适配器。

与此同时,你可以用下面命令来查看蓝牙设备的工作日志:

hcidump

bluez本身还提供了连接和读写工具。但不同版本的bluez相关功能的差异比较大,而且使用起来不太方便,所以我下面使用Node.js的工具来实现相关功能。

树莓派作为BLE外设

下一步,我们尝试用树莓派进行BLE通信。我们先把一个树莓派改造成BLE外设,同时它也将充当连接建立后的服务器。这个过程较为复杂。你可以借用Node.js下的bleno库。首先,安装Node.js:

curl -sL https://deb.nodesource.com/setup_5.x | sudo bash -

sudo apt-get install nodejs

第一行的命令是为了确保安装高版本的Node.js。

安装bleno:

mkdir ble-test-peripheral

cd ble-test-peripheral

npm install bleno

运行pizza的例子:

sudo node node_modules/bleno/examples/pizza/peripheral

你可以在node_modules/bleno/examples/pizza/看到源代码,或者到github查看。这个例子提供了一个Service,它的UUID是1333-3333-3333-3333-3333-333333333337。Service中包含了三个Characteristics,分别是用于披萨饼参数、配料参数和烤披萨:

功能

权限

UUID

披萨饼选项

读/写

13333333333333333333333333330001

配料

读/写

13333333333333333333333333330002

烤披萨

写/通知

13333333333333333333333333330003

通过这些Characteristic,我们可以对树莓派进行BLE读写。读写操作会作用于一个代表比萨的对象。披萨饼选项有:

数值

描述

0x00

正常

0x01

0x02

配料是一个8位的参数,每一位代表了一种配料。当这一位是1时,那么说明添加该配料:

第n位

7

6

5

4

3

2

1

0

描述

SAUSAGE

BELL_PEPPERS

PINEAPPLE

CANADIAN_BACON

BLACK_OLIVES

EXTRA_CHEESE

MUSHROOMS

PEPPERONI

因此,0x1A代表了添加MUSHROOMS、BLACK_OLIVES、CANADIAN_BACON,感觉味道还不错。

对于烤披萨来说,写操作设定了烘烤的温度和时间。时间到了之后,中心设备会发出通知,告诉客户端烘烤完成。我们下一步将用另一个树莓派作为BLE中心设备。不过,即使你没有额外的树莓派,你可以用iPhone上LightBlue这样的App来测试这一部分完成的BLE外设。

树莓派作为BLE中心设备

我们拿另一个作为BLE的中心设备进行扫描,并发起连接请求。连接建立后,该服务器将充当客户。和bleno对应,Node.js下有一个叫noble的项目,可以便捷地完成这一任务。首先,安装noble:

mkdir ble-test-central

cd ble-test-central

npm install noble

noble中有一个同样名为pizza的例子,不过这个例子实现的是客户端。运行该例子:

sudo node node_modules/noble/examples/pizza/peripheral

这个例子将自动执行扫描、连接、服务发现、数据传输的全过程。如果你把bleno和noble部署到两个树莓派上,就可以在这两个树莓派之间进行蓝牙通信了。如果你想自定义开发,那么可以在node_modules/noble/examples/pizza/参考源代码,或者到github查看。

树莓派作为Beacon

苹果在BLE的基础上推出了iBeacon协议。iBeacon使用了BLE的广播部分,但不建立连接。一个遵守iBeacon协议的外设称为Beacon。Beacon会广播自己的身份信息和发射信号的强度。中心设备接收到广播之后,除了可以获知Beacon的身份之外,还能通过信号的衰减算出自己与Beacon的距离。在一个典型的超市应用场景中,每件商品可以带上一个Beacon。消费者可以用手机看到自己周围有哪些商品,工作人员也可以用手机来清点货物。商家还可以在服务器上提供商品相关的质保、促销等信息。用户可以根据Beacon的编号,获得这些附加信息。

f57af0bb240d0401dd8e58d914130e6e.png

我们把配备了蓝牙模块的树莓派改造成一个Beacon。既然Beacon只使用了蓝牙中的广播,那么应该关闭树莓派的扫描,打开广播,并且不接受蓝牙连接:

sudohciconfig hci0 noscan # 不再扫描sudo hciconfig hci0 leadv 3 # 开始广播,并且不接受连接

下一步,把广播信息改为符合iBeacon协议的内容:

sudo hcitool -i hci0 cmd 0x08 0x0008 1E 02 01 1A 1A FF 4C 00 02 15 63 6F 3F 8F 64 91 4B EE 95 F7 D8 CC 64 A8 63 B5 00 01 00 02 C5

上面的命令附加了一串16进制信息。其中0x08说明了整条信息是蓝牙命令,0x0008说明后面的内容将作为广播信息。

1E是广播信息开始的标志。按照蓝牙通信的规定,广播信息最多有31个字节。1E后面的广播信息分为两组:

02 01 1A

1A FF 4C 00 02 15 63 6F 3F 8F 64 91 4B EE 95 F7 D8 CC 64 A8 63 B5 00 01 00 02 C5

每一组一开始的一个字节说明了该组信息的长度。02说明了2个字节,1A说明是26个字节。随后一个字节说明了改组信息的类型。第一组的01说明了该组信息是蓝牙控制标志,第二组的FF说明了该组是蓝牙制造商相关信息。

我们来看第二组信息的细节:

4C 00是制造商信息,即苹果。

02 15是iBeacon协议标识。

63 6F 3F 8F 64 91 4B EE 95 F7 D8 CC 64 A8 63 B5部分是设备的UUID,通常是用户编号。

UUID后面的00 01是主编号(Major)。

再往后的00 02是次编号(Minor)。通过UUID、主编号、次编号的组合,我们可以唯一地确定iBeacon设备。

最后的C5说明了蓝牙信号强度,即在1米处测得的该Beacon的RSSI值。中心设备把接收到的信号强度和该信号强度对比,就可以知道信号衰减了多少,从而推算出自己与Beacon的距离。由于我这里写入的C5没有经过校准,所以距离测量很可能不准确。

在iPhone上安装应用Locate Beacon来测试。当我进入到树莓派的广播范围时,该应用就会显示出手机距离树莓派的距离。

1874610e011678c428e9d6825d045ed7.png

使用结束后,可以用下面命令来恢复扫描和停止广播:

sudohciconfig hci0 piscan # 恢复扫描sudo hciconfig hci0 noleadv # 停止广播

总结

这里简单介绍了蓝牙协议,特别是低功耗蓝牙。我以树莓派的蓝牙模块为基础,实现了BLE通信。

我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/505839.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

禁止更改计算机名_计算机改名、加域脚本集合

windows计算机改名、改密码、设置ip、加域脚本集合今天和大家分享一下,如何将前面的改名加域整合在一起,根据选择执行对应的命令。新建txt文档,输入一下代码,另存为“自动加域脚本.bat”。echo offcolor 1Fmode con cols100 lines…

加载文件流_jvm类加载的过程

一个类从加载到虚拟机到使用结束从虚拟机卸载包括了加载、验证、准备、解析、初始化、使用、卸载,即为一个类的生命周期下面来看一下类加载的过程,即加载、验证、准备、解析、初始化5个阶段都做了什么事:阶段1:加载加载阶段虚拟机…

php 消息队列_消息队列篇——windows本地搭建RabbitMQ Server

前言:最近的PHP项目中有使用AMQP,解耦一些业务性的功能模块。因为工作使用的是线上Linux搭建,为了方便测试所以我决定本地搭建一个MQ服务。RabbitMQ简介:MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。…

小程序底部弹窗css_微信小程序之animation底部弹窗动画(两种方法)

简单分享一下常用的底部弹窗层或下拉框弹出层(代码需要修改)的内容弹窗的动画效果,这里分享的是点击按钮后底部弹窗的动画效果。第一种方式是动态设置显示区域的高度,第二种方法是动态设置显示区域的移动的位置(使用到 transform:translateY )&#xff1…

js cookie 存储checkbox_浏览器数据存储方式总结,网友:“精辟”!

今天主要来讲下前端的数据存储,说起数据存储,大家肯定第一时间想起cookie,localstorage,sessionstorage,而其实还有userData和IndexedDB这两种数据存储,接下来将对它们进行一个比较详细的总结一、为什么要进…

mysql获取一个表的数据作为值插入_请问如何在mysql中得到一个即将插入数据表中的那条数据的id值(id自增长)?...

我们在写数据库程序的时候,经常会需要获取某个表中的最大序号数,一般情况下获取刚插入的数据的id,使用select max(id) from table 是可以的。但在多线程情况下,就不行了。下面介绍三种方法(1) getGeneratedKeys()方法:程序片断:Connection conn ;Serial…

navigator工具_Javascript常用工具类

就算现今是以框架为主流的前端,依然离不开JS,因为他是框架底层的一部分。我先上传代码截图(截图显示会更好看点),截图后面有带源码(源码方便大家复制)。/** * 文件描述:js项目开发中的常用方法工具 * *//** * 全局常量 */var DEFAULT_LINE_KE…

tail将输出的日志放到文件中_如何将Spring Boot应用中日志输出格式改为JSON?

作者:DEV出自:解道JDON原文:jdon.com/55006今天,我们有了一类称为日志聚合系统的应用程序。当我们拥有大量微服务并且我们希望跨微服务跟踪日志时,日志聚合很有用。传统的Java应用程序日志如下所示:2020-09…

造轮子是什么意思_程序员为什么热衷于造轮子,升职加薪吗?

作者:小傅哥博客: https://bugstack.cn-沉淀、分享、成长,让自己和他人都能有所收获!一、前言哪个架构师没造过轮子?你想过这样一件事吗? 是先具备能力在安排职位,还是先安排职位在学习&#xf…

阿里云 服务器 系统 php mysql_阿里云服务器配环境(Ubuntu 16.04+Nginx+MySQL+PHP)并部署hexo博客...

购买服务器阿里云向学生出售优惠价格的服务器,一个月只需9.5块,具体配置看图吧更新Ubuntu源,安装Git123sudo apt updatesudo apt upgradesudo apt-get install git配置Nginx安装Nginxsudo apt install nginx开启Nginx服务sudo service nginx …

python web开发项目 源码_Python + Flask 项目开发实践系列七

对于 Python Flask 这种灵活的web开发框架,在前面的六个系列文章中详细的进行了说明,主要讲到了页面的首页加载时的页面渲染,增加功能,删除功能,修改功能,查询功能,查询详情功能等一些页面常见…

回调函数 相当于线程_阿里面试题:请简述下 Node 的线程模型

前言如果你有一定的前端基础,比如 HTML、CSS、JavaScript、jQuery;那么,Node.js 能让你以最低的成本快速过渡成为一个全栈工程师(我称这个全栈为伪全栈,我认为的全栈也要精通数据库,不喜勿喷),从而触及后端…

微信小程序 本地mysql_微信小程序系列之使用缓存在本地模拟服务器数据库

微信小程序系列之使用缓存在本地模拟服务器数据库现在将data.js这个文件视作是本地数据库的初始化数据,要做的第一件事就是讲这些初始化数据装进缓存中,以形成数据库的初始化数据整个应用程序的生命周期,类比一下页面的生命周期,在…

jvm垃圾回收机制_JVM 垃圾回收机制之堆的分代回收

JVM垃圾回收机制之堆的分代回收前言前文我们了解了Java的GC机制,对于堆中的对象,JVM采用引用计数和可达性分析两种算法来标记对象是否可以清除,本文中我们还会了解到JVM将对分成了不同的区域,以便于更好的回收对象。堆的分代Java的…

roadhog不是内部或外部命令_git:git 不是内部或外部命令,也不是可运行的程序

出现git 不是内部或外部命令,也不是可运行的程序解决方案:Windows下配置Git1、从git官网下载windows版本的git:http://git-scm.com/downloads或是从该链接下载对应版本:https://github.com/git-for-windows/git/releases/tag/v2.1…

前端设定项目奖金有多少_【系列二】建筑工程项目激励模式探讨

​​建筑工程一般以项目制的形式运作,如何在这种“一次性”的组织形式下,确保项目符合质量、安全、进度等方面的要求,并确保项目的经济效益,对项目人员实施恰当的项目激励是关键。我们认为,建筑工程项目的激励主要包括…

xss过滤器无法处理ajax请求_thunkPHP 预防XSS攻击

比如在有人恶意在你的输入框中或文本域中输入<script>标签&#xff0c;如果不做处理的话&#xff0c;输入框中的<script>会保存到我们数据库中&#xff0c;等到将这个数据拿出来展示的时候&#xff0c;就等于将这个内容放入到了html代码中&#xff0c;那么这个<…

mysql bcmod_nextcloud网盘搭建:Ubuntu18.04+Nginx+Mysql

背景前几天在windows系统上搭建了一个seafile网盘服务器&#xff0c;在试用时对它的功能还是比较满意的&#xff0c;有保存文件&#xff0c;共享文件&#xff0c;小组讨论和小组文件共享等功能&#xff0c;基本上可以满足项目式学习的需求。不过在第一节课上课试用时(一个班大概…

mysql mode_MYSQL中的sql_mode模式

mysql数据库的中有一个环境变量sql_mode,定义了mysql应该支持的sql语法&#xff0c;数据校验等&#xff01;我们可以通过以下方式查看当前数据库使用的sql_mode&#xff1a;mysql>select sql_mode一&#xff0c;sql_mode值的含义ONLY_FULL_GROUP_BY对于GROUP BY聚合操作&…

汇编为什么分段执行总是执行不了_为什么我的计划总是执行不了?这里有你要的答案...

公众号配文&#xff1a;有些计划如泰山一样纹丝不动。说它难吧&#xff0c;又不是很难&#xff0c;奈何动不起来&#xff1f;——————————说起每日计划&#xff0c;很多人觉得&#xff0c;不就是写写行动清单&#xff0c;然后赶紧去做呗。不过&#xff0c;是不是有些计…