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

蓝牙透传模块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

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

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

相关文章

众多行业适用的这款Lighthouse Apex Z便携粒子计数器有什么优势

Lighthouse Apex Z粒子计数器围绕易用性和可靠性进行构建。是建立在Lighthouse洁净室行业 40 多年的基于问题的学习基础上的解决方案。 采样设置 ApexZ易于使用的样品设置&#xff0c;可以匹配当前的sop&#xff0c;减少丢失位置或采样错误参数的风险。 用户管理 为了提高效…

霍尔电流传感器的注意事项及其在直流列头柜中的应用

安科瑞虞佳豪 霍尔电流传感器​注意事项 &#xff08;1&#xff09;电流传感器必须根据被测电流的额定有效值适当选用不同的规格的产品。被测电流长时间超额&#xff0c;会损坏末极功放管&#xff08;指磁补偿式&#xff09;&#xff0c;一般情况下&#xff0c;2倍的过载电流…

word怎么转excel?一键转换并不难

在职场中&#xff0c;常常需要将 Word 文档转换为 Excel 表格&#xff0c;以便更好地管理和数据分析。本文将介绍两种 Word 转 Excel 的方法&#xff0c;以及如何在 Excel 中处理转换后的数据。 方法一&#xff1a;使用文本转换向导 使用Word的文本转换向导&#xff0c;将Word文…

【数据库复习】第六章 关系数据理论 2

若R∈BCNF 所有非主属性对每一个码都是完全函数依赖 所有的主属性对每一个不包含它的码&#xff0c;也是完全函数依赖 没有任何属性完全函数依赖于非码的任何一组属性 多值依赖 Teaching具有唯一候选码(C&#xff0c;T&#xff0c;B)&#xff0c; 即全码&#xff0c; ∈3NF …

AJ-Report是一个完全开源,拖拽编辑的可视化设计工具

简介 AJ-Report是全开源的一个BI平台&#xff0c;酷炫大屏展示&#xff0c;能随时随地掌控业务动态&#xff0c;让每个决策都有数据支撑。     多数据源支持&#xff0c;内置mysql、elasticsearch、kudu驱动&#xff0c;支持自定义数据集省去数据接口开发&#xff0c;目前已支…

《Spring Guides系列学习》guide41 - guide45

要想全面快速学习Spring的内容&#xff0c;最好的方法肯定是先去Spring官网去查阅文档&#xff0c;在Spring官网中找到了适合新手了解的官网Guides&#xff0c;一共68篇&#xff0c;打算全部过一遍&#xff0c;能尽量全面的了解Spring框架的每个特性和功能。 接着上篇看过的gu…

Vue(路由插件)

一、介绍路由 1. 路由就是一组key-value的对关系&#xff0c;多个路由需要经过路由器进行管理 2. 主要应用在SPA&#xff08;单页面应用&#xff09; 在一个页面展示功能之间的跳转 特点&#xff1a; 当跳转时候不进行页面刷新路径随着变化展示区变化但是不开启新的页签 …

快来试试!免费用上GPT-4 !!!

GPT-4 简介 GPT-4是OpenAI上个月推出的最新人工智能语言模型&#xff0c;它可以根据给定的文本或关键词生成各种类似于人类语言甚至超越人类语言的文本&#xff0c;例如文章、故事、诗歌、代码、对话等。 GPT-4拥有1750亿个参数&#xff0c;是目前最大的语言模型之一&#xf…

2023.4月及5月最新HCIP 考试战报来袭

2023年HCIP/CCNP考试战报_微思xmws的博客-CSDN博客国内企业或国企、事业单位&#xff0c;华为设备普及&#xff0c;国内未来发展趋势&#xff0c;考华为认证更被认可。如果你在外企上班或有出国打算&#xff0c;推荐考思科。https://blog.csdn.net/XMWS_IT/article/details/129…

手把手教你在昇腾平台上搭建PyTorch训练环境

PyTorch是业界流行的深度学习框架&#xff0c;用于开发深度学习训练脚本&#xff0c;默认运行在CPU/GPU上。在昇腾AI处理器上运行PyTorch业务时&#xff0c;需要搭建异构计算架构CANN&#xff08;Compute Architecture for Neural Networks&#xff09;软件开发环境&#xff0c…

【指针的深刻理解】

如何看待下面代码中的a变量? #include<stdio.h> int main() {int a 0;//同样的一个a&#xff0c;在不同的表达式中&#xff0c;名称是一样的&#xff0c;但是含义是完全不同的&#xff01;a 10;//使用的是a的空间&#xff1a;左值int b a; //使用的是a的内容&#x…

PLC/DCS系统中电磁干扰的来源及解决办法

自动化系统中所使用的各种类型DCS/PLC等自动化设备&#xff0c;有的是集中安装在控制室&#xff0c;有的是安装在生产现场和各种电机设备上&#xff0c;它们大多处在强电电路和强电设备所形成的恶劣电磁环境中。要提高这类控制系统可靠性&#xff0c;必须消除各种干扰才能有效保…

Fourier分析入门——第6章——连续函数的Fourier分析

目录 第 6 章 连续函数的Fourier分析 6.1 引言 6.2 Fourier模型 6.3 求取Fourier系数的实用方法 6.4 相关定理 6.4.1 线性定理(linearity) 6.4.2 平移定理(Shift theorem) 6.4.3 伸缩定理(Scaling theorem) 6.4.4 微分定理(Differentiation theorem) 6.4.5 积分定理…

Spring源码阅读:Spring事务传播特性

一、概述 我们平常工作中经常会听到事务的传播级别&#xff0c;但是使用中基本不会太调整这个事务传播级别&#xff0c;因为没什么业务场景需要我们这么做&#xff0c;只需要使用原有的事务传播级别即可解决95%的业务场景。但是为了那5%的业务场景&#xff0c;我们还是还要学习…

完美解决接口测试难题,数据驱动带签名混合封装框架实现

目录 前言&#xff1a; 一、框架概述 二、框架架构 三、代码实现 四、实战步骤 五、总结 前言&#xff1a; 接口自动化测试是保障软件质量的重要手段之一&#xff0c;其自动化程度越高&#xff0c;越能有效提高软件测试效率。而接口自动化测试中&#xff0c;接口测试框架…

Springboot +spring security,解决跨域问题

一.简介 这篇文章主要是解释什么是跨域&#xff0c;在Spring中如何解决跨域&#xff0c;引入Spring Security后Spring解决跨域的方式失效&#xff0c;Spring Security 如何解决跨域的问题。 二.什么是跨域 跨域的概率&#xff1a; 浏览器不能执行其他网站的脚本&#xff0c…

【新星计划-2023】TCP/IP协议讲解

相信大家在学习的过程中一定听到过TCP/IP这个协议&#xff0c;那么&#xff0c;TCP/IP协议是什么&#xff1f;为什么会有TCP/IP协议&#xff1f; 一、TCP/IP是什么&#xff1f; TCP/IP是用于计算机通信的一组协议&#xff0c;我们通常称它为TCP/IP协议族。它是70年代中期美国…

Taro 项目怎么获取元素的高度和视口高度

最近做小程序&#xff0c;用的Taro&#xff0c;需要设置空状态居中显示&#xff0c;因为空状态出现的地方不一样&#xff0c;所以需要动态设置&#xff0c;那么就需要获取元素的高度来计算 文档翻了一遍也没有找到&#xff0c;原生js获取高度得到的是null&#xff0c;百度了下…

考研数据结构--图

文章目录 图图的基本概念图的定义种类 图的抽象数据类型图的基本术语1. 端点和邻接点2. 顶点的度、入度和出度3. 完全图4. 稠密图、稀疏图5. 子图6. 路径和路径长度7. 回路或环8. 连通、连通图和连通分量9. 强连通图和强连通分量在一个图中找强连通分量的方法 10. 权和网 图的存…

自信裸辞:一晃 ,失业都3个月了.....

最近&#xff0c;找了很多软测行业的朋友聊天、吃饭 &#xff0c;了解了一些很意外的现状 。 我一直觉得他们技术非常不错&#xff0c;也走的测开/管理的路径&#xff1b;二三月份裸辞的&#xff0c;然后一直在找工作&#xff0c;现在还没找到工作 。 经过我的分析&#xff0…