?
一、環(huán)境介紹
藍(lán)牙模塊: HC05 (串口藍(lán)牙)
音頻解碼模塊: VS1053B
開發(fā)軟件:Keil5
上位機(jī):使用QT設(shè)計(jì)Android端APP
二、功能介紹
Android手機(jī)打開APP,設(shè)置好參數(shù)之后,選擇音樂(lè)文件發(fā)送給藍(lán)牙音箱設(shè)備端,HC05藍(lán)牙收到數(shù)據(jù)之后,再傳遞給VS1053進(jìn)行播放。程序里采用環(huán)形緩沖區(qū),接收HC05藍(lán)牙傳遞的數(shù)據(jù),設(shè)置好傳遞的參數(shù)之后,基本播放音樂(lè)是很流暢的。
三、硬件實(shí)物
?
VS1053可以接耳機(jī)或者接音箱設(shè)備即可聽音樂(lè)。
四、設(shè)置HC05藍(lán)牙波特率
HC05藍(lán)牙串口默認(rèn)波特率是38400,為了提高藍(lán)牙傳輸速率,需要修改波特率為: 921600。
?
?
五、APP端界面
?
?
?
六、設(shè)備端:核心代碼
6.1 mian.cpp
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "oled.h"
#include "usart.h"
#include
#include "sdcard.h"
#include "ff.h"
#include "vs1053b.h"
#include "timer.h"
#include "hc05_Bluetooth.h"
//文件系統(tǒng)工作區(qū)注冊(cè)
FATFS FatFs;
//函數(shù)聲明
u8 VS1053_PlayOneMusic(u8 *pname);
u8 VS1053_ScanDirPlayMusic(const char *path);
//存放OLED顯示屏滾動(dòng)顯示的提示語(yǔ)句
u8 oled_show_buff[50];
u8 *oled_data_p;
//藍(lán)牙音樂(lè)的緩沖區(qū)
#define MUSIC_BUFF_LEN 1024*5 //音樂(lè)數(shù)據(jù)緩沖區(qū)大小
u32 rev_len=0;
u32 play_len=0;
u8 musicbuff[MUSIC_BUFF_LEN];
u8 flagbuff[MUSIC_BUFF_LEN];
u8 music_byte;
/*
使用STM32F103C8T6最小系統(tǒng)板編寫的代碼----買的那種
*/
//中文字庫(kù)路徑: 0:/font/gbk16.DZK
//本地是1
//藍(lán)牙是2
int main(void)
{
LED_Init();
KEY_Init();
USART_X_Init(USART1,72,115200);
OLED_Init();
OLED_Clear(0xFF);
OLED_Refresh_GRAM();
printf("SD卡初始化狀態(tài):%d\r\n",SDCardDeviceInit()); //初始化SD卡
f_mount(&FatFs,"0:", 0); //注冊(cè)工作區(qū)
VS1053_Init(); //VS1053初始化
printf("正常工作.\r\n");
if(KEY_UP==0) //按鍵已經(jīng)按下 //播放藍(lán)牙音樂(lè)數(shù)據(jù)
{
u32 hc05_rx_state=1;
VS1053_Reset(); //硬復(fù)位MP3
VS1053_SoftReset(); //軟復(fù)位VS10XX
VS1053_SetVol(250); //設(shè)置音量 最大值
VS1053_PlayOneMusic((u8*)"0:/2.mp3"); //藍(lán)牙語(yǔ)音提示
strncpy((char*)oled_show_buff," Play HCO5 Bluetooth music ",100);
/*1. 初始化HC05串口藍(lán)牙*/
printf("1 藍(lán)牙正在初始化.........\r\n");
USART2_RX_FLAG=0;
while(HC05_Bluetooth_Init()){}
// /*設(shè)置當(dāng)前藍(lán)牙為從機(jī)模式*/
HC05_Bluetooth_SetCmd("AT+ROLE=0\r\n"); //設(shè)置為從機(jī)模式
if(HC05_Bluetooth_GetRoleStatus()==0)printf("當(dāng)前藍(lán)牙處于從機(jī)狀態(tài)!\r\n");
else if(HC05_Bluetooth_GetRoleStatus()==1)printf("當(dāng)前藍(lán)牙處于主機(jī)狀態(tài)!\r\n");
HC05_Bluetooth_SetCmd("AT+RESET\r\n"); //復(fù)位ATK-HC05模塊
DelayMs(1000); //等待藍(lán)牙模塊穩(wěn)定
/*2. 查詢藍(lán)牙主從狀態(tài)*/
if(HC05_Bluetooth_GetRoleStatus()==0)printf("2 當(dāng)前藍(lán)牙處于從機(jī)狀態(tài)!\r\n");
else if(HC05_Bluetooth_GetRoleStatus()==1)printf("2 當(dāng)前藍(lán)牙處于主機(jī)狀態(tài)!\r\n");
else printf("2 當(dāng)前藍(lán)牙主從狀態(tài)查詢失敗!\r\n");
/*3. 查看藍(lán)牙連接狀態(tài)*/
if(HC05_LED)printf("3 當(dāng)前藍(lán)牙連接成功!\r\n");
else printf("3 當(dāng)前藍(lán)牙未連接!\r\n");
/*4. 設(shè)置藍(lán)牙的名稱*/
if(HC05_Bluetooth_SetCmd("AT+NAME=wbyq666_HC-05\r\n"))printf("4 藍(lán)牙名稱設(shè)置失敗!\r\n");
else printf("4 藍(lán)牙名稱設(shè)置為 wbyq666_HC-05 \r\n");
/*5. 設(shè)置藍(lán)牙配對(duì)密碼*/
if(HC05_Bluetooth_SetCmd("AT+PSWD=1234\r\n"))printf("5 藍(lán)牙配對(duì)密碼設(shè)置失敗!\r\n"); //密碼必須是4位
else printf("5 藍(lán)牙配對(duì)密碼設(shè)置為 1234 \r\n");
//if(HC05_Bluetooth_SetCmd("AT+UART=921600,0,0\r\n"))printf("5 藍(lán)牙波特率設(shè)置成功!\r\n"); //密碼必須是4位
//else printf("5 藍(lán)牙波特率設(shè)置失敗!\r\n\r\n");
/*6. 等待藍(lán)牙連接*/
printf("等待藍(lán)牙連接.....\r\n");
while(!HC05_LED){}
printf("當(dāng)前藍(lán)牙連接成功! 進(jìn)入到數(shù)據(jù)透?jìng)髂J絓r\n");
//藍(lán)牙連接成功
USART2->CR1&=~(1<<5);//關(guān)閉串口2接收中斷
TIM2->CR1&=~(1<<0); //關(guān)閉定時(shí)器2
USART1->CR1&=~(1<<5);//關(guān)閉串口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; //設(shè)置接收標(biāo)志
rev_len++;
}
if(rev_len>=MUSIC_BUFF_LEN) //緩沖區(qū)滿
{
rev_len=0;
}
}
if(flagbuff[play_len]==1) //可以播放
{
if(VS1053_DREQ!=0)
{
VS1053_SPI_ReadWriteByte(musicbuff[play_len]);//播放一個(gè)字節(jié)音頻
flagbuff[play_len]=0; //置接已播放標(biāo)志
play_len++;
}
}
if(play_len>=MUSIC_BUFF_LEN)
{
play_len=0;
}
}
}
else //沒(méi)有按下,播放本地SD卡音樂(lè)數(shù)據(jù)
{
printf("播放本地音樂(lè).\r\n");
TIM1_Init(7200,3000); //定時(shí)器用于滾動(dòng)顯示OLED顯示屏的提示語(yǔ)句();
printf("準(zhǔn)備播放本地音樂(lè)提示音:\r\n");
VS1053_PlayOneMusic((u8*)"0:/1.mp3");//藍(lán)牙語(yǔ)音提示
strncpy((char*)oled_show_buff," Play local music ",100);
printf("準(zhǔn)備循環(huán)播放本地音樂(lè).\r\n");
while(1)
{
VS1053_ScanDirPlayMusic("0:/mp3"); //循環(huán)掃描目錄播放音樂(lè)文件
}
}
}
/*
函數(shù)名稱: TIM1_UP_IRQHandler
函數(shù)功能: 定時(shí)器1的更新中斷服務(wù)函數(shù)
*/
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();
}
/*
函數(shù)功能: 播放一曲指定的歌曲
返 回 值: 0,正常播放完成
*/
u8 VS1053_PlayOneMusic(u8 *pname)
{
FIL fmp3;
u16 br;
u8 res,rval;
u8 databuf[4096];
u16 i=0;
VS1053_Reset(); //硬復(fù)位MP3
VS1053_SoftReset(); //軟復(fù)位VS10XX
VS1053_SetVol(250); //設(shè)置音量 最大值
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個(gè)字節(jié)
i=0;
while(i<4096) //每次播放4096個(gè)字節(jié)
{
if(VS1053_SendMusicData(databuf+i)==0)//給VS10XX發(fā)送音頻數(shù)據(jù)
{
i+=32;
}
}
if(br!=4096||res!=0)
{
rval=0;
break;//讀完了.
}
}
printf("%s音樂(lè)文件播放完畢.\r\n",pname);
f_close(&fmp3);
return rval;
}
/*
函數(shù)功能: 掃描音樂(lè)播放目錄下的音樂(lè)數(shù)據(jù)
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. 循環(huán)讀取目錄*/
while(1)
{
res=f_readdir(&dir,&fno);
if(fno.fname[0] == 0 || res!=0)break;
printf("文件名稱: %s,文件大小: %ld 字節(jié)\r\n",fno.fname,fno.fsize);
/*過(guò)濾目錄*/
// if(strstr(fno.fname,".MP3"))
{
//申請(qǐng)存放文件名稱的長(zhǎng)度
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); //播放音樂(lè)
free(abs_path);
}
}
/*3. 關(guān)閉目錄*/
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"
/*
函數(shù)功能:初始化ATK-HC05模塊
返 回 值:0,成功;1,失敗.
HC05_KEY--PB8---推挽輸出模式
HC05_LED--PB9---上下拉輸入模式
*/
u8 HC05_Bluetooth_Init(void)
{
/*1. 基本初始化*/
RCC->APB2ENR|=1<<3; //使能PORTB時(shí)鐘
GPIOB->CRH&=0xFFFFFF00;
GPIOB->CRH|=0x00000083;
USART_X_Init(USART2,36,921600);//初始化串口3為:921600,波特率
TIM2_Init(72,20000);
/*2. 檢測(cè)HC05是否正常*/
if(HCO5_SendCmd("AT\r\n","OK"))
{
printf("初始化失敗!\n");
return 1;
}
return 0; //檢測(cè)成功
}
/*
函數(shù)功能: 向HC05發(fā)送指令,并且查找返回值
返 回 值: 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置高,進(jìn)入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) //收到數(shù)據(jù)
{
USART2_RX_BUFF[USART2_RX_CNT]='\0';
if(strstr((char*)USART2_RX_BUFF,str))
{
return 0; //查找正確
}else break;
}
}
}
return 1;
}
/*
函數(shù) 功能:獲取ATK-HC05模塊的角色(主/從)狀態(tài)
函數(shù)返回值:0,從機(jī);
1,主機(jī);
2,獲取失敗.
*/
u8 HC05_Bluetooth_GetRoleStatus(void)
{
u8 cnt=15;
u8 len,t;
while(cnt--)
{
HC05_KEY=1; //KEY置高,進(jìn)入AT模式
DelayMs(10);
USART_X_SendString(USART2,"AT+ROLE?\r\n"); //查詢角色
for(t=0;t<20;t++) //最長(zhǎng)等待200ms,來(lái)接收HC05模塊的回應(yīng)
{
DelayMs(10);
if(USART2_RX_FLAG)break; //接收到一次數(shù)據(jù)了
}
HC05_KEY=0; //KEY拉低,退出AT模式
len=USART2_RX_CNT; //得到接收數(shù)據(jù)的長(zhǎng)度
USART2_RX_CNT=0; //清空接收數(shù)量
USART2_RX_FLAG=0; //清空接收狀態(tài)
if(len==13&&USART2_RX_BUFF[0]=='+')//接收到正確的應(yīng)答了
{
len=USART2_RX_BUFF[6]-'0';//得到主從模式值
return len;
}
}
return 2;//查詢失敗
}
/*
函數(shù)功能:ATK-HC05設(shè)置命令
說(shuō) 明:此函數(shù)用于設(shè)置ATK-HC05,適用于僅返回OK應(yīng)答的AT指令
函數(shù)參數(shù):atstr:AT指令串。 比如:"AT+RESET"/"AT+UART=9600,0,0"/"AT+ROLE=0"等字符串
返 回 值:0,設(shè)置成功;其他,設(shè)置失敗.
*/
u8 HC05_Bluetooth_SetCmd(char* atstr)
{
u8 cnt=0X0F;
u8 len,t;
while(cnt--)
{
HC05_KEY=1; //KEY置高,進(jìn)入AT模式
DelayMs(10);
USART_X_SendString(USART2,atstr); //發(fā)送AT字符串
HC05_KEY=0; //KEY拉低,退出AT模式
for(t=0;t<20;t++) //最長(zhǎng)等待100ms,來(lái)接收HC05模塊的回應(yīng)
{
if(USART2_RX_FLAG)break;
DelayMs(5);
}
len=USART2_RX_CNT; //得到數(shù)據(jù)長(zhǎng)度
USART2_RX_FLAG=0; //清空接收狀態(tài)
USART2_RX_CNT=0; //清空接收數(shù)量
if(len==4&&USART2_RX_BUFF[0]=='O')//接收到正確的應(yīng)答了
{
return 0; //設(shè)置成功
}
}
return 1; //設(shè)置失敗
}
6.3 vs1053.c
#include "vs1053b.h"
/*
函數(shù)功能:移植接口--SPI時(shí)序讀寫一個(gè)字節(jié)
函數(shù)參數(shù):data:要寫入的數(shù)據(jù)
返 回 值:讀到的數(shù)據(jù)
*/
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;
}
/*
函數(shù)功能:初始化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;
}
/*
函數(shù)功能:軟復(fù)位VS10XX
*/
void VS1053_SoftReset(void)
{
u8 retry=0;
while(VS1053_DREQ==0); //等待軟件復(fù)位結(jié)束
VS1053_SPI_ReadWriteByte(0Xff); //啟動(dòng)傳輸
retry=0;
while(VS1053_ReadReg(SPI_MODE)!=0x0800) // 軟件復(fù)位,新模式
{
VS1053_WriteCmd(SPI_MODE,0x0804); // 軟件復(fù)位,新模式
DelayMs(2);//等待至少1.35ms
if(retry++>100)break;
}
while(VS1053_DREQ==0);//等待軟件復(fù)位結(jié)束
retry=0;
while(VS1053_ReadReg(SPI_CLOCKF)!=0X9800)//設(shè)置VS10XX的時(shí)鐘,3倍頻 ,1.5xADD
{
VS1053_WriteCmd(SPI_CLOCKF,0X9800); //設(shè)置VS10XX的時(shí)鐘,3倍頻 ,1.5xADD
if(retry++>100)break;
}
DelayMs(20);
}
/*
函數(shù) 功 能:硬復(fù)位MP3
函數(shù)返回值:1:復(fù)位失敗;0:復(fù)位成功
*/
u8 VS1053_Reset(void)
{
u8 retry=0;
VS1053_RESET=0;
DelayMs(20);
VS1053_XDCS=1;//取消數(shù)據(jù)傳輸
VS1053_XCS=1; //取消數(shù)據(jù)傳輸
VS1053_RESET=1;
while(VS1053_DREQ==0&&retry<200)//等待DREQ為高
{
retry++;
DelayUs(50);
};
DelayMs(20);
if(retry>=200)return 1;
else return 0;
}
/*
函數(shù)功能:向VS10XX寫命令
函數(shù)參數(shù):
address:命令地址
data :命令數(shù)據(jù)
*/
void VS1053_WriteCmd(u8 address,u16 data)
{
while(VS1053_DREQ==0); //等待空閑
VS1053_XDCS=1;
VS1053_XCS=0;
VS1053_SPI_ReadWriteByte(VS_WRITE_COMMAND);//發(fā)送VS10XX的寫命令
VS1053_SPI_ReadWriteByte(address); //地址
VS1053_SPI_ReadWriteByte(data>>8); //發(fā)送高八位
VS1053_SPI_ReadWriteByte(data); //第八位
VS1053_XCS=1;
}
/*
函數(shù)參數(shù):向VS1053寫數(shù)據(jù)
函數(shù)參數(shù):data:要寫入的數(shù)據(jù)
*/
void VS1053_WriteData(u8 data)
{
VS1053_XDCS=0;
VS1053_SPI_ReadWriteByte(data);
VS1053_XDCS=1;
}
/*
函數(shù)功能:讀VS1053的寄存器
函數(shù)參數(shù):address:寄存器地址
返回值:讀到的值
*/
u16 VS1053_ReadReg(u8 address)
{
u16 temp=0;
while(VS1053_DREQ==0);//非等待空閑狀態(tài)
VS1053_XDCS=1;
VS1053_XCS=0;
VS1053_SPI_ReadWriteByte(VS_READ_COMMAND);//發(fā)送VS10XX的讀命令
VS1053_SPI_ReadWriteByte(address); //地址
temp=VS1053_SPI_ReadWriteByte(0xff); //讀取高字節(jié)
temp=temp<<8;
temp+=VS1053_SPI_ReadWriteByte(0xff); //讀取低字節(jié)
VS1053_XCS=1;
return temp;
}
/*
函數(shù)功能:讀取VS1053的RAM
函數(shù)參數(shù):addr:RAM地址
返 回 值:讀到的值
*/
u16 VS1053_ReadRAM(u16 addr)
{
u16 res;
VS1053_WriteCmd(SPI_WRAMADDR, addr);
res=VS1053_ReadReg(SPI_WRAM);
return res;
}
/*
函數(shù)功能:寫VS1053的RAM
函數(shù)參數(shù):
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值
}
/*
函數(shù)參數(shù):發(fā)送一次音頻數(shù)據(jù),固定為32字節(jié)
返 回 值:0,發(fā)送成功
1,本次數(shù)據(jù)未成功發(fā)送
*/
u8 VS1053_SendMusicData(u8* buf)
{
u8 n;
if(VS1053_DREQ!=0) //送數(shù)據(jù)給VS10XX
{
VS1053_XDCS=0;
for(n=0;n<32;n++)
{
VS1053_SPI_ReadWriteByte(buf[n]);
}
VS1053_XDCS=1;
}else return 1;
return 0;//成功發(fā)送了
}
/*
函數(shù)參數(shù):發(fā)送一次音頻數(shù)據(jù),固定為32字節(jié)
返 回 值:0,發(fā)送成功
1,本次數(shù)據(jù)未成功發(fā)送
*/
void VS1053_SendMusicByte(u8 data)
{
u8 n;
while(VS1053_DREQ==0){}
VS1053_XDCS=0;
VS1053_SPI_ReadWriteByte(data);
VS1053_XDCS=1;
}
/*
函數(shù)功能:設(shè)定VS1053播放的音量
函數(shù)參數(shù):volx:音量大小(0~254)
*/
void VS1053_SetVol(u8 volx)
{
u16 volt=0; //暫存音量值
volt=254-volx; //取反一下,得到最大值,表示最大的表示
volt<<=8;
volt+=254-volx; //得到音量設(shè)置后大小
VS1053_WriteCmd(SPI_VOL,volt);//設(shè)音量
}
七、Android手機(jī)APP核心源碼
7.1 代碼頁(yè)面
?
7.2 mainwindow.cpp代碼
#include "mainwindow.h"
#include "ui_mainwindow.h"
/*
* 設(shè)置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");
//這個(gè)字符串里面的內(nèi)容就是串口模式的Uuid
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->SetStyle(":/qss/blue.css"); //設(shè)置樣式表
this->setWindowTitle("HC05藍(lán)牙音箱"); //設(shè)置標(biāo)題
this->setWindowIcon(QIcon(":/wbyq.ico")); //設(shè)置圖標(biāo)
/*1. 實(shí)例化藍(lán)牙相關(guān)的對(duì)象*/
discoveryAgent = new QBluetoothDeviceDiscoveryAgent();
localDevice = new QBluetoothLocalDevice();
socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
//RfcommProtocol表示該服務(wù)使用RFCOMM套接字協(xié)議。RfcommProtocol屬于模擬RS232模式,就叫串口模式
/*2. 關(guān)聯(lián)藍(lán)牙設(shè)備相關(guān)的信號(hào)*/
/*2.1 關(guān)聯(lián)發(fā)現(xiàn)設(shè)備的槽函數(shù),當(dāng)掃描發(fā)現(xiàn)周圍的藍(lán)牙設(shè)備時(shí),會(huì)發(fā)出deviceDiscovered信號(hào)*/
connect(discoveryAgent,
SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),
this,
SLOT(addBlueToothDevicesToList(QBluetoothDeviceInfo))
);
//藍(lán)牙有數(shù)據(jù)可讀
connect(socket,
SIGNAL(readyRead()),
this,
SLOT(readBluetoothDataEvent())
);
//藍(lán)牙連接建立成功
connect(socket,
SIGNAL(connected()),
this,
SLOT(bluetoothConnectedEvent())
);
//藍(lán)牙斷開連接
connect(socket,
SIGNAL(disconnected()),
this,
SLOT(bluetoothDisconnectedEvent())
);
//藍(lán)牙寫成功的數(shù)據(jù)
connect(socket,
SIGNAL(bytesWritten(qint64)),
this,
SLOT(bluetoothbytesWritten(qint64))
);
/*3.2 設(shè)置標(biāo)簽顯示本地藍(lán)牙的名稱*/
QString name_info("本機(jī)藍(lán)牙:");
name_info+=localDevice->name();
ui->label_BluetoothName->setText(name_info);
ui->plainTextEdit->setEnabled(false); //不可編輯
/*定時(shí)器用于定時(shí)發(fā)送文件*/
timer = new QTimer(this); //創(chuàng)建定時(shí)器
connect(timer, SIGNAL(timeout()), this, SLOT(update())); //關(guān)聯(lián)槽函數(shù)
ConnectStat=0; //連接狀態(tài)
SendStat=0; //文件打開狀態(tài)
FileSendTime=100; //默認(rèn)每次發(fā)送的時(shí)間 單位ms
ui->lineEdit_Timer->setText(QString::number(FileSendTime));
FileSendCnt=32; //默認(rèn)每包數(shù)據(jù)值32字節(jié)
ui->lineEdit_SendFileCnt->setText(QString::number(FileSendCnt));
//創(chuàng)建存放音樂(lè)文件的目錄
QDir dir;
if(dir.mkpath("/sdcard/WBYQ_MP3"))
{
ui->plainTextEdit->insertPlainText("/WBYQ_MP3 目錄創(chuàng)建成功!\n");
}
else
{
ui->plainTextEdit->insertPlainText("/WBYQ_MP3 目錄創(chuàng)建失敗!\n");
}
MusicFileDir="assets:/nansannan.mp3"; //目錄的路徑
Def_MusicName="assets:/nansannan.mp3";
file.setFileName(Def_MusicName); //設(shè)置文件名稱
ui->lineEdit_MusicName->setText(Def_MusicName); //默認(rèn)文件名稱
}
int count=0;
//更新
void MainWindow::update()
{
if(SendStat)
{
int len;
if(file.atEnd()==false) //文件未結(jié)束
{
len=file.read(FileBuff,FileSendCnt);
len=socket->write(FileBuff,len); //發(fā)送數(shù)據(jù)
}
else
{
file.close();
timer->stop(); //停止定時(shí)器
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()
{
/*關(guān)閉藍(lán)牙設(shè)備*/
localDevice->setHostMode(QBluetoothLocalDevice::HostPoweredOff);
}
void MainWindow::on_pushButton_BluetoothScan_clicked()
{
/*3.1 檢查藍(lán)牙是否開啟*/
if(localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff)
{
/*請(qǐng)求打開藍(lán)牙設(shè)備*/
localDevice->powerOn();
}
/*開始掃描周圍的藍(lán)牙設(shè)備*/
discoveryAgent->start();
ui->comboBox_BluetoothDevice->clear(); //清除條目
}
void MainWindow::on_pushButton_DeviceVisible_clicked()
{
/*設(shè)置藍(lán)牙可見,可以被周圍的設(shè)備搜索到,在Android上,此模式最多只能運(yùn)行5分鐘。*/
// localDevice->setHostMode( QBluetoothLocalDevice::HostDiscoverable);
static bool cnt=1;
cnt=!cnt;
if(cnt)
{
MusicFileDir="assets:/nansannan.mp3"; //目錄的路徑
QMessageBox::information(this,"提示","路徑切換為內(nèi)部路徑!\n請(qǐng)選擇文件!",QMessageBox::Ok,QMessageBox::Ok);
}
else
{
MusicFileDir="/mnt/sdcard"; //目錄的路徑
QMessageBox::information(this,"提示","路徑切換為SD路徑!\n請(qǐng)選擇文件!",QMessageBox::Ok,QMessageBox::Ok);
}
}
void MainWindow::on_pushButton_StopScan_clicked()
{
/*停止掃描周圍的藍(lán)牙設(shè)備*/
discoveryAgent->stop();
}
/*當(dāng)掃描到周圍的設(shè)備時(shí)會(huì)調(diào)用當(dāng)前的槽函數(shù)*/
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上
}
//有數(shù)據(jù)可讀
void MainWindow::readBluetoothDataEvent()
{
QByteArray all = socket->readAll();
ui->plainTextEdit->insertPlainText(all);
}
//建立連接
void MainWindow::bluetoothConnectedEvent()
{
QMessageBox::information(this,tr("連接提示"),"藍(lán)牙連接成功!");
ConnectStat=1;
/*停止掃描周圍的藍(lán)牙設(shè)備*/
discoveryAgent->stop();
}
//斷開連接
void MainWindow::bluetoothDisconnectedEvent()
{
ConnectStat=0;
QMessageBox::information(this,tr("連接提示"),"藍(lán)牙斷開連接!");
}
/*
在說(shuō)藍(lán)牙設(shè)備連接之前,不得不提一個(gè)非常重要的概念,就是藍(lán)牙的Uuid,引用一下百度的:
在藍(lán)牙中,每個(gè)服務(wù)和服務(wù)屬性都唯一地由"全球唯一標(biāo)識(shí)符" (UUID)來(lái)校驗(yàn)。
正如它的名字所暗示的,每一個(gè)這樣的標(biāo)識(shí)符都要在時(shí)空上保證唯一。
UUID類可表現(xiàn)為短整形(16或32位)和長(zhǎng)整形(128位)UUID。
他提供了分別利用String和16位或32位數(shù)值來(lái)創(chuàng)建類的構(gòu)造函數(shù),提供了一個(gè)可以比較兩個(gè)UUID(如果兩個(gè)都是128位)的方法,還有一個(gè)可以轉(zhuǎn)換一個(gè)UUID為一個(gè)字符串的方法。
UUID實(shí)例是不可改變的(immutable),只有被UUID標(biāo)示的服務(wù)可以被發(fā)現(xiàn)。
在Linux下你用一個(gè)命令uuidgen -t可以生成一個(gè)UUID值;
在Windows下則執(zhí)行命令uuidgen 。UUID看起來(lái)就像如下的這個(gè)形式:2d266186-01fb-47c2-8d9f-10b8ec891363。當(dāng)使用生成的UUID去創(chuàng)建一個(gè)UUID對(duì)象,你可以去掉連字符。
*/
//發(fā)送音樂(lè)文件
void MainWindow::on_pushButton_SendData_clicked()
{
if(ConnectStat)
{
if(file.open(QIODevice::ReadOnly))
{
SendStat=1;
count=0;
ui->plainTextEdit->insertPlainText("系統(tǒng)提示: 開始發(fā)送文件!\n");
ui->lineEdit_fileSizef->setText(QString::number(file.size()));
ui->lineEdit_fileCount->setText("");
ui->progressBar_SendCount->setMaximum(file.size()); //設(shè)置進(jìn)度條顯示最大值
ui->progressBar_SendCount->setValue(0); //設(shè)置進(jìn)度條的值
timer->start(FileSendTime); //啟動(dòng)定時(shí)器
}
}
}
//連接藍(lán)牙
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="開始連接藍(lán)牙設(shè)備:\n";
connect_device+=ui->comboBox_BluetoothDevice->currentText();
QMessageBox::information(this,tr("連接提示"),connect_device);
//開始連接藍(lán)牙設(shè)備
socket->connectToService(address, QBluetoothUuid(serviceUuid) ,QIODevice::ReadWrite);
}
//幫助提示
void MainWindow::on_pushButton_help_clicked()
{
QMessageBox::information(this,tr("幫助提示"),"本軟件用于HC-05/06系列串口藍(lán)牙連接!\n"
"暫時(shí)不支持BLE4.0版本藍(lán)牙!\n"
"用于發(fā)送音樂(lè)文件數(shù)據(jù),每次發(fā)送32字節(jié),默認(rèn)100ms發(fā)送間隔時(shí)間\n"
"軟件作者:DS小龍哥\n"
"BUG反饋:1126626497@qq.com");
}
//選擇音樂(lè)文件
void MainWindow::on_pushButton_select_clicked()
{
QString filename=QFileDialog::getOpenFileName(this,"選擇發(fā)送的音樂(lè)文件",MusicFileDir,tr("*.mp3 *.MP3 *.WAV"));
//filename==選擇文件的絕對(duì)路徑
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(); //停止定時(shí)器
}
//設(shè)置發(fā)送間隔時(shí)間
void MainWindow::on_pushButton_SendTime_clicked()
{
QString str=ui->lineEdit_Timer->text();
int time=str.toInt();
FileSendTime=time; //保存時(shí)間
if(time<=0)
{
QMessageBox::warning(this,"警告提示","設(shè)置錯(cuò)誤: 發(fā)送的間隔時(shí)間最小1ms\n",QMessageBox::Ok,QMessageBox::Ok);
}
else
ui->plainTextEdit->insertPlainText(tr("設(shè)置發(fā)送間隔時(shí)間為%1ms\n").arg(time));
}
//修改每包發(fā)送的數(shù)量
void MainWindow::on_pushButton_SendFileCnt_clicked()
{
QString str=ui->lineEdit_SendFileCnt->text();
int cnt=str.toInt();
if(cnt>4096 || cnt<=0)
{
QMessageBox::warning(this,"警告提示","設(shè)置錯(cuò)誤: 每包發(fā)送的數(shù)量范圍: 1~4096\n",QMessageBox::Ok,QMessageBox::Ok);
}
else
{
FileSendCnt=cnt;
ui->plainTextEdit->insertPlainText(tr("發(fā)送數(shù)量設(shè)置為%1字節(jié).\n").arg(cnt));
}
}
//寫成功的數(shù)量
void MainWindow::bluetoothbytesWritten(qint64 byte)
{
count+=byte;
ui->lineEdit_fileCount->setText(QString::number(count));
ui->progressBar_SendCount->setValue(count); //設(shè)置進(jìn)度條的值
}
7.3 mianwindows.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#include "QListWidgetItem"
#include
#include "QFile"
#include "QFileDialog"
#include "QTimer"
#include "QInputDialog"
#include
#include
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; //這個(gè)是指掃描周圍藍(lán)牙設(shè)備!
QBluetoothLocalDevice *localDevice;//是指配置獲取設(shè)備的藍(lán)牙狀態(tài)信息等!
QBluetoothSocket *socket; //指進(jìn)行鏈接藍(lán)牙設(shè)備,讀寫信息!
int FileSendTime; //文件發(fā)送時(shí)間
char FileBuff[4096]; //每包最大的字節(jié)數(shù)
int FileSendCnt; //每次發(fā)送的字節(jié)數(shù)
QString Def_MusicName;
QString MusicFileDir;
private slots:
void on_pushButton_CloseBluetooth_clicked();
void on_pushButton_BluetoothScan_clicked();
void addBlueToothDevicesToList(const QBluetoothDeviceInfo&); //發(fā)現(xiàn)藍(lán)牙設(shè)備的槽函數(shù)
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); //樣式表設(shè)置函數(shù)
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
完整項(xiàng)目源碼下載地址: https://download.csdn.net/download/xiaolong1126626497/18621270
審核編輯:符乾江
-
STM32
+關(guān)注
關(guān)注
2266文章
10873瀏覽量
354871 -
藍(lán)牙音箱
+關(guān)注
關(guān)注
6文章
259瀏覽量
22744
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論