单片机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

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值