Rafting button
|
Dokumentace se věnuje pouze finální implementaci distribuovaného systému, která je v repositáři dostupná pod složkou code/rafting-button.
Už od útlých dětských let jezdím na letní tábory a přes rok organizuji mládežnické akce. Na Hudebním týdnu, jedné z akcí pro mladé, tradičně hráváme hru, ve které jsou různé týmy, které mezi sebou soutěží. Cílem je se jako první přihlásit o slovo a zodpovědět otázku. Doposud se nám nepovedlo vymyslet efektivní způsob, jak určit pořadí týmů. Ve výsledku vždy použijeme zvoneček, který při odpovědí více týmů ve stejný čas není vhodným řešením.
Cílem bakalářské práce je vytvořit koncept hlasovacího tlačítka, které bude fungovat jako autonomní zařízení v distribuovaném systému. Systém pomocí vhodných algoritmů bude řešit problematiku konsenzu, respektive kauzálního uspořádaání stisku tlačítek.
Z uživatelského pohledu by zařízení mělo být kompaktní krabička, kterou si bude moci skupina položit před sebe na stůl.2 Krabička by na sobě měla mít tlačítko, které bude odolné i vůči veliké síle způsobené bouchnutí v zápalu hry. Zařízení bude možné přepínat mezi dvěma módy, tj. PRESENTER a NORMAL. V módu NORMAL bude zařízení řešit,jakém pořadí se týmy přihlásily o slovo. V módu PRESENTER navíc pomocí webového serveru vykreslí výsledky.
Samotná hra pak bude probíhat tak, že bude položena otázka, hráči se přihlásí o slovo stiskem tlačítka. Informace se rozdistribuujesíti a zařízenímódu PRESENTER zobrazí výsledky. Hlasování se vyresetuje dlouhým stisknutím hlasovacího tlačítka na jakémkoliv zařízení.
Kód jsem implementoval v jazyce C s použitím ESP-IDF. Jedná se o oficiální vývojový framework pro procesory ze sérií ESP32, ESP32-S, ESP32-C a ESP32-H. Framework se skládá z jednotlivých komponent, jako je například RTOS kernel, který využívá FreeRTOS, ovladače periferií, wifi či komponenta pro optimalizaci spotřeby energie. Většina komponent je vyvíjena jako open-source. ESP-IDF také podporuje balíčkový systém,(balíčkový systém se nazývá IDF Component Registry) který umožňuje pokročilé verzování a efektivní zacházení s jednotlivými komponenty.
Jedním z požadavků na zařízení bylo, aby se jednalo o autonomní systém. Výsledný software je tedy na všech zařízeních z počátku ve stejné konfiguraci.
Z důvodu více jader procesoru a využití callback funkcí v implementaci využívám multitaskové infrastruktury, kterou zajišťuje FreeRTOS. Běh systému je složen z následujících procesů.
app_main
inicializuje nutné komponenty a stará se o správu celého systému. Také se využívá pro vypisování pravidelných hlášení o stavu systému.espnow_handler_task
slouží k zpracování ESP-NOW udláostí. To jsou události vyvolané callbacky, které signalizují odeslání nebo přijmutí zprávy. Proces funguje jako stavový automat a je detailněji popsán v další kapitole.send_rtt_cal_master_task
rozesílá zprávu inicializující výpočet doby přenosu $D$. Pokud lídrem není, nic neprovádí.send_time_task
funguje podobně jako předchozí proces. Tedy pokud je zařízení lídrem, pak rozesílá zprávy synchronizující čas. Tím si lídr udržuje svoji autoritu.send_request_vote_task
inicializuje nové volby a následně je obslouží.handle_ds_event_task
. Ten se stará o distribuci logů mezi zařízeními.Software implementuje 3 přerušení, respektive dvě obsluhy přerušení a jedno přerušení. Konkrétně se jedná o callback funkce espnow_send_cb
a espnow_recv_cb
obsluhující přerušení ESP-NOW a přerušení gpio_handler_isr
pro obsluhu hardwarového tlačítka připojeného na bránu GPIO_NUM_23
.
Priorita jednotlivých procesů je popsána tabulkou *(viz níže)*. Nejvyšší prioritu mají přerušení a rutiny obsluhující přerušení. Po nich následuje espnow_handler_task
, protože se stará o časově kritické procesy jako je synchronizace času. Střední prioritu mají všechny ostatní procesy krom rodičovského, který má prioritu nejnižší.
proces | priorita |
---|---|
app_main | 1 |
espnow_handler_task" | 3 |
send_rtt_cal_master_task | 2 |
send_time_task | 2 |
send_request_vote_task | 2 |
handle_ds_event_task | 2 |
Z měření síťové infrastruktury vyplývá, že velikost zprávy má vliv na dobu odeslání. Algoritmus v ideálním případě očekává symetrickou dobu odesílání zpráv. Proto jsem při implementaci využil maximální velikost rámce a nevyužité místo jsem zaplnil náhodnými daty. Díky tomu nebude mít rozdílná velikost vliv na případnou asymetričnost.
Z důvodu zjednodušení využívám pouze jediný typ rámce pro všechny typy zpráv. Rámce se skládají z následujících částí, které jsou vizualizovány obrázky přiloženými obrázky.
message_type_t
) ovlivňuje stavový automat v procesu, který zpracovává příchozí zprávy.uint32_t
) říká o jakou epochu se jedná, a tak zajišťuje bezpečnost.uint64_t
) používá se k přenášení různých typů zpráv. Ve většině případů obsahuje časovou značku.ds_event_t
) specifikuje, zdali se jednalo o stisk nebo reset. Využívá se k distribuování logu.uint8_t[ESP_NOW_ETH_ALEN]
) se využívá k distribuování logu, konkrétně ke zdrojové adrese dané události.ds_task_t
) se využívá k distribuci logů.neighbour_t[NEIGHBOURS_COUNT]
) je pole obsahující seznam sousedů. Požadavek na systém definuje maximálně 10 připojených zařízení. Do pole se neukládá informace o zařízení, kterému seznam náleží. Proto je velikost pole 9.device_title_t
) určuje, zdali je zařízení lídr nebo následovník.device_status_t
) je informace o stavu zařízení, tj. zdali je aktivní či nikoli.uint8_t[ESP_NOW_ETH_ALEN]
) identifikuje souseda pomocí jeho mac adresy.uint8_t
) neboli payload vyplňuj nevyužitý prostor náhodnými daty.Zprávy zpracovává speciální mechanismus. To tak, že callback funkce zaznamená novou zprávu, pošle ji jako událost do fronty a z ní proces espnow_handler_task
vyjme událost a tu následně odbaví.
Tento proces je klíčovým pro fungování celého systému. Funguje jako stavový automat. Přijatou událost, respektive zprávu zpracuje podle jejího typu. Pokud se jedná o zprávy typu:
HELLO_DS
zařízení ověří, zdali se jedná o neznámé zařízení. Pokud ano, přidá ho do svého seznamu. Pokud již zařízení zná, pouze nastaví jeho status jako aktivní. Jako odpověď mu pošle seznam všech zařízení v sítí.NEIGHBOURS
zařízení si přidá všechny neznámé sousedy do svého listu a aktualizuje status a titul u jednotlivých zařízení.RTT_CAL_MASTER
zařízení odešle zprávy zpět odesílateli se stejným obsahem. Tato zpráva se využívá k výpočtu doby přenosu $D$.RTT_CAL_SLAVE
zařízení vypočítá dobu přenosu a odešle ji odesílateli zprávy.RTT
zařízení uloží hodnotu přenosu do pole, aby bylo možné vypočítat průměrnou dobu přenosu. Pokud se jedná o první takovou informaci, pole se touto hodnotou vyplní celé.TIME
zařízení spočítá chybu synchronizace času $O$ a nastaví proměnou konstantu $c$ pro výpočet času $T_{DS}$ podle algoritmu popsaného v bakalářské práci.Zpráva tohoto typu se využívá i k tomu, aby si lídr udržel svoji autoritu. Pokud je tedy zařízení ve stavu kandidáta a přijme daný typ zprávy, ukončí svoji kandidaturu a přepne se do stavu následovník.
Při každém zpracování této zprávy se ukládá časová značka. Ta se následně využívá k výpočtu timeoutu $t_{sync}$. Pokud je delší než stanovená konstanta, zařízení přechází do stavu kandidát, zvyšuje číslo epochy (epoch ID) a inicializuje volby.
REQUEST_VOTE
zařízení odešle odesílateli zprávu s hlasem.GIVE_VOTE
zařízení zprávu uloží. Pokud má více odpovědí, než je polovina aktivních zařízení v síti, prohlásí se za lídra a ostatní zařízení za následovníky.LOG2MASTER
zařízení pošle log do fronty, která předává tento typ událostí procesu handle_ds_event_task
. Log je uložen a rozeslán všem následovníkům.LOG2SLAVES
zařízení pošle log do fronty, která předává tento typ událostí procesu handle_ds_event_task
. Log je uložen.V případě, že proces obdrží události s informací o neúspěchu odeslání zprávy, se inkrementuje součet neodeslaných zpráv v daném zařízení. Pokud jich je více než počet povolený konstantou COUNT_ERROR_MESSAGE_TO_INACTIVE
, proces je prohlášen za neaktivní a tato informace je rozdistribuována na všechny aktivní sousedy.
Kód je doplněn o ladící výpisy definované v ESP-IDF. Konkrétně se jedná o aplikaci funkcí ESP_LOGI
, ESP_LOGW
a ESP_LOGE
.
Veškerý kód je dostupný v repozitáři projektu.
Dokumentace kódu je dostupná na adrese https://petrkucerak.github.io/rafting-button/.
NO | type | MAC address | Note |
---|---|---|---|
1 | ESP32 small | 70:b8:f6:5b:d3:24 | COM4 |
STM32 | COM5 | ||
2 | ESP32 small | cc:db:a7:1d:c4:08 | COM6 |
3 | ESP32 large | c8:f0:9e:7b:10:8c | COM7 |
4 | ESP32 small | cc:db:a7:1d:c7:cc | COM8 |
5 | ESP32 large | 94:b5:55:f9:f2:f0 | COM9 |