This is a higher-level question because I’m having difficulty finding concise and clear practices for this sort of thing. I don’t want to go implementing something subpar.
I have a mobile application which pairs to my embedded device. The device has characteristics for states, commands, and data. The app periodically reads states to determine what the device is doing. It writes commands, which the device handles and runs. When executing a command, the device then steams data notifications during it’s measurement.
Each point is sent as an individual notification and consists of 6 bytes. There are typically several thousand points and the app receives every notification. This is clearly not something that will work in a real-world product.
There are several points of concern
- What if the app disconnects during the measurement?
The device will still be running the measurement but all data after disconnection will be lost. The firmware should store unsent data in flash. Should I allow partial transmission during measurement or first store all data then send it all?
- Packet formatting
Based on the agreed upon MTU, I should transmit data chunks rather than each individual point. Is there an optimal method to do this or is this typically application-dependent?
- Acknowledgement
My understanding is that I can use indications which have built-in acknowledgement. However, should I also add an application-layer acknowledgement message? The packet would be retransmitted if no ACK was received after some time and that associated sequence number would be used to rebuild the data.
- Error checking and handling
BLE has it’s own error checking built into the protocol so no application CRC is needed.
- Data reconstruction
The app needs to know which packet chunk goes in which order to decode and reconstruct the data. Therefore, should a sequence number be added to each chunk? This would also be used for the acknowledgement characteristic before the device sends the next packet to the app.
This is my overall thought for how I would implement a service with the above points in mind:
Characteristics:
Device Status (read only, int)
- Idle, Measuring, Sending
Control (write only, int)
- Start, Stop
Command Parameters (read and write, packet)
- How to run the measurement
Data (read only, packet)
- Measurement points
CCCD (read and write, bool)
- Indications for Data
Data Send (write only, bool)
- Initiate the device to start data send procedure
Data Ack (write only, int)
- Sequence number acknowledgement
Data Remaining (read only, int)
- How many packets are left to read
It would then have the following flow chart.
I’m thinking it would be best to store all data in flash first then transmit at the end once the app requests it. A timer and sequence number acknowledgements are then used to transmit data until it’s all been sent successfully.
Each packet would have the sequence number for reconstruction as well as the number of points within the packet for decoding. The last sequence number would be determined by the app reading the Data Remaining
characteristic before writing to Data Send
to put the device in send mode.
Does this seems like a decent plan? Please let me know a better or easier way to do this.
This is clearly not something that will work in a real-world product.
Ironically, it’s not clear to me what you mean by this statement.
What if the app disconnects during the measurement?
If it is critical that the app receives the entirety of every measurement data set, OR if the measurement data is collected faster than it can be sent over BLE, then yes, the entire measurement should be stored locally on the device. RAM would be my first choice but if there isn’t enough RAM then flash is fine.
Otherwise, if a missed measurement is not critical (because the app can simply reconnect and request a new measurement), AND if the data can be sent over BLE as fast as it is collected, then the device doesn’t necessarily have to store the entire measurement data set.
Packet formatting: Is there an optimal method to do this or is this typically application-dependent?
It’s application dependent. A larger MTU and larger chunks allow a large amount of data to be transmitted over BLE in less time (because less overhead). If less transmission time is important for your application then the app and device can negotiate a large MTU after establishing a connection. Then the device can send as many points as will fit in the negotiated MTU.
Acknowledgement, Error checking and handling, Data reconstruction
All the rest of this falls under YAGNI. Start by implementing your application without this extra stuff. BLE may be reliable enough already. Then you can add extra application-layer reliability features once you’ve demonstrated that it is necessary.
A couple comments on your diagram.
- The BLE specification defines “properties” including Read, Write, and Notify. Your
Status
characteristic clearly supports Read, andControl
andCommand Properties
support Write. But I suspect theData
characteristic should support Notify. Notify, is typically how a device would stream data to an app, without the app having to request each chunk individually. - BLE devices typically don’t “pair” in the tradition Bluetooth sense. It’s more likely that your device will “advertise” and “listen” for connections, while your app will “scan” for advertisements and initiate a connection.
4