So, thank you for coming to my talk.
It's about, okay, I forgot to change the title apparently.
I had to redo the slides because of some user error and apparently I didn't redo the title properly.
And it's not about service discovery, it's about developing host applications.
Sorry for that.
Yes, it's my first time for them and also my first conference talk, so I'm a bit excited.
So, please.
Thank you.
So, short introduction about me.
I'm Florian from Germany.
I work at Innovax, where IT service house and I'm senior embedded systems engineer.
I started eight years ago with mainly embedded Android, went on to doing C++ on embedded Linux
and real-time systems.
And there are a couple of years I started with.
I still consider myself being a neophyte, so I'm not that experienced.
And I thought some feedback from a newbie would be a bit useful for the project.
And also hopefully other newbies can learn one thing or two.
So, on the agenda, we look in the first half at the basics.
Mostly in the protocols stack and the GAT protocol.
Yeah, I'll explain it later.
And then we look at using Bluetooth low energy in the APIs and what tools are available.
Some terminology, clarification, I've called the talk developing host applications,
but that's not proper Bluetooth terminology.
The experts will already know or be confused by what I meant by it.
But laymen usually don't understand the word central device.
So what I meant by host is a central device.
And yeah, clients are paraphernal devices.
Central devices, these are the devices which usually control the connection in Bluetooth low energy.
A paraphernal device does some advertising and says, hey, I'm here, you can connect.
And the central device is the one who does the connecting.
Usually it's pretty powerful device like a laptop or smartphone.
And it's less resource-constrained than the paraphernal device.
And yeah, that reflects on the protocol design later because most logic resides or should reside on the central device.
And then we have the paraphernal devices which are usually resource-constrained.
And they are low-cost, high volume devices, usually like headphones, mice or other input devices or even cart terminals.
They are designed to be relatively dumb from a protocol standpoint.
So they can be built with a few hardware.
So how does Billy work?
The spec is pretty huge.
I've checked the core specification for 5.4.
And it has over 3,000 pages.
And I didn't read all of them.
And we have several protocols in Bluetooth low energy.
There's the L2 cap, the logical link, control, and adaptation layer protocol.
Then there's the HCI protocol which was mentioned further in the Bluetooth talk,
Bluetooth talk.
There's the protocol that's spoken between Bluetooth controller and the application processor if they are separate.
Then there's the generic access profile that's basically advertising.
And then we have the attribute protocol and the generic attribute protocol.
These are the protocols for transferring actual data.
Once a connection on the GAT layer was established and devices start talking with each other.
For example, the generic access profile is used by IPcans by Apple.
These are just using advertisements and saying, hey, I'm here.
I have this ID and all other devices can receive the advertisements.
But if you have a Bluetooth mouse or something, then you're usually using the attribute protocol to send data parameters between the devices.
And I will focus most on the generic attribute protocol because that's the thing you will interact with as a programmer if you work on a separate application.
The GAT protocol is a client server protocol.
The peripheral device is usually the server.
So the central device or smartphone laptop or something, it controls the connection.
But from a data transfer standpoint, the peripheral device is the server, it serves the data.
It can be the other way around in some applications, so it's not always like that.
But in my experience, as limited as it is, it's usually the way I've written it here.
So one word about UIDs, the Bluetooth spatial interest group, it really loves UIDs.
They use it to identify everything by it, type information and object identity for the most parts.
But the 128 bits of UID are quite big and we have a protocol that's designed for low energy.
So what do we do to reduce data rate and the data amount of data we had to transmit?
The solution is to have one big UID, which is taking the standard UID and you only send 16 bit or 32 bit portions.
Instead of a full 128 bit portion and I've marked it, I hope you can see it on the slides in different colors.
The first part of the big UID are substituted by smaller parts.
So every time I will talk about a UID, which comes from the standard, it's usually a part of the big UID.
So we start with the attribute protocol. As I said, it's a client server protocol. Everything is indexed by a 16 bit handle.
So I have an attribute on my server on my attribute server.
And if I want to retrieve the data, I have to say, give me the data for this handling of write it.
The attribute data consists of the handle, a type, which is a UID, a value which is random data and a number of permission bits.
We have basic operations at the read on write.
I think our self explanatory we receive and send values and we have also notifying indicate.
Notifying is when the client instructs the server to send updates.
So for example, I have Bluetooth track or something and I want to receive every changed value.
Then my client application, my central device instructs the server, the peripheral device to notify the other end of the connection on updates.
And there are also indications which are basically the same, but indications require the client to acknowledge each update.
So that's what you probably want if you have some critical applications like heart rate monitors or something.
That ensures that data updates are not lost.
Then we go on to the generic attribute protocol that has two building blocks basically we have services and we have characteristics.
Services are in effect a bunch of characteristics grouped together.
So when I connect to a device and ask it, do you support the service or do you have the service, then I know all the characteristics for the service are available.
Services are identified by you IDs.
So you don't ask for service by its name or plain text or something, but you have to give the device the name.
These are also protected by permissions.
And characteristics are basically a container for values.
They are also identified by you IDs.
There are several standard characteristics that have a standard UID, but you can also create your own services and your own characteristics with big 128 bit UIDs.
And there's a metadata available for characteristics called the client characteristic configuration descriptor which communicates to the to the gut server that it should enable notifications for the values associated with this characteristic.
Everything is built on attributes.
We looked at before from the ATT protocol and basically you have a database on the server which is a big list of attributes in the at sense which all have a handle.
And now you have to associate your handle with with the service you want to interact with or with the characteristic you want to read or write from the server.
So you have to establish a mapping.
The mapping may differ between devices and sessions, but for efficiency they can be cached.
So if I have a proximity service or something like that.
When I always know that the device is always provided the same handle for the same characteristic, then I can assume that it's cached.
But cafia doesn't support this right now, but this does not.
Specifically says they can be cached.
So we want to build a central device with cafia.
So we just have to enable the module in our product conf and we are basically ready to go.
We have to enable Bluetooth, the Bluetooth subsystem.
Then we say we want the central and the central is usually most useful with a get-clined combine so we can enable it.
But for some implications, if you only want to interact with eye peeking for example, you don't even need to enable the get-clined.
So in general a startup looks like we just call in line 2 the Bluetooth enable function for simplicity.
I've chosen the synchronous version of it because you can pass in a callback.
So it gets in the list asynchronously and the callback is called if we're done.
But for this hello type, hello world type of application it's not necessary.
Then we can just start scanning, we do a passive scan here.
There are a couple of other scan options.
And we also pass a callback function here and important.
You do a sleep forever because one of the mistakes I did a couple of times was a simple endless loop there.
But we already multi-threaded because the Bluetooth subsystem is multi-threaded.
And if we do just an endless loop it will block the main threads, no other thread will run and no scanning will take place.
Yes, in my example I just on the first scan I stopped the scanning and create a new connection.
Yeah, that's it. After that we are connected and can proceed with service discovery, which is a topic on its own.
There are also callbacks when so on the previous slide we just call the connection create function and that's it.
It's also asynchronous.
We get callbacks when the actual connection was established.
You just declare a connection callbacks struct there's some macro magic but you can do it by yourself.
And then you have these two callbacks register there. You get the connection handle passed in and a potential error.
And there you can put your application logic in there.
So that's basically it for a simple Bluetooth, a Bluetooth central device application with Sefer that connects to the first available connected to device.
Yes, now the big elephant in the room my previous talk on a meetup was about service discovery because let's a bit of a pain and the documentation is a bit lacking in my opinion, at least for starters like me.
And we need to set up even more parameters in callbacks and then call them calling to the table bill eStack and the bill eStack calls us back with a results from our service discovery.
So we have to walk through each service one by one and ask it what it's what characteristics is have a sorry.
We have to go through each service and ask it what characteristics it supports and that sounds simple and straightforward but it actually is not.
So before we look at some problems rough overview of the API we have this cover function that takes the connection and some parameters and the parameters are big struct already because there are several options to scan for services.
For example, can't say we just want one service or proximity service then we put in the new idea that we know of the service into the struct and then call got to discover that works fine and easy but we don't have a way to find out how much batteries left on the device.
And now we have a bit of a problem because we don't know if the battery service comes before the before the proximity service or after it and the way the service discovery works is we have this big list of attributes and it runs in there from back to front and if we have to ask it twice then we waste quite a lot of time if we are trying to discover multiple services.
Also we have in line six the discover function it's actually a function pointer which is a callback I just mentioned we are asynchronous again we start the discovery and if the discovery communication with the other endpoint was done we get a callback and that's on this slide.
We have a big struct again with the attributes that were found for our parameter and that's basically the API we have to work with to discover all services available on the device.
And as I said it's everything is asynchronous if you write your code in a naive manner and just call a beauty get discover repeatedly you will get out of all the events because it just runs through the attributes one by one and you get the characteristics and services in the mixed and it's not very useful.
Yeah, that's just a bit of sample code that's not even good code I said it didn't work and we don't look to it at long.
Yeah, the pitfalls as I said there's only API documentation an example code no external explanations why I have or in what order I have to do proper proper services covering.
There is no example for multi services coverage if I want to very my device for its battery status and some other useful information I already have to at least handle two different services.
And that's no example for handling optional services so I had a project where we tried to do a proximity tracker and that profile which requires several services to be present you have either two services or you don't have them so you have.
I think three services in total but you have either three or one service so you have during discovery to to look do I have one of these required services then I have to have the other one to or if only one is it's not a proper device and I can't communicate with it properly.
And getting that right was a bit tricky and the system the subsystem is quite big and complex most of the APIs are asynchronous as I said you get out of order events if you don't be careful.
We have multi threading issues.
The callbacks are usually called from the receive thread and there are several things you can't do in there and if you try to.
I don't remember right now but I think if you try to disable the bluetooth stack in one of the receive callbacks then you will just get a kernel panic from Tefear and.
Yeah, we'll be wondering what happened.
Yes and some of these callbacks are called from interrupt and you can't even start a timer in there so that's a bit annoying because you don't always know what kind of callback you are just implementing.
So hints for being productive if you want to find out if you're called from the receive thread or from interrupt the quickest thing usually is to look in the source code to a search for the callback function pointer.
And yeah look for it the code is correct usually and the doxygen rendering is also suboptimal so looking at the code is really the best thing in my experience to find out what you need to do.
And for interactions with other components during service discovery and even later is using event loop or some other structure which decouples your callbacks from the actual application logic so you are free to use whatever mechanism you want to like there you can just.
Allocates a memory and start timer some stuff and it prevents the calls from the restrictions from the problems from call restrictions.
Yeah, I did a little demo I can't really show it right now, but I had two dev boards one Nordic dev board and an inconvenient dev board and the simplification just flashed fine on both started up and I had to.
I had zero code changes on my application so that was quite nice this paper I had only a simple application which scans for devices list the services and tries to connect on the right ones.
And I didn't do any fancy power saving things I've heard some rumors that there are the things we'll get Harry with Sofia with the APIs.
Yeah, a short word on the tooling unfortunately I fell sick last week and I didn't finish my research into the tools there is emulator support this is basically copied from the documentation.
We can either run our targets application in Qemil and all we can use the POSIX native simulator both are in theory bluetooth enabled but on my device I just got an invalid value return value from the driver and didn't have the time to leave back into what the actual issue was.
Yeah, but another thing for Qemil you need the BT proxy utility on my Linux distribution which is I'm a Linux that isn't available you have to get it from somewhere I've compiled it.
But I said I couldn't debug it in the end.
So where to go from here I have a small demo project for the services coverage stuff which I've tested on the boards which is the one that work fine that has a central device part.
I really like the Nordic semiconductor introduction in their online academy website thing it has a really good introduction over view introduction to bluetooth low energy to get started if you don't have a clue even if you don't work this.
I really found the bluetooth part of their course pretty good.
You can check out the specifications it's big but it's available online and also the S1 numbers because the specification itself doesn't contain any ID.
So when you first look into the specifications I want to ring what's the UID for the battery service it's an a separate document.
And also Nordic same again a bit of a fanboy I guess here and they have a pretty cool mobile app the NIF connect from mobile.
It's basically a generic debugger so you can just start scanning from Android app and connect to random devices and list their services and interact with them it's pretty nice for debugging.
And basically that's it I'll be at the saffron table in building K later with my dev boards so you can come and chat and you can also join the saffron community on discord.
It's pretty active.
Yeah that's easy to get I'm.
Do you have any questions?
We have Germans perfect.
Any questions?
You mentioned with the QA Muslim relation the access to bluetooth does this mean that the emulation will use the bluetooth of your laptop and you can connect the bluetooth devices.
Yes you start the BD proxy utility and that forwards the HCI commands from the saffron applications to the bluetooth controller.
You need to the initialize the bluetooth controller so fun fact I also had to compile HCI control myself so I could disable the bluetooth controller for the operating system for Linux and then saffron can take it over.
And if you would like to simulate or emulate the bluetooth devices themselves.
You mind on the client side?
Yeah I think it works the same way it's the HCI lever layer is shared between the central and the peripheral devices so I think it just works the same way.
Okay thank you.
Any more questions?
Okay if we don't have any questions thank you.