How to advertise AdvertiseData with BluetoothLeAdvertiser on android using BLE 2M PHY

578 views Asked by At

I'm currently building a chat app based on Bluetooth Low Energy Advertisments. I tried to build a client following this tutorial by android, but my BluetoothLeScanner doesnt seem to retrieve any BLE 2M PHY scanResults. I was able to achieve the desired result by using the nRF Connect app, so it's probably not a hardware issue

class BluetoothAdvertiser(private var bluetoothAdapter: BluetoothAdapter) {
    private val TAG: String = BluetoothAdvertiser::class.java.simpleName
    private var advertiseUUID: ParcelUuid =
        ParcelUuid(UUID.fromString("e889813c-5d19-49e2-8bc4-d4596b4f5250"))
    private var advertiser: BluetoothLeAdvertiser = bluetoothAdapter.bluetoothLeAdvertiser
    private var maxDataLength: Int = 0
    private lateinit var currentAdvertisingData: AdvertiseData
    private lateinit var currentAdvertisingSet: AdvertisingSet

    fun init(): Boolean {
        Log.d(TAG, "init: ")
        if (!this.checkDeviceHardware()) {
            return false
        }
        this.initAdvertiser("123")
        return true
    }


    private fun checkDeviceHardware(): Boolean {
        if (!this.bluetoothAdapter.isLe2MPhySupported) {
            Log.e(TAG, "init: 2M PHY not supported")
            return false
        }
        if (!this.bluetoothAdapter.isLeExtendedAdvertisingSupported) {
            Log.e(TAG, "init: LE Extended Advertising not supported")
            return false
        }
        this.maxDataLength = this.bluetoothAdapter.leMaximumAdvertisingDataLength
        Log.d(TAG, "init: leMaximumAdvertisingDataLength: " + this.maxDataLength)
        return true
    }

    private fun initAdvertiser(message: String): Boolean {
        val setParameters: AdvertisingSetParameters = AdvertisingSetParameters.Builder()
            .setLegacyMode(false)
            .setInterval(AdvertisingSetParameters.INTERVAL_HIGH)
            .setTxPowerLevel(AdvertisingSetParameters.TX_POWER_MEDIUM)
            .setPrimaryPhy(BluetoothDevice.PHY_LE_CODED)
            .setSecondaryPhy(BluetoothDevice.PHY_LE_2M)
            .build()
        val advertiseData: AdvertiseData = AdvertiseData.Builder()
            .setIncludeDeviceName(true)
            .addServiceUuid(advertiseUUID)
            .addServiceData(advertiseUUID, message.encodeToByteArray())
            .build()
        val periodicParameters: PeriodicAdvertisingParameters = PeriodicAdvertisingParameters.Builder()
            .setInterval(800)
            .build()
        return try {
            this.advertiser.startAdvertisingSet(
                setParameters,
                advertiseData,
                advertiseData,
                periodicParameters,
                advertiseData,
                this.advertisingSetCallback
            )
            true
        } catch (e: SecurityException) {
            Log.w(TAG, "initAdvertiser: ",e)
            false
        }
    }

     fun changeAdvertisingData(message: String){
        try{
            Log.d(TAG, "changeAdvertisingData: message: $message" )
            this.currentAdvertisingSet.setAdvertisingData(AdvertiseData.Builder().addServiceData(this.advertiseUUID,message.encodeToByteArray()).build())
        } catch (e: SecurityException) {
            Log.w(TAG, "initAdvertiser: ",e)
        }
    }

    private var advertisingSetCallback: AdvertisingSetCallback = object : AdvertisingSetCallback() {
        override fun onAdvertisingSetStarted(
            advertisingSet: AdvertisingSet,
            txPower: Int,
            status: Int
        ) {
            Log.i(
                TAG, "onAdvertisingSetStarted(): txPower:  $txPower  , status: $status "
            )
            currentAdvertisingSet = advertisingSet
        }

        override fun onAdvertisingSetStopped(advertisingSet: AdvertisingSet) {
            Log.i(TAG, "onAdvertisingSetStopped():")
        }
    }
1

There are 1 answers

0
Internetus On

The code above is working as intended, but I didn't set up the BluetoothLeScanner for non-legacy advertisements, below is my now working scanner


class BluetoothScanner(private var bluetoothLeScanner: BluetoothLeScanner) : ScanCallback() {
    private val TAG: String = BluetoothScanner::class.java.simpleName
    private var scanning = false
    private var advertiseUUID: ParcelUuid =
        ParcelUuid(UUID.fromString("e889813c-5d19-49e2-8bc4-d4596b4f5250"))
    private var scanSettings: ScanSettings
    private var scanFilter: ScanFilter

    init {
        this.scanSettings = ScanSettings.Builder()
            .setLegacy(false)
            .setPhy(ScanSettings.PHY_LE_ALL_SUPPORTED)
            .build()
        this.scanFilter = ScanFilter.Builder()
            .setServiceUuid(advertiseUUID)
            .build()
    }

    fun startScan(): Boolean {
        Log.d(TAG, "startScan: ")
        if (scanning) {
            this.stopScan()
        }
        if (!this.scanning) {
            return try {
                this.bluetoothLeScanner.startScan(listOf(this.scanFilter),this.scanSettings, this)
                this.scanning = true
                true
            } catch (e: SecurityException) {
                Log.w(TAG, "startScan: ", e)
                false
            }
        }
        return false
    }