本文讲述的是在OpenHarmony轻量设备(L0设备)上进行BLE相关应用程序的开发。应用所在设备作为Gatt Client与其他Gatt Server设备进行连接和通信。

本文重点讲述BLE设备间的通信,当二个设备BLE连接成功后,就可以开始获取server端的服务,通过获取到的特征值来进行设备间的通信。

BLE设备通信的接口

在进行设备通信之前,需要先创建GattClientDevice并成功建立二台设备间的连接。

// 以下接口都为GattClientDevice类的方法

// client端获取蓝牙低功耗设备的所有服务,即服务发现。
getServices(callback: AsyncCallback<number, Array<GattService>>): void

// client端读取蓝牙低功耗设备特定服务的特征值。
readCharacteristicValue(characteristic: BLECharacteristic, callback: AsyncCallback<number, BLECharacteristic>): void
// client端读取蓝牙低功耗设备特定的特征包含的描述符。
readDescriptorValue(descriptor: BLEDescriptor, callback: AsyncCallback<number, BLEDescriptor>): void
// client端向低功耗蓝牙设备写入特定的特征值。
writeCharacteristicValue(characteristic: BLECharacteristic, writeType: GattWriteType, callback: AsyncCallback<number>): void
// client端向低功耗蓝牙设备特定的描述符写入二进制数据
writeDescriptorValue(descriptor: BLEDescriptor, callback: AsyncCallback<number>): void

// 向服务端发送设置通知此特征值请求。
setCharacteristicChangeNotification(characteristic: BLECharacteristic, enable: boolean, callback: AsyncCallback<number>): void
// 向服务端发送设置通知此特征值请求,需要对端设备的回复。
setCharacteristicChangeIndication(characteristic: BLECharacteristic, enable: boolean, callback: AsyncCallback<number>): void

// 订阅蓝牙低功耗设备的特征值变化事件。需要先调用setCharacteristicChangeNotification接口或
// setCharacteristicChangeIndication接口才能接收server端的通知。
on(type: 'BLECharacteristicChange', callback: Callback<number, BLECharacteristic>): void
// 取消订阅蓝牙低功耗设备的特征值变化事件。
off(type: 'BLECharacteristicChange', callback?: Callback<number, BLECharacteristic>): void

详细的BLE设备通信的接口说明请参考  蓝牙BLE模块

BLE设备通信接口的使用

以下为BLE设备间通信的示例代码,其中省略了连接的操作,BLE的连接可以参考 OpenHarmony轻量设备应用BLE接口的使用(二):设备连接

当BLE设备连接成功后,调用onDeviceConnect函数开始进行后续的业务处理。这里主要完成BLE设备通信通道的配置,以及实现消息收发的接口。

点击按钮通过写特征值向对端设备发送消息,通过订阅notify特征值来接收对端设备发送的消息。

1、在订阅BLE设备连接状态变化事件callback函数中,当设备连接成功时,调用onDeviceConnect函数进行业务层的处理。

通过GattClientDevice的getServices接口获取gatt server端所有的服务、特征值。

2、在获取gatt server端所有服务的回调函数obtainCameraServices中,根据业务指定的UUID查找对应的服务和特征值,并进行设置为设备间的消息通信做好准备。在setAppCharacteristic函数中,记录write特征值用于本端设备消息的发送,订阅notify特征值变化事件用于对端设备消息的接收。

3、在订阅notify特征值变化事件的回调函数bleRecvNotify中,就可以对接收的特征值进行业务上的处理了。

4、当用户点击"发送消息"按钮,在事件响应函数onSendMsgClick中,通过调用GattClientDevice类的writeCharacteristicValue接口发送一段固定的消息给对端设备。

这里需要注意的是,write特征值的属性可能是write或者writeNoResponse,那么在调用writeCharacteristicValue接口时,对应的第二个参数writeType就要选择1(WRITE)或者2(WRITE_NO_RESPONSE),否则写特征值可能会失败。

5、当用户退出本页面时,取消订阅BLE设备notify特征值变化事件,然后断开BLE连接并关闭GATT client。

// connect.js
import ble from '@ohos.bluetooth.ble';

const APP_SERVICE_UUID = 'ff00'; // 128位UUID '0000FF00-0000-1000-8000-00805F9B34FB';
const APP_NOTIFY_UUID = 'ff07';  // 128位UUID '0000FFF7-0000-1000-8000-00805F9B34FB';
const APP_WRITE_UUID = 'ff08';   // 128位UUID '0000FFF8-0000-1000-8000-00805F9B34FB';

let bleData = {
    gattClient: null,
    bleConnected: false, // 当前BLE设备是否为连接状态
    gattReady: false,    // 当前GATT通道是否正常

    // recvChar接收报文,保存notify特征值的参数;sendChar发送报文,保存write特征值的参数
    recvChar: { valid: false, srvUuid: '', charaUuid: '', charaHandle: 0 },
    // 写特征值的属性有write和writeNoResponse,如果为writeNoResponse则不支持写入后应答
    sendChar: { valid: false, srvUuid: '', charaUuid: '', charaHandle: 0, isSupportResp: false },
};

// 打印接收到的特征值
function printCharacteristic(char)
{
    console.debug('service UUID: ' + char.serviceUuid);
    console.debug('characteristic UUID: ' + char.characteristicUuid);
    console.debug('characteristic handle: ' + char.characteristicValueHandle);
    console.debug('characteristic value:');
    // char.characteristicValue中保存的就是对端设备发送的消息
    let value = new Uint8Array(char.characteristicValue);
    let line = '';
    let hex = '';
    for (let i = 0; i < value.length; i++) {
        if ((i > 0) && (i % 16) === 0) {
            console.debug(line);
            line = '';
        }
        hex = value[i].toString(16);
        if (hex.length === 1) {
            hex = '0' + hex;
        }
        line += hex + ' ';
    }
    if (line != '') {
        console.debug(line);
    }
}

function manageRecvMsg(msgArray)
{
    // 根据业务需求处理接收到的消息
}

// 处理对端BLE设备通过notify characteristic发送的消息
function bleRecvNotify(code, notifyChar)
{
    if (code != 0) {
        console.error(`bleRecvNotify code is ${code}`);
        return;
    }
    printCharacteristic(notifyChar);
    if (!bleData.recvChar.valid) {
        console.debug('notify characteristic is invalid, ignore recv message!!');
        return;
    }
    
    if ((notifyChar.characteristicUuid.toLowerCase() != APP_NOTIFY_UUID.toLowerCase()) ||
        (notifyChar.serviceUuid.toLowerCase() != APP_SERVICE_UUID.toLowerCase()) ||
        (notifyChar.characteristicValueHandle != bleData.recvChar.charaHandle)) {
        console.warn('ignore recv notify, it not my notify characteristic');
        console.warn(`characteristic changed: srv ${notifyChar.serviceUuid} chara ${notifyChar.characteristicUuid} handle ${notifyChar.characteristicValueHandle}`);
        console.warn(`except notify: srv ${bleData.recvChar.srvUuid} chara ${bleData.recvChar.charaUuid} handle ${bleData.recvChar.charaHandle}`);
        return;
    }
    
    let value = new Uint8Array(notifyChar.characteristicValue);
    manageRecvMsg(value);    
}

function setAppCharacteristic(charaArray)
{
    for (let i = 0; i < charaArray.length; i++) {
        // 根据UUID获取notify characteristic
        if ((charaArray[i].characteristicUuid.toLowerCase() === APP_NOTIFY_UUID.toLowerCase()) &&
            charaArray[i].properties.notify) {
            bleData.recvChar.valid = true;
            bleData.recvChar.srvUuid = charaArray[i].serviceUuid;
            bleData.recvChar.charaUuid = charaArray[i].characteristicUuid;
            bleData.recvChar.charaHandle = charaArray[i].characteristicValueHandle;

            let char = {
                serviceUuid: charaArray[i].serviceUuid,
                characteristicUuid: charaArray[i].characteristicUuid,
                characteristicValueHandle: charaArray[i].characteristicValueHandle,
            };
            // 设置服务端启用notify通知
            bleData.gattClient.setCharacteristicChangeNotification(char, true, (err) => {
                if (err) {
                    console.error(`set notify characteristic callback failed, err=${err}`);
                    return;
                }
                console.debug('set notify characteristic callback successfully');
                
                // 订阅notify characteristic变化事件
                bleData.gattClient.on('BLECharacteristicChange', bleRecvNotify);
            });
        }  else if ((charaArray[i].characteristicUuid.toLowerCase() === APP_WRITE_UUID.toLowerCase()) &&
            (charaArray[i].properties.write || charaArray[i].properties.writeNoResponse)) {
            bleData.sendChar.valid = true;
            bleData.sendChar.srvUuid = charaArray[i].serviceUuid;
            bleData.sendChar.charaUuid = charaArray[i].characteristicUuid;
            bleData.sendChar.charaHandle = charaArray[i].characteristicValueHandle;
            // 正常情况下write和writeNoResponse是有一个为true,这里只判断一个write即可
            bleData.sendChar.isSupportResp = charaArray[i].properties.write;
        }
    }
    
    if (!bleData.recvChar.valid || !bleData.sendChar.valid) {
        console.error('no write or notify characteristic!!');
        return -1;
    }
    
    bleData.gattReady = true;
    return 0;
}
    
function obtainCameraServices(code, gattServices)
{
    if (code != 0) {
        console.error(`gattclient getServices callback code is ${code}`);
        return;
    }

    console.debug('gattclient services: ' + JSON.stringify(gattServices));
    let chars;
    // 根据服务UUID查找GATT服务
    for (let i = 0; i < gattServices.length; i++) {
        // UUID为字符串类型,比较UUID时注意统一转换大小写
        if (gattServices[i].serviceUuid.toLowerCase() === APP_SERVICE_UUID.toLowerCase()) {
            console.debug('find app service!!');
            chars = gattServices[i].characteristics;
            break;
        }
    }
    if (chars == undefined) {
        console.warn(`can not find app service by UUID ${APP_SERVICE_UUID}`);
        return;
    }

    let ret = setAppCharacteristic(chars);
    if (ret != 0) {
        console.error(`set characteristic failed, ret=${ret}`);
    }
}
    
function startGattClientService()
{
    // 开始发现GATT服务、特征
    bleData.gattClient.getServices(obtainCameraServices);
}


function writeCharacteristicCallback(code) {
    if (code != 0) {
        console.error(`writeCharacteristicCallback: write characteristic failed ${code} in callback`);
    } else {
        console.debug('writeCharacteristicCallback: write characteristic success');
    }
}

function sendMsgWithResp(dataArray)
{
    if (!bleData.gattReady) {
        console.error('gatt client is not ready');
        return -1;
    }
    
    let writeChar = {
        serviceUuid: APP_SERVICE_UUID,
        characteristicUuid: APP_WRITE_UUID,
        characteristicValueHandle: bleData.sendChar.charaHandle,
        characteristicValue: dataArray.buffer,
        descriptors: []
    };
    // 根据写特征值的属性决定调用写特征值接口时,是否需要对端设备应答
    if (bleData.sendChar.isSupportResp) {        
        bleData.gattClient.writeCharacteristicValue(writeChar, 1, writeCharacteristicCallback);
    } else {
        bleData.gattClient.writeCharacteristicValue(writeChar, 2, writeCharacteristicCallback);
    }
    return 0;
}
        
// BLE连接成功后,开始进行设备连接成功后的业务处理
function onDeviceConnect()
{
    console.debug('onDeviceConnect calling');        
    startGattClientService();
}

// BLE连接断开,进行业务层的相应处理
function onDeviceDisconnect()
{
    console.debug('onDeviceDisconnect calling');
    bleData.gattReady = false;
}

export default {
    data: {
        remoteMac: '00:ac:13:fe:65:41', // 保存扫描阶段选择要连接的对端设备mac 
    },
    
    // 点击按钮发送一段数据给对端设备
    onSendMsgClick() {
        // data为要发送给对端设备的数据
        let data = new Uint8Array([0,1,2,3,4,5,6,7,8,9]);
        let ret = sendMsgWithResp(data);
        if (ret != 0) {
            console.error(`send msg with response failed, ret=${ret}`);
        }
    },
    
    onDestroy() {
        if (bleData.gattClient == null) {
            return;
        }        
        bleData.gattClient.off('BLECharacteristicChange');
        bleData.recvChar.valid = false;
        bleData.sendChar.valid = false;
        
        if (bleData.bleConnected) {
            bleData.gattClient.disconnect();
        }
        bleData.gattClient.off('BLEConnectionStateChange');
        bleData.gattClient.close();
        bleData.gattClient = null;
    },
}

 

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐