本文旨在作为入门蓝牙开发的一个简单介绍

安装BlueZ和PyBluez

$ sudo apt install libglib2.0-dev libbluetooth-dev bluetooth

$ pip install pybluez

介绍蓝牙编程

概览

找到通信的设备

确认如何进行通信

创建一个发送的连接

创建一个接收的连接

发送数据

接收数据

选一个通信伙伴

每个蓝牙芯片包含唯一的48-bit地址,称为Bluetooth address和device address。可以把它看作为以太网的MAC地址,同样由IEEE注册授权。在制作的时候就写入到芯片李,作为蓝牙编成最基本的地址单元。

一个蓝牙设备需要和另外一个设备通信,必须要有某种机制获悉到其他设备的蓝牙地址。

在互联网中,通常会利用DNS技术实现一个简单域名来进行IP地址转换,在蓝牙中通常是提供一个友好的名字,例如"My Phone",客户端通过查找附近蓝牙设备来转换为数字地址。

选择传输协议

现在客户端已经确认好要通信的设备,现在就需要确认使用什么样的传输协议了。

RFCOMM + TCP

RFCOMM类似TCP可靠性,虽然协议规范定义设为模仿 RS-232串口通信,就如类似TCP场景操作一样简单。

通常,应用程序使用TCP考虑使用点对点的连接,进行可靠的数据流传输。如果出现了固定次数失败传输,那么连接会断开并且会抛出一个错误。

RFCOMM和TCP最大的不同就是端口的选择,TCP支持最大65525端口,RFCOMM仅支持30。

L2CAP + UDP

UDP通常设计用来在对可靠性传输没有强制要求,就是为了足够轻量。L2CAP提供了类似的设计。

L2CAP,默认提供了一个面向连接,通过发送固定最大长度的单个数据包来提供可靠性。L2CAP可以定制为不同的可靠级别。为了提供该能力,L2CAP提供了传输和确认的方式,为被确认包进行重传。有三种可用的策略:

不重传

传输直到连接失败

丢弃包,如果数据包在特定时间(0-1279毫秒)没有确认放到队列里。这在需要设计为特定时间传输时很有用。

虽然蓝牙允许应用可以使用最大努力通信而不是可靠传输,有几点还是需要注意。

| Requirement | Internet | Bluetooth |

|----------------------|----------|------------------------------------------|

| 基于流的可靠传输 | TCP | RFCOMM |

| 可靠的数据报传输 | TCP | RFCOMM or L2CAP with infinite retransmit |

| 最大努力的数据报传输 | UDP | L2CAP (0-1279 ms retransmit) |

端口号和服务发现协议

在搞清楚传输协议之后,第二个要弄清楚与远程机器通信部分就是端口号。在网络传输协议里,端口号是用来在同一个主机上来区分不同的具体应用的能力。蓝牙也不例外,但是使用了略微不同的术语。在L2CAP中,端口称为Protocol Service Mutiplexers,可以取1到32767基数端口号。在RFCOMM,有1-30通道可以使用。除了这些差别,两个协议提供类似TCP/IP多路复用功能。L2CAP,和RFCOMM不一样,存在(1-1023)保留端口。

| protocol | terminology | reserved/well-known ports | dynamically assigned ports |

|----------|-------------|---------------------------|----------------------------|

| TCP | port | 1-1024 | 1025-65535 |

| UDP | port | 1-1024 | 1025-65535 |

| RFCOMM | channel | none | 1-30 |

| L2CAP | port | odd numbered 1-4095 | odd numbered 4097 - 32765 |

在网络编程中,服务器通常会使用常用端口进行服务,客户端也使用常用端口进行连接。缺点就是不能在同一个服务器使用相同的端口应用程序,应用TCP/UDP可选择的端口非常多,所以这里也没有多大的问题。

蓝牙传输协议中,设计了较少的有效端口,我们不可以随意在设计期间选择任意端口。虽然在L2CAP中也不是什么问题,它存在15,000保留端口,RFCOMM仅有30个端口。结果就是有在7个应用程序就有可能超过50%的可能性端口冲突。蓝牙解决这个问题的方式使用Service Discovery Protocol(SDP)。

不是在设计时确定端口,蓝牙通过在运行时通过发布-订阅模型来确定端口。宿主服务器提供一个叫做SDP服务,它使用了L2CAP一些保留端口。其他服务器在运行时应用程序使用动态端口,并且注册一些它们的描述信息。客户端应用程序会从SDP服务(使用定义号的端口号)获取需要的信息。

这里的问题是客户端端如何知道哪个描述信息时它要找的呢?标准方式时通过给蓝牙赋于128-bits的数值,成为UUID(Universally Unique Identifier),客户端和服务端使用了相同的UUID机制一边SDP服务可以找到它们。

SDP还可以用来描述哪个传输协议在使用,SDP在通信当中也不是必须的,也可以使用TCP/UDP的方式来提前定义端口,但要小心端口冲突。

蓝牙 RFC

PyBluez

PyBluez提供了蓝牙编程的能力。

选择通信伙伴

下面的代码示例,是将附近所有的设备打印出来

import bluetooth

nearby_devices = bluetooth.discover_devices(lookup_names=True)

print("found %d devices" % len(nearby_devices))

for addr, name in nearby_devices:

print(" %s - %s" % (addr, name))

结果:

# python bluetooth_ex1.py

found 1 devices

C0:A5:3E:88:F8:BB - 何文祥的 iPhone

我们可以看到地址使用16进制方式呈现,每个占用8位,一共48-bit。

discover_devices()大概花费10秒来检测设备列表。lookup_names是请求时获取到名称,例如这里的何文祥的 iPhone。

discover_devices()有时候会检测失败,lookup_name()有时也会返回None。

使用RFCOMM通信

在Python中蓝牙编程遵循了socket编程。这对于大部分网络程序编写过的程序员来说应该是非常熟悉,切换过来也相当简单,以下展示了如何使用RFCOMM建立连接,以及传输数据,最后进行了断开操作。

rfcomm_server.py

import bluetooth

server_sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )

port = 1

server_sock.bind(("",port))

server_sock.listen(1)

client_sock,address = server_sock.accept()

print "Accepted connection from ",address

data = client_sock.recv(1024)

print "received [%s]" % data

client_sock.close()

server_sock.close()

rfcomm_client.py

import bluetooth

bd_addr = "01:23:45:67:89:AB"

port = 1

sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )

sock.connect((bd_addr, port))

sock.send("hello!!")

sock.close()

在socket通信中,socket提供了一个通信通道,创建时还没有连接,直到有另一端有发起连接过来,一旦连接建立,就可以进行收发数据。

PyBluez支持两种BluetoothSocket对象:RFCOMM和L2CAP。上面的例子就是RFCOMM socket,通过传递RFCOMM作为BluetootheSocket构造参数。L2CAP我们会在下节描述

我们必须使用bind方法绑定给操作系统,bind方法接受一个元组参数,提供给蓝牙适配器进行监控的地址和端口号。通常我们在设备上只有一个蓝牙适配器,第一个参数使用为空就可以了,之后我们就可以使用listen将socket置入监听模式,等待连接。

BluetoothSocket向外连接需要使用connect方法,需要传递一个元组参数,该传输是指定需要建立连接的地址和端口号。后面我们将介绍使用动态端口号以及使用SDP服务查找端口

使用L2CAP通信

接下来我们了解如何使用L2CAP作为传输协议,L2CAP通信方式和RFCOMM sockets通信方式极其相像。唯一区别就是传递给BluetoothSocket构造参数更改为L2CAP,选择一个(0x1001到0x8FFF)基数号码,默认连接配置提供了可靠的数据包大小为672bytes。

l2cap-server.py

import bluetooth

server_sock = bluetooth.BluetoothSocket(bluetooth.L2CAP)

port = 0x1001

server_sock.bind(("", port))

server_sock.listen(1)

client_sock, address = server_sock.accept()

print("Accepted connection from", address)

data = client_sock.recv(1024)

print("Received [{:r}]".format(data))

client_sock.close()

server_sock.close()

l2cap-client.py

import bluetooth

sock = bluetooth.BluetoothSocket(bluetooth.L2CAP)

bd_addr = "9C:B6:D0:E8:F9:D4"

port = 0x1001

sock.connect((bd_addr, port))

sock.send(b"hello!")

sock.close()

L2CAP发送的数据包有最大限制,两个设备端都维护了一个MTU来指定可以收到的最大包大小。如果两者调整各自的MTU,那么它们的默认672字节可以调整到65535字节。但通常,都会使用默认的MTU值进行茶unshu。在PyBluez通过设置set_l2cap_mtu方法来调整该值

bluetooth.set_l2cap_mtu( l2cap_sock, 65535 )

该方法使用也很直观,第一个参数是创建BluetoothSocket对象,第二个参数就是具体要调整的值大小了。

有时候连接不可靠,需要使用set_packet_timeout方法()

bluetooth.set_packet_timeout( bdaddr, timeout )

set_packet_timeout接收蓝牙地址,和一个毫秒参数,作为调整L2CAP和RFCOMM连接发送包的超时时间,需要有管理员权限,而且该操作是全局影响的。

服务发现协议

当前我们已经学习如何检测到附近蓝牙设备,并且进行两种传输协议的连接,均使用的是固定的蓝牙地址和端口号。在实践中我们并不推荐这么做。

动态开辟端口和使用SDP(Service Discovery Protocol)进行查找,get_available_port方法来找到有效的L2CAP和RFCOMM端口,advertise_service通过本地SDP服务器发送服务,find_service找到特定设备的蓝牙设备。

server_sock.bind(("", bluetooth.PORT_ANY))

bluetooth.PORT_ANY任意端口,后面我可以使用server_sock.getsockname()[1]来获取到实际端口号

bluetooth.advertise_service( sock, name, uuid )

bluetooth.stop_advertising( sock )

bluetooth.find_service( name = None, uuid = None, bdaddr = None )

这三个方法提供了一个在本地蓝牙设备提供了通知服务的方式.advertise_service接收一个socket用来绑定监听, service name和UUID作为参数。socket打开的话通知功能也一直打开,直到调用stop_advertising来关闭该socket。

find_service可以查找单个或者所有附近特定设备。通过匹配name和uuid进行service查找,必须至少指定其中一个。如果bdaddr是None,那么所有附近的设备都会进行查找。如果提供了localhost作为bdaddr参数,那么会对本地的SDP进行查找。否着,就会指定的bdaddr蓝牙设备进行查找。

find_service返回一个列表,每个列表是个字典类型,包含了host, name, protocol, port。

rfcomm-server-sdp.py

import bluetooth

server_sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)

server_sock.bind(("", bluetooth.PORT_ANY))

server_sock.listen(1)

port = server_sock.getsockname()[1]

print("listening on port {:d}".format(port))

uuid = "1e0ca4ea-299d-4335-93eb-27fcfe7fa848"

bluetooth.advertise_service(server_sock, "FooBar Service", uuid)

client_sock, address = server_sock.accept()

print("Accepted connection from ", address)

data = client_sock.recv(1024)

print("received [{:r}]".format(data))

client_sock.close()

server_sock.close()

rfcomm-client-sdp.py

import sys

import bluetooth

uuid = "1e0ca4ea-299d-4335-93eb-27fcfe7fa848"

service_matches = bluetooth.find_service(uuid=uuid)

if len(service_matches) == 0:

print("couldn't find the FooBar service")

sys.exit(0)

first_match = service_matches[0]

port = first_match["port"]

name = first_match["name"]

host = first_match["host"]

print("connecting to {} on {}".format(name, host))

sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )

sock.connect((host, port))

sock.send(b"hello!!")

sock.close()

python 蓝牙开发_蓝牙开发快速入门相关推荐

  1. Python学习笔记--10.Django框架快速入门之后台管理admin(书籍管理系统)

    Python学习笔记--10.Django框架快速入门之后台管理 一.Django框架介绍 二.创建第一个Django项目 三.应用的创建和使用 四.项目的数据库模型 ORM对象关系映射 sqlite ...

  2. 涂鸦蓝牙SDK开发系列教程——1.快速入门

    本系列课程将介绍如何使用 涂鸦蓝牙模组及其 SDK 进行产品开发,帮助开发者更快掌握 涂鸦蓝牙模组.涂鸦蓝牙 SDK.涂鸦 IoT 平台.涂鸦三明治开发套件.涂鸦云模组烧录授权平台 等开发工具的使用方 ...

  3. python开发视频大全_2019年python开发编程21天快速入门视频教程+书籍大全和面试大礼包...

    极力推荐这套python资料,不是那种庞大的复杂的难以入门的课程,这套课程十分简单.其中一套21天入门python的课让你以最快的速度入门,加上另一套python资料包(其中包括了几十本python学 ...

  4. Android开发_蓝牙基础

    蓝牙开发需要的权限 <uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" /> <u ...

  5. AndroidStudio_安卓原生开发_蓝牙连接设备需要动态申请位置权限---Android原生开发工作笔记137

    android 6.0 以后,搜索蓝牙设备,打开蓝牙,除了需要蓝牙权限以外, <!--蓝牙权限--> <uses-permission android:name="andr ...

  6. 有关python的参考文献_测试开发论文,关于Python在嵌入式项目中的辅助开发相关参考文献资料-免费论文范文...

    导读:本文关于测试开发论文范文,可以做为相关论文参考文献,与写作提纲思路参考. 摘 要:嵌入式系统设计开发过程中常会遇到诸如算法分析.原型验证.自动化测试.辅助工具设计等工作,其开发效率和质量直接影响 ...

  7. 【720开发】 spring boot 快速入门

    spring boot 快速入门 通过构建简单的REST应用,了解spring boot的开发基本流程,验证其简单.易用特性. 环境要求 Spring Boot 2.0.0.BUILD-SNAPSHO ...

  8. 【DSP开发】帮您快速入门 TI 的 Codec Engine

    德州仪器半导体技术(上海)有限公司 通用DSP 技术应用工程师 崔晶 德州仪器(TI)的第一颗达芬奇(DaVinci)芯片(处理器)DM6446已经问世快三年了.继DM644x之后,TI又陆续推出了D ...

  9. python调试蓝牙适配器_蓝牙调试 - 小蜗牛叽咕往前 - 博客园

    1 设置 USB转TTL-蓝牙模块 vcc-vcc gnd-gnd rx-rx  (你没看错我也没打错 就是这个) tx-tx 两种进入设置模式 1 先按住蓝牙按键 然后上电 进入2秒一闪  (不要使 ...

  10. python云计算架构开发_云计算开发一般负责什么工作呢?更偏向于运维么?

    云计算工作内容基本就是对云服务器,存储数据,数据库,中间件,网络通信设备,机房设备进行运行维护管理 云计算的"云"就是网络.互联网的一种比喻说法,至于计算,就不解释了.有人给出一个 ...

最新文章

  1. asd.equals(s)与s.equals(asd)为什么前者可避免NullPointerException
  2. Linux基本目录解释
  3. python异步处理请求_如何一次在python中发送异步http请求?
  4. 《OOD启思录》—第2章2.6节角色与类
  5. scala递归求斐波那契数列
  6. ClassyShark——apk分析利器
  7. java队列类_用Java编写一个队列类
  8. python如何输入数据形成列表_将Python字典/列表插入到SQL数据库中最有效的方法是什么?...
  9. 电商常用三大数据分析模型--深入浅出
  10. Linux系统字符终端自动登录问题
  11. 托福听力速记符号单词表
  12. Android组件化开发,组件间的Activity页面跳转。
  13. Virtual Friends HDU - 3172(并查集)
  14. 关于长江的题目_高中优秀议论文题目【高中关于长江的作文题目加优秀范文】...
  15. 金融学本科跨考计算机,跨考研究生怎么选专业,计算机金融最喜欢谁?小编今天告诉你...
  16. keras保存历史准确率与loss值
  17. Ubuntu系统安装英伟达显卡
  18. 娣卞叆娴呭嚭绁炵粡缃戠粶pdf
  19. arch linux 出现 edb7,EDB Linux Debugger 0.9.16
  20. GX Works3 (三):FX5U官方文档细节总结

热门文章

  1. 【线性代数笔记】正交矩阵的性质
  2. select去重 sqlserver_sqlserver查询去掉重复数据的实现
  3. mysql查询表里的重复数据方法:
  4. PDF 合并方法:5个顶级的免费PDF 合并工具
  5. c语言大作业书店图书管理系统,C语言_课程设计—书店图书管理系统.doc
  6. 【已解决】【Appium】请教大神,Appium配置正常,但是运行脚本异常停止,提示[UiAutomator] Moving to state 'stopped'
  7. html自动播放提示音,html播放提示音
  8. React学习笔记汇总
  9. Hadoop——Configuration类详解
  10. 城市轨道交通信号系统学习笔记(二)信号系统的组成