SWIFT, RASPBERRYPI, BLUETOOTH — X — FILE PART 2
About 3 years ago I started a really new interesting project in collaboration with my boxing gym Glorious Fight Gym.
I still don’t remember how it started, but I ended up developing a machine that can be used to measure boxer reflexes.
This is the second part of this article.
In the first part we had an overview about how is possible to install Swift on linux (in this case on a RaspberryPI) and all the tools, libraries and hardware components that I’ve used to create the mentioned device.
The second part will focus more on the running software on the embedded side and the mobile application.
The article will be divided in 2 parts: one dedicated to the RaspberryPI software and the other one to the mobile application.
RASPBERRYPI SOFTWARE
SWIFTLINUXBLE LIBRARY
SwiftLinuxBLE is wrapper around the Pure Swift Bluetooth Linux library . Pure Swift doesn’t come with a lot of examples and could be really overwhelming to use it if you are at your first experiences.
The things I’ve loved about SwiftLinuxBLE
is that is using property wrappers around characteristics in a very usefull way, making very easy to create a GATT server and communicate with other devices.
Why am I excited about the usage of property wrappers? because they make the code more readable and elegant. If you want to know more about them, I strongly suggest you to read you this article from Donny Wals.
Here a breakdown about how to create a service along with its characteristics.
First we create a class that inherits fromService
, we assign a UUID that defines the service, then we annotate the temperature variable as a Characteristic
property wrapper adding a UUID and the characteristic properties. In the example our characteristic can be read and will notify observers for value changes.
In my software the service has 3 characteristic:
punchResponseCharacteristic
is used to notify an observer about the punch response. Is the target being hit? and if yes, how long did it take?PunchResponse contains those informationpunchTimeSeqCharacteristic
is used to set time intervals between each target switch in a sequencepunchSequenceCharacteristic
is used to set each target in a sequence
The last two characteristics are in write
. The sequence information are provided by the mobile application after the user has set them.
EVERYTHING IS DATA
This sounds pretty expected but a lot of programmers when they start to face IOT world they don’t know what they are sending and what they are receiving under the hood.
Guys… maybe you are sending a String but not as String, you are sending the Data representation of that String.
This means that everything that you want to send from the RaspberryPI using bluetooth (or other systems as well) is the raw data representation of that information and the data received on the other side (the application) should be able to recreate the same information.
In SwiftLinuxBLE
a protocol is provided to serialize and deserialize information, theDataConveritible
protocol.
It defines an initialization method from a Data
and a computed property to obtain the Data
type serialization from the object itself. The abstraction of a protocol makes you able to send all the information you want by implementing it.
In my case information such as the so called PunchResponse
is composed by different types of data and to create the Data
instance that I want to send or read I needed two more steps.
The initialization from data requires the Data
object to be split in different subset of information, this must be done because the payload contains the target and the time passed before the actual hit.
The time
information is stored in the first 4 bytes of the data and that should not suprise at all since is extracted as a UInt32
. Doing this process of extraction is mandatory to use the correct type, if I had used an Int
I would have lost portability. A generic Int
is redefined based on the architecture where the software is running. A UInt32
is a 32bit unsigned integer in every architecture.
Another thing that you can notice is that the extract value is divided by 1000, this is a so called multiplier factor. Sending float values as raw data between different devices can be complicated due to how float values are stored. Converting a float to an int by multipling its value really simplify a lot.
The target name is stored in just one byte and that byte is at the position 4 of the payload.
According to what you read above, converting the information into a Data
object requires to assemble the data representation of time
and tap
(target name).
To do that I’ve created a function that extends Data
and that given an array of DataConvertible
creates a Data
instance that respect the order of the array and the size of each concrete type.
The assemble
function is super easy to understand, it takes advance of the mutability of a Data
instance by appending a data representation from the given array. Since each element of the array must conform to the DataConvertible
protocol we can guarantee that a valid data
property exist.
SWIFTYGPIO LIBRARY
To communicate with sensors and leds I’ve used the GPIO port of the Raspberry, to better understand what a GPIO does and why I need one check the first part of that article.
To configure and drive ports I’ve used SwiftyGPIO library. In my software I need to use 6 GPIO port ad digital output as switches for LED and 6 as input from the proximity sensors.
Once you configured the library to work with the correct Raspberry model you can start to work with PINs.
PIN configuration is based on setting some properties:
- direction: configures the PIN as input or output
- value: the pin value 1 (HIGH) or 0 (LOW)
- pull: if available you can set also pull up or down resistor
- callbacks:
onFalling
,onRaising
,onChanging
provide an easy way to be informed when the value of a PIN set as input changes - debounce: this is a super useful feature that helps in debounce input values by using a timer. You can find more about bouncing here.
Note that using PINs as digital I/O is just one of the multiple functionalities of a GPIO. GPIO supports SPI, I2C protocol etc.
THE MOBILE APPLICATION
The application has a lot of functionalities, it was made to track different characteristic about an athlete:
- athlete anagraphic
- weight
- reflex responses
On the coach side it can be used to configure sequences on the device and keep track of the result for each athlete. Results can be displayed on a graph.
The UI is super raw, each button is colored the same way as the target LED color, it is also possible to customize the maximum interval between each punches.
The communication between the iOS system and the device is made using Bluetooth low energy.
One of the mantra of open source should be “if you create a library try to use it by yourself” so here I am, I’ve used my library LittleBluetooth.
LittleBluetooth uses a react approach in dealing with bluetooth and is built on top of of Apple combine.
Once a sequence has been configured the application tries to create a connection with the RaspberryPI and write the sequence to it. Doing this using LittleBluetooth is a piece o cake, each LittleBluetooth action is made as a pipeline.
First the application start a scan with a 30 seconds timeout to search a peripheral that expose a specific primary service and filter results by a specific name.
Once the peripheral has been found it makes a connection, after the a connection has been established it writes the target sequence and the time interval sequence. If everything has completed successfully it will show the live results.
The live results view will observe the punch result charcteristic to get updated in real time about the hit results. Again we used LittleBluetooth to attach the app as listener and be notified about results.
EVERYTHING IS DATA BIS
Probably you remember this chapter from before and it shouldn’t surprise you, because on the mobile side serializaton and deserialization is the same. This is something very powerful about using the same language on both parts: you can reuse the same logic or build a multi-platform module.
To serialize and deserialize information a similar approach has been used, two protocol have been defined.
As you can see together ( Readable & Writable ) t
hey are the DataConvertible
and the same concept as before can be applied.
If you are asking why there is this little difference in naming is simply because I still didn’t had a chance to refactor the code. Linux library and iOS library have been develop by different person, nonetheless concepts are the same.
WHAT’ S NEXT
The third article will be about the hardware, we will look into sensors, leds, mechanical support etc.