Bluetooth (BLE) Hinweis zum Empfang der Daten. Was ist das Problem? Arbeiten Sie unter iOS, aber nicht unter Android?Android

Forum für diejenigen, die für Android programmieren
Anonymous
 Bluetooth (BLE) Hinweis zum Empfang der Daten. Was ist das Problem? Arbeiten Sie unter iOS, aber nicht unter Android?

Post by Anonymous »

Ich versuche, einige Daten an ein BLE-Gerät zu senden, aber aus irgendeinem Grund heißt es, dass die Daten erfolgreich gesendet wurden, aber tatsächlich macht das BLE nichts, da ich dasselbe in iOS implementiert habe und es ordnungsgemäß funktioniert.
Ich stelle den Code von iOS und Android zur Verfügung. Bitte helfen Sie mir.
Android-Code

Code: Select all

private val UART_SERVICE_UUID = UUID.fromString("00000077-0000-1000-8000-00805f9b34fb")
private val UART_RX_TX_CHARACTERISTIC_UUID = UUID.fromString("0000ff01-0000-1000-8000-00805f9b34fb")
private val CLIENT_CONFIG_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")

class BluetoothManager(private val context: Context) {

private val TAG = "BluetoothManager"

private val systemBluetoothManager =
context.getSystemService(Context.BLUETOOTH_SERVICE) as android.bluetooth.BluetoothManager

private val bluetoothAdapter: BluetoothAdapter? = systemBluetoothManager.adapter

// Scanning
private val _discoveredDevices = MutableStateFlow(emptyList())
val discoveredDevices: StateFlow = _discoveredDevices.asStateFlow()

// Connection & Data
private val _connectionStatus = MutableStateFlow("Disconnected")
val connectionStatus: StateFlow = _connectionStatus.asStateFlow()

private val _receivedData = MutableStateFlow("")
val receivedData: StateFlow = _receivedData.asStateFlow()

private var gatt: BluetoothGatt? = null
private var rxTxCharacteristic: BluetoothGattCharacteristic? = null
private var isReadyToSend = false  // True only after notifications enabled

private val leScanCallback: ScanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
val device = result.device
val deviceAddress = device.address
val rssi = result.rssi

val deviceName = if (ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) ==
PackageManager.PERMISSION_GRANTED) {
device.name ?: "Unknown"
} else {
"Unknown (Permission Required)"
}

if (deviceName.contains("Unknown", ignoreCase = true)) return

val currentList = _discoveredDevices.value.toMutableList()
val newDevice = BLEDevice(device, deviceName, deviceAddress, rssi)

val existingIndex = currentList.indexOfFirst { it.address == deviceAddress }
if (existingIndex != -1) {
currentList[existingIndex] = newDevice.copy(rssi = rssi)
} else {
currentList.add(newDevice)
}
_discoveredDevices.value = currentList
}

override fun onBatchScanResults(results: MutableList?) {
results?.forEach { onScanResult(0, it) }
}

override fun onScanFailed(errorCode: Int) {
Log.e(TAG, "Scan failed: $errorCode")
}
}

private val gattCallback = object : BluetoothGattCallback() {

@SuppressLint("MissingPermission")
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
when (newState) {
BluetoothProfile.STATE_CONNECTED -> {
Log.d(TAG, "GATT Connected")
_connectionStatus.value = "Connected"
gatt.discoverServices()
}
BluetoothProfile.STATE_DISCONNECTED ->  {
Log.d(TAG, "GATT Disconnected")
_connectionStatus.value = "Disconnected"
cleanupGatt()
}
}
if (status != BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "Connection error: $status")
_connectionStatus.value = "Failed"
cleanupGatt()
}
}

@SuppressLint("MissingPermission")
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
if (status != BluetoothGatt.GATT_SUCCESS) {
_connectionStatus.value = "Discovery Failed"
return
}

val service = gatt.getService(UART_SERVICE_UUID) ?: run {
Log.e(TAG, "UART Service not found")
_connectionStatus.value = "Service Not Found"
return
}

rxTxCharacteristic = service.getCharacteristic(UART_RX_TX_CHARACTERISTIC_UUID) ?: run {
Log.e(TAG, "UART Characteristic not found")
_connectionStatus.value = "Char Not Found"
return
}

Log.d(TAG, "UART characteristic found")

// Use Write With Response — matches iOS .withResponse
rxTxCharacteristic!!.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT

isReadyToSend = false

// Enable notifications
if (rxTxCharacteristic!!.properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY >  0) {
gatt.setCharacteristicNotification(rxTxCharacteristic, true)
val descriptor = rxTxCharacteristic!!.getDescriptor(CLIENT_CONFIG_DESCRIPTOR_UUID)
if (descriptor != null) {
descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
gatt.writeDescriptor(descriptor)
} else {
isReadyToSend = true
_connectionStatus.value = "Ready"
}
} else {
isReadyToSend = true
_connectionStatus.value = "Ready"
}
}

override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "Notifications enabled")
isReadyToSend = true
_connectionStatus.value = "Ready"
} else {
Log.e(TAG, "Descriptor write failed: $status")
isReadyToSend = true
_connectionStatus.value = "Ready (Notify Failed)"
}
}

@Deprecated("Deprecated in Java")
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
val data = characteristic.value?.let { String(it, Charsets.UTF_8) } ?: ""
if (data.isNotEmpty()) {
Log.d(TAG, "Received: $data")
_receivedData.value += "$data\n"
}
}

override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "Write SUCCESS (With Response)")
} else {
Log.e(TAG, "Write FAILED: $status")
}
}
}

// Scanning
@SuppressLint("MissingPermission")
fun startBleScan() {
if (bluetoothAdapter?.isEnabled != true) return
if (ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) return

bluetoothAdapter.bluetoothLeScanner?.startScan(null, ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(), leScanCallback)
Log.d(TAG, "Scan started")
}

@SuppressLint("MissingPermission")
fun stopBleScan() {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) return
bluetoothAdapter?.bluetoothLeScanner?.stopScan(leScanCallback)
}

fun clearDiscoveredDevices() {
_discoveredDevices.value = emptyList()
}

// Connection
@SuppressLint("MissingPermission")
fun connect(device: BluetoothDevice) {
if (bluetoothAdapter?.isEnabled != true) {
_connectionStatus.value = "Bluetooth Off"
return
}
if (ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
_connectionStatus.value = "Permission Missing"
return
}

_connectionStatus.value = "Connecting..."
cleanupGatt()
gatt = device.connectGatt(context, false, gattCallback)
}
@SuppressLint("MissingPermission")
fun writeData(command: String) {
if (!isReadyToSend) {
Log.w(TAG, "Not ready to send yet")
return
}

val char = rxTxCharacteristic ?: return

val fullCommand = "$command\r\n"
val data = fullCommand.toByteArray(Charsets.UTF_8)
val hex = data.joinToString("  ") { "%02X".format(it) }
Log.d(TAG, "Sending Data → HEX: [$hex]  Text: \"$fullCommand\"")

char.value = data
val success = gatt?.writeCharacteristic(char) ?: false
if (!success) Log.e(TAG, "Failed to queue write")
}

@SuppressLint("MissingPermission")
fun disconnect() {
gatt?.disconnect()
}
@SuppressLint("MissingPermission")
private fun cleanupGatt() {
gatt?.close()
gatt = null
rxTxCharacteristic = null
isReadyToSend = false
_receivedData.value = ""
}

fun clearReceivedData() {
_receivedData.value = ""
}
}
Der Code ist UUID ist korrekt, weil ich eine Verbindung zum BLE herstellen kann, also muss es noch etwas anderes geben?

Code: Select all

Button(
onClick = {
coroutineScope.launch {
bluetoothManager.writeData("PLAY")
delay(4000)
bluetoothManager.writeData("Rd1")
delay(4000)
bluetoothManager.writeData("Rd0")
}
},
enabled = isReady
) {
Text("Send PLAY → Rd1 → Rd0 Sequence")
}
iOS-Code

Code: Select all

import CoreBluetooth
import Foundation

final class BlueToothManager: NSObject {

static let shared = BlueToothManager()
var scanningState: Bool = false
var centralManager: CBCentralManager!
var discoveredDevices: [UUID: BLEDevice] = [:]
var deviceConnectionState: ConnectionState = .disconnected
weak var bluetoothDelegate: BlueToothManagerDelegate?
private var connectionTimeoutTimer: Timer?
private override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: .main)
}
}
/// It initiallly check for the state of the bluetooth i.e if its on , off etc.
extension BlueToothManager: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .unknown:
print("Bluetooth: Unknown state")
case .resetting:
print("Bluetooth: Resetting")
case .unsupported:
print("Bluetooth: Device does NOT support Bluetooth")
case .unauthorized:
print("Bluetooth: Permission DENIED")
case .poweredOff:
print("Bluetooth: OFF")
case .poweredOn:
print("Bluetooth: ON")
self.startScanning()
@unknown default:
break
}
}
}

/// Functions for scanning bluetooth devices
extension BlueToothManager {
func startScanning(){
guard centralManager.state == .poweredOn else {
//TODO: need to show pop-up for turning on the bluetooth
return
}
discoveredDevices = [:]
scanningState = true
bluetoothDelegate?.onScanningStateChange(scanningState)
centralManager
.scanForPeripherals(
withServices: nil,
options: [CBCentralManagerScanOptionAllowDuplicatesKey: false]
)
DispatchQueue.main.asyncAfter(deadline: .now() + 8) {
self.stopScan()
}
}
func stopScan(){
scanningState = false
bluetoothDelegate?.onScanningStateChange(scanningState)
centralManager.stopScan()
}
}

/// manager for showing the devices which we have recived.
extension BlueToothManager {
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String : Any],
rssi RSSI: NSNumber) {
guard let name = peripheral.name else { return }
let deviceUuid = peripheral.identifier
print("Device: \(name), deviceId:  \(deviceUuid)")

if let existing = discoveredDevices[deviceUuid] {
// Update RSSI only
existing.rssi = RSSI.intValue
discoveredDevices[deviceUuid] = existing
}else{
let newDevice = BLEDevice(
peripheral: peripheral,
name: name,
deviceUuid: deviceUuid,
rssi: RSSI.intValue
)
discoveredDevices[deviceUuid] = newDevice
}
let devicesArray = Array(discoveredDevices.values)
.sorted { $0.rssi > $1.rssi }
bluetoothDelegate?.onDeviceChange(devicesArray)
}
}

/// Functions for connecting device and diss-connectiong device
extension BlueToothManager: CBPeripheralDelegate {
func connect(to device: BLEDevice) {
guard deviceConnectionState == .disconnected || deviceConnectionState == .failed || deviceConnectionState == .unKnownError else {
return
}
device.peripheral.delegate = self
deviceConnectionState = .connecting
bluetoothDelegate?.onConnectionStateChange(deviceConnectionState)
centralManager.connect(device.peripheral)

startConnectionTimeout(for: device)
centralManager.connect(device.peripheral)
}

func disconnect(from device: BLEDevice) {
guard deviceConnectionState != .disconnected && deviceConnectionState != .disconnecting else { return }
deviceConnectionState = .disconnecting
bluetoothDelegate?.onConnectionStateChange(deviceConnectionState)
centralManager.cancelPeripheralConnection(device.peripheral)
}
func disconnectAllDevices() {
for device in discoveredDevices.values {
// Only disconnect devices that are currently connected or connecting
if deviceConnectionState == .connected || deviceConnectionState == .connecting {
centralManager.cancelPeripheralConnection(device.peripheral)
}
}
// Clear stored connected device in SharedPreferences
SharedPreferenceManager.shared.clearConnectedDevice()
deviceConnectionState = .disconnected
bluetoothDelegate?.onConnectionStateChange(deviceConnectionState)
print("All connected devices have been disconnected")
}

func disconnectSavedDevice() {
guard let savedUUID = SharedPreferenceManager.shared.getSavedDeviceUUID(),
let device = discoveredDevices[savedUUID] else {
print("No saved device to disconnect")
return
}

guard deviceConnectionState != .disconnected && deviceConnectionState != .disconnecting else {
print("Device is already disconnected or disconnecting")
return
}

deviceConnectionState = .disconnecting
bluetoothDelegate?.onConnectionStateChange(deviceConnectionState)

centralManager.cancelPeripheralConnection(device.peripheral)
SharedPreferenceManager.shared.clearConnectedDevice()
print("Disconnecting device: \(device.name)")
}
func startConnectionTimeout(for device: BLEDevice) {
connectionTimeoutTimer?.invalidate()

connectionTimeoutTimer = Timer.scheduledTimer(withTimeInterval: 30.0, repeats: false) { [weak self] _ in
guard let self = self else { return }
if self.deviceConnectionState == .connecting {
print("Connection Timeout for device: \(device.name)")

self.centralManager.cancelPeripheralConnection(device.peripheral)
self.deviceConnectionState = .failed
SharedPreferenceManager.shared.clearConnectedDevice()
self.bluetoothDelegate?.onConnectionStateChange(.failed)
}
}
}

}

/// functions for manageing the connection state of the device...
extension BlueToothManager { ///extend with this "CBCentralManagerDelegate"  if not already extended

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
connectionTimeoutTimer?.invalidate()

print("Device Connected: \(peripheral.name ?? "Unknown")")
deviceConnectionState = .connected

if let device = discoveredDevices[peripheral.identifier] {
SharedPreferenceManager.shared.saveConnectedDevice(device)
}

bluetoothDelegate?.onConnectionStateChange(deviceConnectionState)

peripheral.delegate = self
peripheral.discoverServices(nil)
}

func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("Failed to Connect: \(peripheral.name ?? "Unknown") | Error: \(error?.localizedDescription ?? "none")")
deviceConnectionState = .failed
SharedPreferenceManager.shared.clearConnectedDevice()
bluetoothDelegate?.onConnectionStateChange(deviceConnectionState)
}

func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
connectionTimeoutTimer?.invalidate()
if let error = error {
print("Device Disconnected Unexpectedly: \(peripheral.name ?? "Unknown") | Reason: \(error.localizedDescription)")
deviceConnectionState = .unKnownError
} else {
print("Device Disconnected by User: \(peripheral.name ?? "Unknown")")
deviceConnectionState = .disconnected
}
SharedPreferenceManager.shared.clearConnectedDevice()
bluetoothDelegate?.onConnectionStateChange(deviceConnectionState)

}
}

///function for sending and receving the data
extension BlueToothManager {

/// Send data to a specific characteristic of a device
func sendData(_ data: Data, to device: BLEDevice) {

// Print raw byte data
let hexString = data.map { String(format: "%02X", $0) }.joined(separator: " ")
let binaryString = data.map { String($0, radix: 2) }.joined(separator: " ")
print("Sending Data → HEX: [\(hexString)]  BINARY: [\(binaryString)]")

guard let savedUUID = SharedPreferenceManager.shared.getSavedWriteCharacteristicUUID() else {
print("ERROR: No saved write characteristic UUID")
return
}
let targetUUIDString = savedUUID.uuidString.uppercased()

guard let characteristic = device.characteristics.first(where: {
$0.key.uuidString.uppercased() == targetUUIDString
})?.value else {

print("ERROR: Characteristic \(targetUUIDString) not found in this device")
print("Available characteristics:")
for (key, _) in device.characteristics {
print(" → \(key.uuidString)")
}
return
}

let props = characteristic.properties
if !props.contains(.write) &&  !props.contains(.writeWithoutResponse) {
print("ERROR: Characteristic does not support write → \(props)")
return
}

print("Writing \(data.count) bytes to \(characteristic.uuid.uuidString)")
device.peripheral.writeValue(data, for: characteristic, type: .withResponse)
}

/// Read data from a specific characteristic of a device
func readData(from device: BLEDevice, characteristicUUID: CBUUID) {
guard let characteristic = device.characteristics[characteristicUUID],
characteristic.properties.contains(.read) else {
print("Characteristic not readable")
return
}
device.peripheral.readValue(for: characteristic)
}

/// Subscribe for notifications/updates on a characteristic
func subscribe(to device: BLEDevice, characteristicUUID: CBUUID) {
guard let characteristic = device.characteristics[characteristicUUID],
characteristic.properties.contains(.notify) else {
print("Characteristic not notifiable")
return
}
device.peripheral.setNotifyValue(true, for: characteristic)
}

/// Unsubscribe from notifications
func unsubscribe(from device: BLEDevice, characteristicUUID: CBUUID) {
guard let characteristic = device.characteristics[characteristicUUID] else { return }
device.peripheral.setNotifyValue(false, for: characteristic)
}
}

/// auto data recive
extension BlueToothManager { /// need to implemet this "CBPeripheralDelegate"  I have already done in prev extention
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
for service in services {
peripheral.discoverCharacteristics(nil, for: service)
}
}

///In this function we save the characteristic for the write channel
func peripheral(_ peripheral: CBPeripheral,
didDiscoverCharacteristicsFor service: CBService,
error: Error?) {

guard let characteristics = service.characteristics else { return }

print("=== FOUND CHARACTERISTICS FOR \(peripheral.name ?? "Device") ===")

for characteristic in characteristics {

let props = characteristic.properties

// Save characteristic inside the device model
if let device = discoveredDevices[peripheral.identifier] {
device.characteristics[characteristic.uuid] = characteristic
discoveredDevices[peripheral.identifier] = device
}

// Auto-save WRITE characteristic
if props.contains(.write) || props.contains(.writeWithoutResponse) {
print("WRITE SUPPORTED: \(characteristic.uuid.uuidString)")
SharedPreferenceManager.shared.saveWriteCharacteristicUUID(characteristic.uuid)
}

// Auto enable notifications
if props.contains(.notify) || props.contains(.indicate) {
peripheral.setNotifyValue(true, for: characteristic)
}

if props.contains(.read) {
peripheral.readValue(for: characteristic)
}
}

print("=============================================\n")
}

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
guard let data = characteristic.value else { return }
print("Data received: \(characteristic.uuid): \(data)")
if let str = String(data: data, encoding: .utf8) {
print("As String: \(str)")
}
}
}
Aufruf der Funktion

Code: Select all

 @objc private func sendDataTapped() {
guard let device = device else { return }
guard bluetoothManager.deviceConnectionState == .connected else { return }

let cmd1 = "PLAY"
bluetoothManager.sendData(encodeCommand(cmd1), to: device)
print("Sent → \(cmd1)")

DispatchQueue.main.asyncAfter(deadline: .now() + 4.0) {

let cmd2 = "Rd1"
self.bluetoothManager.sendData(self.encodeCommand(cmd2), to: device)
print("Sent → \(cmd2)")
DispatchQueue.main.asyncAfter(deadline: .now() + 4.0) {

let cmd2 = "Rd0"
self.bluetoothManager.sendData(self.encodeCommand(cmd2), to: device)
print("Sent → \(cmd2)")
}
}

}
Jemand kann mir bitte schnell helfen.
Vielen Dank!!!

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post