CoreBluetooth CBCharacteristicWriteWithoutResponse: Do requests return instantly or after write? - ios

As the title suggests, I would like to know whether writes to CBPeripherals using CBCharacteristicWriteWithoutResponse return instantly (and do the writing on another thread) or return only when the write is complete.
I would expect the latter, but a colleague of mine has given me reason to believe the former might be true (he's seeing packets not being sent by the iOS device when repeatedly writing).

While Michal is right about the asynchronous nature of write request, I would like to elaborate a bit more on why packets might be not sent during repeatable writes without response.
I have recently experimented with BLE throughput under different conditions and found the following. When you repeatably write data for characteristic supporting write without response, it first gathers in the internal buffer. If you write too fast, this buffer overflows and all the data that doesn't make it into the buffer gets silently discarded. This has nothing to do with synchronous/asynchronous structure of BLE writes, it's just an iOS implementation of the internal buffer. As I figured out, on iPhone 6 running iOS 9 and connected to a single peripheral device, the available buffer size is around 3kb for uni-directional transfer, for bi-directional transfer its size decreases. So to prevent data loss you should ensure the buffer doesn't get overflown. You cannot control it at iOS side, but you can add some integrity checks to the data transfer protocol you use and orchestrate flow control from the peripheral side to e.g. retransmit lost packets.
It's funny, by the way, that in case of writes with responses you can write whatever large data to your characteristics (well, I didn't try too big values, but tried ones exceeding the MTU) and these data will be split into chunks and transmitted to the peer transparently for your code by iOS means without data loss. However, if the data sent to the characteristic will exceed MTU, you won't receive delivery callback.

Bluetooth Low Energy supports writing to the characteristic's value with or without response. In the latter case, the central won't receive any response from the peripheral, so it won't know whether write succeeded. In the former case, the central will be notified and Core Bluetooth will call peripheral:didWriteValueForCharacteristic:error: method from CBPeripheralDelegate; in the latter case, this method won't be called.
The documentation says:
When you call this method to write the value of a characteristic, the
peripheral calls the peripheral:didWriteValueForCharacteristic:error:
method of its delegate object only if you specified the write type as
CBCharacteristicWriteWithResponse.
I understand your colleague may have thought that instead of getting the write result in delegate method, you will get it in the return value. But actually you won't get any write result when using CBCharacteristicWriteWithoutResponse.
So writing to the characteristic's value will always be asynchrounous and the method will return immediately. If it didn't, it could block the main thread. And Core Bluetooth is designed to be used safely in from the main thread if you want to. For example, if you don't specify a dispatch queue for handling CBCentralManager's events, it will handle them in the main queue by default.

Related

Inhibit Time in Tx-PDO

Objects 180Nh have the following subindices:
0x00:----
0x01:----
0x02:----
0x03 (inhibit time): This subindex contains a time lock in 100 µs steps (see following figure). This can be used to set a time that must elapse after the sending of a PDO before the PDO is sent another time. This time only applies for asynchronous PDOs. This is intended to prevent PDOs from being sent continuously if the mapped object constantly changes.
0x04 (compatibility entry): This subindex has no function and exists only for compatibility reasons.
0x05 (event timer): This time (in ms) can be used to trigger an Event which handles the copying of the data and the sending of the PDO.
According to the above point, we realize that when the event occurs, a certain time is determined, which is blocked, and it is for Tx-PDO; now, if the event occurs in this interval, it will be executed in the next section.
Why should the whole section be implemented? Why is the second, third, and fourth event executed in the last part?
Shouldn't the third and fourth events be executed separately?
By default, common CANopen device profiles like for example CiA 401 "generic I/O module" are configured to suit large automation networks. That is: a large network with lots of nodes where it is important to keep bus traffic low. On such networks nodes only transmit PDOs when there has been a data update (an internal event has occurred).
However, such a setup is very much unsuitable when CANopen is used for real-time control systems, like for example having a PLC controlling a bunch of actuator I/O modules that control motions of a machine. Which could also be a safety-related application. In such systems, it is custom to always send data repeatedly at even intervals, even if it has not changed. For example send all data once every 10ms/100ms.
Only the last data sent is used by the receiving node(s), so in case data goes missing/corrupt, new reliable data will arrive soon again. And if no data arrives at all, that's an indication that something is broken and the system ought to revert to a safe state, after receiving no new data in a certain time period. This is how mobile/automotive control systems are most commonly designed, since it is safe, deterministic and proven in use. Custom, non-standard CAN bus protocols by OEM are often implemented exactly like this.
Now, to achieve this with CANopen, we have to configure the TPDO communication parameters. Event timer to set the interval and inhibit time to prevent the node spamming extra data as soon as something has changed. If I remember correctly we also need to set 180N:2 transmission type to asynchronous (which sounds counter-intuitive).
With a setup like this, only the most recent event matters. The most up to date data will always get sent, at fixed intervals.

Corebluetooth terminate large data transfer without terminate bluetooth connection

I am developing an app that needs to send large amounts of data between an iPhone and a device (it takes approximately 10 seconds to send the data). But I want to be able to cancel the data communication anytime. I am aware I can simply drop the connection to the device at anytime with
centralManager.cancelPeripheral(peripheral)
but that is not what I am actually looking for, as I want to stop sending data but without terminating the bluetooth connection.
Is there a way to terminate the data transmission without dropping the connection to the device?
the codes of sending data is as follow:
for (var Hex: UInt8 = 0x01; Hex <= 0x14; Hex+=1){
var outbuffer = [UInt8](count: 16, repeatedValue: 0x00)
outbuffer[0] = (0x68)
outbuffer[1] = (Hex)
let data = NSData(bytes: outbuffer, length: 7)
print("data\(data)")
connectingPeripheral.writeValue(data, forCharacteristic: connectingCharacteristicPassword , type: CBCharacteristicWriteType.WithResponse)
}
I figured that I would go ahead and give my input on this. There is no way in CoreBluetooth to stop the transmission of a data packet that has already been written to the output buffer. The reason for why this is the case is simply because it is not needed and it would be a useless functionality. The only reason for why you are having this issue is because your methodology is wrong in my opinion. Do not put everything in a for-loop and push the data all at once. Instead you should implement some sort of flow control mechanism.
In Bluetooth LE there are two main ways of writing data to a peripheral: “Write Commands” and “Write Requests”. You can look at it a bit like the TCP vs UDP protocols. With write commands you are just sending data without knowing whether or not the data was received by the application on the other side of the bluetooth link. With write requests you are sending data and letting the peripheral know that you want to be notified (ack’ed) that the data was in fact received. These two types are in CoreBluetooth called CBCharacteristicWriteWithResponse and CBCharacteristicWriteWithoutResponse. When writing data using the CBCharacteristicWriteWithResponse (like you are doing in your code) you will get a peripheral:didWriteValueForCharacteristic:error: callback which verifies that the data has arrived at the other side. At this point you now have the option to go ahead and send the next packet if you want to, but if you for some reason want to stop sending data, then you can do that as well. Doing it this way you are in control of the whole flow and not just simply pushing everything though a for-loop.
But wait, why would you ever want to use write commands then? Well, since write requests requires the receiver to respond back to the sender it means that data must be sent in both directions. In this case, since the ack is sent by the application layer, you have to wait for the next connection interval before the ack can be sent. This means that when sending large amounts of data you can only send one packet per every two connection intervals which will give you a very poor overall bit rate.
With write commands, since they are not ack’ed, you can send as manny packets as possible within one connection event window. In most cases you should be able to send about 10-20 packets per connection window. But be aware that if you send too many packets then you will fill the outgoing buffer and packets will be lost. So, something that you can try is to directly send 9 packets with the WriteWithoutResponse type, followed by 1 packet of the WriteWithResponse type. After doing this you can wait for the peripheral:didWriteValueForCharacteristic:error: callback in which you can then send 10 more packets the same way. This way you will manage to send 10 packets per every 2 connection intervals while still being able to control the flow better.
You can of course experiment with the ratio a bit, but remember that the buffer is shared between multiple applications on the iOS device so you don’t want to be too close to the limit.

iOS Bluetooth LE - Proper Way to Write Without Response

With iOS CoreBluetooth, when sending a relatively large amount data, it's important to break it up into 20 byte chunks and then write them one at a time into the peripheral object. This is pretty easy to do when using a WriteWithResponse characteristic: write 20 bytes, wait for the callback, write the next 20 bytes, and so on.
But what about with a WriteWithoutResponse characteristic? I need to send of 1-2kB of data as quickly as I can over BLE. WriteWithResponse is very inefficient at doing this, because it acks every 20 byte packet. Error correction and reliability are taken care of at my application layer, so I have no need for BLE to ack the data.
The issue is that WriteWithoutResponse does not give you a callback, because there is no way for CoreBluetooth to know when the data was actually written. So the question is: how do we properly space out sending a large amount of data using WriteWithoutResponse?
The only solution I've thought of is to do the following:
Get the connection interval and the number of packets that the link is capable of per connection interval.
Immediately write X packets of 20 bytes each, wait Y time, and repeat until there is no data left. (X = Number of packets per connection interval, Y = The connection interval)
There are two glaring problems with this approach:
CoreBluetooth does not expose the Connection Interval to us (why??). So there are two options. The first being: guess. Probably either a worse-case or average-case depending on your preferred connection parameters, I think iOS likes to pick 30ms. But this is a bad idea because a central has the right to completely ignore the suggested parameters. The second is that you could have the peripheral store and transmit the agreed upon CI to the iOS device. The issue with this is that you can't send the CI until iOS device has finished discovering the services and characteristics and subscribed to the appropriate notification. So you'd have to either put in a somewhat arbitrary fixed delay after connection before sending the CI, or send a small amount of data from the iOS device notifying the peripheral that it is ready. Both create latencies and are pretty poor solutions.
We don't know how many packets per connection interval can be supported. There is a theoretical maximum of 6. But the average case is probably 4 or less. It is also dependent on the peripheral.
Of course a great option for sending large amounts of data is to increase the MTU size to larger than 20 bytes to accommodate our large amount of data. But it seems few peripherals support this; ours does not.
Anyone have any insights on how to solve this?
If You are supporting iOS 11:
iOS Website
#property(readonly) BOOL canSendWriteWithoutResponse;
This property lets you know, if Buffer is filled up or not and can transmit more without response.
After each transmission keep an eye on this variable and a call back: Peripheral Delegate
peripheralIsReadyToSendWriteWithoutResponse:
Which should be sufficient to let you know, when to send more data.

Playing back a WAV file streamed gradually over a network connection in iOS

I'm working with a third party API that behaves as follows:
I have to connect to its URL and make my request, which involves POSTing request data;
the remote server then sends back, "chunk" at a time, the corresponding WAV data (which I receive in my NSURLConnectionDataDelegate's didReceiveData callback).
By "chunk" for argument's sake, we mean some arbitrary "next portion" of the data, with no guarantee that it corresponds to any meaningful division of the audio (e.g. it may not be aligned to a specific multiple of audio frames, the number of bytes in each chunk is just some arbitrary number that can be different for each chunk, etc).
Now-- correct me if I'm wrong, I can't simply use an AVAudioPlayer because I need to POST to my URL, so I need to pull back the data "manually" via an NSURLConnection.
So... given the above, what is then the most painless way for me to play back that audio as it comes down the wire? (I appreciate that I could concatenate all the arrays of bytes and then pass the whole thing to an AVAudioPlayer at the end-- only that this will delay the start of playback as I have to wait for all the data.)
I will give a bird's eye view to the solution. I think that this will help you a great deal in the direction to find a concrete, coded solution.
iOS provides a zoo of audio APIs and several of them can be used to play audio. Which one of them you choose depends on your particular requirements. As you wrote already, the AVAudioPlayer class is not suitable for your case, because with this one, you need to know all the audio data in the moment you start playing audio. Obviously, this is not the case for streaming, so we have to look for an alternative.
A good tradeoff between ease of use and versatility are the Audio Queue Services, which I recommend for you. Another alternative would be Audio Units, but they are a low level C API and therefor less intuitive to use and they have many pitfalls. So stick to Audio Queues.
Audio Queues allow you to define callback functions which are called from the API when it needs more audio data for playback - similarly to the callback of your network code, which gets called when there is data available.
Now the difficulty is how to connect two callbacks, one which supplies data and one which requests data. For this, you have to use a buffer. More specifically, a queue (don't confuse this queue with the Audio Queue stuff. Audio Queue Services is the name of an API. On the other hand, the queue I'm talking about next is a container object). For clarity, I will call this one buffer-queue.
To fill data into the buffer-queue you will use the network callback function, which supplies data to you from the network. And data will be taken out of the buffer-queue by the audio callback function, which is called by the Audio Queue Services when it needs more data.
You have to find a buffer-queue implementation which supports concurrent access (aka it is thread safe), because it will be accessed from two different threads, the audio thread and the network thread.
Alternatively to finding an already thread safe buffer-queue implementation, you can take care of the thread safety on your own, e.g. by executing all code dealing with the buffer-queue on a certain dispatch queue (3rd kind of queue here; yes, Apple and IT love them).
Now, what happens if either
The audio callback is called and your buffer-queue is empty, or
The network callback is called and your buffer-queue is already full?
In both cases, the respective callback function can't proceed normally. The audio callback function can't supply audio data if there is none available and the network callback function can't store incoming data if the buffer-queue is full.
In these cases, I would first try out blocking further execution until more data is available or respectively space is available to store data. On the network side, this will most likely work. On the audio side, this might cause problems. If it causes problems on the audio side, you have an easy solution: if you have no data, simply supply silence as data. That means that you need to supply zero-frames to the Audio Queue Services, which it will play as silence to fill the gap until more data is available from the network.
This is the concept that all streaming players use when suddenly the audio stops and it tells you "buffering" next to some kind of spinning icon indicating that you have to wait and nobody knows for how long.

Writing multiple Bluetooth Low Energy characteristic values with response

The Bluetooth Core Spec V4.0 Vol. 3 Part G Section 4.9.3 states, that for writes of characteristic values with response, the Attribute Protocol Write Request procedure is used.
Bluetooth Core Spec V4.0 Vol 3 Part F Section 3.3.2 describes, that
Once a client sends a request to a server, that client shall send no other request to the same server until a response PDU has been received.
I want to write multiple values with response in an iOS app using CoreBluetooth. Do I have to manage this specification myself? Or can I simply use - writeValue:forCharacteristic:type to write all of the values at once, and iOS manages that each request is only sent after the preceding one has been processed?
I somehow guess that iOS will manage it, because according to Bluetooth Core Spec V4.0 Vol 3 Part F Section 3.4.5.2 the Write Response does not contain a link to the written characteristic. However, the - peripheral:didWriteValueForCharacteristic:error method suggests that iOS somehow keeps track of what characteristic the response is linked to.
Can someone confirm or deny this?
I don't think you need to worry about the ATT/GATT so much when dealing with CB. The reason is many people who are using CB does not have access to the Bluetooth Core 4.0 spec, neither are they expected to read them.
The reason that CB knows how to associate the characteristic is that the protocol dictates that each command and response are paired. When you send a command, you will receive a response.
So you can use writeValue multiple times, and CB will properly queue the calls for you, i.e. it will wait for the response at the ATT layer before the next write. And the delegate callbacks are guaranteed to be in the same order the writes are executed.
I was able to write "multiple requests with response" i.e
[self peripheral] writeValue:valueToWrite forCharacteristic:dataPointCharacteristic type:CBCharacteristicWriteWithResponse];
together in one bunch - actually I sent 14 of them - and all of them came back with responses after a delay. But the - Write Responses did not contain data that was written to the characteristic - i.e - Only the value inside the characteristic was invalid in the response.
Seems to be close to the note "according to Bluetooth Core Spec V4.0 Vol 3 Part F Section 3.4.5.2 the Write Response does not contain a link to the written characteristic" with one difference that it is only the [characteristc value] that is incorrect - but ios took care of the sequencing internally. So it does not seem feasible to use the write responses (i.e the BLE acknowledgements) to to hook up sequencing logic to handle a sequence of steps to perform next.
So - the takeaway is - if you ask BLE to "writeWithReponse" the message "Do Task #1" to the peripheral, the BLE response from the peripheral is "OK!". The response does not tell you that the peripheral received the message "Do Task #1" but instead is something like - Yes, I got what you are saying. I'm lazy to repeat the exact command you sent me :)

Resources