Anonymous
Bluetooth (BLE) Примечание о получении данных. В чем проблема? Работаете в iOS, но не в Android?
Сообщение
Anonymous » 07 янв 2026, 09:06
Я пытаюсь отправить некоторые данные на устройство 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
1767765987
Anonymous
Я пытаюсь отправить некоторые данные на устройство BLE, но по какой-то причине оно говорит, что данные отправлены успешно, но на самом деле BLE ничего не делает, поскольку я реализовал то же самое в iOS, и оно работает правильно. Я предоставляю код для iOS и Android, кто-нибудь, пожалуйста, помогите. Код Android [code]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 = "" } } [/code] код UUID правильный, потому что я могу подключиться к BLE, значит, должно быть что-то еще? [code]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") } [/code] Код iOS [code]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)") } } } [/code] вызов функции [code] @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)") } } } [/code] Кто-нибудь, пожалуйста, помогите мне побыстрее. Спасибо !!! Подробнее здесь: [url]https://stackoverflow.com/questions/79862129/bluetoothble-note-receiving-the-data-what-is-the-issue-working-in-ios-but-n[/url]