天天看點

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 三、硬體實物
STM32+HC05序列槽藍牙設計簡易的藍牙音箱
VS1053可以接耳機或者接音箱裝置即可聽音樂。

四、設定HC05藍牙波特率

HC05藍牙序列槽預設

波特率 是38400,為了提高藍牙傳輸速率,需要修改波特率為: 921600。
STM32+HC05序列槽藍牙設計簡易的藍牙音箱
STM32+HC05序列槽藍牙設計簡易的藍牙音箱
五、APP端界面
STM32+HC05序列槽藍牙設計簡易的藍牙音箱
STM32+HC05序列槽藍牙設計簡易的藍牙音箱
STM32+HC05序列槽藍牙設計簡易的藍牙音箱
六、裝置端:核心代碼  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  代碼頁面

STM32+HC05序列槽藍牙設計簡易的藍牙音箱

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回報:[email protected]");
}
 
 
//選擇音樂檔案
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      

繼續閱讀