Bluetooth (BLE) Примечание о получении данных. В чем проблема? Работаете в iOS, но не в Android?IOS

Программируем под IOS
Ответить
Anonymous
 Bluetooth (BLE) Примечание о получении данных. В чем проблема? Работаете в iOS, но не в Android?

Сообщение Anonymous »

Я пытаюсь отправить некоторые данные на устройство BLE, но по какой-то причине оно говорит, что данные отправлены успешно, но на самом деле BLE ничего не делает, поскольку я реализовал то же самое в iOS, и оно работает правильно.
Я предоставляю код для iOS и Android, кто-нибудь, пожалуйста, помогите.
Код Android

Код: Выделить всё

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 = ""
}
}
код UUID правильный, потому что я могу подключиться к BLE, значит, должно быть что-то еще?

Код: Выделить всё

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

Код: Выделить всё

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)")
}
}
}
вызов функции

Код: Выделить всё

 @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)")
}
}

}
Кто-нибудь, пожалуйста, помогите мне побыстрее.
Спасибо !!!


Подробнее здесь: https://stackoverflow.com/questions/798 ... -ios-but-n
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «IOS»