作为嵌入式开发工程师,蓝牙模块怎能少呢?
蓝牙模块广泛应用在各种电子器件,比如手机、蓝牙耳机/音箱、蓝牙手环、扫地机器人,等等。大家在学嵌入式的时候,玩过的智能家居、智能小车、无人机,都有蓝牙模块的应用。
所以,蓝牙模块的学习势在必行。
蓝牙模块的学习其实也没大家想的那么难,只需要你玩好串口就行了,再加上会一些 AT 指令,你就可以称得上蓝牙高手了。但关于蓝牙协议栈,那学起来估计没一年半载下不来。
对于初学者来讲,只需要懂得如何使用这个蓝牙模块,就已经绰绰有余了。
本文首发 良许嵌入式网 :https://www.lxlinux.net/e/ ,欢迎关注!
本文所涉及的源码及安装包如下(由于平台限制,请点击以下链接阅读原文下载):
https://www.lxlinux.net/e/stm32/bluetooth-turorial.html
如前面所述,玩蓝牙模块就是玩串口,所以大家一定要先把串口玩好。如果你不懂得如何玩串口,可以看下面这篇文章:
STM32串口接收不定长数据(接收中断+超时判断):https://www.lxlinux.net/e/stm32/stm32-usart-receive-data-using-rxne-time-out.html
如果你是个零基础的小白,连 STM32 都没见过,我也给你准备了一个保姆级教程,手把手教你搭建好 STM32 开发环境,并教你如何下载程序,简直业界良心!
零基础快速上手STM32开发(手把手保姆级教程):https://www.lxlinux.net/e/stm32/stm32-quick-start-for-beginner.html
如果你连代码都不知道怎么烧录到 STM32 的,可以参考下文,提供了 5 种代码烧录方式:
STM32下载程序的五种方法:https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to-stm32.html
现在市面上流行的蓝牙模块有很多,例如广州汇承公司的蓝牙模块应用非常的广泛,它们公司生产的 HC 系列的蓝牙模块如下图所示:
型号 | 主/从机 | 通信协议 | 工作频段 | 通信距离 | 嵌入方式 | 空中速率 | 尺寸 |
---|---|---|---|---|---|---|---|
HC-02 | 从机 | 蓝牙2.0 / 蓝牙4.0 | 2.4G | 10米 | 贴片 | 2Mbps | 26.9 * 13 * 2 mm |
HC-04 | 从机 | 蓝牙2.0 / 蓝牙4.0 | 2.4G | 10米 | 贴片 | 60KB/s | 18.5 * 13 * 2 mm |
HC-05 | 主从机一体 | 蓝牙2.0 | 2.4G | 10米 | 贴片 | 2Mbps | 27.0 * 13 * 2 mm |
HC-06 | 主从机一体 | 蓝牙2.0 | 2.4G | 10米 | 贴片 | 2Mbps | 27.0 * 13 * 2 mm |
HC-08 | 主从机一体 | 蓝牙4.0 | 2.4G | 80米 | 贴片 / 焊接 | 1Mbps | 26.9 * 13 * 2 mm |
HC-09 | 主从机一体 | 蓝牙4.0 | 2.4G | 60米 | 贴片 / 焊接 | 3KB/s | 18.5 * 13 * 2 mm |
HC-42 | 主从机一体 | 蓝牙5.0 | 2.4G | 40米 | 贴片 / 焊接 | 1Mbps / 2Mbps | 26.9 * 13 * 2 mm |
这些蓝牙模块,在主/机、工作频段、通信距离、空中速率等方面都存在差异,大家可以根据自己的业务需求进行选择。
对于初学者而言,HC-08 是一款非常适合入门的蓝牙模块,本文就是以 HC-08 作为对象,介绍蓝牙模块的玩法。
HC-08 采用 TI CC25540 芯片方案,蓝牙 BLE4.0 主从一体,通过 BLE 的软件连接,传输速率 1Mbps ,传输距离 80m ,低功耗,详细参数如下:
HC-08 蓝牙模块是通过串口与单片机进行通信,这个模块既可以作为主机也可以作为从机(通过 AT 指令配置)。有些蓝牙模块不支持主机(如 HC-02 、HC-04),所以在使用时需要注意区分。
HC-08 蓝牙模块实物图如下所示:
可以看到,HC-08 模块一共有 6 个引脚,下面详细介绍各个引脚的作用。
上面的「连接」是指模块通过蓝牙协议连接上主机或从机,并非物理意义上的连接。下同。
正常通信下,只需接 RXD、TXD、GND、VCC 四条线就够了。
蓝牙模块上还有一个 LED灯和一个小按键 (按键控制着引脚 KEY )。默认情况下,当 LED灯闪烁时表示蓝牙模块当前为从机,正在等待连接。而长亮的时候就代表已经有主机连接上该模块,可以正常进行透传通讯了。
当按键按下后,主机将清除已被记录的从机地址。另外,也可使用 AT+CLEAR
指令,实现「主机清除已记录的从机地址」的功能。
注意,在硬件接线的时候蓝牙模块的 TXD 要和单片机的 RXD 相连接,蓝牙模块的 RXD 要和单片机的 TXD 相连接,也就是所谓的「交叉接线」。
当蓝牙模块处于主机模式的时候,可以与一个从机进行连接。在此模式下可以对周围设备进行搜索并选择需要连接的从机进行连接。理论上,一个蓝牙主端设备,可同时与 7 个蓝牙从端设备进行通讯。
一个具备蓝牙通讯功能的设备,可以在两个角色之间进行切换。比如:平时工作在从机模式,等待其它主机来连接;在需要时,可转换为主机模式,向其它设备发起连接。一个蓝牙设备以主机模式发起连接时,需要知道对方的蓝牙地址,配对密码等信息,配对完成之后,可直接发起连接。
当蓝牙模块处于从机模式的时候,只能被主机搜索,不能主动搜索。从机与主机连接以后,也可以和主机进行发送和接收数据。
主机是指能够搜索别人并主动建立连接的一方,从机则不能主动建立连接,只能等待主机连接自己。
HC-08 上电之后,默认情况下就是从机模式。如果需要手动配置,可以使用 AT+ROLE=S
指令。
我们需要通过 AT 指令 AT+ROLE=M
来设置蓝牙模块为主机模式。
AT 指令(AT Commands)最早是由发明拨号调制解调器的贺氏公司为了控制拨号调制解调器而发明的控制协议。后来随着网络带宽的升级,速度很低的拨号调制解调器基本退出市场,但是 AT 指令被保留了下来。
在嵌入式开发中,经常是使用 AT 命令去控制各种通讯模块,比如 WiFi 模块、蓝牙模块、GPRS 模块等等。一般就是主芯片通过硬件接口(比如串口、SPI)发送 AT 指令给通讯模块,模块接收到数据之后回应响应的数据。
AT 指令分为四种类型:
类型 | 格式 | 功能 |
---|---|---|
测试指令 | AT + < X > = ? | 查询设置命令或内部程序设置的参数及其取值范围 |
查询指令 | AT + < X > ? | 返回参数的当前值 |
设置指令 | AT + < X > = < … > | 设置用户自定义的参数值 |
执行指令 | AT + < X > | 执行受模块内部程序控制的变参数不可变 |
蓝牙模块的 AT 指令相较于 ESP8266 要少非常多,后者有近百条。现在举例一些常用指令,并使用这些指令一步一步的通过串口实现蓝牙模块的收发数据。
序号 | AT指令(小写 x 表示参数) | 作用 | 默认状态 | 主/从生效 |
---|---|---|---|---|
1 | AT | 检测串口是否正常工作 | - | M/S |
2 | AT+RX | 查看模块基本参数 | - | M/S |
3 | AT+DEFAULT | 恢复出厂设置 | - | M/S |
4 | AT+RESET | 模块重启 | - | M/S |
5 | AT+VERSION | 获取模块版本、 日期 | - | M/S |
6 | AT+ROLE=x | 主/从角色切换 | S | M/S |
7 | AT+NAME=xxx | 修改蓝牙名称 | HC-08 | M/S |
8 | AT+ADDR=xxxxxxxxxxxx | 修改蓝牙地址 | 硬件地址 | M/S |
9 | AT+RFPM=x | 更改无线射频功率 | 0(4dBm) | M/S |
10 | AT+BAUD=xx,y | 修改串口波特率 | 9600,N | M/S |
11 | AT+CONT=x | 是否可连接 | 0(可连) | M/S |
12 | AT+AVDA=xxx | 更改广播数据 | - | S |
13 | AT+MODE=x | 更改功耗模式 | 0 | S |
14 | AT+AINT=xx | 更改广播间隔 | 320 | M/S |
15 | AT+CINT=xx,yy | 更改连接间隔 | 6,12 | M/S |
16 | AT+CTOUT=xx | 更改连接超时时间 | 200 | M/S |
17 | AT+CLEAR | 主机清除已记录的从机地址 | - | M |
18 | AT+LED=x | LED 开/关 | 1 | M/S |
19 | AT+LUUID=xxxx | 搜索 UUID | FFF0 | M/S |
20 | AT+SUUID=xxxx | 服务 UUID | FFE0 | M/S |
21 | AT+TUUID=xxxx | 透传数据 UUID | FFE1 | M/S |
22 | AT+AUST=x | 设置自动进入睡眠的时间 | 20 | S |
请注意,只有当蓝牙模块未连接上主/从机,通过串口发送的数据才会被识别为 AT
指令。否则一旦连接上主/从机,则发送的字符串则被视为普通数据,直接透传给对方。
测试指令
指令 | 响应 | 功能 | 说明 |
---|---|---|---|
AT | OK | 测试指令 | 最基础的测试指令 |
当模块连接上 MCU 之后,我们不知道模块是否连接到位、是否有虚连、模块是否正常工作,我们可以发送 AT
这条指令进行测试,如果接收到 OK
响应,则代表模块一切正常,可以进行后续的操作了。
模块重启指令
指令 | 响应 | 功能 | 说明 |
---|---|---|---|
AT+RESET | OK | 重启蓝牙模块 | 蓝牙模块会自动重启,重启 200ms 后可执行新的操作 |
恢复出厂设置指令
指令 | 响应 | 功能 | 说明 |
---|---|---|---|
AT+DEFAULT | OK | 恢复出厂设置 | 注:不会清除主机已记录的从机地址!若要清除,请在未连线状态下使用 AT+CLEAR 指令进行清除。 蓝牙模块会自动重启,重启 200ms 后可进行新的操作。 |
更改功耗模式指令
指令 | 响应 | 功能 | 说明 |
---|---|---|---|
AT+MODE=? | 0/1/2 | 获取当前功耗模式 | 获取当前功耗模式 |
AT+MODE=0 | OK | 更改功耗模式(仅限从机) | 全速功耗模式(出厂默认) |
AT+MODE=1 | OK | 更改功耗模式(仅限从机) | 一级节能模式 |
AT+MODE=2 | OK | 更改功耗模式(仅限从机) | 二级节能模式(睡眠模式) |
节能模式说明:
一级节能模式是模块最主要的低功耗模式,可为透传提供低功耗待机,也可以作为低功耗的广播数据;
二级节能模式是睡眠模式,在睡眠下时不可发现、不可连接,串口唤醒后可发现、可连接。
两种节能模式都可以通过串口发送 1 个字节以上的数据来唤醒,但唤醒后前面几个字节的数据可能会乱码。
修改模块角色指令
指令 | 响应 | 功能 | 说明 |
---|---|---|---|
AT+ROLE=? | Master/Slave | 获取当前模块的主从机状态 | 获取当前的蓝牙模块为主机/从机模式 |
AT+ROLE=M | Master | 设置为主机模式 | 设置后模块将自动重启,重启 200ms 后可进行新的操作 |
AT+ROLE=S | Slave | 设置为从机模式 | 设置后模块将自动重启,重启 200ms 后可进行新的操作 |
设置 LED 开/关指令
指令 | 响应 | 功能 | 说明 |
---|---|---|---|
AT+LED=? | OK+LED=? | 查询LED工作模式 | 查询LED当前的工作模式 |
AT+LED=0 | OK+LED=0 | 设置LED工作模式 | 设置LED的工作模式为关闭 |
AT+LED=1 | OK+LED=1 | 设置LED工作模式 | 设置LED的工作模式为打开 |
使用指令关闭LED后再打开,需要重启蓝牙模块才能生效
修改蓝牙地址指令
指令 | 响应 | 功能 | 说明 |
---|---|---|---|
AT+ADDR=? | (当前蓝牙模块MAC地址) | 获取蓝牙模块MAC地址 | 地址必须为 12 位的 0~F 数字或大写字符,即 16 进制字符。 |
AT+ADDR=xxx | OKsetADDR | 修改蓝牙模块的MAC地址 | 建议不要修改模块的 MAC 地址,避免冲突 |
查看软件版本指令
指令 | 响应 | 功能 | 说明 |
---|---|---|---|
AT+VERSION | HC-08V3.1, 2017-07-07 | 获取软件版本和发布日期 | 获取软件版本和发布日期 |
查看当前基本参数
指令 | 响应 | 功能 | 说明 |
---|---|---|---|
AT+RX | Name:HC-08 | 查询模块的基本参数 | 蓝牙名是用户设定的名字 |
Role:Slave | 模块角色(主/从) | ||
Baud:9600,NONE | 串口波特率,校验位 | ||
Addr:xx,xx,xx,xx,xx,xx | 蓝牙地址 | ||
PIN :000000 | 蓝牙密码(密码无效) |
修改蓝牙名称指令
指令 | 响应 | 功能 | 说明 |
---|---|---|---|
AT+NAME=? | OK+NAME=HC-08 | 获取蓝牙当前名称 | 获取蓝牙模块的当前名称 |
AT+NAME=xxx | OKsetNAME | 设置蓝牙名称 | 设置蓝牙模块的名称 |
设置模块是否可连接指令
指令 | 响应 | 功能 | 说明 |
---|---|---|---|
AT+CONT=? | Connectable/Non-Connectable | 获取蓝牙模块当前是否可连接 | 设置可连接性,不可连接时主要用于广播数据 |
AT+CONT=0 | OK | 设置“可连接”成功 | 设置“可连接”成功 |
AT+CONT=1 | OK | 设置“不可连接”成功 | 设置“不可连接”成功 |
模式 | 主机 | 从机 |
---|---|---|
可连接 | 中心(Central)可连接,连线后进入普通透传模式 | 外设(Peripheral)可连接,连线后进入普通透传模式 |
不可连接 | 观察者(Observer)当前模块不能连接到其它模块或者设备,但是会自动扫描 HC-08 从机的广播数据包,固定 2s 刷新一次 | 广播者(Broadcaster)不会和主机连接,但可以结合低功耗模式 1,实现广播数据包发送 |
设置连接超时指令
指令 | 响应 | 功能 | 说明 |
---|---|---|---|
AT+CTOUT=? | OK+CTOUT=200(默认) | 查询连接超时时间 | 查询连接超时时间,单位 10ms,范围 103200(100ms32s)。默认为200 |
AT+CTOUT=100 | OK+CTOUT=100 | 设置连接超时时间 | 设置连接超时时间为100ms |
设置自动进入睡眠时间指令
指令 | 响应 | 功能 | 说明 |
---|---|---|---|
AT+AUST=? | OK+AUST=20 | 查询自动睡眠定时 | (默认 20s,该时间越小则越省电)设置的时间范围为:1s~300s 。在低功耗模式下,激活模块后如无操作,x 秒后将返回用户原先所设置的低功耗模式 |
AT+AUST=100 | OK+AUST=100 | 设置自动睡眠时间 | 设置自动睡眠时间为100s |
修改串口波特率指令
指令 | 响应 | 功能 | 说明 |
---|---|---|---|
AT+BAUD=xx,y | xx, y | 设置串口 | 设置串口,参数如下表格 |
AT+BAUD=? | 9600,NONE | 查询串口设置 | 查询串口设置 |
xx 为串口波特率,y 为校验位
参数 | 串口波特率(xx) | 参数 | 校验位(y) |
---|---|---|---|
1200 | 1200bps | N | 无校验NONE |
2400 | 2400bps | E | 偶校验EVEN |
4800 | 4800bps | O | 奇校验ODD |
9600 | 9600bps(默认波特率) | ||
19200 | 19200bps | ||
38400 | 38400bps | ||
57600 | 57600bps | ||
115200 | 115200bps |
本文使用的是 STM32F103C8T6 最小系统板,价格很便宜,普遍 5~8 元。
ST-Link 是一种用于 STM32 微控制器的调试和编程工具,它可以通过 SWD 或 JTAG 接口与开发板进行通信。一般价格在 6~8 元左右。
这玩意儿大家应该非常熟悉了,通常我们用它来打印单片机 log。
当然,配合上位机(比如串口调试助手),我们也可以使用它对一些模组进行调试,比如:wifi模块、4G模组、蓝牙,等等。
当然价格也很便宜,普遍 5~8 元。
如果对这个工具使用不熟悉的小伙伴,可以阅读下文:
零基础快速上手STM32开发(手把手保姆级教程):https://www.lxlinux.net/e/stm32/stm32-quick-start-for-beginner.html
本文使用的是 HC-08 蓝牙模块,价格普遍 5~20 元左右。
在项目开始前,我们需要使用 USB 转 TTL 工具对我们的蓝牙模块进行测试,确保蓝牙模块工作正常。
硬件接线如下表格所示:
HC-08 | USB转TTL |
---|---|
RXD | TXD |
TXD | RXD |
GND | GND |
VCC | 3V3 |
线路接好之后如下图所示:
线路接好之后,将 USB 转 TTL 工具插入电脑,在串口助手输入指令 AT
,模块正常情况下会返回 OK
。
接着我们输入 AT+VERSION
,获取蓝牙模块的版本信息,结果如下:
在使用串口调试蓝牙模块的过程中,有可能会出现波特率正常、驱动正常、蓝牙模块正常,且可以被手机连接上,但是输入 AT 指令,却没有返回的情况,出现这种情况可以试试换个串口助手。
蓝牙模块确认正常之后,我们就可以使用单片机通过编程的方式来操作蓝牙模块。
本文使用 串口2 连接蓝牙,串口1 连接 USB 转 TTL 来打印 log。
HC-08 | STM32 | USB转TTL |
---|---|---|
VCC | 3.3V | VCC |
RXD | A2 | |
TXD | A3 | |
GND | GND | GND |
A10 | TXD | |
A9 | RXD |
烧录的时候接线如下表,如果不会烧录的话可以看我之前的文章 STM32下载程序的五种方法:https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to-stm32.html。
ST-Link V2 | STM32 |
---|---|
SWCLK | SWCLK |
SWDIO | SWDIO |
GND | GND |
3.3V | 3V3 |
接好之后像这样,我这里使用了面包板,用公对母的杜邦线将他们串在了一起
蓝牙模块通过串口与 MCU 进行通讯,所以第一步需要先做好串口的配置。
关于串口的配置,我写过一篇文章手把手教你玩串口,大家可以移步下文查看:
STM32串口接收不定长数据(接收中断+超时判断):https://www.lxlinux.net/e/stm32/stm32-usart-receive-data-using-rxne-time-out.html
具体代码如下:
uint8_t bt_uart_rx_buf[BT_RX_BUF_SIZE]; uint8_t bt_uart_tx_buf[BT_TX_BUF_SIZE]; uint16_t bt_uart_rx_len = 0; void bt_init(uint32_t baudrate) { bt_uart_handle.Instance = BT_INTERFACE; /* BT */ bt_uart_handle.Init.BaudRate = baudrate; /* 波特率 */ bt_uart_handle.Init.WordLength = UART_WORDLENGTH_8B; /* 数据位 */ bt_uart_handle.Init.StopBits = UART_STOPBITS_1; /* 停止位 */ bt_uart_handle.Init.Parity = UART_PARITY_NONE; /* 校验位 */ bt_uart_handle.Init.Mode = UART_MODE_TX_RX; /* 收发模式 */ bt_uart_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 无硬件流控 */ bt_uart_handle.Init.OverSampling = UART_OVERSAMPLING_16; /* 过采样 */ HAL_UART_Init(&bt_uart_handle); /* 使能BT */ } void bt_rx_clear(void) { memset(bt_uart_rx_buf, 0, sizeof(bt_uart_rx_buf)); //清空接收缓冲区 bt_uart_rx_len = 0; //接收计数器清零 } void BT_IRQHandler(void) { uint8_t receive_data = 0; if(__HAL_UART_GET_FLAG(&bt_uart_handle, UART_FLAG_RXNE) != RESET){ //获取接收RXNE标志位是否被置位 if(bt_uart_rx_len >= sizeof(bt_uart_rx_buf)) //如果接收的字符数大于接收缓冲区大小, bt_uart_rx_len = 0; //则将接收计数器清零 HAL_UART_Receive(&bt_uart_handle, &receive_data, 1, 1000); //接收一个字符 bt_uart_rx_buf[bt_uart_rx_len++] = receive_data; //将接收到的字符保存在接收缓冲区 } if (__HAL_UART_GET_FLAG(&bt_uart_handle, UART_FLAG_IDLE) != RESET) //获取接收空闲中断标志位是否被置位 { printf("recv: %s\r\n", bt_uart_rx_buf); //将接收到的数据打印出来 bt_rx_clear(); __HAL_UART_CLEAR_IDLEFLAG(&bt_uart_handle); //清除UART总线空闲中断 } }
通过这几个函数,我们就可以读取蓝牙返回的数据,并保存在数组 bt_uart_rx_buf
里。
如果需要通过串口向蓝牙模块发送数据,可以使用下面函数:
void bt_send(char *fmt, ...) { va_list ap; uint16_t len; va_start(ap, fmt); vsprintf((char *)bt_uart_tx_buf, fmt, ap); va_end(ap); len = strlen((const char *)bt_uart_tx_buf); HAL_UART_Transmit(&bt_uart_handle, bt_uart_tx_buf, len, HAL_MAX_DELAY); }
至此,蓝牙模块的初始化、发送、接收部分就做好了。
在 main 函数里,我们可以先调用 bt_init()
函数进行初始化,然后调用 bt_send()
函数发送数据,如下:
int main(void) { HAL_Init(); /* 初始化HAL库 */ sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟,72M */ delay_init(72); /* 初始化延时函数 */ usart_init(115200); /* 串口1波特率设为115200 */ bt_init(9600); /* 串口2波特率设为9600 */ printf("蓝牙实验……\r\n"); while(1) { bt_send("bt send\r\n"); delay_ms(1000); } }
接着我们打开电脑串口软件。设置串口助手波特率 115200 (你们不一定要用我这款,随便的串口助手都可以),选择串口号,最后打开串口开始准备接收数据。
这个串口工具接收的是 MCU 串口 1 的数据,也就是 log 。蓝牙接收到数据后,我们使用串口 1 打印到下面的串口助手里。
然后打开手机蓝牙助手准备开始调试,点击蓝牙模块开始连接。没有蓝牙助手的同学,可以在前文找到下载地址。
到这里,我们就完成了 MCU 通过蓝牙将数据透传到手机 APP (蓝牙助手)。
当然,我们也可以通过手机 APP 向蓝牙发送数据,MCU 接收到透传的数据之后通过串口助手打印在电脑上。比如我们给蓝牙模块发送数据 111 、aaa 、123123。
可以看到串口助手成功接收到 111 、aaa 、123123,这些数据。
到此,蓝牙模块的调试就完成了。
另外,想进大厂的同学,一定要好好学算法,这是面试必备的。这里准备了一份 BAT 大佬总结的 LeetCode 刷题宝典,很多人靠它们进了大厂。
刷题 | LeetCode算法刷题神器,看完 BAT 随你挑!
推荐阅读:
欢迎关注我的博客:良许嵌入式教程网,满满都是干货!