找到了 BLE 设备,但找不到我需要的东西

问题描述 投票:0回答:1

以下是处理扫描并将结果发送到注入它的 viewModel 的类:

@SuppressLint("MissingPermission")
class BLEReceiverManagerImpl
@Inject
constructor(
    private val bluetoothAdapter: BluetoothAdapter,
    private val context: Context,
) : BLEReceiverManager {

    private val TAG = BLEReceiverManagerImpl::class.java.simpleName
    private val _data = MutableSharedFlow<Resource<Beacon>>()
    override val data: SharedFlow<Resource<Beacon>> get() = _data

    /**
     * A characteristic contains a single value and optional descriptors that describe the characteristic’s value.
     */
    private val SERVICE_UUID = ""

    /**
     * A characteristic contains a single value and optional descriptors that describe the characteristic’s value.
     */
    private val CHARACTERISTICS_UUID = ""

    /**
     * This class provides methods to perform scan-related operations for Bluetooth LE devices.
     */
    private val bleScanner by lazy {
        bluetoothAdapter.bluetoothLeScanner
    }


    private val scanSettings = ScanSettings.Builder()
        .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
        .build()

    private val scanFilters = listOf(
        ScanFilter.Builder()
            .build()
    )

    /**
     * The GATT profile is a general specification for sending and receiving short pieces of data
     * known as “attributes” over a BLE link. Using this profile we can transmit data between BLE devices.
     */
    private var gatt: BluetoothGatt? = null

    private var isScanning = false
    private val coroutineScope = CoroutineScope(Dispatchers.Default)

    private val scanResults =
        mutableListOf<ScanResult>()

    private fun calculateSignalQuality(rssi: Int): String {
        val result = when {
            rssi >= -30 -> "Eccellente "
            rssi in -31 downTo -60 -> "Forte"
            rssi in -61 downTo -70 -> "Moderato"
            else -> "Debole"
        }

        return "$result (RSSI: $rssi dBm)"
    }


    private val scanCallback = object : ScanCallback() {
        override fun onScanResult(callbackType: Int, result: ScanResult) {

            val indexQuery = scanResults.indexOfFirst { it.device.address == result.device.address }
            if (indexQuery != -1) { // A scan result already exists with the same address
                scanResults[indexQuery] = result
            } else {
                with(result.device) {
                    Log.i(
                        TAG,
                        "Found BLE device! Name: ${name ?: "Unnamed"}, address (MAC): $address, Signal strength Quality: ${
                            calculateSignalQuality(
                                result.rssi
                            )
                        }"
                    )
                    val scanRecord = result.scanRecord
                    scanRecord?.serviceUuids?.let {
                        Log.i(
                            TAG,
                            "UUIDS: $it, Manifacturer Data: ${scanRecord.manufacturerSpecificData}"
                        )
                    }

                }
                scanResults.add(result)
            }

           
        }

        override fun onScanFailed(errorCode: Int) {
            super.onScanFailed(errorCode)
            Log.e(TAG, "onScanFailed: Error code ($errorCode)")
        }
    }

    private var currentConnectionAttempt = 1
    private val MAXIMUM_CONNECTION_ATTEMPTS = 5

    private val gattCallback = object : BluetoothGattCallback() {
        override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
            val deviceAddress = gatt.device.address

            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d("BluetoothGattCallback", "GATT SUCCESS")
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    Log.w("BluetoothGattCallback", "Successfully CONNECTED to $deviceAddress")

                  
                    coroutineScope.launch {
                        _data.emit(Resource.Loading(message = "Discovering services..."))
                    }
                    Log.d("BluetoothGattCallback", "Discovering services...")
                    gatt.discoverServices()
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {

                    Log.w("BluetoothGattCallback", "Successfully disconnected from $deviceAddress")

                    coroutineScope.launch {

                        val beaconExample = Beacon(
                            macAddress = "lectus",
                            uuid = null,
                            major = null,
                            minor = null,
                            idReservation = "null",
                            connectionState = ConnectionState.Disconnected
                        )
                        _data.emit(
                            Resource.Success(
                                data = beaconExample
                            )
                        )
                    }
                    gatt.close()
                }

            } else {
                Log.w(
                    "BluetoothGattCallback",
                    "Error $status encountered for $deviceAddress! Disconnecting..."
                )

                gatt.close()

                currentConnectionAttempt += 1
                coroutineScope.launch {
                    _data.emit(Resource.Loading(message = "Attempting to connect $currentConnectionAttempt/$MAXIMUM_CONNECTION_ATTEMPTS"))
                }
                if (currentConnectionAttempt <= MAXIMUM_CONNECTION_ATTEMPTS) {
                    Log.d(
                        "BluetoothGattCallback",
                        "Attempting to connect $currentConnectionAttempt/$MAXIMUM_CONNECTION_ATTEMPTS"
                    )
                    startReceiving()
                } else {
                    Log.e("BluetoothGattCallback", "Connection Exception")
                    coroutineScope.launch {
                        _data.emit(Resource.Error(BluetoothExceptions.ConnectionException))
                    }
                }
            }
        }

        override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
            with(gatt) {
                //Mostra quali servizi il BLE device può fornirci
                Log.w(
                    "BluetoothGattCallback",
                    "Discovered ${services.size} services for ${device.address}"
                )
                printGattTable()
                coroutineScope.launch {
                    _data.emit(Resource.Loading(message = "Adjusting MTU space..."))
                }
               
                gatt.requestMtu(GATT_MAX_MTU_SIZE)
            }
        }

        override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
            Log.w(
                "BluetoothGattCallback",
                "ATT MTU changed to $mtu, success: ${status == BluetoothGatt.GATT_SUCCESS}"
            )
          
            val characteristic =
                findCharacteristics(SERVICE_UUID, CHARACTERISTICS_UUID)
            if (characteristic == null) {
                Log.e(TAG, "onMtuChanged: Characteristics not found")
                coroutineScope.launch {
                    _data.emit(Resource.Error(BluetoothExceptions.CharacteristicNotFoundException))
                }
                return
            }
          
        }

        override fun onCharacteristicRead(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic,
            value: ByteArray,
            status: Int,
        ) {
            with(characteristic) {
                when (status) {
                    BluetoothGatt.GATT_SUCCESS -> {
                        Log.i(
                            "BluetoothGattCallback",
                            "Read characteristic $uuid:\n${value.toHexString()}"
                        )
                    }

                    BluetoothGatt.GATT_READ_NOT_PERMITTED -> {
                        Log.e("BluetoothGattCallback", "Read not permitted for $uuid!")
                    }

                    else -> {
                        Log.e(
                            "BluetoothGattCallback",
                            "Characteristic read failed for $uuid, error: $status"
                        )
                    }
                }
            }
        }

        override fun onCharacteristicChanged(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic,
            value: ByteArray,
        ) {
            with(characteristic) {
                Log.i(
                    "BluetoothGattCallback",
                    "Characteristic $uuid changed | value: ${value.toHexString()}"
                )
                when (uuid) {
                    UUID.fromString(CHARACTERISTICS_UUID) -> {
                        //TODO emit result here
                    }

                    else -> Unit// Non facciamo nulla
                }
            }

        }
    }

    private fun ByteArray.toHexString(): String =
        joinToString(separator = " ", prefix = "0x") { String.format("%02X", it) }

  

    private fun findCharacteristics(
        serviceUUID: String,
        characteristicUUID: String,
    ): BluetoothGattCharacteristic? {
        return gatt?.services?.find { service ->
            service.uuid.toString() == serviceUUID
        }?.characteristics?.find { characteristics ->
            characteristics.uuid.toString() == characteristicUUID
        }
    }

    override fun startReceiving() {
        coroutineScope.launch {
            _data.emit(Resource.Loading(message = "Scanning BLE devices..."))
        }
        isScanning = true
        Log.d(TAG, "startReceiving: Start scan")
        bleScanner.startScan(scanFilters, scanSettings, scanCallback)
    }

    override fun stopReceiving() {
        coroutineScope.launch {
            _data.emit(Resource.Loading(message = "Stopping scanning BLE devices..."))
        }
        isScanning = false
        Log.d(TAG, "stopReceiving: ")
        bleScanner.stopScan(scanCallback)
    }

    override fun reconnect() {
        Log.d(TAG, "reconnection...")
        gatt?.connect()
    }

    override fun disconnect() {
        Log.d(TAG, "disconnection..")
        gatt?.disconnect()
    }

    override fun closeConnection() {
        Log.d(TAG, "closeConnection..")
        bleScanner.stopScan(scanCallback)
        val characteristic =
            findCharacteristics(SERVICE_UUID, CHARACTERISTICS_UUID)
        if (characteristic != null) {
            //Ci sono delle caratteristiche che devo disattivare
            // Altrimenti drenano la batteria
            disconnectCharacteristic(characteristic)
        }
        gatt?.close()

    }

}

问题

问题是这个类做了它应该做的事情——也就是说,它找到附近的 BLE 设备,甚至可能使用名称或 MAC 连接到其中一个设备。

它找不到的是安装了模拟 iBeacon、AltBeacon 或 Eddystone Beacon 应用程序的手机。

此设备不以任何方式可见。我哪里出错了?我应该指定信标的类型吗?如果是的话在哪里?

android kotlin bluetooth-lowenergy
1个回答
0
投票
Android 本身不支持 iBeacons。您需要使用第三方库。最常用的是

https://github.com/AltBeacon/android-beacon-library

在扫描之前,请确保询问每个 Android 版本所需的所有权限。您可以在上述图书馆的网站文档中找到此信息。

© www.soinside.com 2019 - 2024. All rights reserved.