Broadcast and Scan Development Guide
Note:
Currently in the beta phase.
Introduction
Broadcast and scan primarily provide methods for Bluetooth devices to start/stop broadcasting and scanning. Through broadcasting and scanning, peer Bluetooth devices can be discovered to achieve low-power communication.
Scenarios
Main scenarios include:
- Starting/stopping broadcasting
- Starting/stopping scanning
API Description
For complete Cangjie API documentation and sample code, refer to: BLE APIs.
Specific API descriptions are shown in the following table.
| API Name | Description |
|---|---|
| startBLEScan() | Initiates BLE scanning process. |
| stopBLEScan() | Stops BLE scanning process. |
| startAdvertising() | Starts BLE broadcasting. |
| disableAdvertising() | Temporarily stops BLE broadcasting. |
| enableAdvertising() | Temporarily resumes BLE broadcasting. |
| stopAdvertising() | Stops BLE broadcasting. |
on(type: BluetoothBleCallbackType) |
Subscribes to BLE broadcast state changes. |
off(type: BluetoothBleCallbackType) |
Unsubscribes from BLE broadcast state changes. |
on(type: BluetoothBleCallbackType) |
Subscribes to BLE device discovery events. |
off(type: BluetoothBleCallbackType) |
Unsubscribes from BLE device discovery events. |
Key Scenario Development Steps
Starting/Stopping Broadcasting
-
Import required BLE modules.
-
Enable Bluetooth on the device.
-
Requires SystemCapability.Communication.Bluetooth.Core capability.
-
Start broadcasting for peer devices to scan.
-
Stop broadcasting.
-
Sample code:
import kit.ConnectivityKit.* import ohos.callback_invoke.* import ohos.business_exception.* let TAG: String = 'BleAdvertisingManager' class BleAdvertisingManager { private var advHandle: UInt32 = 0xFF // default invalid value // 1 Subscribe to broadcast state changes public func onAdvertisingStateChange() { try { on(BluetoothBleCallbackType.AdvertisingStateChange, StateChangeInfoCb()) } catch (e: BusinessException) { Hilog.error(0x0000, 'Tag', 'errCode: ${e.code}, errMessage: ' + e.message) } } // 2 First-time broadcast startup public func startAdvertising() { // 2.1 Configure broadcast parameters let setting: AdvertiseSetting = AdvertiseSetting(interval:160, txPower:0, connectable:true) // 2.2 Construct broadcast data let manufactureValueBuffer: Array<UInt8> = [1, 2, 3, 4] let serviceValueBuffer: Array<UInt8> = [5, 6, 7, 8] let manufactureDataUnit: ManufactureData = ManufactureData(4567, manufactureValueBuffer) let serviceDataUnit: ServiceData = ServiceData("00001888-0000-1000-8000-00805f9b34fb", serviceValueBuffer) let advData: AdvertiseData = AdvertiseData(["00001888-0000-1000-8000-00805f9b34fb"], [manufactureDataUnit], [serviceDataUnit], includeDeviceName: false // Optional parameter indicating whether to include device name. Note: Total broadcast packet length cannot exceed 31 bytes when including device name. ) let advResponse: AdvertiseData = AdvertiseData(["00001888-0000-1000-8000-00805f9b34fb"], [manufactureDataUnit], [serviceDataUnit]) // 2.3 Construct complete AdvertisingParams let advertisingParams: AdvertisingParams = AdvertisingParams( setting, advData, advertisingResponse: advResponse, duration: 0 // Optional parameter. If >0, broadcasting will temporarily stop after specified duration and can be restarted ) // 2.4 First-time broadcast startup and obtaining broadcast ID try { this.onAdvertisingStateChange() this.advHandle = startAdvertising(advertisingParams) } catch (e: BusinessException) { Hilog.error(0x0000, 'Tag', 'errCode: ${e.code}, errMessage: ' + e.message) } } // 3 Completely stop broadcasting and release resources public func stopAdvertising() { try { stopAdvertising(this.advHandle) off(BluetoothBleCallbackType.AdvertisingStateChange) } catch (e: BusinessException) { Hilog.error(0x0000, 'Tag', 'errCode: ${e.code}, errMessage: ' + e.message) } } } class StateChangeInfoCb <: Callback1Argument<AdvertisingStateChangeInfo> { public func invoke(err: ?BusinessException, info: AdvertisingStateChangeInfo): Unit { Hilog.info(0xFF00, 'Tag', "advertisingId: ${info.advertisingId}, AdvertisingState: ${info.state}") } } let bleAdvertisingManager = BleAdvertisingManager() -
For error codes, refer to Bluetooth Service Subsystem Error Codes.
Starting/Stopping Scanning
-
Import required BLE modules.
-
Enable Bluetooth on the device.
-
Requires SystemCapability.Communication.Bluetooth.Core capability.
-
Peer device starts broadcasting.
-
Local device starts scanning and obtains scan results.
-
Stop scanning.
-
Sample code:
import kit.ConnectivityKit.* import ohos.callback_invoke.* import ohos.business_exception.* import std.collection.{HashMap, ArrayList} import std.math.numeric.BigInt const BLE_ADV_TYPE_FLAG = 0x01 const BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_INCOMPLETE = 0x02 const BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_COMPLETE = 0x03 const BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_INCOMPLETE = 0x04 const BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_COMPLETE = 0x05 const BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_INCOMPLETE = 0x06 const BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_COMPLETE = 0x07 const BLE_ADV_TYPE_LOCAL_NAME_SHORT = 0x08 const BLE_ADV_TYPE_LOCAL_NAME_COMPLETE = 0x09 const BLE_ADV_TYPE_TX_POWER_LEVEL = 0x0A const BLE_ADV_TYPE_16_BIT_SERVICE_SOLICITATION_UUIDS = 0x14 const BLE_ADV_TYPE_128_BIT_SERVICE_SOLICITATION_UUIDS = 0x15 const BLE_ADV_TYPE_32_BIT_SERVICE_SOLICITATION_UUIDS = 0x1F const BLE_ADV_TYPE_16_BIT_SERVICE_DATA = 0x16 const BLE_ADV_TYPE_32_BIT_SERVICE_DATA = 0x20 const BLE_ADV_TYPE_128_BIT_SERVICE_DATA = 0x21 const BLE_ADV_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF const BLUETOOTH_UUID_16_BIT_LENGTH: UInt8 = 2 const BLUETOOTH_UUID_32_BIT_LENGTH: UInt8 = 4 const BLUETOOTH_UUID_128_BIT_LENGTH: UInt8 = 16 const BLUETOOTH_MANUFACTURE_ID_LENGTH: UInt8 = 2 class BleScanManager { // 1 Subscribe to scan results public func onScanResult() { on(BluetoothBleCallbackType.BleDeviceFind, ScanResultCb()) } // 2 Start scanning public func startScan() { // 2.1 Construct scan filter to match expected broadcast packets let manufactureId: UInt16 = 4567 let manufactureData: Array<UInt8> = [1, 2, 3, 4] let manufactureDataMask: Array<UInt8> = [0xFF, 0xFF, 0xFF, 0xFF] var scanFilter: ScanFilter = ScanFilter() // Define filter based on actual requirements scanFilter.manufactureId = manufactureId scanFilter.manufactureData = manufactureData scanFilter.manufactureDataMask = manufactureDataMask // 2.2 Configure scan parameters let scanOptions: ScanOptions = ScanOptions( interval: 0, dutyMode: ScanDuty.ScanModeLowPower, matchMode: MatchMode.MatchModeAggressive, phyType: PhyType.PhyLe1M ) try { this.onScanResult() // Subscribe to scan results startBLEScan([scanFilter], options: scanOptions) Hilog.info(0xFF00, 'Tag', 'startBleScan success') } catch (e: BusinessException) { Hilog.error(0x0000, 'Tag', 'errCode: ${e.code}, errMessage: ' + e.message) } } // 3 Stop scanning public func stopScan() { try { off(BluetoothBleCallbackType.BleDeviceFind) // Unsubscribe from scan results stopBLEScan() Hilog.info(0xFF00, 'Tag', 'stopBleScan success') } catch (e: BusinessException) { Hilog.error(0x0000, 'Tag', 'errCode: ${e.code}, errMessage: ' + e.message) } } } private func parseScanResult(data: Array<UInt8>) { if (data.size == 0) { Hilog.warn(0xFF00, 'Tag', 'nothing, adv data length is 0') return } Hilog.info(0xFF00, 'Tag', 'data: ' + String.fromUtf8(data)) var advFlags: UInt8 = 0 var txPowerLevel: UInt8 = 0 var localName: String = "" var serviceUuids: ArrayList<String> = ArrayList<String>() var serviceSolicitationUuids: ArrayList<String> = ArrayList<String>() var serviceDatas: HashMap<String, Array<UInt8>> = HashMap() var manufactureSpecificDatas: HashMap<UInt16, Array<UInt8>> = HashMap() var curPos = 0 while (curPos < data.size) { let length = data[curPos] curPos++ if (length == 0) { break } let dataLength = length - 1 let dataType = data[curPos] curPos++ match (dataType) { case BLE_ADV_TYPE_FLAG => advFlags = data[curPos] case BLE_ADV_TYPE_LOCAL_NAME_SHORT => localName = data[curPos..curPos + Int64(dataLength)].toString() case BLE_ADV_TYPE_LOCAL_NAME_COMPLETE => localName = data[curPos..curPos + Int64(dataLength)].toString() case BLE_ADV_TYPE_TX_POWER_LEVEL => txPowerLevel = data[curPos] case BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_INCOMPLETE => parseServiceUuid(BLUETOOTH_UUID_16_BIT_LENGTH, curPos, dataLength, data, serviceUuids) case BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_COMPLETE => parseServiceUuid(BLUETOOTH_UUID_16_BIT_LENGTH, curPos, dataLength, data, serviceUuids) case BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_INCOMPLETE => parseServiceUuid(BLUETOOTH_UUID_32_BIT_LENGTH, curPos, dataLength, data, serviceUuids) case BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_COMPLETE => parseServiceUuid(BLUETOOTH_UUID_32_BIT_LENGTH, curPos, dataLength, data, serviceUuids) case BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_INCOMPLETE => parseServiceUuid(BLUETOOTH_UUID_128_BIT_LENGTH, curPos, dataLength, data, serviceUuids) case BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_COMPLETE => parseServiceUuid(BLUETOOTH_UUID_128_BIT_LENGTH, curPos, dataLength, data, serviceUuids) case BLE_ADV_TYPE_16_BIT_SERVICE_SOLICITATION_UUIDS => parseServiceSolicitationUuid( BLUETOOTH_UUID_16_BIT_LENGTH, curPos, dataLength, data, serviceSolicitationUuids) case BLE_ADV_TYPE_32_BIT_SERVICE_SOLICITATION_UUIDS => parseServiceSolicitationUuid( BLUETOOTH_UUID_32_BIT_LENGTH, curPos, dataLength, data, serviceSolicitationUuids) case BLE_ADV_TYPE_128_BIT_SERVICE_SOLICITATION_UUIDS => parseServiceSolicitationUuid( BLUETOOTH_UUID_128_BIT_LENGTH, curPos, dataLength, data, serviceSolicitationUuids) case BLE_ADV_TYPE_16_BIT_SERVICE_DATA => parseServiceData(BLUETOOTH_UUID_16_BIT_LENGTH, curPos, dataLength, data, serviceDatas) case BLE_ADV_TYPE_32_BIT_SERVICE_DATA => parseServiceData(BLUETOOTH_UUID_32_BIT_LENGTH, curPos, dataLength, data, serviceDatas) case BLE_ADV_TYPE_128_BIT_SERVICE_DATA => parseServiceData(BLUETOOTH_UUID_128_BIT_LENGTH, curPos, dataLength, data, serviceDatas) case BLE_ADV_TYPE_MANUFACTURER_SPECIFIC_DATA => parseManufactureData(curPos, dataLength, data, manufactureSpecificDatas) case _ => () } curPos += Int64(dataLength) } } private func parseServiceUuid(uuidLength: UInt8, curPos: Int64, dataLength: UInt8, data: Array<UInt8>, serviceUuids: ArrayList<String>) { var dataLength_ = dataLength var curPos_ = curPos while (dataLength > 0) { let tmpData: Array<UInt8> = data.slice(curPos, Int64(uuidLength)) serviceUuids.add(getUuidFromArray(Int64(uuidLength), tmpData)) dataLength_ -= uuidLength curPos_ += Int64(uuidLength) } } private func parseServiceSolicitationUuid(uuidLength: UInt8, curPos: Int64, dataLength: UInt8, data: Array<UInt8>, serviceSolicitationUuids: ArrayList<String>) { var dataLength_ = dataLength var curPos_ = curPos while (dataLength > 0) { let tmpData: Array<UInt8> = data.slice(curPos, Int64(uuidLength)) serviceSolicitationUuids.add(getUuidFromArray(Int64(uuidLength), tmpData)) dataLength_ -= uuidLength curPos_ += Int64(uuidLength) } } private func getUuidFromArray(uuidLength: Int64, uuidData: Array<UInt8>): String { var uuid = "" var temp: String = "" for (i in uuidLength - 1..-1 : -1) { temp = temp + BigInt .parse(uuidData[i].toString()) .toString(radix: 16) .padStart(2, padding: "0") } match (uuidLength) { case BLUETOOTH_UUID_16_BIT_LENGTH => uuid = "0000${temp}-0000-1000-8000-00805F9B34FB" case BLUETOOTH_UUID_32_BIT_LENGTH => uuid = "${temp}-0000-1000-8000-00805F9B34FB" case BLUETOOTH_UUID_128_BIT_LENGTH => uuid = "${temp[0..8]}-${temp[8..12]}-${temp[12..16]}-${temp[16..20]}-${temp[20..32]}" case _ => () } return uuid } private func parseServiceData(uuidLength: UInt8, curPos: Int64, dataLength: UInt8, data: Array<UInt8>, serviceDatas: HashMap<String, Array<UInt8>>) { let tmpUuid: Array<UInt8> = data.slice(curPos, Int64(uuidLength)) let tmpValue: Array<UInt8> = data.slice(curPos + Int64(uuidLength), Int64(dataLength - uuidLength)) serviceDatas[tmpUuid.toString()] = tmpValue } private func parseManufactureData(curPos: Int64, dataLength: UInt8, data: Array<UInt8>, manufactureSpecificDatas: HashMap<UInt16, Array<UInt8>>) { let manufactureId: UInt16 = UInt16(data[curPos + 1]) << 8 + UInt16(data[curPos]) let tmpValue: Array<UInt8> = data.slice(curPos + Int64(BLUETOOTH_MANUFACTURE_ID_LENGTH), Int64(dataLength - BLUETOOTH_MANUFACTURE_ID_LENGTH)) manufactureSpecificDatas[manufactureId] = tmp