一、环境介绍
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
三、硬件实物
VS1053可以接耳机或者接音箱设备即可听音乐。
HC05蓝牙串口默认波特率是38400,为了提高蓝牙传输速率,需要修改波特率为: 921600。
五、APP端界面
六、设备端:核心代码
#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.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