树莓派 + 蓝牙

最近买了一些有意思的设备, 但是却发现往往在使用设备的时候都要被迫带上一台电脑, 而偏工控的设备往往又只有 Windows 客户端, 方便程度感人, 所以就萌发了用树莓派采集实验设备数据的想法. 经过短暂的尝试把经验记录一下.

蓝牙连接

在设备都接好之后 (例如接上 USB 蓝牙适配器, 打开设备配对模式, 树莓派 3B 自带蓝牙尝试未成功), 检查蓝牙设备列表

1
2
3
4
5
6
7
8
9
10
11
12
# 列出所有 usb 设备, 如果你用的非 usb 设备可以跳过
lsusb

# 扫描蓝牙设备
hciconfig -a
# 在这之后会列出所有可见蓝牙设备, 找到你要连接的目标的物理地址

# 测试连接, 使用你设备的物理地址, 这一步可以跳过
sudo l2ping -i hci0 -c 4 20:**:**:**:**:03

# 关键: 绑定蓝牙设备到 /dev/rfcomm*, 这里是绑定到 /dev/rfcomm0
sudo rfcomm bind 0 20:**:**:**:**:03

自此, 你就可以在 /dev/ 目录下找到你的设备了, 接下来我们尝试在 Python 中读取数据.

读取数据

首先你需要安装 PySerialI 来读取串口数据, 为了方便安装你可以选择顺手安装 pip, 然后用 pip 安装PySerial. 如果你常用的 Python 版本是 Python3, 你可能还需要另外安装 Python3, 树莓派自带的是 Python2.

PySerial 文档: Welcome to pySerial’s documentation — pySerial 3.4 documentation

接下来使用一下脚本读取串口数据:

1
2
3
4
import serial
ser = serial.Serial('/dev/rfcomm0')
ser.inWaiting() # 获取缓冲区当前等待读取的数据量大小
ser.read(200)

当然上面是一个简短的示例, 下面给出一个可以自动采集数据的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import sys
from datetime import datetime
import serial

def save_data(data, start_time, mode='a'):
output_filename = f'{start_time.strftime("%y%m%dT%H%M%S")}.txt'
print(f'Writing to file: {output_filename}')
with open(output_filename, mode) as f:
f.write(data)
print('done.')

def connect(device, baud_rate=9600, timeout=.5):
try:
print('Connecting device...')
ser = serial.Serial(device, baud_rate, timeout=timeout)
return ser
except serial.serialutil.SerialException:
print(f'Unable to open "{device}". Please check the connection status.')
sys.exit(1)

def read_ser(device, baud_rate=9600, time_interval=.1, to_file=False, save_interval=30):
ser = connect(device, baud_rate)
print(f'Reading Data...(Device: {device}; Baud Rate: {baud_rate}; Save to File: {to_file})')

if to_file and save_interval > 0:
print('Press "Ctrl+C" to terminate collection and save data')

data = ''
start_time = datetime.now() # time.strftime("%y%m%dT%H%M%S", time.localtime())
last_save = start_time
try:
while True:
# 获得接收缓冲区字符
count = ser.inWaiting()
if count != 0:
# 读取内容并回显
recv = ser.read(count)
if not to_file:
print(recv.hex())
else:
data += recv.hex()
if save_interval > 0:
current_time = datetime.now()
if (current_time - last_save).seconds > save_interval:
last_save = current_time
save_data(data, start_time)
data = ''
# 必要的延时, 等待缓冲区数据
time.sleep(time_interval)
except KeyboardInterrupt:
if ser != None:
ser.close()
if to_file:
save_data(data, start_time, 'a' if save_interval > 0 else 'w')


if __name__ == '__main__':
read_ser(device='/dev/rfcomm0', to_file=False)

释放设备

当结束使用时, 记得执行sudo rfcomm release 0释放设备, 否则下次连接可能会报错.