Trochę o moim frameworku IoT - część pierwsza

Wstępniak

Kilka lat temu rozpoczęła się moja przygoda z układami firmy Espressif czyli ESP8266 oraz ESP32. Te małe scalaki to w zasadzie mikrokomputery, na których działa cały stack protokołu TCP/IP w ramach sieci bezprzewodowej WiFi. Oczywiście, Wiedźmina III na tym nie odpalimy ale nie takie też jest przeznaczenie tych urządzeń.

Czyli... co Ty chcesz w ogóle tym osiągnąć?

Początkowo motywacja do zabawy z ESP8266 była prozaiczna. Mieliśmy w domu starego kopciucha (taki piec do centralnego ogrzewania), do którego co jakiś czas trzeba było dorzucać węgla. Problemem było to, że do kotłowni wchodzi się na zewnątrz budynku i po prostu czasochłonnym było co chwilę tam zaglądać. Termometr I2C rozwiązał sprawę, dodatkowo dołożyłem przekaźnik załączający pompkę CO, by przy braku ognia nie było zbędnego, kompletnie niepotrzebnego obiegu wody. Zaprojektowałem fatalną ale działającą płytkę PCB, która realizowała te zadania.

Kolejnym motywatorem do zabawy z ESP8266 stała się chęć zdalnej kontroli - włączania/wyłączania świątecznych dekoracji, bez konieczności szukania przewodów pod choinką. Kupiłem w ciemno pilota z gniazdkami na 433 MHz i moduły odbiorczo-nadawcze do Arduino. Udało się to podsłuchać i zrobić własną bramkę WiFi.

Czasy się jednak zmieniły... w związku z prowadzonymi termomodernizacjami, śmieciuch został wymieniony na nowoczesny kocioł pelletowy. Nowy kocioł posiada w zasadzie całą automatykę ogrzewania. Od samego początku miałem plan, by wykorzystać magistralę CAN, która wyprowadzona jest na złączu z tyłu kotła. Zadanie udało się zrealizować przy użyciu modułów ESP32 ale też dzięki doświadczeniu, które pozwoliło mi zaprojektować czterowarstwowe, dużo lepsze PCB.

W pewnym momencie, podczas opisywanej powyżej przygody, dotarło do mnie, że przyda się coś, co umożliwi uniwersalne i szybkie tworzenie oprogramowania na moje urządzenia.

... i tak powstał ksIotFramework

Każde z opisanych wcześniej urządzeń dzieliło parę wspólnych cech. Stwierdziłem, że warto to wyrzucić do jakiejś biblioteki / frameworka.

Oto ich lista:
  • Konfigurowanie i odczyt parametrów w portalu konfiguracyjnym (WiFiManager).
  • Wymiana danych z serwerem MQTT (za pomocą PubSubClient).
  • Utrzymywanie połączenia WiFi (połączenie ponowne po utracie sieci itp).
  • Architektura aplikacji (komponenty, modularność).
  • Wspólne komendy debugowania (zapytanie o wolny RAM, adres IP itd.)
  • Timery, obsługa peryferiów i inne (reset button, miganie diodami etc.).
Framework został zbudowany w oparciu o architekturę kompozycji z wykorzystaniem platformy Arduino i przeznaczony jest dla platform sprzętowych ESP8266/ESP32. 

Na samej górze jest aplikacja, na którą składają się komponenty. Podczas inicjalizacji aplikacji dodawane są komponenty. Następnie są inicjalizowane. Po inicjalizacji, każdy komponent może wyciągnąć sobie wskaźnik do innych komponentów i dzięki temu bezpośrednio z nimi się komunikować. Główna pętla programu przechodzi przez aplikację i dalej po wszystkich komponentach, umożliwiając wykonywanie zadań.

Schemat działania aplikacji w ksIotFramework.

Tyle teorii a w praktyce...

Rozwiązanie ma kilka wad, na przykład to, że zakłada budowanie przez wtyczkę Visual Micro. Drugą wadą jest konieczność manualnej instalacji kilku bibliotek. Dalej jest z górki.

Jak zacząć?
  • Pobieramy Visual Studio 2019 oraz wtyczkę Visual Micro.
  • Pobieramy Arduino IDE.
  • Instalujemy platformy ESP32 i/lub ESP8266.
  • Pobieramy zipa z libką ksIotFrameworkLib.
    Ddajemy do Arduino: Szkic -> Dołącz bibliotekę -> Dodaj bibliotekę ZIP.
  • Poprzez menedżer bibliotek zaciągamy biblioteki ArduinoOTA oraz PubSubClient.
  • Pobieramy zipa z libką WiFiManager.
    Ddajemy do Arduino: Szkic -> Dołącz bibliotekę -> Dodaj bibliotekę ZIP.
  • Zaciągamy przykładowy projekt i modyfikujemy na swoje potrzeby.

Realne urządzenie - studium przypadku

Analizujemy kod urządzenia do zdalnego sterowania kotłem grzewczym. Aplikacja łączy się z kotłem po magistrali CAN oraz po protokole MQTT z brokerem MQTT. Następnie systematycznie wysyła dane statusowe kotła (temperatura CWU, temperatura kotła, informacje o błędach, informacje o mocy oraz zużyciu paliwa). Poza tym może sterować grzaniem centalnym oraz wody użytkowej.


W związku z tym, że framework nie posiada jeszcze kreatorów projektów i dokumentacji (w sumie nie wiem czy kiedykolwiek będzie posiadał - ale im więcej użytkowników / kontrybutorów, tym większe na to szanse), najlepiej będzie przespacerować się po kodzie przykładowego projektu. Nie będzie to przejście linia po linii, bo nie starczy nam na to czasu.



Plik .ino w aplikacji budowanej w oparciu o ksIotFramework ma być jak najmniejszy. To definicja odgórnego działania aplikacji. To właśnie tutaj zaczyna się typowy Arduino'wy "sketch". W funkcji setup musimy koniecznie wywołać funkcję initKsfFramework, która inicjalizuje między innymi system plików.

W funkcji loop, wywoływane jest makro, które tworzy instancję podanej klasy typu ksApplication, inicjalizuje ją i wywołuje funkcję loop w aplikacji, dopóki ta nie wywoła przerwania tej pętli. Przerwanie tej pętli nastąpi również, gdy metoda init zwróci false. Inicjalizację jak również pętlę aplikacji mogą przerwać jej komponenty.

Tak wygląda inicjalizacja aplikacji. Tworzone są komponenty - ksWiFiConnector, który przyjmuje nazwę urządzenia (hostname), komponent ksMqttDebugResponder (który daje dostęp do komend debugowania) oraz ksMqttConnector, umożliwiający łączenie się z brokerem MQTT. Tutaj warto wspomnieć, że ksMqttDebugResponder ptrzebuje połączenia MQTT (ksMqttConnector) do działania. Dodawany jest też komponent sterujący diodą LED - wskaźnikiem stanuPo dodaniu komponentów wywoływana jest metoda z klasy bazowej ksApplication::init, która wykonuje proces inicjalizacji komponentów. Najpierw wywołuje metodę init a następnie postInit na wszystkich komponentach. Jeśli któryś komponent zwróci false, również inicjalizacja aplikacji zostanie przerwana. W tym momencie wpadniemy do drugiej zdefiniowanej aplikacji - konfiguratora połączenia WiFi (patrz - pierwszy zrzut ekranu).

Jeśli natomiast wszystko przebiegnie pomyślnie, zostanie zainicjalizowana biblioteka ArduinoOTA i uruchomione zostanie "mruganie" diodą (co 500 ms zaneguj poprzedni stan).

Na koniec inicjalizacja aplikacji zwraca true, co spowoduje przejście w tryb pętli aplikacji. Od teraz ze zdefiniowaną częstotliwością (domyślnie co 1 ms) będzie uruchamiana funkcja loop w aplikacji i na wszystkich komponentach.(zob. definijcę makra RUN_APP_BLOCKING_LOOPED).


Powyższy zrzut pokazuje jak wygląda metoda loop aplikacji. Można powiedzieć, że to tutaj bije jej serce. Kodu na pierwszy rzut oka jest mało. Zapytacie - A gdzie tutaj logika aplikacji? Ano logika kryje się w komponentach, które wyołuje (analogicznie jak przy inicjalizacji) metoda klasy bazowej ksApplication::loop.

Huh. Sporo tego, jak na jeden wpis...

Nie myślałem, zaczynając tego posta, że tyle z tego wyjdzie. Jeśli będziecie zainteresowanie działaniem szczegółowym komponentu VideNetClient, w którym znajduje się realna logika aplikacji, chętnie podzielę się kolejnym postem z Wami.

Dajcie znać, jak zwykle - w komentarzach. Przy okazji - Szczęśliwego Nowego Roku 2022!

Komentarze

Prześlij komentarz