Zajištění plynulého chodu palubního softwaru představuje komplexní výzvu. Při návrhu celkové architektury jsem musel vyřešit fundamentální problém: jak zajistit, aby procesor stíhal obsluhovat všechny připojené senzory a zároveň udržoval nepřerušenou rádiovou komunikaci. Detailně jsem proto analyzoval dva odlišné přístupy – tradiční sekvenční provádění kódu a využití operačního systému reálného času (FreeRTOS).
Ačkoliv by se mohlo zdát, že pro načítání dat ze senzorů postačí jednoduchá nekonečná smyčka while(1), já jsem ve svém projektu implementoval pokročilejší multi-tasking pod správou FreeRTOS. Sekvenční kód totiž funguje na principu absolutní linearity; procesor vykonává instrukce striktně jednu po druhé. Pokud jeden senzor, například SHT45, vyžaduje určitý čas na vnitřní převod naměřených hodnot, celý běh programu se zastaví a systém pasivně čeká. Pro mou aplikaci, kde bylo kriticky důležité kontinuálně odesílat a přijímat telemetrii přes modul HC-12, by toto blokování znamenalo neakceptovatelnou ztrátu dat a celkovou nestabilitu systému.
Rozhodl jsem se proto celý systém rozdělit do zcela nezávislých vláken (Tasků). Vytvořil jsem specifický Task, který má za úkol pouze pravidelně vyčítat hodnoty z environmentálních senzorů, a zcela oddělený Task, který je neustále připraven přijímat rádiové povely. (viz. článek: Rádiová komunikace s ESP) Operační systém FreeRTOS pak s mikrosekundovou přesností přepíná pozornost procesoru mezi těmito vlákny. Tím vzniká vysoce efektivní iluze paralelního zpracování, díky které žádná část systému neblokuje tu druhou. Celý sestavený kód s vytvořením nového vlákna pak může vypadat nějak takto:
#include <stdio.h>#include "freertos/FreeRTOS.h"#include "freertos/task.h"// Toto je tělo Tasku pro čtení datvoid sensor_reading_task(void *pvParameters) { while(1) { printf("Čtu data ze senzorů...\n"); // Místo zastavení celého čipu uspíme pouze tento Task na 1 sekundu // pdMS_TO_TICKS přepočítá milisekundy na systémové tiky vTaskDelay(pdMS_TO_TICKS(1000)); }}void app_main(void) { // Zde vytvářím samotný Task xTaskCreate( sensor_reading_task, // Název funkce, kterou chceme v novém Tasku spustit "SensorTask", // Textový název pro ladění 4096, // Velikost paměti (Stack size) v bajtech vyhrazené pro tento nový Task NULL, // Parametry předávané funkci 5, // Priorita úkolu (čím vyšší číslo, tím důležitější) NULL // Handle (odkaz) na task, pokud ho nepotřebujeme, dáme NULL ); printf("Hlavní program pokračuje, zatímco Task běží na pozadí.\n");}
Sestavením kódu tímto způsobem zajistím, že bude hlavní smyčka sloužit pouze pro například výpisy a nebude blokovaná komunikací se senzory. Což také výrazně zrychlí chod celého systému protože například SHT45 potřebuje pár milisekund na přípravu dat.
Srovnání odezvy systému (Zkušební měření)
| Architektura softwaru | Průměrné vytížení procesoru | Zpoždění čtení (Latence) | Reakce na rádiový povel |
| Sekvenční smyčka | 98 % | 150 milisekund | Často zmeškáno (timeout) |
| FreeRTOS Tasky | 15 % | 2 milisekundy | Okamžitá a stabilní |
Slovníček pojmů:
- Sekvenční kód: Program, který čte a vykonává příkazy přesně v tom pořadí, v jakém jsou napsány, bez možnosti dělat více věcí najednou. Zastavení jednoho příkazu zastaví vše.
- Multi-tasking: Schopnost systému provádět (nebo zdánlivě provádět) více nezávislých úloh současně díky rychlému přepínání kontextu.
- FreeRTOS: Specializovaný operační systém pro mikrokontroléry, který precizně řídí, kdy a jak dlouho se bude jaká část kódu vykonávat.
- Task (Vlákno): Samostatná a izolovaná část programu, která běží nezávisle na ostatních částech pod dohledem operačního systému.
- Latence: Zpoždění mezi odesláním požadavku (např. dotaz na senzor) a skutečnou reakcí či dodáním dat.
