树莓派智能车AlphaBot教程12:蓝牙控制

来自丢石头百科
Admin讨论 | 贡献2019年11月18日 (一) 16:26的版本 (一、安装蓝牙依赖库升级安装蓝牙相关软件包 cpp代码:sudo apt-get update sudo apt-get upgrade -y sudo apt-get dist-upgrade -y sudo apt-get install pi-bluetooth bluez bluez-firmware blueman 最关键一点:添 ...)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)



一、安装蓝牙依赖库


升级安装蓝牙相关软件包


=== cpp代码: ===

sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get dist-upgrade -y
sudo apt-get install pi-bluetooth bluez bluez-firmware blueman

最关键一点:添加pi用户到蓝牙组


=== cpp代码: ===

sudo usermod -G bluetooth -a pi


重启树莓派


=== cpp代码: ===

sudo reboot


二 ,开启蓝牙设备


启动/增加SPP


=== cpp代码: ===

sudo vi /etc/systemd/system/dbus-org.bluez.service


修改文件中这两个语句。


095315uexxxzexclll6h13.png


重启树莓派后,输入hciconfig命令(类似ifconfig命令)查看蓝牙服务


115952vudxauqxdsd9nqud.png

如果有看到hci0设备则蓝牙已经开启工作。如果没有则没有识别到蓝牙设备。


注意:如果/boot/config.txt文件中有 dtoverlay=pi3-miniuart-bt 这语句需要注释掉,否则蓝牙设备不能正常工作。


查看蓝牙适配器提供的各种功能

=== cpp代码: ===

pi@raspberrypi:~ $ sudo sdptool browse local
Browsing FF:FF:FF:00:00:00 ...
Service RecHandle: 0x10000
Service Class ID List:
  "PnP Information" (0x1200)
Profile Descriptor List:
  "PnP Information" (0x1200)
    Version: 0x0103
 
Browsing FF:FF:FF:00:00:00 ...
Service Search failed: Invalid argument
Service Name: Serial Port
Service Description: COM Port
Service Provider: BlueZ
Service RecHandle: 0x10001
Service Class ID List:
  "Serial Port" (0x1101)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 1
Language Base Attr List:
  code_ISO639: 0x656e
  encoding:    0x6a
  base_offset: 0x100
Profile Descriptor List:
  "Serial Port" (0x1101)
    Version: 0x0100
 
Service Name: Generic Access Profile
Service Provider: BlueZ
Service RecHandle: 0x10002
Service Class ID List:
  "Generic Access" (0x1800)
Protocol Descriptor List:
  "L2CAP" (0x0100)
    PSM: 31
  "ATT" (0x0007)
    uint16: 0x0001
    uint16: 0x0008
 
Service Name: Generic Attribute Profile
Service Provider: BlueZ
Service RecHandle: 0x10003
Service Class ID List:
  "Generic Attribute" (0x1801)
Protocol Descriptor List:
  "L2CAP" (0x0100)
    PSM: 31
  "ATT" (0x0007)
    uint16: 0x0010
    uint16: 0x0010
 
Service Name: AVRCP CT
Service RecHandle: 0x10004
Service Class ID List:
  "AV Remote" (0x110e)
  "AV Remote Controller" (0x110f)
Protocol Descriptor List:
  "L2CAP" (0x0100)
    PSM: 23
  "AVCTP" (0x0017)
    uint16: 0x0103
Profile Descriptor List:
  "AV Remote" (0x110e)
    Version: 0x0105
 
Service Name: AVRCP TG
Service RecHandle: 0x10005
Service Class ID List:
  "AV Remote Target" (0x110c)
Protocol Descriptor List:
  "L2CAP" (0x0100)
    PSM: 23
  "AVCTP" (0x0017)
    uint16: 0x0103
Profile Descriptor List:
  "AV Remote" (0x110e)
    Version: 0x0104

三、链接蓝牙设备


115952q7jyat992jn1haz3.png


输入如下命令进入蓝牙控制界面,(可进入界面输入help查看命令列表)


=== cpp代码: ===

sudo bluetoothctl

agent on


然后进入界面之后(可进入界面输入help查看命令列表)

=== cpp代码: ===

agent on
default-agent

scan on之后 就可以看到扫描得到的蓝牙设备的物理地址了,类似XX:XX:XX:XX:XX:XX之类的地址


115952of1ng9w19k9nl111.png

配对使用以下命令(以下请把XX:XX:XX:XX:XX:XX替换为你自己设备的地址,android手机可以在设置->关于手机->状态信息中看到蓝牙地址。) pair XX:XX:XX:XX:XX:XX


115952cd277qjcoa2sc25f.png


115953x3rxqhece3qun9e6.png


配对之后把设备添加到信任列表 trust XX:XX:XX:XX:XX:XX


115953npljy3wzs86nt6sw.png

之后连接设备 connect XX:XX:XX:XX:XX:XX


115953xnk09okfwjj1tuhu.png

最后quit退出就可以了,以后蓝牙设备开机后树莓派会自动进行连接。


四,手机端通过蓝牙发送数据到树莓派


运行如下命令后等待链接


=== cpp代码: ===

sudo rfcomm watch hci0


115953d7hmphhm7ybtvpfm.png

手机端打开蓝牙软件(例如 WS EDR),搜索蓝牙设备并链接到树莓派


115954wv63zv3rt67z6vvo.png


如果没有搜索到树莓派蓝牙,可以运行如下命令开启蓝牙可发现。


115954sp8m8qrz9bp99pqt.png




115954hxeda0ijmzz0dhzd.png


另外打开一个终端运行如下命令可以看到/dev目录下有一个rfcomm0设备,只有蓝牙设备连接上树莓派后才有这个设备。


115955xof7i3q5vbui7u3u.png

运行如下命令,用串口软件minicom 打开这个设备。(如果没有安装minicom,可以运行sudo apt-ge install minicom 安装)


=== cpp代码: ===

sudo minicom -D /dev/rfcomm0


minicom界面如下,此时按下手机中的方向键,minicom会接受到相应的指令。


115955uafz52s7hnzb2285.png


按Ctrl+A,再按Z进入菜单,再按Q退出minicom。


四,蓝牙控制小车示例程序

废话也不多说,直接上代码。bluetooth.py文件

=== cpp代码: ===

#!/usr/bin/python
# -*- coding:utf-8 -*-
import serial
import time
import json
from AlphaBot import AlphaBot
 
LOW_SPEED    =  30
MEDIUM_SPEED =  50
HIGH_SPEED   =  80
 
Ab = AlphaBot()
BT = serial.Serial("/dev/rfcomm0",115200)
print('serial test start ...')
BT.flushInput()
 
try:
    while True:
        data = ""
        while BT.inWaiting() > 0:
            data += BT.read(BT.inWaiting())
        if data != "":
            #print data
            try:
                s = json.loads(data)             
                cmd =  s.get("Forward")
                if cmd != None:
                    if cmd == "Down":
                        BT.write("{\"State\":\"Forward\"}")
                    elif cmd == "Up":
                        BT.write("{\"State\":\"Stop\"}")
                        Ab.stop()
 
                cmd = s.get("Backward")
                if cmd != None:
                    if cmd == "Down":
                        BT.write("{\"State\":\"Backward\"}")
                    elif cmd == "Up":
                        BT.write("{\"State\":\"Stop\"}")
                        Ab.stop()
                 
                cmd = s.get("Left")
                if cmd != None:
                    if cmd == "Down":
                        BT.write("{\"State\":\"Left\"}")
                    elif cmd == "Up":
                        BT.write("{\"State\":\"Stop\"}")
                        Ab.stop()
                         
                cmd = s.get("Right")
                if cmd != None:
                    if cmd == "Down":
                        BT.write("{\"State\":\"Right\"}")
                    elif cmd == "Up":
                        BT.write("{\"State\":\"Stop\"}")
                        Ab.stop()
                         
                cmd = s.get("Low")
                if cmd == "Down":
                    BT.write("{\"State\":\"Low\"}")
                    Ab.setPWMA(LOW_SPEED);
                    Ab.setPWMB(LOW_SPEED);
 
                cmd = s.get("Medium")
                if cmd == "Down":
                    BT.write("{\"State\":\"Medium\"}")
                    Ab.setPWMA(MEDIUM_SPEED);
                    Ab.setPWMB(MEDIUM_SPEED);
 
                cmd = s.get("High")
                if cmd == "Down":
                    BT.write("{\"State\":\"High\"}")
                    Ab.setPWMA(HIGH_SPEED);
                    Ab.setPWMB(HIGH_SPEED);                 
 
                BT.flushInput()                 
            except ValueError:       #not json format
                continue
except KeyboardInterrupt:
    if BT!= None:
        BT.close()




{程序分析]: 1.由上一节可知,蓝牙只需作为串口使用即可,不需要了解复杂的蓝牙协议。关于Python串口控制可以参考 树莓派系列教程13:Serial串口

2.BT = serial.Serial("/dev/rfcomm0",115200) 创建蓝牙设备示例,波特率115200 3.BT.flushInput() 清空接受缓存,丢弃之前的数据。 4.BT.inWaiting() 读取接受缓冲区是否有数据。 5.BT.read() 读取数据 6.BT.write() 发送数据 7.s = json.loads(data) 将json格式的字符转化为Python字典对象。如果蓝牙发送的字符串不是json格式,则会出现ValueError异常,数据丢弃继续接收。 8.cmd = s.get("Forward") 查询字典中是否有Forward键,返回的是键值,如果没有则返回None


运行如下命令启动程序

=== cpp代码: ===

sudo rfcomm watch hci0 1 python bluetooth.py


[命令分析]: 通过rfcomm 命令检测 hci0设备,当蓝牙设备连接到树莓派时,会创建/dev/rfcomm0 节点。 参数 1 表示有设备连接时创建一个进程来运行 python bluetooth.py 当连接成功时进程启动,连接断开时进程结束,或当进程结束时连接断开。