Flitsmeister ONE — technische uitdagingen

Frank Bouwens
Flitsmeister blog
Published in
7 min readDec 6, 2019

Hoi! 👋 Ik ben Frank van Team Android bij Flitsmeister. Een paar maanden geleden werd mij gevraagd of ik wilde aansluiten bij een speciaal team binnen Flitsmeister, dat aan de slag zou gaan met een heus “Flitsmeister hardware device”.

Mijn taak: Zorgen dat plannen die gemaakt worden goed aansluiten met de mogelijkheden op Android.

De belangrijkste punten:
- Waarschuwingen (zoals: “Pas op, er staat een flitser!”)
- Beoordelen (“Staat die flitser er nog?”)
- Flitsmeister automatisch starten

Bij Android was het al mogelijk om de app te starten zodra verbinding wordt gemaakt met een Bluetooth autoradio. Bij iOS niet, de Flitsmeister ONE biedt hier een uitkomst voor.

De Flitsmeister ONE

Een extern bedrijf heeft voor ons de uiteindelijke hardware en firmware van de Flitsmeister ONE ontwikkeld. We kregen hierbij ook technische documentatie en stukjes broncode voor Android en iOS.

De documentatie heb ik helemaal doorgespit en volgeschreven met notities.
Bit voor bit werd er in uitgelegd hoe het communicatieprotocol in elkaar stak. Maar toch kwam ik voor een paar raadsels te staan.

Notities op de documentatie

Het apparaat maakt gebruik van Bluetooth LE. En dat werkte anders dan ik aanvankelijk dacht. Buiten wat iBeacons had ik geen ervaring met Bluetooth LE. Ik kreeg het niet voor elkaar om via de Instellingen app met de Flitsmeister ONE te pairen. Blijkbaar kan dat alleen via een app.

Aha juist ja. Alleen moet de app nog gebouwd worden, de broncode die ik heb gekregen compileert niet en de App wil geen verbinding maken met de Flitsmeister ONE.

In de broncode vond ik een referentie naar een bluetooth library van Nordic Semiconductors. Als zij een library hebben gemaakt, hebben ze vast ook wel een demo-app. En gelukkig hadden ze die. Met “nRF Connect” heb ik alles wat in de documentatie staat kunnen uitproberen.

De Flitsmeister ONE heeft meerdere services, die weer bestaan uit meerdere characteristics.

Je kan characteristics lezen om data van de ONE te krijgen en schrijven om het apparaat dingen te laten doen.

Geluiden afspelen en het lampje laten knipperen op de Flitsmeister ONE was met nRF Connect een eitje, simpelweg wat bytes schrijven naar de juiste characteristic. Mooi! Belangrijkste functie werkt. 😄🎉

Volgende was beoordelen. Als de gebruiker op een knop drukt, moet dit worden doorgegeven aan de app. In de documentatie vond ik de characteristic die je moet uitlezen om te zien of, en welke, knop wordt ingedrukt. Hmmm… moet ik die characteristic non-stop gaan pollen? Gelukkig niet, die characteristic ondersteund naast “READ” ook “NOTIFY”.

Ok, prima. Dan vraag ik om notificaties wanneer die characteristic veranderd.

Eerste versie

Huh? Hoe hard ik ook op de knopjes drukte, ik kreeg geen notificaties binnen in de app. Terwijl in nRF Connect ik de characteristic wel zag wijzigen. Stackoverflow had een antwoord. Je moet niet alleen aangeven dat je notificaties wilt voor een bepaalde characteristic, je moet ook de descriptor daarvan aanpassen. Had ik niet geweten, merkte toen pas voor het eerst op dat descriptors überhaupt bestonden voor characteristics.

Volgens nRF Connect was de UUID van de descriptor “0x2902”, wacht eens even… Hier klopt iets niet. Alle andere UUID’s waren veel langer, zoals: “27831530–33ee-9b9c-124d-109d6b0c0a91”. Ik vond op bluetooth.com dat je de echte UUID kon krijgen door de korte UUID-notatie in te voegen aan het einde van de eerste groep van de BASE_UUID.

Oftewel:
“00000000–0000–1000–8000–00805F9B34FB” + “0x2902” = “00002902–0000–1000–8000–00805F9B34FB”
( https://www.bluetooth.com/specifications/assigned-numbers/service-discovery/ )

Top, we hebben de UUID van de descriptor, we zetten de descriptor op “NOTIFY”. Poging 2.

Tweede versie

Nog steeds kreeg ik geen notifications binnen. Terug naar Stackoverflow.

Blijkbaar zorgt de functie “setCharacteristicNotification” voor een write-operation, het schrijven van de descriptor doet dat ook. Als je deze 2 dingen direct achter elkaar doet, dan mislukt tenminste een van de calls. Je moet wachten tot de eerste call klaar is (dat kan je zien in de “onCharacteristicWrite” callback van de BluetoothGatt) voordat je volgende stuurt.

Oh ok, dat klinkt logisch.. Tijd om een queue systeem te gaan gebruiken. Jammer dat dit niet in Android zit ingebouwd. Android geeft low-level access, maar dat zorgt er ook voor dat je soms dit soort dingen zelf moet bouwen.
(Ondertussen zat team iOS op te scheppen dat ze geen queue hoefden te bouwen aangezien dit zat ingebouwd in de CoreBluetooth library van iOS.)

Na het inbouwen van een queue kwamen de notificaties goed binnen. Weer een functie klaar, op naar het volgende. Batterij-status uitlezen.

De documentatie was hier heel erg kort over. Er is een characteristic die je moet uitlezen en de waarde die je terug krijgt is een: “Unsigned 16-bit value representing battery voltage in mV.”

Eh… een waarde in mV? Niet een percentage of zo? Eerst maar eens kijken via nRF Connect wat ik terugkrijg. Ik zag staan “0xAC0B”, omgerekend naar decimaal is dat 44043 millivolt, omgerekend naar volt is dat 44,043 volt. Hier klopt iets niet, de knoopcel-batterij levert maar maximaal 3 volt. Tijd om een mailtje te sturen naar de leverancier van de ONE. Ik kreeg heel snel het volgende antwoord:

“The value returned in the battery voltage characteristic is little endian. This means that the value you read is 0x0BAC instead of 0xAC0B, corresponding to 2988, so 2988 mV or 2.988V. The battery is low when it is below 2.6V. So you could alert the user when it reaches this point.”Little endian? Die term had ik wel eens gehoord, maar geen idee wat er bedoeld werd.

Mijn collega Stijn kon het gelukkig uitleggen. Bij little endian zijn de bytes in volgorde omgedraaid. 0x0BAC bestaat uit 2 bytes, als je die bytes omdraait krijg je 0xAC0B. Aha, nu snap ik hem. We hebben nog steeds geen batterij-percentage, maar we kunnen in ieder geval wel de gebruiker waarschuwen wanneer de batterij bijna leeg is.

Op naar het volgende: de app automatisch laten starten als je in de auto stapt. 😄

Flitsmeister ONE vastgeplakt in een auto

De Flitsmeister ONE zit vastgeplakt in de auto, zodra je in bluetooth range komt moet de app starten. De Flitsmeister ONE zendt om de seconde een eigen bluetooth advertisement uit waar we naar kunnen scannen. Daarnaast zendt het ook een iBeacon uit. We hebben geprobeerd om met Google Nearby te luisteren naar deze iBeacon. Maar dit bleek niet erg snel en betrouwbaar. Nearby scant slechts eens in de 10 minuten, de Flitsmeister ONE zendt maar eens in de 2 minuten een iBeacon uit. De kans dat die 2 elkaar precies tegenkomen is extreem klein. En elke keer als ze elkaar missen ben je alweer 10 minuten aan het rijden.

We hebben gekeken hoe de app het doet, ze gebruiken in de Android versie de iBeacon helemaal niet, ze blijven non-stop scannen naar de bluetooth advertisement.

De app blijft nu altijd actief door middel van een foreground-notification die je niet kan wegswipen, met een scanner die in intervallen van 10 seconden scant. Hierdoor werd auto-start echter een beetje te gevoelig. Mijn auto staat op de parkeerplaats staat vlak naast het kantoor, achter mijn bureau is de Flitsmeister ONE nog te dichtbij, waardoor Flitsmeister telkens weer start. Om dit op te lossen kijken kijken we of de Flitsmeister ONE signaalsterkte minimaal -67dB is voordat we verbinding maken, daarnaast kijken we of de Flitsmeister ONE beweging detecteert, pas als de auto beweegt start de Flitsmeister app.

De auto’s staan vlak naast het kantoor

De laatste uitdaging was het koppelen, dit bleek op sommige toestellen nog niet goed te gaan. Eerst moet een bluetooth scanner gestart worden, dan krijg je een ScanResult terug, daarmee kan je dan verbinding maken. Maar moet de scanner gestopt worden voor of na dat je verbinding maakt? We kozen ervoor om eerst de scanner te stoppen, dan te verbinden. Anders blijf je ScanResult’s binnenkrijgen terwijl je al aan het verbinden bent. Op het toestel waar ik mee heb getest (Samsung Galaxy Note 8) werkte dit prima. Echter op Google Pixel toestellen bleek dit niet te werken. Daar moet hij eerst klaar zijn met verbinden voordat je de scanner stopt, anders mislukt het verbinding maken.

Wat niet hielp met testen was dat team iOS hier ook mee bezig was op hetzelfde moment, hierdoor wilde het nog wel eens gebeuren dat we met de verkeerde Flitsmeister ONE verbinding maakten. Zowel bij Android als iOS hebben we daarom bij het koppelen een waarschuwing ingebouwd als er meerdere Flitsmeister ONE’s zijn gedetecteerd. Je kan dan de juiste Flitsmeister ONE dichterbij leggen. Dan weten we op basis van de signaalsterkte welke de juiste is.

Het was een heel interessant project, we hebben veel geleerd over hoe Bluetooth LE werkt. Ik wens iedereen veel veilige kilometers met de Flitsmeister ONE!

--

--