Přejít na obsah
Hardware a mobilní aplikace 24. září 2026

Bluetooth se nikdy nedodělá: co jsme se naučili při napojování hardwaru na mobilní aplikace

Spárovat zařízení a přečíst z něj data je zhruba pětina práce. Zbytek tvoří rozdíly mezi verzemi Bluetooth, telefony a operačními systémy, oprávnění, životní cyklus spojení, protokol nad ním a diagnostika v terénu. Vycházíme z reálného projektu, na kterém s Bluetooth čtečkou pracujeme od roku 2023 – i s omyly, kterými jsme si prošli.

Shrnutí

  • Samotné spárování a přečtení dat je jen malá část práce – většinu tvoří platformy, verze, oprávnění, životní cyklus spojení a protokol nad ním.
  • „Bluetooth“ není jedna technologie: klasický Bluetooth a BLE mají jiné události i jiné callbacky a v praxi je potřeba zvládnout obojí.
  • Android v posledních letech pravidla oprávnění opakovaně měnil a iOS je úplně jiný svět – jedno rozhraní v Dartu stojí na dvou zcela odlišných implementacích.
  • U zařízení se zabezpečeným protokolem se bez SDK výrobce neobejdete – a vážete se na jeho vydávací cyklus. Nás to jednou stálo přepis nativní vrstvy.
  • Chyby, které nejdou zreplikovat při vývoji, se řeší jen sběrem kontextu z terénu.
  • Bluetooth integrace stojí i po spuštění dál – v rozpočtu s ní počítejte jako s průběžnou položkou, ne s jednorázovým vývojem.

Projekt, ze kterého vycházíme

Jde o mobilní aplikaci pro zdravotnický personál v terénu. Aplikace čte data z čipové karty přes externí čtečku a odesílá je na server. O obsahu karet mluvit nebudeme – jde nám o cestu, kterou data urazí od čtečky do aplikace.

První verze aplikace vznikla v březnu 2022 za zhruba deset dní. Čtečka se připojovala kabelem přes USB, na Androidu i na iOS. Bluetooth přišlo v květnu 2023. Od té doby se aplikace každý rok měnila – s novým operačním systémem, s novou verzí SDK výrobce, s novým telefonem v terénu. USB dnes slouží už jen jako záložní cesta na starších Androidech.

Aplikace je napsaná ve Flutteru. S čtečkou ale nemluví Dart (jazyk Flutteru), nýbrž nativní kód pro každou platformu. Dart ho volá přes takzvaný MethodChannel – kanál mezi Flutterem a kódem psaným přímo pro Android nebo iOS. Na Androidu šlo původně o Javu a C++, na iOS o knihovnu výrobce.

Většinu věcí, o kterých píšeme níže, jsme nevěděli hned. Naučili jsme se je z chyb, které přišly z terénu.

Spárování se ukáže na stole. Všechno ostatní se ukáže až v provozu, na telefonech, které jste nikdy neviděli.

„Bluetooth“ není jedna věc

Když zákazník řekne „chceme Bluetooth čtečku“, může tím myslet dvě různé technologie. Klasický Bluetooth (v projektu mu říkáme BT3) a Bluetooth Low Energy (BLE, také BT4) sdílejí název, ale v kódu se chovají jako dvě samostatné věci. Každé má jinou sadu událostí a jiné callbacky – funkce, které systém zavolá, když se něco stane.

Na Androidu proto naše aplikace musí podporovat obojí najednou – drží tři instance čtečky (pro klasický Bluetooth, pro BLE a pro USB) a hledá je paralelně. Jakmile něco najde, musí rozhodnout, o jaký druh čtečky jde, a přiřadit ji správné instanci. Na iOS to funguje jinak – tomu se věnujeme dál.

Klasický Bluetooth (BT3)

  • Události – vlastní sada
  • Callbacky – vlastní sada
  • V aplikaci – samostatná instance čtečky

Bluetooth Low Energy (BLE)

  • Události – jiná sada než u BT3
  • Callbacky – jiná sada než u BT3
  • V aplikaci – samostatná instance čtečky

USB

  • Použití – záložní cesta na starších Androidech
  • V aplikaci – samostatná instance čtečky

Oprávnění na Androidu: pravidla se v čase mění

Pravidla pro práci s Bluetooth se v Androidu postupně měnila a aplikace je musela následovat:

  1. Do Androidu 11

    Aplikace musela žádat i o oprávnění k poloze, přestože ji vůbec nepoužívá – jinak Bluetooth zařízení nenajde.

  2. Od Androidu 12

    Přibyla samostatná oprávnění pro hledání a připojení k zařízení; poloha už nutná být nemusí.

  3. Od Androidu 14

    Přibyl další technický požadavek na to, jak aplikace poslouchá systémová oznámení o stavu Bluetooth.

Každé oprávnění navíc znamená systémový dialog. Dialogy se ukazují uživateli jeden po druhém a pro každý musíme vyřešit, co se stane po zamítnutí. K tomu přibývá další stav: Bluetooth je vypnuté, případně ho uživatel vypne, zatímco aplikace běží.

Důležité: Uživatel neřekne „nefunguje mi to“. Řekne „aplikace nic nedělá“. Příčinou bývá jeden odmítnutý dialog – a aplikace proto musí umět srozumitelně říct, co chybí.

iOS je jiný svět

iOS se řídí jinými pravidly – a nejde jen o jiná oprávnění. Ke klasickému Bluetooth se na iOS aplikace bez zvláštní certifikace výrobce (program Apple MFi) prakticky nedostane, proto tam spoléháme hlavně na BLE. Systém navíc ukáže vlastní dialog s textem, který si určujeme sami, a k detailům spojení se nedostaneme – iOS je nezobrazuje.

Výsledek: v Dartu vypadá rozhraní pro obě platformy stejně, ale pod ním běží dvě zcela odlišné implementace. Každá má vlastní chyby a vlastní chování.

Vlastní GATT, nebo SDK výrobce?

GATT je způsob, jakým BLE zařízení zpřístupňuje svá data: vystaví je jako služby a charakteristiky (hodnoty), které lze číst nebo do nich zapisovat. Pokud zařízení svůj GATT profil zdokumentuje, dá se s ním mluvit přímo, bez knihovny výrobce.

U naší čtečky to ale nebyla volba. Čtečky, které pracují s citlivými daty na kartě, obvykle nekomunikují přes otevřený, zdokumentovaný GATT profil – přenos si výrobce zabezpečuje vlastním, uzavřeným protokolem, a ten bez jeho SDK nerozlousknete. Ve Flutteru proto balíček flutter_blue_plus používáme jen ke zjištění stavu Bluetooth adaptéru (zapnuto, vypnuto). Samotné spojení se čtečkou i čtení dat řeší SDK výrobce.

SDK výrobce

  • Plus – rychlejší vývoj, hotové rozhraní ke čtečce
  • Mínus – závislost na knihovně, kterou nekontrolujeme

Vlastní GATT

  • Plus – plná kontrola nad každým krokem spojení
  • Mínus – víc práce a vlastní odpovědnost za chování na každém telefonu

Tahle závislost má cenu. V říjnu 2025 ji zaplatila naše aplikace naplno – knihovnu, na které závisíme, nemáme pod kontrolou, a když výrobce vydal novou verzi, museli jsme přepsat nativní vrstvu (viz níže).

Vlastní GATT proto dává smysl jen u zařízení, která svůj profil zveřejňují – typicky tam, kde na datech nezáleží bezpečnost. SDK výrobce naopak bývá jediná cesta u zařízení, která řeší zabezpečení dat vlastním protokolem, jako naše čtečka. Otázku „potřebujeme jeho SDK, nebo si vystačíme s obecnou Bluetooth knihovnou?“ je proto dobré položit hned na začátku, ještě před odhadem rozpočtu.

Životní cyklus spojení

Připojení ke čtečce není jednorázová akce. Je to smyčka, která běží celou dobu, co je uživatel na obrazovce. V našem projektu vypadá takto:

  • čtečku hledáme periodicky, přibližně každých 2,5 s
  • data čteme přibližně každé 2 s
  • USB čtečku nezahlcujeme dotazy – smyčka se jí neptá při každém průchodu, ale jen při každém n-tém
  • při odchodu z obrazovky se vše zastaví, při odpojení se vyčistí stav
  • karta může být vložená, vyjmutá nebo nenapájená a každý stav se obsluhuje jinak
  • uživatel může ručně zkusit připojení znovu (3 pokusy)

Čísla patří k naší čtečce, u jiného zařízení budou jiná – princip (pravidelné hledání, opakované čtení, úklid po odpojení) je ale u hardwarových čteček běžný.

I zdánlivě triviální věci se dají pokazit. V dubnu 2025 jsme opravovali chybu, kdy se čtečka nezastavila, když uživatel obrazovku opustil. Důsledkem byl souběh spojení a zbytečná spotřeba baterie.

Protokol nad Bluetoothem

Fungující spojení ještě neznamená fungující čtení. Nad Bluetoothem běží další protokol – ten, kterým se ptáme přímo karty. Jeho základem jsou příkazy APDU, krátké binární zprávy, kterými čipové kartě říkáme, co má udělat. Tahle vrstva patří ke čtení čipových karet, ne k Bluetooth samotnému – u jiného typu zařízení (senzoru, tiskárny) bude protokol nad spojením úplně jiný, ale stejně tak bude nějaký existovat. Tady nás překvapilo hned několik věcí:

  • Příkazy posíláme po jednom, s krátkou pauzou mezi nimi – karta nezvládne dotaz hned za dotazem.
  • Odpověď někdy nedorazí najednou – o zbytek dat si musíme říct zvlášť.
  • Data jsou zabalená a v jiném textovém kódování, než je dnes běžné – bez správného rozbalení jsou nečitelná.

Tahle konkrétní kombinace není vlastnost Bluetooth ani čteček obecně – řídí se přesně tím, jak je postavená karta a formát dat na ní. Jiná karta, jiný projekt, jiné zvláštnosti; obecné je jen to, že nějaké budou.

Důležité: I když spojení funguje, chyby v datech vypadají stejně jako chyby spojení. Uživatel vidí jen to, že se karta nenačetla. Nepozná, jestli vypadlo Bluetooth, nebo jsme špatně rozbalili data.

Diagnostika v terénu

V lednu 2023 začaly ze čteček chodit chyby, které se nám při testování nepodařilo zreplikovat. Změnili jsme proto přístup: místo hledání příčiny u stolu jsme začali sbírat kontext přímo z terénu. Ke každé chybě připojujeme model telefonu, verzi operačního systému a atributy karty (ATR, tedy krátkou identifikaci, kterou karta vrátí po zapnutí, a stavové kódy). Odesíláme jen nové chyby, aby nevznikla záplava hlášení.

Hlavním zdrojem chyb je variabilita zařízení: různí výrobci telefonů a řada verzí Androidu. Takovou pestrost vlastním testováním těžko pokryjete.

UX pro lidi, kteří nejsou technici

Uživatelé aplikace nejsou technici a nemají být. Chtějí vložit kartu a vidět výsledek. To, co jsme popsali výše, musí zůstat schované za jednoduchým rozhraním:

  • Automatické připojení – aplikace čtečku najde a připojí sama.
  • Ukazatel průběhu čtení – uživatel vidí, že se něco děje.
  • Chybové hlášky zvlášť pro iOS a Android – příčiny i způsob nápravy se na platformách liší.
  • Zamykání aplikace na pozadí – při přechodu na pozadí se aplikace zamkne kvůli ochraně osobních údajů.

Vendor lock-in a aktualizace

SDK výrobce přichází jako binární knihovny zvlášť pro Android a iOS, navíc podle architektury procesoru. Musíme je držet ve správné verzi a aktualizovat s každou novou verzí operačního systému.

V dubnu 2025 to znamenalo nové verze SDK čtečky a zvýšení cílové verze Androidu (target SDK) na 34. V říjnu 2025 výrobce vydal novou verzi SDK. Náš původní most mezi Javou a C++ (JNI), postavený kolem staršího SDK, jsme zahodili a přešli na nové Java API výrobce s nativními knihovnami pro každou architekturu procesoru.

Trvalo to týden: 16. října jsme aplikaci poprvé úspěšně sestavili, zatím bez Bluetooth, a 23. října už Bluetooth na Androidu fungovalo. Přepsali jsme zhruba 500 řádků nativního kódu. Přepis nebyl velký, přesto šlo o týden práce, který jsme předem nemohli naplánovat.

Dá se párování zjednodušit?

Ano, a stojí za to novinky sledovat. Android i iOS postupně nabízí novější způsoby párování, které část výše popsaných problémů řeší už na úrovni systému – najde a spáruje zařízení samo, bez zbytečných dialogů. Jestli se to hodí i pro vaše zařízení, záleží na tom, co dovoluje jeho hardware a SDK výrobce – to je dobré zjistit už při odhadu projektu.

Technický detail: na Androidu jde o CompanionDeviceManager (od Androidu 8), na iOS o AccessorySetupKit (od iOS 18). Novější verze obou platforem navíc lépe hlásí ztrátu spárování a Android 17 přidává automatické opětovné spárování.

Co z toho plyne pro rozpočet a plánování

Z našich zkušeností vychází několik doporučení pro každého, kdo plánuje aplikaci s hardwarem:

  • Počítejte s pravidelným rozpočtem na údržbu, ne jen na první vývoj. Nový operační systém nebo nové SDK výrobce může znamenat práci.
  • Ještě před odhadem zjistěte, jaký druh Bluetooth zařízení používá (klasický, BLE, obojí) a jaké SDK k němu výrobce nabízí – pro které platformy a procesorové architektury.
  • Rozpočtujte Android a iOS jako dvě samostatné implementace, i když je Flutter nad nimi sjednotí.
  • Zařaďte diagnostiku už do první verze – my jsme ji přidali až po prvních chybách z terénu. Chyby, které nejdou zreplikovat, bez sběru kontextu nevyřešíte.
  • Ptejte se výrobce, jak často vydává nové verze SDK a jak dlouho starší verze podporuje. Na odpovědi závisí, jak často budete nativní vrstvu upravovat.

Plánujete aplikaci propojenou s hardwarem?

Propojujeme mobilní aplikace s čtečkami, senzory, tiskárnami, měřicími přístroji i zdravotnickými pomůckami. Než začneme, rádi s vámi projdeme, jaké zařízení máte, jaké SDK k němu výrobce nabízí a kde vidíme největší rizika.

Napište nám – nezávazná konzultace vám pomůže odhadnout rizika a rozpočet ještě předtím, než padne rozhodnutí.

Konzultovat projekt
Zpět na blog