canSendWriteWithoutResponse always returns false after some time - ios

I am creating an app which connects with BLE device and continuously writes on one characteristic(withoutResponse write), in one session there can be around 150 write on an interval of 2-4 seconds, and after each session we are manually disconnecting the BLE device and before start of each session BLE device is reconnected, now when writing characteristics in second session and sessions after that, 'canSendWriteWithoutResponse' always returns false, also when i try to write to characteristic anyways it is not writing(ATT Send packet is not send) nor am I getting any error message. [I am using Packet Logger to keep an eye on Bluetooth packets].
If we disconnect to BLE device and reconnect it multiple times then it resolves the issue and it starts sending the packets again.
Software - iOS 15.2 |
Hardware - iPad 8th Gen |
BLE Board - Cypress CYBT-343026 EZ-BT WICED Bluetooth Module |
Xcode - 13.1 |
CODE -
I am only writing to the characteristic again after pervious write is successful, BLE device send an ack when it receives the write. So no race condition and stuff. Also I cannot write again unless previous write is successful, it is a business requirement. Also none of the packets are being lost[I am using Packet logger as well and logging the BLE device logs using serial connection.]
func sendBytesToDevice(_ bytes: [UInt8], characteristic: CBCharacteristic) {
guard isReady else { return }
if connectedPeripheral!.canSendWriteWithoutResponse{
waiting = false
print("Ready-",characteristic.uuid)
let data = Data(bytes: UnsafePointer<UInt8>(bytes), count: bytes.count)
connectedPeripheral!.writeValue(data, for: characteristic, type: .withoutResponse)
}else{
print("Not Ready-",characteristic.uuid)
waiting = true
sendBytes = bytes
sendCharacteristic = characteristic
}
}
func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral) {
print("Peripheral is ready now.")
if waiting{
waiting = false
let data = Data(bytes: UnsafePointer<UInt8>(sendBytes!), count: sendBytes!.count)
connectedPeripheral!.writeValue(data, for: sendCharacteristic!, type: .withoutResponse)
}
}

Related

WiFi Packets Delayed on Real iPhone but not Simulator

I'm working on an app that connects to a device on the same local WiFi network. The device takes measurements and streams the data to the iPhone. The iPhone plots it in real time.
The problem I've run into is the iPhone delays the packets by up to 200ms every half second or so. This causes noticeable stutter in the UI that I'd like to eliminate.
I started debugging the issue by placing signposts in the code when packet is received.
Looking at the profiler, you can easily see the gaps in data.
Zooming in on the space after a gap, you can see a burst of packets received.
I've checked and it isn't dropping any packets. It is simply delaying them from my app.
The weird thing is this isn't an issue on the simulator or with the Android version of the app so I know it isn't an issue with the device or the WiFi network.
Here is the same code running on the simulator showing a much more even distribution of packets.
Has anyone experienced anything like this? Is this just some kind of battery saving limitation of the iPhone hardware? Is there anyway to ensure a more timely delivery of the data to my application?
I tried rewriting the connection using SwiftNIO and ended up with the same results. I've also tried changing the serviceClass parameter of the connection to all the possibilities with no change.
Here is the relevant connection code.
private func udpReceive() {
if udpConnection?.state == .ready {
udpConnection?.receive(minimumIncompleteLength: 1, maximumLength: Int(Hangboard.shared.BufferSize), completion: { content, contentContext, isComplete, error in
os_signpost(
.begin,
log: log,
name: "udpReceive",
signpostID: signpostID
)
Task {
if let content = content {
let _ = await asyncResult(for: Hangboard.shared.udpDataReceivedNative(data: content.toKotlinByteArray(), offset: 0, length: Int32(content.count)))
}
os_signpost(
.end,
log: log,
name: "udpReceive",
signpostID: signpostID
)
self.udpReceive()
}
})
} else {
disconnect(hadError: true)
}
}
private func startUdpListener(port: NWEndpoint.Port) {
let params = NWParameters.udp
params.allowFastOpen = true
params.serviceClass = .responsiveData
let listener = try? NWListener(using: params, on: port)
self.udpListener = listener
listener?.newConnectionLimit = 1
listener?.newConnectionHandler = { connection in
connection.parameters.serviceClass = .responsiveData
self.startUdpConnection(connection: connection)
}
listener?.start(queue: .global(qos: .userInteractive))
}
private func startUdpConnection(connection: NWConnection) {
self.udpConnection = connection
connection.stateUpdateHandler = { state in
switch state {
case .ready:
self.udpReceive()
case .failed(let error):
print("Connection error! \(error.localizedDescription)")
self.disconnect(hadError: true)
default:
break
}
}
connection.start(queue: .global(qos: .userInteractive))
}
Turns out the reason for this was because I was still running a Bonjour search in the background.
Disabling the search when connecting and restarting it on disconnect removed the latency issues.
Apple's tech support mentioned this can be a problem when includePeerToPeer is enabled on a connection.

Is there a guaranteed ordering in the writing of Characteristics to Peripherals?

I'm trying to understand if there is any ordering in how BLE packets are sent from an iOS device using CoreBluetooth to connected Peripherals.
I have two Peripherals connected to an iOS device, and are able to write to a Characteristic on both Peripherals successfully. This Characteristic has the effect of taking a signal high or low upon the Peripherals.
I am turning the Characteristics on in an ordered fashion (A then B), and then turning them off in the same order.
toggleState(peripheral: primary, stateChar: g1StateChar, state: true)
toggleState(peripheral: secondary, stateChar: g2StateChar, state: true)
sleep(1)
toggleState(peripheral: primary, stateChar: g1StateChar, state: false)
toggleState(peripheral: secondary, stateChar: g2StateChar, state: false)
func toggleState(peripheral: CBPeripheral, stateChar: CBCharacteristic, state: Bool) {
var devState = "S"
//S for Stop, A for Active
if(state){
devState = "S"
} else {
devState = "A"
}
let newState = Data(devState.utf8)
//Transmit new State of System
peripheral.writeValue(newState, for: stateChar, type: CBCharacteristicWriteType.withResponse)
}
What I expect to see is the signal associated with A going high, followed by the signal associated with B, separated by about 7.5ms as the Peripherals are programmed to use a 7.5ms connection interval.
What is actually observed is many times the above is the case, but in others Signal B goes high first, followed by A going high 22ms later.

CoreBluetooth Peripheral takes a long time to write value to Characteristic

I am writing an app that needs to write a byte value to a CBPeripheral using iOS CoreBluetooth. I am able to read values, and the write is successful and triggers the didWriteValueFor delegate like it should. Basically, it does work, BUT, it takes a long time (about 3 seconds and reports of over 10). When I test the write process in another app (nRF Connect), the write is almost instantaneous. Here's the code I am using to write to the Characteristic:
func setConfigurationValue(_ i:UInt8){
guard peripheral != nil, configurationStateCharacteristic != nil else { return }
var vint:UInt8 = i
let intData = Data(bytes: &vint, count: MemoryLayout<UInt8>.size)
isWriting = true
peripheral?.writeValue(intData, for: configurationStateCharacteristic!, type: CBCharacteristicWriteType.withResponse)
// Notify any views that they should show a progress overlay
NotificationName.showProgressOverlay.post(userInfo: ["message":"Device configuring".localized], object: self)
}
Note, that peripheral is a stored reference to the CBPeripheral object, and configurationStateCharacteristic is a reference to the CBCharacteristic I am writing to. This code does work, but what is causing the peripheral BLE device to take so long in writing the data and sending a response?
Any ideas?

swift - central writing characteristic to Peripheral

I have connected to two iPhones and found the services and characteristics in it. I am facing problems in sending write requests from central and receiving response from peripheral. I have written the code below to write data in central and received data in Peripheral. But the function didReceiveWriteRequests didn't execute. I don't know Why didn't work.
In central view write, the data code:
func writeValue(data:NSData){
if let discoveredPeripheral = discoveredPeripheral{
if let deviceCharacteristics = deviceCharacteristics{
discoveredPeripheral.writeValue(data, forCharacteristic: deviceCharacteristics, type: CBCharacteristicWriteType.WithResponse)
}
}
}
and in peripheral view, the receive code:
func peripheralManager(peripheral: CBPeripheralManager!, didReceiveWriteRequests requests: [AnyObject]!) {
println("didReceiveWriteRequests")
println("\(requests.count)")
}

Write to BLE Receiver in iOS Swift

I'm new to iOS and BLE. I've been following a tutorial to connect and send data to a BLE receiver board on an Arduino. The tutorial was designed for a different board, and I'm using the (the BLE board is the Adafruit nrf8001 if that might help).
The following code sets up the UUIDs...
let BLEServiceUUID = CBUUID(string: "6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
let PositionCharUUID = CBUUID(string: "6E400002-B5A3-F393-E0A9-E50E24DCCA9E")
let BLEServiceChangedStatusNotification = "kBLEServiceChangedStatusNotification"
And this code connects my iOS device to the BLE board.
central.connectPeripheral(peripheral, options: nil)
I then have a slider set up in my ViewController, and I want to send the value of that slider to the BLE board. Here's my writePosition function:
func writePosition(position: UInt8)->NSData {
let currentValue = NSUserDefaults.standardUserDefaults().floatForKey("sliderValue")
var currentValueString=NSString(format: "%.5f", currentValue)
var writeType:CBCharacteristicWriteType
let newString:NSString = currentValueString
let data = NSData(bytes: newString.UTF8String, length: newString.length)
peripheral?.writeValue(data, forCharacteristic: self.positionCharacteristic, type: writeType)
return data
}
Note that both my arduino AND my app confirm that they are indeed connected. However, if I change the slider value in the app, it does not send its value to the BLE board. What am I doing wrong??
Thanks in advance!
Please note that writing data to a BLE can not be done (typically), faster than 35 to 50 ms. It's a good idea to use a timer (which in iOS has a minimum tick between 50 to 100 ms) to write to the charactestistic, instead of writing directly from your slider. So the timer polls the slider's value and writes it. More advanced apps should use a tx queue.
Also, you may want to take a look to this answer: SWIFT - BLE communications

Resources