单片机GD32F303RCT6 (Macos环境)开发 (二十八)—— 蓝牙透传模块HC-08 Android App开发

news/2024/4/28 23:23:36/文章来源:https://blog.csdn.net/weixin_43932857/article/details/130878854

蓝牙透传模块HC-08 Android App开发

1、App整体开发思路

a、首先要申请权限,采用动态申请的方式,用户点击确认后方可操作蓝牙。
b、搜索蓝牙,之前的版本用startLeScan函数搜索蓝牙,虽然高版本中依然可用,但是google已经废弃了这个函数。目前推荐大家使用BluetoothLeScanner中的startScan这个函数。
c、连接蓝牙,用device.connectGatt函数连接透传模块,其中参数BluetoothGattCallback比较关键,这个回调函数会贯穿我们整个代码,写数据,读数据,获取服务都在这个回调中处理。
d、读写数据,gatt服务连接以后,我们得到蓝牙模块的服务以及特征值以后,我们就可以对特定的特质值读写操作,HC-08的用户特征值为0000ffe1-0000-1000-8000-00805f9b34fb。这些值都可以打印出来,上一节用树莓派也都扫描出来过。
注意,这个特质值的读是通过通知发过来的,所以我们还要进行打开通知的操作。
e、界面设置,两个activity,第一个activity是搜索界面,将搜索后的蓝牙设备装填到一个ListView中。如下:
请添加图片描述
第二个Activity,假设gd32,我们做的是一个智能锁,这里我设计两个按钮,一个开锁,一个关锁,如下:
请添加图片描述

2、关键代码讲解

1、申请权限操作。

AndroidManifest.xml中添加操作蓝牙的权限

	<uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Activity Oncreate中添加requestPermission代码

void requestPermission()
{if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)!= PackageManager.PERMISSION_GRANTED){ActivityCompat.requestPermissions(this,new String[] {Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION},2);}else{isHavePermission = true;}}
}

加上这个代码,第一次运行时,会弹出一个授权框,如下

请添加图片描述

用户点击禁止或者仅使用期间允许后,会掉函数处理如下:

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if (requestCode == 2){if ((grantResults[0] == PackageManager.PERMISSION_GRANTED )&& (grantResults[1] == PackageManager.PERMISSION_GRANTED)){isHavePermission = true;// 获得了权限}else{// 没有权限isHavePermission = false;Toast.makeText(mContext,"no permission",Toast.LENGTH_LONG).show();}}
}

2、搜索蓝牙代码

private ScanCallback mScanCallback = new ScanCallback() {@Overridepublic void onScanResult(int callbackType, ScanResult result) {super.onScanResult(callbackType, result);//  只显示hc-08这个蓝牙设备if (result != null && result.getDevice().getName() != null) {//if (result.getDevice().getName().equals("HC-08")) {mleDeviceListAdapter.addDevice(result.getDevice(), result.getRssi());mleDeviceListAdapter.notifyDataSetChanged();//}}}@Overridepublic void onBatchScanResults(List<ScanResult> results) {super.onBatchScanResults(results);}@Overridepublic void onScanFailed(int errorCode) {super.onScanFailed(errorCode);}
};	    

开启搜索

mBluetoothLeScanner.startScan(mScanCallback);

停止搜索

mBluetoothLeScanner.stopScan(mScanCallback);

3、连接
连接放在了一个service里,而连接的页面放在了BleConnectDeviceActivity里面。

真正连接的代码如下

public boolean connect(final String address) {if (mBluetoothAdapter == null || address == null) {LogUtil.w(TAG, "BluetoothAdapter not initialized or unspecified address.");return false;}if (mBluetoothDeviceAddress != null&& address.equals(mBluetoothDeviceAddress)&& mBluetoothGatt != null) {LogUtil.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");if (mBluetoothGatt.connect()){mConnectionState = STATE_CONNECTING;return true;} else {return false;}}final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);if (device == null) {LogUtil.w(TAG, "Device not found.  Unable to connect.");return false;}mBluetoothGatt = device.connectGatt(this, false, mGattCallback);LogUtil.d(TAG, "Trying to create a new connection.");mBluetoothDeviceAddress = address;mConnectionState = STATE_CONNECTING;System.out.println("device.getBondState==" + device.getBondState());return true;
}:

4、mGattCallbak回调函数

private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {String intentAction = null;if (newState == BluetoothProfile.STATE_CONNECTED) {if (mOnCharacteristicListener != null)mOnCharacteristicListener.onDeviceConnected(newState);mConnectionState = STATE_CONNECTED;mBluetoothGatt.discoverServices();} else if (newState == BluetoothProfile.STATE_DISCONNECTED){mConnectionState = STATE_DISCONNECTED;LogUtil.i(TAG, "Disconnected from GATT server.");}}@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {if (status == BluetoothGatt.GATT_SUCCESS) {displayGattServices(gatt.getServices());}}@Overridepublic void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) {if (status == BluetoothGatt.GATT_SUCCESS) {LogUtil.i(TAG, "--onCharacteristicRead called--");byte[] sucString = characteristic.getValue();String string = new String(sucString);}}@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {LogUtil.d(TAG,"onCharacteristicChanged " + new String(characteristic.getValue()));if (mOnCharacteristicListener != null)mOnCharacteristicListener.onDataReceive(characteristic.getValue(),characteristic.getValue().length);}public void onCharacteristicWrite(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) {LogUtil.w(TAG, "--onCharacteristicWrite--: " + status);}@Overridepublic void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {// TODO Auto-generated method stub//super.onReadRemoteRssi(gatt, rssi, status);LogUtil.w(TAG, "--onReadRemoteRssi--: " + rssi + "status " + status);}
};

onConnectionStateChange 是连接状态改变时候的回调,这里可以判断连接还是断开。

onServicesDiscovered 是服务搜索完成的回调,可以在完成后,去加载或者打印出所有的服务,

onCharacteristicRead读完数据的回调,但是hc-08读的操作并没有走这里。

onCharacteristicChanged是特征值改变的回调,从hc-08读取数据就是这个回调。

onCharacteristicWrite 写完数据的回调

onReadRemoteRssi是读取信号强度的回调。

5、读取数据。
hc-08是通知过来的数据,也就是gd32那边通过串口写进来的数据,Android app从hc-08传过来的数据,走的是特征值的通知属性,所以我们在读数据之前,要打开通知。

public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {if (mBluetoothAdapter == null || mBluetoothGatt == null) {LogUtil.w(TAG, "BluetoothAdapter not initialized");return;}mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);BluetoothGattDescriptor clientConfig = characteristic.getDescriptor(UUID.fromString(CHAR_DESCRIPTOR));if (enabled) {clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);} else {clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);}mBluetoothGatt.writeDescriptor(clientConfig);
}

6、写数据

public void writeSpecialCharacteristic(byte[]data) {if (mBluetoothAdapter == null || mBluetoothGatt == null) {LogUtil.w(TAG, "BluetoothAdapter not initialized");return;}mBluetoothGattCharacteristic.setValue(data);mBluetoothGatt.writeCharacteristic(mBluetoothGattCharacteristic);
}

3、GD32 usart1的回调函数处理。

这里同上,收到什么数据返回什么数据。
void USART1_IRQHandler(void)
{
if(RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE)){

    /* clear IDLE flag */usart_data_receive(USART1);/* number of data received */usart1_rxcount = 256 - (dma_transfer_number_get(DMA0, DMA_CH5));if (usart1_rxcount >0){print_register_value(usart1_rx_buffer,usart1_rxcount);printf("get data ==%d\r\n ",usart1_rxcount);usart1_send_string(usart1_rx_buffer,usart1_rxcount);memset(usart1_rx_buffer,0,sizeof(usart1_rx_buffer));}usart_interrupt_flag_clear(USART1,USART_INT_FLAG_IDLE);/* disable DMA and reconfigure */dma_channel_disable(DMA0, DMA_CH5);dma_transfer_number_config(DMA0, DMA_CH5, 256);dma_channel_enable(DMA0, DMA_CH5);
}

}

4、验证。

点击开锁 ,发送{0x00,0x01,0x02,0x03,0x04}数据,gd32 收到数据后,返还,在App页面上显示 {0x00,0x01,0x02,0x03,0x04}。

点击关锁,发送{0x04,0x03,0x02,0x01,0x00}数据,gd32收到数据后,返还,在App页面上显示{0x04,0x03,0x02,0x01,0x00}。

Android App蓝牙透传

GD32的打印如下:
在这里插入图片描述

代码路径https://gitee.com/xiaoguo-tec_0/hc-08-app.git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_306523.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

4.Ansible Inventory介绍及实战 - A list or group of lists nodes

什么是inventory&#xff1f; 官方解释&#xff1a;Ansible automates tasks on managed nodes or “hosts” in your infrastructure, using a list or group of lists known as inventory. Ansible可以同时与您基础设施中的一个或多个系统协同工作&#xff61;为了与多台服务…

接口测试的请求和响应

接口测试的请求和响应 在软件开发中&#xff0c;接口测试是必不可少的一环节。接口测试主要涉及到测试请求和响应的过程。请求是指客户端向服务器发送的一些指令或数据&#xff0c;而响应则是服务器对这些请求做出的回应。 请求通常包括请求方法、请求头以及请求体。请求方法有…

【什么是iMessage苹果推?】什么是苹果推信?什么是苹果推?

挑选得当的IM推送平台&#xff1a;选择合用于PC真个IM推送平台 开辟或集成API&#xff1a;依照所选平台的开发文档&#xff0c;利用响应的编程语言&#xff08;如Python、Java等&#xff09;开发或集成API&#xff0c;以便与平台举行交互和节制。API可用于建立、办理和发送消息…

【HarmonyOS】【FAQ】HarmonyOS应用开发相关问题解答(二)

【写在前面】 之前和大家分享过一下HarmonyOS应用开发相关问题&#xff0c;今天继续和大家分享&#xff01; 【前提简介】 本文档主要总结HarmonyOS开发过程中可能遇到的一些问题解答&#xff0c;主要围绕HarmonyOS展开&#xff0c;包括但不限于不同API版本HarmonyOS开发、UI…

单体项目偶遇并发漏洞!短短一夜时间竟让老板蒸发197.83元

事先声明&#xff1a;以下故事基于真实事件而改编&#xff0c;如有雷同&#xff0c;纯属巧合~ 眼下这位正襟危坐的男子&#xff0c;名为小竹&#xff0c;他正是本次事件的主人公&#xff0c;也即将成为熊猫集团的被告&#xff0c;嗯&#xff1f;这究竟怎么一回事&#xff1f;欲…

Linux网络编程—Day10

Linux服务器程序规范 Linux服务器程序一般以后台进程形式运行。后台进程又称守护进程。它没有控制终端&#xff0c;因而也不会意外接收到用户输入。 守护进程的父进程通常是init进程&#xff08;PID为1的进程&#xff09;&#xff1b;Linux服务器程序通常有一套日志系统&#…

设备快线客户端软件V1.0用户手册

1.前言欢迎使用设备快线客户端软件产品。设备快线客户端软件简称DYClient,DYClient客户端是东用科技有限公司推出的一款用于远程维护的控制软件&#xff0c;主要为客户远程访问现场终端设备提供便捷的接入服务&#xff0c;并且通过DYClient客户端软件用户可以非常方便快捷的访问…

基于RetinaNet和TensorFlow Object Detection API实现目标检测(附源码)

文章目录 一、RetinaNet原理二、RetinaNet实现1. tf.train.CheckPoint简介2. RetinaNet的TensorFlow源码 一、RetinaNet原理 待补充 二、RetinaNet实现 1. tf.train.CheckPoint简介 待补充 2. RetinaNet的TensorFlow源码 Step 1&#xff1a;安装Tensorflow 2 Object Detect…

云原生之深入解析Docker容器退出码的含义和产生原因

一、前言 为什么我的容器没有运行?回答这个问题之前,需要知道 Docker 容器为什么退出?退出码会提示容器停止运行的情况?本文列出最常见的退出码,来回答两个重要问题:这些退出码是什么意思?导致该退出码的动作是什么?exit code:代表一个进程的返回码,通过系统调用 exi…

chatgpt赋能python:Python修改密码:一种安全可靠、快速高效的方式

Python 修改密码&#xff1a;一种安全可靠、快速高效的方式 在数字化时代&#xff0c;越来越多的信息被存储在计算机系统中&#xff0c;因此密码的保护变得尤为重要。人们需要保证他们的密码是安全可靠的&#xff0c;并定期更换密码。Python作为一种强大而且通用的编程语言&am…

iOS-最全的App上架教程

App上架教程 在上架App之前想要进行真机测试的同学&#xff0c;请查看《iOS- 最全的真机测试教程》&#xff0c;里面包含如何让多台电脑同时上架App和真机调试。 P12文件的使用详解 注意&#xff1a; 同样可以在Build Setting 的sign中设置证书&#xff0c;但是有点麻烦&…

生态伙伴 | 携手深圳科创学院,持续推动项目落地与成长

01 大赛介绍 中国硬件创新创客大赛始于2015年&#xff0c;由深圳华秋电子有限公司主办&#xff0c;至今已经成功举办八届&#xff0c;赛事范围覆盖华南、华东、华北三大地区&#xff0c;超10个省市区域。 大赛影响了超过45万工程师群体&#xff0c;吸引了35000多名硬创先锋报…

Linux文件系统、磁盘I/O是怎么工作的?

同CPU、内存一样&#xff0c;文件系统和磁盘I/O&#xff0c;也是Linux操作系统最核心的功能。磁盘为系统提供了最基本的持久化存储。文件系统则在磁盘基础上&#xff0c;提供了一个用来管理文件的树状结构。 目录&#xff1a; 一. 文件系统 1. 索引节点和目录项 2. 虚拟文件系…

抖音短视频APP的益与害都存在,今日详解其利弊

抖音是一款音乐创意短视频社交软件&#xff0c;是一个专注年轻人的15秒音乐短视频社区。这两年抖音太火了&#xff0c;不若与众身边的朋友百分之八十的朋友手机上都有这个软件&#xff0c;即使不拍也会抱着手机刷到停不下来。 首先&#xff0c;抖音其实给人们带来了许多乐趣和娱…

兼容性测试点和注意项,建议收藏

一&#xff1a;兼容性测试的概念&#xff1a;就是验证开发出来的程序在特定的运行环境中与特定的软件、硬件或数据相组合是否能正常运行、有无异常的测试过程。 二&#xff1a;兼容性测试的分类&#xff1a; &#xff08;1&#xff09;浏览器兼容性测试 指的是在浏览器上检查…

【CCF- CSP 202104-2 邻域均值 二维数组前缀和满分题解】

代码思路&#xff1a; 本题如果直接用暴力求解的话只能得70分。 运用到了二维数组的前缀和&#xff0c;难点是如何求出二维数组的前缀和并计算出领域所有元素的和。 注意计算平均数的时候要保证精度相同&#xff0c;所有都要化为double型&#xff0c;否则会出错。 首先&…

基于SpringBoot+Vue的闲一品交易平台设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架下…

160743-62-4,DMG PEG2000,1,2-二肉豆蔻酰-rac-甘油-3-甲氧基聚乙二醇2000

DMG PEG2000&#xff0c;DMG-mPEG2000&#xff0c;1,2-二肉豆蔻酰-rac-甘油-3-甲氧基聚乙二醇2000 Product structure&#xff1a; Product specifications&#xff1a; 1.CAS No&#xff1a;160743-62-4 2.Molecular formula&#xff1a; C34H66O 3.Molecular weight&#xff…

基于openfaas托管脚本的实践

作者 | 张曦 一、openfaas产品背景 在云服务架构发展之初&#xff0c;这个方向上的思路是使开发者不需要关心搭建和管理后端应用程序。这里并没有提及无服务器这个概念&#xff0c;而是指后端基础设施由第三方来托管&#xff0c;需要的基础架构组建均以服务的形式提供&#x…

Paragon NTFS2023最新mac免费实用工具磁盘工具

mac虽然系统稳定&#xff0c;但在使用过程中也有一些瑕疵&#xff0c;如当mac连接到ntfs格式移动磁盘时&#xff0c;可能会出现移动磁盘无法在mac被正常读写的状况。遇到移动磁盘无法正常读写的状况&#xff0c;我们可以在mac中使用磁盘工具&#xff0c;以使mac获得对ntfs格式移…