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

news2024/12/27 15:44:57

蓝牙透传模块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() {
    @Override
    public 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();
            //}
        }
    }

    @Override
    public void onBatchScanResults(List<ScanResult> results) {
        super.onBatchScanResults(results);
    }

    @Override
    public 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() {
    @Override
    public 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.");
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {

        if (status == BluetoothGatt.GATT_SUCCESS) {
            displayGattServices(gatt.getServices());
        }
    }

    @Override
    public 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);
        }
    }

    @Override
    public 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);
    }

    @Override
    public 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.coloradmin.cn/o/571826.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么时候该停止使用Scrum?

01、TL;DR:一个团队什么时候应该停止使用Scrum? 什么时候才能超越Scrum?毕竟许多类似思想、实践等事务迟早会过时;那为什么Scrum会是个例外?此外&#xff0c;我们不是通过实践Scrum来获得报酬&#xff0c;而是在既定的约束条件下解决客户的问题&#xff0c;同时又能为组织的…

( 链表) 707. 设计链表 ——【Leetcode每日一题】

❓707. 设计链表 难度&#xff1a;中等 你可以选择使用单链表或者双链表&#xff0c;设计并实现自己的链表。 单链表中的节点应该具备两个属性&#xff1a;val 和 next 。val 是当前节点的值&#xff0c;next 是指向下一个节点的指针/引用。 如果是双向链表&#xff0c;则还…

ubuntu 安装ffmpeg

一、我的编译环境 ubuntu 22 ffmpeg 4.36 二、安装必要的依赖 sudo apt-get update sudo apt-get install -y \autoconf \automake \build-essential \cmake \git-core \libass-dev \libfreetype6-dev \libsdl2-dev \libtool \libva-dev \libvdpau-dev \libvorbis-dev \lib…

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…

Ubuntu18.04 dash to dock启动器安装教程

1.安装主题工具&#xff1a;GNOME Tweaks sudo apt-get update sudo apt-get install gnome-tweak-tool2.手动安装dash-to-dock插件 Dash-to-dock不支持3.32以上版本的gnome&#xff0c;git clone dash to dock的仓库 yeatsyeats-virtual-machine:~/Tools$ git clone https:/…

Axure教程—垂直方向多色图(中继器)

本文将教大家如何用AXURE制作动态垂直方向多色图 一、效果介绍 如图&#xff1a; 预览地址&#xff1a;https://9fxtte.axshare.com 下载地址&#xff1a;https://download.csdn.net/download/weixin_43516258/87822547?spm1001.2014.3001.5503 二、功能介绍 简单填写中继…

mfc100.dll丢失如何解决?修复mfc100.dll的方法分享

mfc100.dll是Microsoft Visual C 2010中的一个动态链接库文件。如果该文件丢失&#xff0c;将会导致某些应用程序无法正常运行。在本文中&#xff0c;我们将探讨关于mfc100.dll丢失的问题&#xff0c;以及如何解决它。 一.什么是mfc100.dll mfc100.dll是Microsoft Visual C 20…

头羊部落亮相第26届北京餐食会

第26届AIFE2023亚洲&#xff08;北京&#xff09;国际食品饮料暨餐饮食材展览会&#xff08;简称&#xff1a;BCFE北京餐食会) 于2023年5月23-25日在北京中国国际展览中心火热召开。顺应时代发展下的餐饮新潮流&#xff0c;北京餐食会首次聚焦预制菜市场&#xff0c;为彰显预制…

Web安全:代码执行漏洞 测试.

Web安全&#xff1a;代码执行漏洞 测试 攻击者可以通过构造恶意输入来欺骗应用程序执行恶意代码。这种漏洞通常出现在应用程序中使用动态语言(如 PHP、Python、Ruby 等)编写的代码中&#xff0c;因为这些语言允许在运行时动态执行代码。攻击者可以通过构造特定的输入来欺骗应用…

分布式事务的21种武器 - 5

在分布式系统中&#xff0c;事务的处理分布在不同组件、服务中&#xff0c;因此分布式事务的ACID保障面临着一些特殊难点。本系列文章介绍了21种分布式事务设计模式&#xff0c;并分析其实现原理和优缺点&#xff0c;在面对具体分布式事务问题时&#xff0c;可以选择合适的模式…

经典组件知识(zookeeper,kafka,ngix)

关于zookeeper的具体介绍 优化的点可以在于zookeeper吗&#xff1f; 如何安装使用&#xff1f; #include <zookeeper/zookeeper.h> 1、先配置java环境JDK&#xff0c;因为需要用java编译&#xff1b; 2、下载zk源码&#xff0c;解压&#xff1b; 3、重命名配置文件zoo_sa…

5000 字手把手实战|Kubernetes+极狐GitLab CI,获得极致 CI/CD 体验

目录 极狐GitLab CI K8s 架构解析 极狐GitLab CI 流程图 流程详解 极狐GitLab CI K8s 架构优点 开启极狐GitLab CI K8s 实战 环境准备 记录注册信息 获取极狐GitLab Runner 绑定 docker.sock 配置缓存 安装极狐GitLab Runner 集成 CI 定义文件 注意事项 配置…

Python实战基础11-函数

1 函数的创建于调用 1.1 创建一个函数 创建函数也称为定义函数&#xff0c;定义函数的格式如下&#xff1a; def functionname([parameterlist]): [comments] [functionbody] 参数说明&#xff1a; functionname&#xff1a;函数名称&#xff0c;在调用函数时使用。 paramete…

用echarts绘制的柱状图、折柱结合图,源码文末免费拿!

文章目录 Apache EchartsNPM 安装 ECharts在线定制 ECharts使用 Echarts 绘制基础柱状图绘制带背景的柱状图绘制带背景的柱状图绘制多条柱状图绘制条形柱状图绘制带标记的柱状图绘制折线图和柱状图绘制多轴折线图和柱状图源码地址 Apache Echarts 本文中的所有代码&#xff0c…

剑指offer(C++)-JZ46:把数字翻译成字符串(算法-动态规划)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 题目描述&#xff1a; 有一种将字母编码成数字的方式&#xff1a;a->1, b->2, ... , z->26。 现在给一串数字&#xf…

如何在Angular应用程序中插入自定义 CSS?这里有答案!

Kendo UI for Angular是专用于Angular开发的专业级Angular组件&#xff0c;telerik致力于提供纯粹的高性能Angular UI组件&#xff0c;无需任何jQuery依赖关系。 Kendo UI R1 2023正式版下载(Q技术交流&#xff1a;726377843&#xff09; 为什么需要在 Angular 应用程序中插入…

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

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

航空公司预订票数学建模论文

航空公司预订票数学建模论文篇1 试谈机票订票模型与求解 一、概述 1. 问题背景描述 在激烈的市场竞争中&#xff0c;航空公司为争取更多的客源而开展的一个优质服务项目是预订票业务,本模型针对预订票业务&#xff0c;建立二元规划订票方案&#xff0c;既考虑航空公司的利润最大…

form-create和form-create-designer创建自定义组件

在项目中&#xff0c;我需要使用表单设计器form-create-designer设计带有选择用户的弹窗组件&#xff0c;而设计器内置的组件不能满足需求&#xff0c;因此要创建自定义组件。form-create 支持在表单内部生成任何 vue 组件。 1.开发选择用户的组件 SelectUser.vue <!--用户…

论文《Diagnostic accuracy of CT imaging parameters in pelvic lipomatosis》案例分析

一、引言 在该篇文章的引言部分&#xff0c;作者明确阐述了本篇论文的研究目的、问题和方法&#xff0c;并指出了研究的贡献和创新点。以下是具体内容&#xff1a; 研究目的&#xff1a;本研究的目的是评估盆腔脂肪肥大的CT成像特征&#xff0c;并探讨其在诊断和管理中的应用…