树莓派3B使用板载蓝牙与手机蓝牙进行Socket通信(RFCOMM)
来源:互联网 发布:电动牙刷 知乎 推荐 编辑:程序博客网 时间:2024/06/05 10:02
最近遇到一个项目,需要使用树莓派的板载蓝牙和手机进行通信。开发语言使用Python,对于Python直接调用蓝牙串口进行通信在Github上找到了一个pybluez的库(GitHub传送门),提供了基于套接字的蓝牙串口通信接口。
首先安装pybluez库,pybluez库会在使用pip安装时自动编译相关的蓝牙库,所以需要在安装pybluez之前先把蓝牙开发环境配置好:
sudo
apt-get
install
Python-dev
sudo
apt-get
install
libbluetooth-dev
sudo
pip3
install
pybluez
pybluez库的接口和Python中对于TCP的socket模块接口类似。
首先进行架构的设计。考虑到树莓派需要同时连接多个手机,接收多个手机发来的消息,并作出相应,所以应当将树莓派作为套接字的服务端,手机作为客户端。
先在手机上搞个串口调试工具,我直接在Google Play上找了一款名为蓝牙串口调试助手的App:
在开始的时候,发现树莓派的蓝牙有个问题:手机压根搜不到。这就尴尬了,如果搜不到树莓派,那么手机跟谁连接呢。后来在网上搜了一下,树莓派上有个程序叫bluetoothctl,可以进入一个蓝牙专用的命令行来操作蓝牙。蓝牙中需要开启发现才能让别的设备搜索到,进入bluetoothctl命令,使用discovery命令开启其他设备可见。
$ bluetoothctl
[bluetooth]
# discoverable yes
当打开了可被其他设备发现后,手机上刷新列表后就能够看到树莓派了。
需要注意的是,树莓派的蓝牙可见和手机的类似,开启后有一定的延时,超过时间就会自动关闭可被其他设备发现。目前还没有找到方法能够保持可被发现的状态。
然后就是编写程序了。这里先介绍一下socket在服务器端的工作原理。作为一个服务器,需要监听端口,等待客户端的连接。当有客户端连接时,新开一个线程专门服务新链接,用于处理新连接的所有请求数据。
在pybluez中提供了一个BluetoothSocket类,用于生成socket对象。在BluetoothSocket类中拥有与socket类类似的结构,所以使用方法也类似。
第一步是创建一个BluetoothSocket对象,使其运行在服务器模式下,等待其他设备的连接,通信协议选择RFCOMM:
#创建一个服务器套接字,用来监听端口
server_socket
=
bluetooth.BluetoothSocket(bluetooth.RFCOMM);
#允许任何地址的主机连接,端口号
server_socket.bind(("",
1
))
#监听端口
server_socket.listen(
1
);
随后,使用accept函数接受新的连接,这个函数的返回值是元组,其中包含了新连接的信息,这里我使用的RFCOMM协议返回的元组中第一个是一个连接对象,另一个是一个信息表。这里直接使用一个死循环来处理这些事情,无需担心死循环会过多占用资源,对于accept函数,如果没有新链接,则会阻塞当前线程,也就不会占用CPU时间了。之后的就是开线程服务这个连接了。
#开死循环 等客户端连接
#本处应放在另外的子线程中
while
True
:
#等待有人来连接,如果没人来,就阻塞线程等待
sock,info
=
server_socket.accept();
#打印有人来了的消息
print
(
str
(info[
0
])
+
' Connected!'
);
#创建一个线程专门服务新来的连接
t
=
threading.Thread(target
=
serveSocket,args
=
(sock,info[
0
]))
#设置线程守护,防止程序在线程结束前结束
t.setDaemon(
True
)
#启动线程
t.start();
在这个Demo中,设定为当客户端发来信息后,将其蓝牙地址与信息打印在控制台中并将其回传给客户端,与accept函数相同,在接收缓存中没有数据的时候recv函数也会将当前线程阻塞。
#连接套接字服务子线程
def
serveSocket(sock,info):
#开个死循环等客户端来信息
while
True
:
#接收1024个字节,然后以UTF-8解码(中文),如果没有可以接收的信息则自动阻塞线程(API)
receive
=
sock.recv(
1024
).decode(
'utf-8'
);
#打印刚刚读到的东西(info=地址)
print
(
'['
+
str
(info)
+
']'
+
receive);
#为了返回好看点,加个换行
receive
=
receive
+
"\n"
;
#回传数据给发送者
sock.send(receive.encode(
'utf-8'
));
于是就得到了如下的运行效果
综上所述,当树莓派作为服务器端时的程序:
#-*- coding:utf-8 -*-
import
bluetooth
import
threading
#服务器套接字(用来接收新链接)
server_socket
=
None
#连接套接字服务子线程
def
serveSocket(sock,info):
#开个死循环等待客户端发送信息
while
True
:
#接收1024个字节,然后以UTF-8解码(中文),如果没有可以接收的信息则自动阻塞线程(API)
receive
=
sock.recv(
1024
).decode(
'utf-8'
);
#打印刚刚读到的东西(info=地址)
print
(
'['
+
str
(info)
+
']'
+
receive);
#为了返回好看点,加个换行
receive
=
receive
+
"\n"
;
#回传数据给发送者
sock.send(receive.encode(
'utf-8'
));
#主线程
#创建一个服务器套接字,用来监听端口
server_socket
=
bluetooth.BluetoothSocket(bluetooth.RFCOMM);
#允许任何地址的主机连接,未知参数:1(端口号,通道号)
server_socket.bind(("",
1
))
#监听端口/通道
server_socket.listen(
1
);
#开死循环 等待客户端连接
#本处应放在另外的子线程中
while
True
:
#等待有人来连接,如果没人来,就阻塞线程等待(这本来要搞个会话池,以方便给不同的设备发送数据)
sock,info
=
server_socket.accept();
#打印有人来了的消息
print
(
str
(info[
0
])
+
' Connected!'
);
#创建一个线程专门服务新来的连接(这本来应该搞个线程池来管理线程的)
t
=
threading.Thread(target
=
serveSocket,args
=
(sock,info[
0
]))
#设置线程守护,防止程序在线程结束前结束
t.setDaemon(
True
)
#启动线程
t.start();
另外,树莓派也可以作为客户端访问其他设备也可以通过BluetoothSocket类,只不过在调用bind部分换成了调用connect函数。
- 树莓派3B使用板载蓝牙与手机蓝牙进行Socket通信(RFCOMM)
- 手机蓝牙通信设计(三) RFCOMM协议客户端+语音传送与接收
- 关于蓝牙通信文档 Android建立蓝牙RFCOMM通信
- 蓝牙模块与手机通信
- android实现手机通过蓝牙连接使用socket与芯片进行数据交换
- 在Linux下蓝牙进行rfcomm连接
- 如何使用XP自带超级终端及蓝牙方式与手机进行AT Command通信
- 两个手机进行蓝牙通信项目制作
- Android使用蓝牙与PC端进行通信
- android6.0手机蓝牙与ble蓝牙模块通信
- 蓝牙RFCOMM连接
- android蓝牙开发---与蓝牙模块进行通信
- android蓝牙开发---与蓝牙模块进行通信
- android蓝牙开发---与蓝牙模块进行通信
- android蓝牙开发---与蓝牙模块进行通信
- 1 android蓝牙开发---与蓝牙模块进行通信
- android蓝牙开发---与蓝牙模块进行通信
- android蓝牙开发---与蓝牙模块进行通信
- 目标分割和检测笔记(OpenCV实例精解)
- c++方言
- (crm-bug)Struts has detected an unhandled exception
- 《程序设计入门—Java语言.翁恺》第五周编程作业(1)-多项式加法
- Rust: codewars的Bleatrix Trotter
- 树莓派3B使用板载蓝牙与手机蓝牙进行Socket通信(RFCOMM)
- Advanced Programming in UNIX Environment Episode 7
- Bomb (数位DP)
- STL讲解 容器(map,set,vector,stack,queue)
- CentOS下使用crontab命令来定时执行任务
- 最小生成树 Freckles
- MongoDB:更改数据库位置(Windows)
- Java并发编程 01 并发注意事项
- HDU-3605 Escape(状态压缩+最大流求多重匹配、改版匈牙利算法)