

Obecnie, używając ekosystemu .NET możemy wytwarzać oprogramowanie na różne platformy, począwszy od podstawowego zastosowania jakim jest tworzenie backendów aplikacji webowych, poprzez aplikacje desktopowe, skończywszy na aplikacjach mobilnych dla systemów Android i iOS. Czy możliwe jest jednak pisanie w .NET-cie oprogramowania dla systemów wbudowanych, w przypadku których zasoby sprzętowe w postaci ilości dostępnej pamięci operacyjnej i mocy obliczeniowej procesora bywają mocno ograniczone?
Programowanie systemów wbudowanych, opartych o 8 lub 32-bitowe mikrokontrolery może kojarzyć się z koniecznością używania języków niższego poziomu niż C#, takich jak C/C++. Okazuje się jednak, że dzięki .NET nanoFramework możemy, z użyciem języka C#, tworzyć oprogramowanie działające na takich popularnych rodzinach mikrokontrolerów jak ESP32 czy STM32.
W tym artykule zaprezentuję przykład użycia nanoFrameworka oraz ESP32 do budowy prostego modułu stacji pogodowej, umożliwiającego pomiar temperatury, ciśnienia i wilgotności powietrza.
nanoFramework jest platformą umożliwiającą pisanie kodu w języku C# działającego na wielu rodzinach mikrokontrolerów. Kluczowym modułem jest nanoCLR – implementacja maszyny wirtualnej języka C# (ang. Common Language Runtime, CLR) przeznaczona specjalnie dla mikrokontrolerów.
nanoCLR implementuje tylko część standardu języka C# i API jego biblioteki standardowej głównie ze względu na bardzo ograniczone zasoby sprzętowe, którymi dysponują mikrokontrolery. Braki te nie stanowią jednak poważnego ograniczenia. Programista nadal może korzystać z takich udogodnień jak na przykład mechanizm automatycznej dealokacji pamięci (ang. garbage collection) czy składnia związana z obiektowym paradygmatem programowania (klasy, struktury, polimorfizm, itp.). W założeniu przyspiesza to tworzenie oprogramowania w porównaniu do użycia języka C/C++, zwłaszcza w sytuacji, kiedy programista nie posiada dużego doświadczenia w programowaniu systemów wbudowanych.
ESP32 jest 32-bitowym mikrokontrolerem produkowanym przez firmę Espressif Systems od 2016 roku. To następca 8-bitowego mikrokontrolera ESP8266, który tak jak swój poprzednik, szybko zdobył dużą popularność dzięki dobrej relacji ceny do oferowanych możliwości. Mikrokontroler został zaprojektowany między innymi dla zastosowań w urządzeniach ubieralnych (ang. wearable electronics), a także dla rozwiązań z zakresu Internetu rzeczy.
Jedną z jego głównych zalet jest zintegrowany interfejs Bluetooth Low Energy oraz WiFi. Umożliwia to łatwą integrację urządzeń opartych o ten mikrokontroler w rozwiązaniach z zakresu Internetu rzeczy. ESP32 jest powszechnie dostępny na rynku w postaci płytek prototypowych z zintegrowanym interfejsem USB.

Tego typu moduły sprawdzają się idealnie do tworzenia prototypów ze względu na łatwość zasilania i programowania mikrokontrolera poprzez interfejs USB oraz wyprowadzenie wszystkich portów wejścia-wyjścia pod postacią pinów GPIO.
Do pracy z nanoFrameworkiem najprościej wykorzystać IDE Visual Studio. Twórcy frameworka dostarczają odpowiednia wtyczkę dostępną w Visual Studio Market Place . Wtyczka ta umożliwia wygodne wgrywanie kodu do mikrokontrolera oraz jego debuggowanie. Potrzebujemy również narzędzia nanoff umożliwiającego wgranie odpowiedniej wersji nanoFrameworka do pamięci mikrokontrolera.
Posiadając płytkę prototypową z mikrokontrolerem ESP32 (np. ESP-WROOM-32 dostępny w wielu sklepach z podzespołami elektronicznymi) możemy przystąpić do pracy. Po podpięciu jej do komputera poprzez port USB sprawdzamy w menadżerze urządzeń, jaki numer portu został jej przypisany:

Na powyższym przykładzie widzimy, że numer portu to COM4. Następnie używamy narzędzia nanoff aby wgrać do mikrokontrolera najnowszą wersję nanoframeworka używając polecenia:
nanoff --update --target ESP32_PSRAM_REV0 --serialport COM4
Jeśli operacja przebiegnie poprawnie, zostanie wyświetlony stosowny komunikat:

Po wgraniu nanoFrameworka do mikrokontrolera powinien on być wykrywany w Visual Studio z poziomu sekcji „Device Explorer”, która jest dostępna w zakładce „View” -> „Other Windows” po instalacji pluginu:

Możemy następnie utworzyć nowy projekt z poziomu kreatora Visual Studio:

Nowy projekt domyślnie będzie posiadał implementację aplikacji typu „Hello world”:

Możemy teraz wgrać projekt do mikrokontrolera i uruchomić w trybie debuggowania w taki sam sposób, jak każdy inny projekt w Visual Studio. Jeśli operacja przebiegnie poprawnie, na wyjściu konsoli debuggowania zobaczymy napis „Hello from nanoFramework”:

Aby nasz mikrokontroler mógł dokonywać pomiarów temperatury, ciśnienia i wilgotności, potrzebne są odpowiednie czujniki i ich podłączenie do mikrokontrolera poprzez jego porty wejścia/wyjścia. Bardzo popularnym czujnikiem, który umożliwia pomiar jednocześnie tych 3 parametrów jest BME280 firmy Bosch. Jest szeroko dostępny na rynku, również w postaci płytki prototypowej:

BME280 do komunikacji z mikrokontrolerem używa magistrali I2C lub SPI. Schemat podłączenia tego czujnika do płytki prototypowej mikrokontrolera ESP32 poprzez magistralę I2C może wyglądać następująco:

Linię danych (SDA, połączenie oznaczone kolorem zielonym) podłączamy do portu 2, a linię sygnału zegarowego (SCL, połączenie oznaczone kolorem niebieskim) do portu 22 mikrokontrolera. Czujnik zasilany jest napięciem 3,3V wyprowadzonym z odpowiedniego pinu płytki prototypowej (połączenie oznaczone kolorem czerwonym).
Aby nawiązać komunikację pomiędzy ESP32 i BME280 potrzebujemy odpowiedniego sterownika. Nie musimy jednak implementować własnego rozwiązania w oparciu o dokumentację BME280 bowiem w pakiecie Nuget Iot.Device.Bmxx80 dostępny jest gotowy sterownik dla tego czujnika. Po dodaniu tego pakietu do naszego projektu w Visual Studio możemy skonfigurować komunikację z czujnikiem:

Najpierw należy skonfigurować określone piny mikrokontrolera (w tym przypadku piny 21 i 22) jako porty danych (SDA) i sygnału zegarowego (SCL) magistrali I2C. Następnie tworzymy konfigurację urządzenia dla magistrali I2C i przekazujemy ją do konstruktora klasy Bme280 reprezentującej sterownik czujnika BME280. Na końcu, w nieskończonej pętli, co każde 5 sekund odczytujemy wartości zmierzonej temperatury, ciśnienia i wilgotności z czujnika i przesyłamy wartości pomiarów jako sformatowany string na standardowe wyjście.
Po wgraniu tego prostego programu do pamięci mikrokontrolera i uruchomieniu go w trybie debugowania mikrokontroler zacznie co 5 sekund wypisywać na standardowe wyjście wyniki pomiarów:

Największą zaletą nanoFrameworka jest niski próg wejścia dla programistów, którzy dobrze znają język C# i ekosystem .NET ale nie mają doświadczenia w programowaniu mikrokontrolerów z użyciem najpowszechniej używanych w tym przypadku języków C i C++. Dzięki temu programista może skoncentrować się głównie na implementacji logiki biznesowej przy użyciu wysokopoziomowego języka C#, a nie zagłębianiu się w niuanse SDK dla konkretnego mikrokontrolera.
Do wad nanoFrameworka należy zaliczyć przede wszystkim to, że dodaje on sporą, dodatkową warstwę abstrakcji do oprogramowania działającego na mikrokontrolerze. W przypadku użycia języków C/C++ kod jest kompilowany bezpośrednio do kodu maszynowego wykonywanego przez CPU mikrokontrolera. W przypadku nanoFrameworka kod napisany w języku C# nie jest kompilowany do kodu maszynowego, lecz do kodu pośredniego (ang. Common Intermediate Language, CIL), który następnie jest wykonywany przez maszynę wirtualną. Może to mieć znaczący wpływ na wydajność w przypadku, kiedy kod wykonywany jest na mikrokontrolerze, czyli środowisku z natury wyposażonym w bardzo ograniczone zasoby (pamięć RAM, wydajność CPU).
Przedstawiony w artykule przykład użycia nanoFrameworka jest bardzo prosty. Czy można użyć tej platformy do implementacji znacznie bardziej skomplikowanego rozwiązania? Rozwijając pierwotny pomysł, w kolejnym artykule z tej serii przedstawię możliwości nanoFrameworka i ESP32 w zakresie integracji z usługą Azure IoT Hub. Pozwoli to na implementację dwustronnej komunikacji mikrokontrolera z dowolnymi innymi usługami działającymi w chmurze Azure.
Tak. Dzięki platformie .NET nanoFramework programiści mogą pisać kod w języku C# dla mikrokontrolerów, takich jak ESP32 i STM32, nawet jeśli urządzenia te mają ograniczoną pamięć i moc obliczeniową. Umożliwia to programistom zaznajomionym z ekosystemem .NET ponowne wykorzystanie swoich umiejętności i narzędzi do tworzenia rozwiązań wbudowanych lub IoT bez konieczności przechodzenia na języki C lub C++.
.NET nanoFramework to platforma typu open source, która zapewnia moc i wygodę środowiska .NET dla małych mikrokontrolerów o ograniczonych zasobach. Pozwala programistom korzystać z nowoczesnych funkcji programowania, takich jak klasy, async/await i wyjątki. NanoCLR (Common Language Runtime) to lekki silnik uruchomieniowy, który interpretuje i wykonuje kod C# bezpośrednio na urządzeniu, podobnie jak .NET CLR działa na komputerach stacjonarnych, ale jest zoptymalizowany pod kątem systemów wbudowanych.
Najpopularniejsze są rodziny ESP32 i STM32. ESP32 jest preferowany w projektach IoT ze względu na wbudowane funkcje Wi-Fi i Bluetooth, dzięki czemu doskonale nadaje się do urządzeń podłączonych do sieci i automatyki domowej. Z kolei seria STM32 oferuje szeroki zakres opcji pod względem wydajności i efektywności energetycznej, odpowiednich zarówno do zastosowań hobbystycznych, jak i przemysłowych.
Aby rozpocząć programowanie, zainstaluj Visual Studio i dodaj rozszerzenie .NET nanoFramework. Następnie użyj narzędzia wiersza poleceń nanoff, aby wgrać framework do mikrokontrolera przez USB. Integracja z Visual Studio pozwala pisać, debugować i wdrażać kod C# bezpośrednio na urządzeniu, zapewniając płynną pracę podobną do standardowego programowania w środowisku .NET.
Tak. Platforma .NET nanoFramework obsługuje integrację z platformami chmurowymi, takimi jak Azure IoT Hub, umożliwiając dwukierunkową komunikację między urządzeniami a usługami w chmurze. Oznacza to, że można tworzyć skalowalne ekosystemy IoT, które wysyłają dane telemetryczne, odbierają zdalne polecenia lub uruchamiają zautomatyzowane działania — wszystko napisane w języku C# przy użyciu znanych bibliotek i narzędzi .NET.
Komentarze
Good insights on .NET. I’m curious if it’s suitable for low-power devices.