[香橙派开发系列]使用蓝牙和手机进行信息的交换
前言
隔了这么久我准备再玩一下香橙派,最近这段时间还是比较的忙,我搭建了个论坛和博客,经常被网络攻击,所以我也是一直在弄网络去了,然后今天比较空闲就想着把单子做一下,这个单子需要使用到HC05蓝牙模块,所以我准备写一篇博客来使用香橙派控制HC05蓝牙模块。
一、HC05蓝牙模块
这个模块是非常经典的一个蓝牙模块,我之前有一单也是使用到这个模块,可以看一下我的这个视频【作息控制系统使用说明】,这个项目也是使用到蓝牙模块的,只不过当时时间紧任务重所以就没有写一篇博客来好好记录一下这个项目,等后面我会单独拿一个模块来介绍一下我做的这些小项目。
1.HC05概述
HC05是嵌入式蓝牙串口通讯模块,也就是使用串口就可以和HC05进行通讯并发送信息给蓝牙的接收端,这个模块有两种工作方式:
1 命令响应工作模式
2 自动连接工作模式
在自动连接的工作模式下又可以分为主、从、回环这三种模式,当传输数据时,根据事先设定的方式连接并传输数据。
在命令模式下,用户可以使用串口连接模块,并发送AT
指令对模块进行设置。
2.HC05的连接图
这里我直接使用TTL to USB
进行连接,因为我要设置一下这个模块的一些传输和内容。
这里和串口连接的模式一样
HC05 | TTL |
---|---|
3.3 | VCC |
TXD | RXD |
RXD | TXD |
GND | GND |
3.进入HC05的命令模式
首先我们需要通过AT
指令来设置模块的一些内容,然后我们才好进入下面的一些操作,首先在这个模块上有一个按钮
在上电之前需要长按这个按键,然后再上电就可以进入命令模式,在进入命令模式后,模块上的LED等会缓慢的闪烁,如果没有进入就会闪得很快。
4.常用的AT指令
AT指令列表已经烂大街了,所以这里不全部说明,我只拿出一些常用的来说明即可。
4.1 检查AT是否上线
AT\r\n
如果模块在就会返回OK
4.2 重启模块
AT+RESET\r\n
如果执行成功就会返回OK
,并进入自动连接模式
4.3 获取软件版本号
AT_VERSION?\r\n
执行完成后会返回版本信息和OK
4.4 恢复默认状态
AT+ORGL\r\n
执行完成后会返回OK
,并将模块恢复为出厂设置。
4.5 获取蓝牙的名称
AT+NAME?\r\n
执行完成后会返回蓝牙名称并返回OK
4.6 设置蓝牙模块的波特率
AT+UART=波特率,停止位,校验位\r\n
这里我们设置一个比较常见的,波特率9600,停止位1位,无校验位
AT+UART=9600,1,0
这样就可以了。
4.7 查询蓝牙的连接模式
AT+CMODE?\r\n
返回当前模式,1代表任意模式,也就是蓝牙名称和蓝牙地址都可以连接,这里就不多说了,一般默认是1就可以了。
4.8 查询模块角色
AT+ROLE?
这里默认的是Slave
从角色,也就是被动连接,不用动。
5.连接电脑
这里我踩了个大坑,因为我之前的设置是将波特率改为9600了,所以在串口的时候我就用9600进行连接,结果没有任何的反应,后面查了一下手册才发现它默认的波特率是38400
,就很尴尬。
这里连接好电脑后先发送个AT
指令来查看一下是否返回OK,如果没返回有可能是因为坏了或者是你没长按按键进入命令模式。
然后修改一下波特率,因为我们后面初始化串口不想给那么大的波特率
初始化完成后要记得重启一下模块,然后模块的波特率就设置好看,但是AT指令模式下还是那个波特率(又踩坑了,当不想改文章)。
6.通过HC05发送消息
这里我先用stm32来演示,因为我还在做单子所以先拿这个演示。
首先先写串口的初始化函数,这里我使用的是USART1
,初始化代码就不展示了,因为今天的主题不是stm32,然后在main函数中我们使用重写的printf
来通过串口发送数据。不知道的可以看我这篇博客异步通讯点灯。
int main(){
OLED_Init();
MX_Init_KEY();
MX_Init_HC05();
while(1){
printf("hello\r\n");
OLED_ShowString(1, 1, "hello");
}
}
然后需要在手机上下载蓝牙调试宝
,在应用商城都可以下载,这我就不给你们压缩包了,要不然我老喜欢挂码。
首先要把蓝牙模块连接到面包板上,然后就用手机连接好蓝牙
记住串口反接并且要接地即可。
然后打开蓝牙助手的界面
找到里面有一个HC-05
,然后连接即可,第一次连接需要你输入一个配对码,这里输入1234或者0000都可以
然后给单片机上电后就会显示出hello来
学到这你已经会使用蓝牙进行发送信息了,stm32就先不再出现了,后面就是香橙派的内容了。
7.stm32完整代码
usart.c
#include "hc05.h"
void MX_Init_HC05(){
GPIO_InitTypeDef GPIO_InitStruct = {0};
USART_InitTypeDef USART_InitStruct = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStruct);
USART_InitStruct.USART_BaudRate = 9600;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1, &USART_InitStruct);
USART_Cmd(USART1, ENABLE);
}
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);
USART1->DR = (u8) ch;
return ch;
}
二、香橙派串口通讯
在介绍使用香橙派使用HC05模块前先介绍一下串口通讯,并如何配置串口通讯。
1.打开串口设备
在香橙派中,串口通讯是使用设备来进行传输的, 所以我们需要在系统设置中将串口进行打开。
在打开之前先了解一下香橙派的串口引脚分布
在3b中有3个串口可供选择,然后通过
sudo orangepi-config
来开启设备,进入到设置页面
然后选择第一个,按一下回车即可,然后在下面的页面中选择Hardware
然后找到你要开启的串口
我这选择的是UART7
,使用方向键移动到那后按一下空格就选中,然后按一下回车保存,之后会提示是否重启,然后按一下回车就可以重启了。
重启完成后使用
ls /dev/ ttyS*
来查看可操作的设备文件,这里只会显示你开启的设备
我们刚才选择了UART7
后它这里就会有相应的设备,那个ttyS1
是默认开启的,我们不用理他。
2.接线
这里就需要非常非常注意了,我在测试的时候就是这里没有弄好,线接反了,导致一直发送不了。
按照前面给的那张图进行接线后我们就可以写代码了。
注意:这里很重要,很多人玩51单片机来玩这个会忽略一个问题就是GND必须要和单片机的GND接在一起,很多人会单独给这个引脚接GND是错误的。
3.串口函数
这里使用的串口函数库是
#include <wiringSerial.h>
3.1 打开串口文件
int serialOpen(const char* path, int butrl);
第一个参数是设备的路径,比如说我们这需要使用到UART1
,那设备地址就是/dev/ttyS7
。
第二个参数是波特率,比如9600。
这个函数的返回值是一个文件描述符,我们使用一个变量来接收就可以了。
3.2 关闭串口文件
void serialClose(int fd);
参数是打开的设备描述符。
3.3 发送一个字符
void serialPutchar(int fd, char c);
将一个字符发送到打开的文件描述符fd所对应的设备文件。
3.4 发送一个规定好的字符串
void serialPuts(int fd, char* s);
发送一个规定好的字符串,然后以0结尾。
3.5 printf
void serialPrintf(int fd, char* message, ...);
这个和printf的用法类似。
3.6 返回等待读取的字符数
int serialDataAvail(int fd);
返回的是等待读取的字符数。
3.7 读取字符
int serialGetChar(int fd);
返回下一个待读取的字符,如果这个在10s内没有读取到字符就会返回-1。
3.8 缓冲区函数
void serialFlush(int fd);
抛弃所有接收的数据或者等待写入设备完成。
4.输出内容
通过学习了上面的内容后我们可以简单写一个输出字符的代码来输出0~9
#include <stdio.h>
#include <wiringPi.h>
#include <wiringSerial.h>
int main(){
int fd = -1, i;
wiringPiSetup();
fd = serialOpen("/dev/ttyS7", 9600); // 设置串口设备和波特率
if (fd < 0){
// 设备打开失败
printf("设备打开失败\n");
return -1;
}
serialFlush(fd);
for (i = 0; i < 10; i++){
serialPutchar(fd, i + '0');
}
serialClose(fd); // 关闭设备
}
运行效果如下:
然后也可以使用printf来个高级一点的输出
#include <stdio.h>
#include <wiringPi.h>
#include <wiringSerial.h>
int main(){
int fd = -1, i;
wiringPiSetup();
fd = serialOpen("/dev/ttyS7", 9600); // 设置串口设备和波特率
if (fd < 0){
// 设备打开失败
printf("设备打开失败\n");
return -1;
}
serialFlush(fd);
for (i = 0; i < 10; i++){
serialPrintf(fd, "this is%d\r\n", i);
}
serialClose(fd); // 关闭设备
}
输出的内容如下:
5.接收内容
我们可以通过串口来接收一下用户输入的内容,并判断输入的字符是不是我们需要的,然后通过串口返回
#include <stdio.h>
#include <wiringPi.h>
#include <wiringSerial.h>
int main(){
int fd = -1, i;
int c;
wiringPiSetup();
fd = serialOpen("/dev/ttyS7", 9600); // 设置串口设备和波特率
if (fd < 0){
// 设备打开失败
printf("设备打开失败\n");
return -1;
}
serialFlush(fd);
while(1){
c = serialGetchar(fd);
if (c == -1){
break;
}
if (c == '1'){
serialPrintf(fd, "this is 1");
}
printf("%c", c);
}
serialClose(fd); // 关闭设备
}
输出的内容:
这里大家可以看到我在这个while循环中添加了一个printf,但是没有截屏给出效果,是因为在这个串口运行的过程中一直在读取的是串口的设备,没有使用输入输出流设备,所以导致我们在输入后没办法显示出内容来,这个的解决办法我还不太清楚,等我研究好了我在出一篇文章来介绍。
6.接收字符串
做串口最让人沉醉的就是读取字符串的操作了,在wiringSerial
中有一个函数可以获取缓冲区的长度,我们可以判断这个来读取输入的数据的长度,然后使用一个for
循环来循环读取和放置在一个数组中,之后进行输出即可,代码如下:
#include <stdio.h>
#include <wiringPi.h>
#include <wiringSerial.h>
int main(){
int fd = -1;
int i, ch, j = 0, len;
char str[1024];
wiringPiSetup();
fd = serialOpen("/dev/ttyS7", 9600);
if (fd < 0){
printf("打开设备文件失败\n");
return -1;
}
while(1){
len = serialDataAvail(fd); // 获取缓冲区的长度
if (len == -1){
// 超时退出循环
break;
}
else if(len > 0){
serialPrintf(fd, "%d\n", len); // 这一行是测试的
for (i = 0; i < len; i++){
ch = serialGetchar(fd); // 读取字符
str[j++] = ch;
}
break;
}
}
str[j] = '\0';
printf("%s\n", str);
serialClose(fd);
return 0;
}
三、使用hc05连接香橙派
学会了上面的操作后我们就可以来用香橙派连接hc05了,这个操作也非常非常的简单,和单片机的连接一致,注意不要连错就行了。
1.通过蓝牙发送字符串给手机
本质的思路还是一样的,就是使用串口是输入输出就可以搞定,代码如下:
#include <stdio.h>
#include <wiringPi.h>
#include <wiringSerial.h>
int main(){
int fd = -1, i;
int c;
wiringPiSetup();
fd = serialOpen("/dev/ttyS7", 9600); // 设置串口设备和波特率
if (fd < 0){
// 设备打开失败
printf("设备打开失败\n");
return -1;
}
serialFlush(fd);
while(1){
serialPrintf(fd, "hello\r\n");
delayMicroseconds(30000); // 延时函数,免得发送得过快
}
serialClose(fd); // 关闭设备
}
然后在调试助手中就可以看到蓝牙模块发送的内容了:
2.手机发送内容给香橙派
这里可以使用刚才我们测试的接收字符串,只不过这里有一个问题就是这个工具只能发送字符所对应的ascii码,不能正常的输入字符进行输出。
比如我们手机输入字符0,那就得输入ascii码的30
然后在香橙派中就会显示:
然后我们还可以用这个来输入0~9
然后显示
学会了这个后我们就可以编写一个用蓝牙点灯的程序了,其实就是使用蓝牙接收到关键字后给LED灯高低电平,这里就直接给代码了,不展示图片了
#include <stdio.h>
#include <wiringPi.h>
#include <wiringSerial.h>
#include <string.h>
int main(){
int fd = -1;
int i, ch, j = 0, len;
char str[1024];
wiringPiSetup();
fd = serialOpen("/dev/ttyS7", 9600);
if (fd < 0){
printf("打开设备文件失败\n");
return -1;
}
while(1){
len = serialDataAvail(fd);
if (len == -1){
break;
}
else if(len > 0){
serialPrintf(fd, "%d\n", len);
for (i = 0; i < len; i++){
ch = serialGetchar(fd);
str[j++] = ch;
}
break;
}
}
str[j] = '\0';
printf("%s\n", str);
if (strcmp(str, "0")){
// 点灯
}
else{
// 灭灯
}
serialClose(fd);
return 0;
}
总结
串口很好玩,蓝牙模块也好玩,学会了这一节我们就可以使用蓝牙做一个简单的远程开关灯的项目了,当然拿香橙派做这个项目就很浪费,毕竟香橙派可以直接使用网络来实现,做一个网页,然后内网穿透,这样可以很容易的在远程进行控制了,等我最近忙完有时间我做一个。
大家可以关注一下我的论坛和博客,有不懂的或者有趣的问题可以放在我的论坛中进行提问