STM32+HC05串口蓝牙设计简易的蓝牙音箱

简介: STM32+HC05串口蓝牙设计简易的蓝牙音箱

一、环境介绍

MCU:  STM32F103C8T6


蓝牙模块:  HC05 (串口蓝牙)


音频解码模块:  VS1053B


OLED显示屏:  0.96寸SPI接口OLED


开发软件: Keil5


上位机: 使用QT设计Android端APP


二、功能介绍

Android手机打开APP,设置好参数之后,选择音乐文件发送给蓝牙音箱设备端,HC05蓝牙收到数据之后,再传递给VS1053进行播放。程序里采用环形缓冲区,接收HC05蓝牙传递的数据,设置好传递的参数之后,基本播放音乐是很流畅的。


完整项目源码下载地址:  https://download.csdn.net/download/xiaolong1126626497/18621270


三、硬件实物

image.png

VS1053可以接耳机或者接音箱设备即可听音乐。

四、设置HC05蓝牙波特率

HC05蓝牙串口默认波特率是38400,为了提高蓝牙传输速率,需要修改波特率为: 921600。

image.png

image.png

五、APP端界面

image.png

image.png

image.png

六、设备端:核心代码

6.1  mian.cpp

#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "oled.h"
#include "usart.h"
#include <stdlib.h>
#include "sdcard.h"
#include "ff.h"
#include "vs1053b.h"  
#include "timer.h"
#include "hc05_Bluetooth.h" 
//文件系统工作区注册
FATFS FatFs;
//函数声明
u8 VS1053_PlayOneMusic(u8 *pname);
u8 VS1053_ScanDirPlayMusic(const char *path);
//存放OLED显示屏滚动显示的提示语句
u8 oled_show_buff[50];
u8 *oled_data_p; 
//蓝牙音乐的缓冲区
#define MUSIC_BUFF_LEN 1024*5 //音乐数据缓冲区大小
u32 rev_len=0;
u32 play_len=0;
u8 musicbuff[MUSIC_BUFF_LEN];
u8 flagbuff[MUSIC_BUFF_LEN];
u8 music_byte;
/*
                       使用STM32F103C8T6最小系统板编写的代码----买的那种
*/
//中文字库路径: 0:/font/gbk16.DZK
//本地是1
//蓝牙是2
int main(void)
{
  LED_Init();
  KEY_Init();
  USART_X_Init(USART1,72,115200);
  OLED_Init();
  OLED_Clear(0xFF);
  OLED_Refresh_GRAM();
  printf("SD卡初始化状态:%d\r\n",SDCardDeviceInit()); //初始化SD卡
  f_mount(&FatFs,"0:", 0);      //注册工作区
  VS1053_Init();                //VS1053初始化
  printf("正常工作.\r\n");
  if(KEY_UP==0) //按键已经按下    //播放蓝牙音乐数据
  {
    u32 hc05_rx_state=1;
    VS1053_Reset();                     //硬复位MP3
    VS1053_SoftReset();                 //软复位VS10XX
    VS1053_SetVol(250);                 //设置音量  最大值
    VS1053_PlayOneMusic((u8*)"0:/2.mp3"); //蓝牙语音提示
    strncpy((char*)oled_show_buff,"   Play HCO5 Bluetooth music         ",100);
    /*1. 初始化HC05串口蓝牙*/
    printf("1 蓝牙正在初始化.........\r\n");
    USART2_RX_FLAG=0;
    while(HC05_Bluetooth_Init()){}
//    /*设置当前蓝牙为从机模式*/
    HC05_Bluetooth_SetCmd("AT+ROLE=0\r\n"); //设置为从机模式
    if(HC05_Bluetooth_GetRoleStatus()==0)printf("当前蓝牙处于从机状态!\r\n");
    else if(HC05_Bluetooth_GetRoleStatus()==1)printf("当前蓝牙处于主机状态!\r\n");
    HC05_Bluetooth_SetCmd("AT+RESET\r\n");  //复位ATK-HC05模块
    DelayMs(1000);      //等待蓝牙模块稳定
    /*2. 查询蓝牙主从状态*/
    if(HC05_Bluetooth_GetRoleStatus()==0)printf("2 当前蓝牙处于从机状态!\r\n");
    else if(HC05_Bluetooth_GetRoleStatus()==1)printf("2 当前蓝牙处于主机状态!\r\n");
    else printf("2 当前蓝牙主从状态查询失败!\r\n");
    /*3. 查看蓝牙连接状态*/
    if(HC05_LED)printf("3 当前蓝牙连接成功!\r\n");
    else printf("3 当前蓝牙未连接!\r\n");
    /*4. 设置蓝牙的名称*/
    if(HC05_Bluetooth_SetCmd("AT+NAME=wbyq666_HC-05\r\n"))printf("4 蓝牙名称设置失败!\r\n");
    else printf("4 蓝牙名称设置为 wbyq666_HC-05 \r\n");
    /*5. 设置蓝牙配对密码*/
    if(HC05_Bluetooth_SetCmd("AT+PSWD=1234\r\n"))printf("5 蓝牙配对密码设置失败!\r\n"); //密码必须是4位
    else printf("5 蓝牙配对密码设置为 1234 \r\n");
    //if(HC05_Bluetooth_SetCmd("AT+UART=921600,0,0\r\n"))printf("5 蓝牙波特率设置成功!\r\n"); //密码必须是4位
    //else printf("5 蓝牙波特率设置失败!\r\n\r\n");
    /*6. 等待蓝牙连接*/
    printf("等待蓝牙连接.....\r\n");
    while(!HC05_LED){}
    printf("当前蓝牙连接成功! 进入到数据透传模式\r\n");
    //蓝牙连接成功
    USART2->CR1&=~(1<<5);//关闭串口2接收中断
    TIM2->CR1&=~(1<<0);  //关闭定时器2
    USART1->CR1&=~(1<<5);//关闭串口1接收中断
    VS1053_XDCS=0; 
  //VS1053_XDCS=1;
    while(1)
    { 
      if(USART2->SR&(1<<5)) 
      {
        music_byte=USART2->DR;
        if(flagbuff[rev_len]==0)      //可以接收
        {
          musicbuff[rev_len]=music_byte;
          flagbuff[rev_len]=1;      //设置接收标志
          rev_len++;
        }
        if(rev_len>=MUSIC_BUFF_LEN)   //缓冲区满
        {
          rev_len=0;        
        }
      }
      if(flagbuff[play_len]==1)     //可以播放
      {
        if(VS1053_DREQ!=0)  
        {
          VS1053_SPI_ReadWriteByte(musicbuff[play_len]);//播放一个字节音频
          flagbuff[play_len]=0;     //置接已播放标志
          play_len++;
        }
      }
      if(play_len>=MUSIC_BUFF_LEN)  
      {
        play_len=0;       
      }
    } 
  }
  else  //没有按下,播放本地SD卡音乐数据
  {
        printf("播放本地音乐.\r\n");
        TIM1_Init(7200,3000);         //定时器用于滚动显示OLED显示屏的提示语句(); 
        printf("准备播放本地音乐提示音:\r\n");        
        VS1053_PlayOneMusic((u8*)"0:/1.mp3");//蓝牙语音提示
        strncpy((char*)oled_show_buff,"   Play local music         ",100);
        printf("准备循环播放本地音乐.\r\n");  
        while(1)
        {
             VS1053_ScanDirPlayMusic("0:/mp3");  //循环扫描目录播放音乐文件
        }
  }
}
/*
函数名称: TIM1_UP_IRQHandler
函数功能: 定时器1的更新中断服务函数
*/    
void TIM1_UP_IRQHandler(void)
{
    static u8 *p=oled_show_buff;
    static u32 data1[]={1,3,5,1,2,1,2,1,4,1,2,1,1,4,1,1};
    static u8 x=0;
    u8 i=0;
    u32 cnt=0;
    TIM1->SR=0;
    for(i=0;i<16;i++)
    {
        data1[i]=rand()%6;
    }
    if(*p=='\0')p=oled_show_buff;
    cnt=OLED_DisplayString(x,48,16,p);
    if(x==0 && cnt<16)OLED_DisplayString(128-(16-cnt)*8,48,16,oled_show_buff);
    if(x!=0)x-=8;
    else 
    {
        if(*p>80)p+=2;
        else p++;
    }
    OLED_DisplayData_MP3_Check(16,44,data1);
    OLED_DrawBMP(0,0,16,7,(u8 *)SignalTower);
    OLED_DrawBMP(88,0,16,9,(u8 *)Messages);
    OLED_DrawBMP(109,0,24,7,(u8 *)Battery);
    OLED_Refresh_GRAM();
    OLED_Clear_GRAM();
}
/*
函数功能: 播放一曲指定的歌曲                                        
返 回 值: 0,正常播放完成
*/
u8 VS1053_PlayOneMusic(u8 *pname)
{  
  FIL fmp3;
  u16 br;
  u8 res,rval;    
  u8 databuf[4096];          
  u16 i=0;
  VS1053_Reset();                     //硬复位MP3
  VS1053_SoftReset();                 //软复位VS10XX
  VS1053_SetVol(250);                 //设置音量  最大值
  res=f_open(&fmp3,(const TCHAR*)pname,FA_READ);//打开文件   
  if(res!=0)return 1;  //文件打开失败 
  printf("%s文件打开成功!\r\n",pname);           
  while(1)
  {
    res=f_read(&fmp3,databuf,4096,(UINT*)&br);//读出4096个字节  
    i=0;
    while(i<4096) //每次播放4096个字节
    {   
        if(VS1053_SendMusicData(databuf+i)==0)//给VS10XX发送音频数据
        {
          i+=32;
        }
    }
    if(br!=4096||res!=0)
    {
      rval=0;
      break;//读完了.      
    }                
  }
    printf("%s音乐文件播放完毕.\r\n",pname);
  f_close(&fmp3);   
  return rval;                  
}
/*
函数功能: 扫描音乐播放目录下的音乐数据
0表示成功 1表示失败
*/
u8 VS1053_ScanDirPlayMusic(const char *path)
{
    DIR dir;
    FRESULT res; 
    FILINFO fno; //存放读取的文件信息
    char *abs_path=NULL;  
    /*1. 打开目录*/    
    res=f_opendir(&dir,path);
    if(res!=FR_OK)return res;
    /*2. 循环读取目录*/
     while(1)
     {
        res=f_readdir(&dir,&fno);
        if(fno.fname[0] == 0 || res!=0)break;
        printf("文件名称: %s,文件大小: %ld 字节\r\n",fno.fname,fno.fsize);
        /*过滤目录*/
       // if(strstr(fno.fname,".MP3"))
        {
            //申请存放文件名称的长度
            abs_path=malloc(strlen(path)+strlen(fno.fname)+1);
            if(abs_path==NULL)break;
            strcpy(abs_path,path);
            strcat(abs_path,"/");
            strcat(abs_path,fno.fname);
            printf("abs_path=%s\n",abs_path);
            VS1053_PlayOneMusic((u8*)abs_path);     //播放音乐
            free(abs_path);
        }
    }
    /*3. 关闭目录*/
    f_closedir(&dir);
    return 0;
}

6.2 hc05.c

#include "delay.h"       
#include "usart.h"               
#include "hc05_Bluetooth.h" 
#include "led.h" 
#include "string.h" 
#include "timer.h"
/*
函数功能:初始化ATK-HC05模块
返 回 值:0,成功;1,失败.
HC05_KEY--PB8---推挽输出模式
HC05_LED--PB9---上下拉输入模式
*/
u8 HC05_Bluetooth_Init(void)
{
  /*1. 基本初始化*/
  RCC->APB2ENR|=1<<3;     //使能PORTB时钟 
  GPIOB->CRH&=0xFFFFFF00;
  GPIOB->CRH|=0x00000083; 
  USART_X_Init(USART2,36,921600);//初始化串口3为:921600,波特率
  TIM2_Init(72,20000);
  /*2. 检测HC05是否正常*/
  if(HCO5_SendCmd("AT\r\n","OK"))
  {
    printf("初始化失败!\n");
        return 1;
  }   
  return 0;  //检测成功
}
/*
函数功能: 向HC05发送指令,并且查找返回值
返 回 值: 0表示成功  1表示失败
*/
u8 HCO5_SendCmd(char *cmd,char *str)
{
  u32 i,j;
  for(i=0;i<5;i++)
  {
    USART2_RX_FLAG=0;
    USART2_RX_CNT=0;
    memset(USART2_RX_BUFF,0,sizeof(USART2_RX_BUFF));
    HC05_KEY=1;             //KEY置高,进入AT模式
    delay_ms(10);
    USART_X_SendString(USART2,cmd);
    HC05_KEY=0;               //KEY拉低,退出AT模式
    for(j=0;j<500;j++)
    {
        delay_ms(30);
        if(USART2_RX_FLAG) //收到数据
        {
            USART2_RX_BUFF[USART2_RX_CNT]='\0';
            if(strstr((char*)USART2_RX_BUFF,str))
            {
                return 0; //查找正确
            }else break;
        }
    }
  }
  return 1;
}
/*
函数  功能:获取ATK-HC05模块的角色(主/从)状态
函数返回值:0,从机;
            1,主机;
            2,获取失败. 
*/              
u8 HC05_Bluetooth_GetRoleStatus(void)
{         
  u8 cnt=15;
  u8 len,t;
  while(cnt--)
  {
    HC05_KEY=1;         //KEY置高,进入AT模式
    DelayMs(10);
    USART_X_SendString(USART2,"AT+ROLE?\r\n");  //查询角色
    for(t=0;t<20;t++)           //最长等待200ms,来接收HC05模块的回应
    {
      DelayMs(10);
      if(USART2_RX_FLAG)break;  //接收到一次数据了
    }
    HC05_KEY=0;                 //KEY拉低,退出AT模式
    len=USART2_RX_CNT;            //得到接收数据的长度
    USART2_RX_CNT=0;             //清空接收数量
    USART2_RX_FLAG=0;            //清空接收状态
    if(len==13&&USART2_RX_BUFF[0]=='+')//接收到正确的应答了
    {
      len=USART2_RX_BUFF[6]-'0';//得到主从模式值
      return len;
    }
  }
  return 2;//查询失败
}
/*
函数功能:ATK-HC05设置命令
说    明:此函数用于设置ATK-HC05,适用于仅返回OK应答的AT指令
函数参数:atstr:AT指令串。 比如:"AT+RESET"/"AT+UART=9600,0,0"/"AT+ROLE=0"等字符串
返 回 值:0,设置成功;其他,设置失败. 
*/              
u8 HC05_Bluetooth_SetCmd(char* atstr)
{         
  u8 cnt=0X0F;
  u8 len,t;
  while(cnt--)
  {
    HC05_KEY=1;             //KEY置高,进入AT模式
    DelayMs(10);
    USART_X_SendString(USART2,atstr); //发送AT字符串
    HC05_KEY=0;             //KEY拉低,退出AT模式
    for(t=0;t<20;t++)       //最长等待100ms,来接收HC05模块的回应
    {
      if(USART2_RX_FLAG)break;
      DelayMs(5);
    }   
    len=USART2_RX_CNT;  //得到数据长度
    USART2_RX_FLAG=0; //清空接收状态
    USART2_RX_CNT=0;    //清空接收数量
    if(len==4&&USART2_RX_BUFF[0]=='O')//接收到正确的应答了
    {     
      return 0; //设置成功
    }       
  }
  return 1; //设置失败
}

6.3 vs1053.c

#include "vs1053b.h"  
/*
函数功能:移植接口--SPI时序读写一个字节
函数参数:data:要写入的数据
返 回 值:读到的数据
*/
u8 VS1053_SPI_ReadWriteByte(u8 tx_data)
{          
  u8 rx_data=0;        
  u8 i;
  for(i=0;i<8;i++)
  {
    VS1053_SCLK=0;  
    if(tx_data&0x80){VS1053_OUTPUT=1;}
    else {VS1053_OUTPUT=0;}
    tx_data<<=1;  
    VS1053_SCLK=1;
    rx_data<<=1;
    if(VS1053_INPUT)rx_data|=0x01;
  }
  return rx_data; 
}
/*
函数功能:初始化VS1053的IO口   
*/
void VS1053_Init(void)
{
   RCC->APB2ENR|=1<<0;
   AFIO->MAPR&=~(0x7<<24);  //释放PA13/14/15
   AFIO->MAPR|=0x4<<24;
   RCC->APB2ENR|=1<<2;
   RCC->APB2ENR|=1<<3;
   GPIOA->CRH&=0x00000FFF;
   GPIOA->CRH|=0x33338000;
   GPIOB->CRL&=0xFFF00FFF;
   GPIOB->CRL|=0x00083000;
   VS1053_SCLK=1;
   VS1053_XCS=1;
} 
/*
函数功能:软复位VS10XX
*/
void VS1053_SoftReset(void)
{  
  u8 retry=0;            
  while(VS1053_DREQ==0);              //等待软件复位结束     
  VS1053_SPI_ReadWriteByte(0Xff);     //启动传输
  retry=0;
  while(VS1053_ReadReg(SPI_MODE)!=0x0800) // 软件复位,新模式  
  {
    VS1053_WriteCmd(SPI_MODE,0x0804);   // 软件复位,新模式     
    DelayMs(2);//等待至少1.35ms 
    if(retry++>100)break;     
  } 
  while(VS1053_DREQ==0);//等待软件复位结束   
  retry=0;
  while(VS1053_ReadReg(SPI_CLOCKF)!=0X9800)//设置VS10XX的时钟,3倍频 ,1.5xADD 
  {
    VS1053_WriteCmd(SPI_CLOCKF,0X9800); //设置VS10XX的时钟,3倍频 ,1.5xADD
    if(retry++>100)break;       
  }  
  DelayMs(20);
} 
/*
函数 功 能:硬复位MP3
函数返回值:1:复位失败;0:复位成功 
*/
u8 VS1053_Reset(void)
{
  u8 retry=0;
  VS1053_RESET=0;
  DelayMs(20);
  VS1053_XDCS=1;//取消数据传输
  VS1053_XCS=1; //取消数据传输
  VS1053_RESET=1;    
  while(VS1053_DREQ==0&&retry<200)//等待DREQ为高
  {
    retry++;
    DelayUs(50);
  };
  DelayMs(20);  
  if(retry>=200)return 1;
  else return 0;           
}
/*
函数功能:向VS10XX写命令
函数参数:
        address:命令地址
        data   :命令数据
*/
void VS1053_WriteCmd(u8 address,u16 data)
{  
  while(VS1053_DREQ==0);  //等待空闲           
  VS1053_XDCS=1;   
  VS1053_XCS=0;    
  VS1053_SPI_ReadWriteByte(VS_WRITE_COMMAND);//发送VS10XX的写命令
  VS1053_SPI_ReadWriteByte(address);  //地址
  VS1053_SPI_ReadWriteByte(data>>8);  //发送高八位
  VS1053_SPI_ReadWriteByte(data);     //第八位
  VS1053_XCS=1;            
} 
/*
函数参数:向VS1053写数据
函数参数:data:要写入的数据
*/
void VS1053_WriteData(u8 data)
{
  VS1053_XDCS=0;   
  VS1053_SPI_ReadWriteByte(data);
  VS1053_XDCS=1;      
}
/*
函数功能:读VS1053的寄存器 
函数参数:address:寄存器地址
返回值:读到的值
*/
u16 VS1053_ReadReg(u8 address)
{ 
  u16 temp=0;     
  while(VS1053_DREQ==0);//非等待空闲状态     
  VS1053_XDCS=1;       
  VS1053_XCS=0;        
  VS1053_SPI_ReadWriteByte(VS_READ_COMMAND);//发送VS10XX的读命令
  VS1053_SPI_ReadWriteByte(address);        //地址
  temp=VS1053_SPI_ReadWriteByte(0xff);      //读取高字节
  temp=temp<<8;
  temp+=VS1053_SPI_ReadWriteByte(0xff);     //读取低字节
  VS1053_XCS=1;      
   return temp; 
}  
/*
函数功能:读取VS1053的RAM
函数参数:addr:RAM地址
返 回 值:读到的值
*/
u16 VS1053_ReadRAM(u16 addr) 
{ 
  u16 res;            
  VS1053_WriteCmd(SPI_WRAMADDR, addr); 
  res=VS1053_ReadReg(SPI_WRAM);  
  return res;
} 
/*
函数功能:写VS1053的RAM
函数参数:
        addr:RAM地址
        val:要写入的值 
*/
void VS1053_WriteRAM(u16 addr,u16 val) 
{           
  VS1053_WriteCmd(SPI_WRAMADDR,addr); //写RAM地址 
  while(VS1053_DREQ==0);              //等待空闲     
  VS1053_WriteCmd(SPI_WRAM,val);      //写RAM值 
} 
/*
函数参数:发送一次音频数据,固定为32字节
返 回 值:0,发送成功
          1,本次数据未成功发送   
*/ 
u8 VS1053_SendMusicData(u8* buf)
{
  u8 n;
  if(VS1053_DREQ!=0)  //送数据给VS10XX
  {          
    VS1053_XDCS=0;  
    for(n=0;n<32;n++)
    {
      VS1053_SPI_ReadWriteByte(buf[n]);       
    }
    VS1053_XDCS=1;               
  }else return 1;
  return 0;//成功发送了
}
/*
函数参数:发送一次音频数据,固定为32字节
返 回 值:0,发送成功
          1,本次数据未成功发送   
*/ 
void VS1053_SendMusicByte(u8 data)
{
  u8 n;
  while(VS1053_DREQ==0){}      
  VS1053_XDCS=0;  
  VS1053_SPI_ReadWriteByte(data);       
  VS1053_XDCS=1;               
}
/*
函数功能:设定VS1053播放的音量
函数参数:volx:音量大小(0~254)
*/
void VS1053_SetVol(u8 volx)
{
    u16 volt=0;             //暂存音量值
    volt=254-volx;          //取反一下,得到最大值,表示最大的表示 
    volt<<=8;
    volt+=254-volx;         //得到音量设置后大小
    VS1053_WriteCmd(SPI_VOL,volt);//设音量 
}

七、Android手机APP核心源码

7.1  代码页面

image.png

7.2  mainwindow.cpp代码

#include "mainwindow.h"
#include "ui_mainwindow.h"
/*
 * 设置QT界面的样式
*/
void MainWindow::SetStyle(const QString &qssFile) {
    QFile file(qssFile);
    if (file.open(QFile::ReadOnly)) {
        QString qss = QLatin1String(file.readAll());
        qApp->setStyleSheet(qss);
        QString PaletteColor = qss.mid(20,7);
        qApp->setPalette(QPalette(QColor(PaletteColor)));
        file.close();
    }
    else
    {
        qApp->setStyleSheet("");
    }
}
static const QLatin1String serviceUuid("00001101-0000-1000-8000-00805F9B34FB");
//这个字符串里面的内容就是串口模式的Uuid
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->SetStyle(":/qss/blue.css");     //设置样式表
    this->setWindowTitle("HC05蓝牙音箱"); //设置标题
    this->setWindowIcon(QIcon(":/wbyq.ico")); //设置图标
    /*1. 实例化蓝牙相关的对象*/
    discoveryAgent = new QBluetoothDeviceDiscoveryAgent();
    localDevice = new QBluetoothLocalDevice();
    socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
    //RfcommProtocol表示该服务使用RFCOMM套接字协议。RfcommProtocol属于模拟RS232模式,就叫串口模式
    /*2. 关联蓝牙设备相关的信号*/
    /*2.1 关联发现设备的槽函数,当扫描发现周围的蓝牙设备时,会发出deviceDiscovered信号*/
    connect(discoveryAgent,
            SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),
            this,
            SLOT(addBlueToothDevicesToList(QBluetoothDeviceInfo))
            );
    //蓝牙有数据可读
    connect(socket,
            SIGNAL(readyRead()),
            this,
            SLOT(readBluetoothDataEvent())
            );
    //蓝牙连接建立成功
    connect(socket,
            SIGNAL(connected()),
            this,
            SLOT(bluetoothConnectedEvent())
            );
    //蓝牙断开连接
    connect(socket,
            SIGNAL(disconnected()),
            this,
            SLOT(bluetoothDisconnectedEvent())
            );
    //蓝牙写成功的数据
    connect(socket,
            SIGNAL(bytesWritten(qint64)),
            this,
            SLOT(bluetoothbytesWritten(qint64))
            );
    /*3.2 设置标签显示本地蓝牙的名称*/
    QString name_info("本机蓝牙:");
    name_info+=localDevice->name();
    ui->label_BluetoothName->setText(name_info);
     ui->plainTextEdit->setEnabled(false); //不可编辑
     /*定时器用于定时发送文件*/
     timer = new QTimer(this); //创建定时器
     connect(timer, SIGNAL(timeout()), this, SLOT(update())); //关联槽函数
     ConnectStat=0;     //连接状态
     SendStat=0;        //文件打开状态
     FileSendTime=100;  //默认每次发送的时间 单位ms
     ui->lineEdit_Timer->setText(QString::number(FileSendTime));
     FileSendCnt=32;    //默认每包数据值32字节
     ui->lineEdit_SendFileCnt->setText(QString::number(FileSendCnt));
     //创建存放音乐文件的目录
     QDir dir;
     if(dir.mkpath("/sdcard/WBYQ_MP3"))
     {
          ui->plainTextEdit->insertPlainText("/WBYQ_MP3 目录创建成功!\n");
     }
     else
     {
          ui->plainTextEdit->insertPlainText("/WBYQ_MP3 目录创建失败!\n");
     }
      MusicFileDir="assets:/nansannan.mp3";  //目录的路径
      Def_MusicName="assets:/nansannan.mp3";
      file.setFileName(Def_MusicName); //设置文件名称
      ui->lineEdit_MusicName->setText(Def_MusicName); //默认文件名称
}
int count=0;
//更新
void MainWindow::update()
{
    if(SendStat)
    {
        int len;
        if(file.atEnd()==false)  //文件未结束
        {
            len=file.read(FileBuff,FileSendCnt);
            len=socket->write(FileBuff,len); //发送数据
        }
        else
        {
           file.close();
           timer->stop(); //停止定时器
           SendStat=0;
           ui->plainTextEdit->setPlainText("文件读取写入完毕!\n");
        }
    }
}
MainWindow::~MainWindow()
{
    delete ui;
    delete discoveryAgent;
    delete localDevice;
    delete socket;
    timer->stop();
    delete timer;
}
void MainWindow::on_pushButton_CloseBluetooth_clicked()
{
    /*关闭蓝牙设备*/
    localDevice->setHostMode(QBluetoothLocalDevice::HostPoweredOff);
}
void MainWindow::on_pushButton_BluetoothScan_clicked()
{
    /*3.1 检查蓝牙是否开启*/
    if(localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff)
    {
        /*请求打开蓝牙设备*/
        localDevice->powerOn();
    }
     /*开始扫描周围的蓝牙设备*/
    discoveryAgent->start();
    ui->comboBox_BluetoothDevice->clear(); //清除条目
}
void MainWindow::on_pushButton_DeviceVisible_clicked()
{
    /*设置蓝牙可见,可以被周围的设备搜索到,在Android上,此模式最多只能运行5分钟。*/
    //  localDevice->setHostMode( QBluetoothLocalDevice::HostDiscoverable);
    static bool cnt=1;
    cnt=!cnt;
    if(cnt)
    {
        MusicFileDir="assets:/nansannan.mp3";  //目录的路径
        QMessageBox::information(this,"提示","路径切换为内部路径!\n请选择文件!",QMessageBox::Ok,QMessageBox::Ok);
    }
    else
    {
        MusicFileDir="/mnt/sdcard";  //目录的路径
        QMessageBox::information(this,"提示","路径切换为SD路径!\n请选择文件!",QMessageBox::Ok,QMessageBox::Ok);
    }
}
void MainWindow::on_pushButton_StopScan_clicked()
{
    /*停止扫描周围的蓝牙设备*/
    discoveryAgent->stop();
}
/*当扫描到周围的设备时会调用当前的槽函数*/
void MainWindow::addBlueToothDevicesToList(const QBluetoothDeviceInfo &info)
{
   // QString label = QString("%1 %2").arg(info.name()).arg(info.address().toString());
    QString label = QString("%1 %2").arg(info.address().toString()).arg(info.name());
    ui->comboBox_BluetoothDevice->addItem(label); //添加字符串到comboBox上
}
//有数据可读
void MainWindow::readBluetoothDataEvent()
{
    QByteArray all = socket->readAll();
    ui->plainTextEdit->insertPlainText(all);
}
//建立连接
void MainWindow::bluetoothConnectedEvent()
{
    QMessageBox::information(this,tr("连接提示"),"蓝牙连接成功!");
    ConnectStat=1;
    /*停止扫描周围的蓝牙设备*/
    discoveryAgent->stop();
}
//断开连接
void MainWindow::bluetoothDisconnectedEvent()
{
    ConnectStat=0;
    QMessageBox::information(this,tr("连接提示"),"蓝牙断开连接!");
}
/*
在说蓝牙设备连接之前,不得不提一个非常重要的概念,就是蓝牙的Uuid,引用一下百度的:
在蓝牙中,每个服务和服务属性都唯一地由"全球唯一标识符" (UUID)来校验。
正如它的名字所暗示的,每一个这样的标识符都要在时空上保证唯一。
UUID类可表现为短整形(16或32位)和长整形(128位)UUID。
他提供了分别利用String和16位或32位数值来创建类的构造函数,提供了一个可以比较两个UUID(如果两个都是128位)的方法,还有一个可以转换一个UUID为一个字符串的方法。
UUID实例是不可改变的(immutable),只有被UUID标示的服务可以被发现。
在Linux下你用一个命令uuidgen -t可以生成一个UUID值;
在Windows下则执行命令uuidgen 。UUID看起来就像如下的这个形式:2d266186-01fb-47c2-8d9f-10b8ec891363。当使用生成的UUID去创建一个UUID对象,你可以去掉连字符。
*/
//发送音乐文件
void MainWindow::on_pushButton_SendData_clicked()
{
    if(ConnectStat)
    {
        if(file.open(QIODevice::ReadOnly))
        {
            SendStat=1;
            count=0;
            ui->plainTextEdit->insertPlainText("系统提示: 开始发送文件!\n");
            ui->lineEdit_fileSizef->setText(QString::number(file.size()));
            ui->lineEdit_fileCount->setText("");
            ui->progressBar_SendCount->setMaximum(file.size()); //设置进度条显示最大值
            ui->progressBar_SendCount->setValue(0); //设置进度条的值
            timer->start(FileSendTime); //启动定时器
        }
    }
}
//连接蓝牙
void MainWindow::on_pushButton_ConnectDev_clicked()
{
    QString text = ui->comboBox_BluetoothDevice->currentText();
    int index = text.indexOf(' ');
    if(index == -1) return;
    QBluetoothAddress address(text.left(index));
    QString connect_device="开始连接蓝牙设备:\n";
    connect_device+=ui->comboBox_BluetoothDevice->currentText();
    QMessageBox::information(this,tr("连接提示"),connect_device);
    //开始连接蓝牙设备
    socket->connectToService(address, QBluetoothUuid(serviceUuid) ,QIODevice::ReadWrite);
}
//帮助提示
void MainWindow::on_pushButton_help_clicked()
{
    QMessageBox::information(this,tr("帮助提示"),"本软件用于HC-05/06系列串口蓝牙连接!\n"
                                             "暂时不支持BLE4.0版本蓝牙!\n"
                                             "用于发送音乐文件数据,每次发送32字节,默认100ms发送间隔时间\n"
                                             "软件作者:DS小龙哥\n"
                                             "BUG反馈:1126626497@qq.com");
}
//选择音乐文件
void MainWindow::on_pushButton_select_clicked()
{
    QString filename=QFileDialog::getOpenFileName(this,"选择发送的音乐文件",MusicFileDir,tr("*.mp3 *.MP3 *.WAV"));
    //filename==选择文件的绝对路径
    file.setFileName(filename);
    ui->lineEdit_MusicName->setText(filename);
    Def_MusicName=filename; //保存文件名称
}
//清除
void MainWindow::on_pushButton_clear_clicked()
{
     ui->plainTextEdit->setPlainText("");
}
void MainWindow::on_pushButton_StopSend_clicked()
{
       timer->stop(); //停止定时器
}
//设置发送间隔时间
void MainWindow::on_pushButton_SendTime_clicked()
{
    QString str=ui->lineEdit_Timer->text();
    int time=str.toInt();
    FileSendTime=time; //保存时间
    if(time<=0)
    {
        QMessageBox::warning(this,"警告提示","设置错误: 发送的间隔时间最小1ms\n",QMessageBox::Ok,QMessageBox::Ok);
    }
    else
    ui->plainTextEdit->insertPlainText(tr("设置发送间隔时间为%1ms\n").arg(time));
}
//修改每包发送的数量
void MainWindow::on_pushButton_SendFileCnt_clicked()
{
    QString str=ui->lineEdit_SendFileCnt->text();
    int cnt=str.toInt();
    if(cnt>4096 || cnt<=0)
    {
         QMessageBox::warning(this,"警告提示","设置错误: 每包发送的数量范围: 1~4096\n",QMessageBox::Ok,QMessageBox::Ok);
    }
    else
    {
        FileSendCnt=cnt;
        ui->plainTextEdit->insertPlainText(tr("发送数量设置为%1字节.\n").arg(cnt));
    }
}
//写成功的数量
void MainWindow::bluetoothbytesWritten(qint64 byte)
{
   count+=byte;
   ui->lineEdit_fileCount->setText(QString::number(count));
   ui->progressBar_SendCount->setValue(count); //设置进度条的值
}

7.3  mianwindows.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QBluetoothDeviceDiscoveryAgent>
#include <QBluetoothLocalDevice>
#include <QBluetoothSocket>
#include "QListWidgetItem"
#include <QMessageBox>
#include "QFile"
#include "QFileDialog"
#include "QTimer"
#include "QInputDialog"
#include <QAndroidJniObject>
#include <QtAndroid>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    QFile file;
    QTimer *timer;
    bool ConnectStat;
    bool SendStat;
    QBluetoothDeviceDiscoveryAgent *discoveryAgent;  //这个是指扫描周围蓝牙设备!
    QBluetoothLocalDevice *localDevice;//是指配置获取设备的蓝牙状态信息等!
    QBluetoothSocket *socket; //指进行链接蓝牙设备,读写信息!
    int FileSendTime; //文件发送时间
    char FileBuff[4096]; //每包最大的字节数
    int FileSendCnt;     //每次发送的字节数
    QString Def_MusicName;
    QString MusicFileDir;
private slots:
    void on_pushButton_CloseBluetooth_clicked();
    void on_pushButton_BluetoothScan_clicked();
    void addBlueToothDevicesToList(const QBluetoothDeviceInfo&);  //发现蓝牙设备的槽函数
    void on_pushButton_DeviceVisible_clicked();
    void on_pushButton_StopScan_clicked();
    void readBluetoothDataEvent();
    void bluetoothConnectedEvent();
    void bluetoothDisconnectedEvent();
    void on_pushButton_SendData_clicked();
    void SetStyle(const QString &qssFile); //样式表设置函数
    void on_pushButton_ConnectDev_clicked();
    void on_pushButton_help_clicked();
    void on_pushButton_select_clicked();
    void on_pushButton_clear_clicked();
    void on_pushButton_StopSend_clicked();
    void on_pushButton_SendTime_clicked();
    void on_pushButton_SendFileCnt_clicked();
    void bluetoothbytesWritten(qint64 byte);
public slots:
    void update();
private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H


目录
相关文章
|
4月前
stm32f407探索者开发板(十七)——串口寄存器库函数配置方法
stm32f407探索者开发板(十七)——串口寄存器库函数配置方法
705 0
|
4月前
STM32CubeMX 串口收发一帧数据
STM32CubeMX 串口收发一帧数据
56 9
|
4月前
|
芯片
STM32CubeMX 串口数据收发
STM32CubeMX 串口数据收发
141 2
|
4月前
|
监控
stm32f407探索者开发板(十八)——串口通信实验讲解(USART_RX_STA流程图详解)
stm32f407探索者开发板(十八)——串口通信实验讲解(USART_RX_STA流程图详解)
290 0
|
6月前
|
物联网 程序员 语音技术
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结-3
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结-3
|
6月前
|
传感器 语音技术
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结-1
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结-1
|
6月前
|
存储 缓存 芯片
STM32--USART串口
STM32--USART串口
107 0
|
6月前
|
Java C语言
STM32使用printf重定向到USART(串口)并打印数据到串口助手
STM32使用printf重定向到USART(串口)并打印数据到串口助手
319 0