Wyślij zapytanie Dołącz do Sii

Poniższy artykuł przedstawia wewnętrzny projekt, realizowany przez inżynierów-programistów z Centrum Kompetencyjnego Embedded firmy Sii.

Od strony technicznej projekt pokazuje rozwiązanie uniwersalnego koncentratora (HUBa) zbierającego dane z wszelkiej maści sensorów czym wpisuje się jednoznacznie w tematykę Internet of Things.  Z drugiej strony tekst skupia się na całym procesie myślowym jaki doprowadził do realizacji technicznej – a więc od pomysłu, przez projekt po implementację oraz zapewnienie jakości.

Jak do tego doszło?

Idea projektów wewnętrznych narodziła się dawno temu wraz z samym Centrum Kompetencyjnym Embedded. Skoro bowiem ponad setka inżynierów jest w stanie realizować projekty dla szerokiego grona klientów, to dlaczego by nie połączyć ich umiejętności programistycznych, pracy w grupie wraz z dobrymi praktykami projektowania oprogramowania. To wszystko okrasić elementami Agile’a i mamy gotowy przepis na sukces oraz ciekawy projekt zarazem.

Szybko znalazła się w firmie grupka pasjonatów gotowa połączyć swe siły pod szyldem wspomnianego projektu wewnętrznego.

Bezpośrednim celem było zaprojektowanie i wykonanie kompletnego systemu/produktu z jednoczesnym poszerzeniem i pogłębieniem wiedzy z dziedziny Internet of Things oraz spięcie w ramach jednego projektu możliwie dużej liczby technologii ze wspomnianego już IoT. Jednocześnie chcieliśmy po prostu zrobić coś ciekawego od A do Z i mieć z tego czysto inżynierską przyjemność.

Okazało się jednak, że sam wybór tematu projektu – części wydawałoby się prozaiczna (w porównaniu oczywiście do późniejszej realizacji) stanowi pierwszą kość niezgody. Padały przeróżne pomysły związane z technologiami bezprzewodowymi, kryptografią, domeną automotive, czy nawet sztuczną inteligencją. Do kompletu chmura i element technologii webowych… aż chciałoby się zapytać czy może jeszcze frytki do tego.

W końcu jeden z kolegów przypomniał, że już od dawna dyskutowaliśmy na forum wewnętrznym o zrobieniu uniwersalnego koncentratora do wszelkiej maści urządzeń z domeny IoT. Może warto więc efekty wcześniejszych dyskusji przekuć w czyn? Tak oto dobrnęliśmy do końca początku – znaleźliśmy temat projektu: Thálassa, Thálassa![1]

This is madness

Pierwotnym pomysłem było zastosowanie wspomnianego koncentratora (nazwanego na potrzeby projektu IoT HUBem) do celów typu home automation. Ale zaraz, zaraz. Przecież istnieje szereg gotowych rozwiązań – tutaj każdy zaznajomiony z tematem automatyki domowej wymieni jednym tchem takie rozwiązania open source jak Domoticz[2], OpenHAB[3], czy Home Assistant[4]. Prawda, za to zespół postawił sobie za cel:

  • opracowanie WŁASNEGO, uniwersalnego koncentratora (IoT HUBa) umożliwiającego komunikację z całą gamą urządzeń końcowych
  • przygotowanie prostego, otwartego protokołu komunikacyjnego
  • integrację z możliwie największą ilością urządzeń końcowych

Urządzenie to zostanie następnie wyprodukowane w ilości kilku egzemplarzy i umieszczone w różnych oddziałach Sii.

Od teorii do praktyki, czyli projekt rozwiązania

Najtrudniej jest zacząć (aka przypadki użycia)

Kiedy już zespół chciał ochoczo i z kopyta wziąć się do wyboru platformy oraz pisania kodu, postanowiłem zadać kluczowe pytanie – „Czy aby na pewno wiecie co mamy do opracowania?”. W pierwszym odruchu wszyscy przytaknęli nieśmiało, ale gdy każdy opisał słowami swoją wizję produktu, to wyszły na jaw znaczne rozbieżności co do ogólnej koncepcji, użytej platformy i systemu operacyjnego.

Zaproponowałem więc odłożenie dyskusji nad technicznymi szczegółami na później oraz zastosowanie w tym projekcie zasad, jakimi kierujemy się w projektach komercyjnych. Odrzucając najlepsze praktyki z początków takich gigantów jak Apple, Google czy Microsoft (a wszystkie one powstały w przydomowych garażach swoich właścicieli) postanowiliśmy nie robić przysłowiowej garażówy i zacząć od wyspecyfikowania abstrakcyjnych przypadków użycia.

Zakładając, że nasz IoT HUB będzie czarną skrzynką, każdy z nas zadał sobie pytanie – „Jak i do czego chciałbym używać wspomnianego urządzenia?”.  W efekcie powstało 12 przypadków użycia w formie graficznej, każdy poparty bardziej szczegółowym opisem słowno-muzycznym.

Kilka zagregowanych przypadków użycia
Ryc. 1. Kilka zagregowanych przypadków użycia

Wymagania i ogólna koncepcja urządzenia

W następnym kroku poddaliśmy pod dyskusję ogólną koncepcję sprzętową urządzenia biorąc pod uwagę dwie możliwości:

  • all-in-one z wbudowanymi w IoT HUBa modułami komunikacyjnymi
  • rozszerzalną: z płytą główną i doczepianymi expanderami/gateway’ami

Ostatecznie zdecydowaliśmy się na wersję rozszerzalną, zaś koronnym argumentem była możliwość zbudowania takiej konfiguracji przy użyciu ogólnodostępnych zestawów developerskich.

Uzgodniwszy ogólny schemat sprzętowy, przystąpiliśmy do przekucia przypadków użycia w proste, jasne i krótkie wymagania. Tym sposobem powstała lista kilkudziesięciu wymagań, które wraz z przypadkami użycia zamknięte zostały w LaTexowym dokumencie.

Krótki wyciąg z  wymagań
Ryc. 2. Krótki wyciąg z  wymagań

Wybór platformy i systemu operacyjnego

Mając zdefiniowaną listę wymagań oraz uzgodniony ogólny schemat części sprzętowej przystąpiliśmy do wyboru platformy i systemu operacyjnego. Zespół podzielił się na dwie, równoliczne grupy: zwolenników:

  • rozwiązań opartych o Linuxa
  • rozwiązań opartych o systemy operacyjne czasu rzeczywistego

Dyskusjom, argumentom i kontr-argumentom nie było końca. Ostatecznie zwaśnione strony pogodziła koncepcja rozwiązania (o zgrozo) uniwersalnego, mogącego pracować pod kontrolą

  • dowolnego systemu operacyjnego
  • szerokiego spektrum platform sprzętowych: głównie 32 bitowych

Struktura oprogramowania

Mając na uwadze wspomnianą wcześniej uniwersalność podzieliliśmy oprogramowanie na 4 zasadnicze bloki:

  • warstwę abstrakcji od sprzętu (HAL), wystawiającą quasi-CMSISowe[5] API
  • warstwę services, wystawiającą za pomocą dedykowanego API bloki funkcji do aplikacji oraz systemu operacyjnego
  • warstwę aplikacji, wykorzystującą zarówno API od services jak i funkcje wystawione przez system operacyjny
  • complex driver[6], będący ukłonem w stronę świata automotive i zawierający te moduły, które nie dadzą się wpiąć/przyporządkować do żadnej z powyższych warstw.

Na poniższym rysunku widzimy poszczególne, opisane powyżej bloki funkcjonalne. Wszelkie interfejsy z modułów zaznaczone są kolorami.

Podział oprogramowania na warstwy funkcjonalne
Ryc. 3. Podział oprogramowania na warstwy funkcjonalne

Osobną kwestią stało się opakowanie systemu operacyjnego w taki sposób, aby łatwo dało się go podmienić na inny. Stąd dedykowane API wzorowane na POSIXie.

Do kompletu powstał jeszcze długi (i nudny) opis API wystawianego przez każdą z w/w warstw.

Specyfikacja bloków funkcjonalnych

Kolejnym przed-implementacyjnym etapem projektowania była specyfikacja bloków funkcjonalnych na warstwie aplikacji naszego IoT HUBa. Uzgodniliśmy z całą pewnością, że potrzebujemy bloków odpowiedzialnych za:

  • ogólne zarządzanie urządzeniem, co spadło na barki System Managera

System Manager jest najwazniejszym „bytem”. Jego zadaniem jest inicjalizacja wszystkich podległych mu modułów funkcjonalnych, obieranie od nich eventów informujących o zdarzeniach oraz wyzwalanie akcji w odpowiedzi na nie.

  • zarządzanie urządzeniami końcowymi (sensorami i efektorami)

W zamyśle blok ten odpowiada za dodawanie/usuwanie/aktualizowanie stanu urządzeń końcowych. Zarówno bezprzewodowych, jak i przewodowych.

  • zarządzanie expanderami

Podobnie jak to ma miejsce w przypadku urządzeń końcowcych istnieje możliwość wykrywania/dodawania/usuwania expanderów (modułów rozszerzeń) za pośrednictwem dedykowanego bloku.

  • obsługę akcji

Użytkownik IoT HUBa ma możliwość definiowania zdarzeń/akcji w stylu „jeżeli nastąpiło X to wykonaj Y”. Manager akcji ma za zadanie umożliwić dodawanie/usuwanie/wykonywanie wspomnianych zdarzeń w reakcji na inne zdarzenie.

  • bazę danych

Służącą do zapisu konfiguracji oraz wszelkich potrzebnych systemowi danych nieulotnych (np. logów)

  • zarządzanie poborem energii i stanem baterii

IoT HUB pozwala na pracę zarówno przy zasilaniu z sieci (domyślne) jak i z baterii. Stąd istnieje konieczność racjonalizacji zużycia energii oraz generowania akcji gdy np. napięcie baterii spadnie poniżej pewnego poziomu

  • komunikację

Wszelka komunikacja przewodowa/bezprzewodowa/szeregowa/równoległa jest zarządzana z poziomu tego bloku.

Ogólny zarys bloków funkcjonalnych IoT HUBa
Ryc. 4. Ogólny zarys bloków funkcjonalnych IoT HUBa

Wspomniane bloki funkcjonalne należało podzielić między poszczególne wątki – uzgodniliśmy w końcu, że korzystamy z dobrodziejstw systemu operacyjnego. Sztywne podejście z jednym wątkiem na każdy blok funkcjonalny wydało nam się zbytnią rozrzutnością. Stąd zdecydowaliśmy się na 5 współpracujących ze sobą wątków o 3 różnych priorytetach.

Podział bloków funkcjonalnych między wątki
Ryc. 5. Podział bloków funkcjonalnych między wątki

Ostatnim elementem architektonicznej układanki jest sposób komunikacji między wątkami. Każdy wątek dysponuje pojedyncza kolejką przez którą przekazywane są wiadomości w ustandaryzowanym formacie. Wiadomość zawiera m.in.:

  • identyfikator wątku nadawczego (wartość liczbowa)
  • identyfikator wiadomości, jest to wartość odpowiadająca konkretnemu zdarzeniu/żądaniu
  • wskaźnik na payload (opcjonalny)
  • wielkość payloadu w bajtach (wartość liczbowa)

Realizacja, czyli wcielamy słowo w czyn

Dość już teorii, przejdźmy do opisu realizacji projektu.

Zespół projektowy

Zespół projektowy był, jest i będzie płynny oraz z założenia rozproszony lokalizacyjnie. Składają się na niego osoby pracujące ze wszystkich biur firmy Sii i dokładające swoją cegiełkę w ramach posiadanych umiejętności/doświadczenia/opanowanego zakresu technologii jak również posiadanego czasu.

Zarządzanie zadaniami

Wszystkie zadania projektowe zarówno te wynikające z pierwszej analizy wymagań jak i dodane w czasie późniejszym są trzymane w trackerze Jira w formie user stories i wynikających z nich zadań.

W ramach zwinnego podejścia do wytwarzania oprogramowania zespół na bieżąco (w 2 tygodniowych cyklach) analizuje, estymuje, wdraża i testuje kolejne funkcjonalności systemu.

Przegląd kodu

W ramach wspólnej kolaboracji nad kodem, wdrożyliśmy prosty proces przeglądu i oceny kodu oparty o GitHuba. W ramach niego każda zmiana, wykonana na odpowiednim branchu i wystawiona w formie pull-requesta jest oceniania przez co najmniej dwóch członków zespołu pod kątem:

  • Poprawności implementacji względem dokumentacji/projektu
  • Poprawności implementacji pod kątem programistycznym
  • Istnienia testów pokrywających nową funkcjonalność
Mały wyciąg z historii pull requestów
Ryc. 6. Mały wyciąg z historii pull requestów

Struktura projektu

Kod źródłowy podzieliliśmy na części odpowiedzialne za:

  • Zależną od sprzętu część zawierającą niskopoziomowe sterowniki (zaznaczone na zielono)
  • Aplikację (zaznaczoną na zielono) implementującą opisane wcześniej moduły funkcjonalne
  • Warstwę pośrednią (services) dostarczającą niezależne od sprzętu funkcjonalności (zaznaczone na pomarańczowo)
Struktura katalogów w projekcie
Ryc. 7. Struktura katalogów w projekcie

Do tego kilka katalogów zawierających pliki nagłówkowe od:

  • POSIX API oddzielającego system operacyjny od aplikacji
  • API warstwy services (pośredniej)
  • CMSIS API warstwy abstrakcji od sprzętu

Kod źródłowy i podejście do programowania

Sam kod źródłowy napisaliśmy w języku C. Poniżej nieco przydługi, ale jednocześnie treściwy fragment obrazujący proces inicjalizacji systemu, jego wątków i kolejek.

Wszystkie funkcjonalności dostarczane przez system operacyjny zostały opakowane – widzimy je w postaci makr i funkcji z przedrostkiem OS.

iot8 - IoT HUB, czyli zróbmy ciekawy projekt

Identyczne podejście zastosowaliśmy przy implementacji poszczególnych wątków. Wewnątrz umieszczonej w wątku pętli nieskończonej następuje:

  • Cykliczne aktualizowanie statusu (jak w przypadku zamieszonego poniżej wątku systemowego)
  • Oczekiwanie na pojawienie się wiadomości w przypisanej kolejce
iot9 1024x648 - IoT HUB, czyli zróbmy ciekawy projekt

Wybrane rozwiązania

Konsola debugowa

Zgodnie z wymaganiami istnieje możliwość diagnozowania i podglądania stanu IoT HUBa. W tym celu zaimplementowaliśmy konsolę debugową wraz z prostym menu.

Z jednej strony kluczowe dla działania systemu informacje są „wyrzucane” na port szeregowy –  wystarczy umieścić w kodzie wywołanie stosownego makra. Jednocześnie istnieje też możliwość wysłania prostego, znakowego zapytania (jest to pojedynczy znak ASCII) i otrzymania np. aktualnego IP urządzenia.

Obecny kształt konsoli debugowej
Ryc. 8. Obecny kształt konsoli debugowej

Kolejnym etapem będzie implementacja logu w pamięci nieulotnej wraz z możliwością jego odczytywania i czyszczenia z poziomu powyższej konsoli.

Serwer WWW

Aby usprawnić i przyspieszyć konfigurację IoT HUBa oraz umożliwić zdalny dostęp do jego zasobów, postawiliśmy na nim serwer WWW, oparty o LwIP. Sama integracja LwIP do projektu poszła sprawnie – wspieraliśmy się przy tym oprogramowaniem do generowania kodu, dostarczonym przez ST (CubeMX).

Na poniższym przykładzie widoczne jest okno służące do konfigurowania i wizualizowania funkcji termostatu – o czym opowiem szerzej w części opisującej przygotowane demo.

Wizualizacja stanu funkcji termostatu
Ryc. 9. Wizualizacja stanu funkcji termostatu.

Manager scen

Obecnie trwają pracę nad dodatkiem pozwalającym na dodawanie/usuwanie/monitorowanie akcji (nazwanym dalej scenami). W tym celu zaprzęgnięty został silnik graficzny oparty o googlowe Blockly.[7]

Za jego pomocą można w prosty sposób zmontować konkretną scenę i zapisać ją w pamięci IoT HUBa w formie pliku JSON. Następnie scena jest wykonywana, przy zajściu zdefiniowanych w niej warunków, przez wbudowany w IoT HUBa interpreter kodu Java Script.

Manager scen w obecnym (nieukończonym jeszcze) kształcie
Ryc. 10. Manager scen w obecnym (nieukończonym jeszcze) kształcie

Jenkins, testowanie – jednym słowem jakość

Żaden projekt, do którego kontrybuuje wiele osób, nie może się obyć bez zapewnienia choćby minimum testowania/jakości.

Do tego kwestia:

  • automatyzacji wykonywanych testów i generowania raportów testowych,
  • tworzenia paczek releasowych,
  • statycznej analizy kodu

również powinna zostać zaadresowana i zaimplementowana w projekcie.

Google test (Gtest)

Struktura testów jednostkowych w projekcie
Ryc. 11. Struktura testów jednostkowych w projekcie

Testy niskopoziomowych driverówZdecydowaliśmy się na wdrożenie w projekcie biblioteki unit testowej od Google’a.  Wymagało to napisania sporej liczby mocków. Przy czym same testy podzieliliśmy w sposób identyczny jak samo oprogramowanie:

  • Testy warstwy pośredniej (services)
  • Testy warstwy aplikacyjnej

Sam framework testowy daje dużo możliwości jeżeli chodzi o raportowanie – za jego pomocą możemy wygenerować szczegółowy raport dla każdego z plików źródłowych z konkretnym podziałem pokrycia testami na:

  • Linie kodu źródłowego
  • Rozgałęzienia w kodzie (branche)
  • Konkretne funkcje
Gtestowy raport dla IoT HUBa
Ryc. 12. Gtestowy raport dla IoT HUBa

Statyczna analiza kodu (CppCheck)

Dodatkowo postanowiliśmy narzucić sobie jeszcze jeden element – narzędzie do statycznej analizy kodu, celem wyeliminowania potencjalnych i realnych luk w kodzie źródłowym oraz zabezpieczenia się przed wszelkimi programistycznymi gafami, które umknęły uwadze recenzentów.

Znowu rozgorzały jednak dyskusje na temat wyboru konkretnego narzędzia. Ostatecznie podjęliśmy decyzję o użyciu CppChecka i traktowaniu generowanych przez niego wyników w charakterze wskazówek.

Testy automatyczne

Wymienione wyżej elementy zostały spięte pod sztandarem Jenkinsa, zainstalowanego na dedykowanej maszynie testowej. Dzięki temu w ciągu kilku minut otrzymujemy w pełni automatyczny i obiektywny status na temat jakości oprogramowania IoT HUBa.

Szybki rzut okiem w IoT HUBowego Jenkinsa
Ryc. 13. Szybki rzut okiem w IoT HUBowego Jenkinsa

Podsumowanie

Projekt IoT HUB pozwolił nam znacząco poszerzyć i ugruntować kompetencje z dziedziny Internet of Things. W ramach realizacji powstał, w dalszym ciągu rozwijany i udoskonalany, produkt łączący w sobie wiele technologii.

W toku prac dotychczasowych rozwiązaliśmy sporą ilość inżynierskich i typowo organizacyjnych problemów, takich jak na przykład:

  • Szybkie wdrażanie nowych osób, z różnych lokalizacji, do projektu

W tej kwestii pomogło opracowanie kompleksowej instrukcji, przeprowadzającej krok po kroku przez proces zestawiania środowiska oraz wdrażającej w arkana projektu

  • Nieoczekiwany reset urządzenia spowodowany pojawieniem się wyjątku

Tutaj pomogło wyposażenie exception handlera w dedykowany fragment kodu, służący do odszukania adresu w pamięci programu, z którego nastąpiło wygenerowanie wyjątku. Zwykle problem był spowodowany wyciekiem pamięci lub nadpisaniem stosu wątku przez inny wątek.

  • Brakiem przestrzeni dyskowej na maszynie Jenkinsowej

Na wczesnym etapie projektu wszystkie artefakty budowania były niepotrzebnie archiwizowane po stronie Jenkinsa. Gdy ilość danych przekroczyła 300 GB zaczęły pojawiać się błędy  spowodowane niemożnością zapisu kolejnych plików. Wyczyszczenie przestrzeni roboczej Jenkinsa połączone z ograniczeniem ilości archiwizowanych artefaktów do ostatnich 30 buildów rozwiązało problem.

Patrząc na projekt od strony czysto technicznej wykorzystaliśmy następujące technologie i narzędzia:

  • Od strony programistycznej
    • C
    • C++
    • Java Script
    • Groovy
  • Od strony komunikacyjnej
    • Bluetooth Low Energy
    • LoRa
    • UART, I2C, SPI
    • LwIP
  • Narzędzia
    • Eclipse
    • CubeMX,
    • GitHub
    • Jira
  • Zapewnienie jakości
    • Jenkins
    • GTest
    • CppCheck

.223rem

[1] Za Historią Grecką Ksenofronta

[2] http://www.domoticz.com/,

[3] https://www.openhab.org/

[4] https://www.home-assistant.io/

[5] CMSIS – Cortex Microcontroller Software Interface Standard

[6] Complex driver jest pojęciem ze standardu AUTOSAR i określa moduł/moduły, które nie są zdefiniowane przez ten standard. Głównym celem complex driver’a jest zamknięcie w nim złożonych, niestandardowych czy odziedziczonych sterowników (legacy drivers).

[7] https://developers.google.com/blockly/

5/5 ( głosy: 5)
Ocena:
5/5 ( głosy: 5)
Autor
Avatar
Mateusz Januszkiewicz

Ze światem urządzeń wbudowanych związany od 10 lat. Na co dzień pracuje na stanowisku Architekta Rozwiązań w Centrum Kompetencji Embedded, gdzie ma do czynienia z różnymi ARM-owymi i nie-ARM-owymi platformami oraz dotyka tematów związanych z niskopoziomowym bezpieczeństwem. W wolnym czasie zajmuje się strzelectwem i majsterkowaniem przez duże O.

Zostaw komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Może Cię również zainteresować

Pokaż więcej artykułów

Bądź na bieżąco

Zasubskrybuj naszego bloga i otrzymuj informacje o najnowszych wpisach.

Otrzymaj ofertę

Jeśli chcesz dowiedzieć się więcej na temat oferty Sii, skontaktuj się z nami.

Wyślij zapytanie Wyślij zapytanie

Natalia Competency Center Director

Get an offer

Dołącz do Sii

Znajdź idealną pracę – zapoznaj się z naszą ofertą rekrutacyjną i aplikuj.

Aplikuj Aplikuj

Paweł Process Owner

Join Sii

ZATWIERDŹ

This content is available only in one language version.
You will be redirected to home page.

Are you sure you want to leave this page?