[PRZYKŁAD] Wysyłanie danych z Raspberry Pi Pico W do chmury Microsoft Azure
W niniejszym artykule zaprezentuję sposób połączenia układu z Raspberry Pi Pico W poprzez sieć Wi-Fi z chmurą Microsoft Azure. Opiszę konfigurację chmury oraz kod wysyłający dane z czujnika ciśnienia atmosferycznego do usługi. W projekcie wykorzystam platformę Micropython oraz prosty protokół MQTT, a także płytkę KAmodMPL3115A2 z czujnikiem ciśnienia atmosferycznego.
Usługi chmurowe we współczesnych systemach IoT
We współczesnych systemach IoT usługi chmurowe pełnią niezwykle ważną rolę. Umożliwiają gromadzenie danych z wielu czujników, analizę, wnioskowanie, inicjowanie akcji na podstawie danych, a także wiele innych działań w ramach systemu. Wysyłanie danych ze zdalnych sensorów do chmury jest podstawowym zadaniem podczas projektowania systemów IoT. Jest bowiem obecne w większości rozwiązań, niezależnie od innych wymagań projektowych, zastosowanych elementów czy technologii komunikacyjnych.
Jedną z płytek przystosowanych dla rozwiązań IoT jest Raspberry Pi Pico W z układem Wi-Fi. Płytka zawiera wszystko co potrzeba do zastosowania w tego typu projektach – dwurdzeniowy mikrokontroler RP2040 oraz moduł komunikacji bezprzewodowej. Niewielkie wymiary płytki oraz wygodne wyprowadzenia pozwalają w dość prosty sposób dołączyć dodatkowe peryferia i stworzyć prototyp (a może nawet ostateczną wersję) zdalnego sensora lub też innego urządzenia IoT.
Co prawda można zaprojektować własne, dedykowane rozwiązanie chmurowe, ale w większości przypadków korzysta się z wykupienia zasobów od jednej z platform. Oferują one różne plany subskrypcyjne, również w modelu Pay-as-you-go (płatność tylko za wykorzystane zasoby). Często dostępne są także opcje bezpłatne, które można wykorzystać do przetestowania działania chmury, lub też uruchomienia aplikacji nie potrzebującej zbyt dużych zasobów. W artykule skorzystam z jednego z najpopularniejszych dostawców usług, a mianowicie Microsoft Azure.
Informacje do chmury będą przesyłane z wykorzystaniem protokołu MQTT. Jest to lekki i prosty protokół transmisji danych oparty o wzorzec publikacja/subskrypcja. Protokół jest szeroko stosowany w aplikacjach IoT, również w przemyśle (IIoT), ale nie tylko. Na podstawie tego protokołu działa m.in. komunikator Facebook Messenger. Więcej informacji można znaleźć na stronie mqtt.org.
Kod przykładu opracowano w języku Micropython. Jak sama nazwa wskazuje jest to implementacja języka Python przeznaczona do programowania systemów wbudowanych. Bardzo szybko może ją opanować każdy, kto choć trochę zna język Python. Jako środowisko programistyczne wykorzystam Thonny, które zapewnia wygodną edycję kodu, a także możliwość wgrywania go na płytkę.
Utworzenie Azure IoT Hub w chmurze Microsoft
Pracę rozpoczniemy od utworzenia w serwisie Azure modułu IoT Hub. Ten moduł pozwala na połączenie urządzeń zdalnych z chmurą, a następnie zarządzanie przesłanymi wiadomościami. Korzysta on z koncepcji rejestracji urządzeń, co oznacza, że każdy układ otrzymuje indywidualne poświadczenie konieczne do połączenia z chmurą. IoT Hub oparto o moduł Event Hub z tą różnicą, że umożliwia ona dwukierunkową komunikację z urządzeniem. Hub obsługuje protokoły AMQP oraz MQTT.
Aby utworzyć moduł należy posiadać lub założyć konto w serwisie Azure. Można zarejestrować się na darmowy okres próbny. Projektowana aplikacja nie będzie wymagała zasobów płatnych, a więc przetestowanie jej nie będzie kosztowało ani grosza.
Po zalogowaniu do panelu użytkownika można utworzyć nowy zasób przyciskiem „Utwórz zasób”.
Rys. 1. Panel usług Azure – zaznaczony przycisk „Utwórz zasób”
Po przyciśnięciu przycisku należy wyszukać moduł „IoT Hub”, a następnie utworzyć go przyciskiem „Utwórz”. W kolejnym ekranie należy podać dane modułu, w tym nazwy grupy zasobów oraz centrum IoT. W szczególności należy wiedzieć, że centrum IoT musi mieć unikalną nazwę, gdyż będzie to część adresu służącego do łączenia z zasobami chmury. Powinniśmy w efekcie otrzymać efekt jak na obrazku poniżej.
Rys. 2. Podstawowe dane centrum IoT
W sekcji „Sieć” w ramach uproszczenia demonstracji zostawię oznaczoną opcję Dostęp publiczny. Natomiast w ekranie „Zarządzanie” można wybrać plan cenowy – najlepiej na początku oznaczyć Warstwę Bezpłatną.
Rys. 3. Konfiguracja planu subskrypcyjnego
To już wszystkie zmiany, które należy wprowadzić w konfiguracji modułu. Po kliknięciu na „Przeglądanie + tworzenie” otrzymamy podsumowanie danych modułu z uwzględnieniem ceny usługi. Po stwierdzeniu zgodności z założeniami można utworzyć Hub przyciskiem Utwórz.
Rys. 4. Podsumowanie konfiguracji modułu IoT Hub
Po zakończeniu procesu można przejść do zasobu.
Utworzenie urządzenia dla Azure IoT Hub
Kolejnym ważnym krokiem jest utworzenie i rejestracja urządzenia dla huba. Aby to zrobić należy na pasku bocznym wejść w opcję Urządzenia i kliknąć Dodaj urządzenie. Oczywiście będziemy dodawać płytkę Raspberry Pi Pico W.
Rys. 5. Przycisk „Dodaj urządzenie” w panelu Urządzenia
W następnym okienku można wybrać nazwę urządzenia. Ważniejszą opcją jest jednak wybór typu uwierzytelniania. Należy tu wybrać opcję „Klucz symetryczny” i oznaczyć opcję „Automatycznie generuj klucze”. Z kolei opcję „Połącz to urządzenie z centrum IoT” należy pozostawić włączoną.
Rys. 6. Ekran tworzenia urządzenia dla modułu IoT Hub
Narzędzie Azure IoT Explorer
Dane z czujnika ciśnienia oraz płytki Pico W będą przesyłane w postaci tzw. wiadomości telemetrycznych. Narzędzie Azure IoT Explorer pozwala na monitorowanie tych wiadomości. Program umożliwia również uzyskanie informacji o połączeniu, a także danych uwierzytelniających urządzenie. Azure IoT Explorer jest dostępny na portalu GitHub, skąd można pobrać pliki instalacyjne.
Po instalacji i uruchomieniu oprogramowania należy dodać połączenie z modułem IoT Hub. W tym celu należy kliknąć przycisk „Add Connection” i w nowo otwartym polu wkleić definicję połączenia. Te definicje można znaleźć w panelu chmury, otwierając opcję „Zasady dostępu współużytkowanego”.
Rys. 7. Ekran „Zasady dostępu współużytkowanego” w panelu chmury Azure
Na ekranie widzimy kilka poziomów dostępu, które są definiowane automatycznie podczas tworzenia huba. Odpowiednie poziomy dostępu są dostosowane do typowych zadań urządzeń w systemie. My skorzystamy z poziomu „iothubowner”, która daje pełen dostęp do wszystkich operacji, co nieco uprości demonstrację. Pamiętaj jednak, aby stosować odpowiednią politykę nadawania uprawnień, gdyż jest to niezbędny element bezpieczeństwa systemu.
W nowo otwartym oknie ukaże się zestaw kluczy oraz możliwość zmiany uprawnień. Naszym zadaniem jest skopiowanie pola „Podstawowe parametry urządzenia” i wklejenie zawartości do pola „Connection String” w Azure IoT Explorer.
Rys. 8. Okno z parametrami połączenia w panelu chmury
Po wklejeniu danych i zapisaniu ukaże się okno z listą urządzeń. Aktualnie dostępny jest jedynie jedno o nazwie picow.
Połączenie sprzętowe układu
Jak wspomniano w roli zdalnego sensora wystąpi płytka Raspberry Pi Pico W połączona do modułu KAmodMPL3115A2 z czujnikiem ciśnienia atmosferycznego. Sensor jest podłączony do mikrokontrolera za pośrednictwem magistrali I2C. Sposób połączenie zaprezentowano w tabeli, natomiast pełen układ zaprezentowano na fotografii 9.
Tab. 1. Połączenie Raspberry Pi Pico W z modułem KAmodMPL3115A2
Raspberry Pi Pico W | KAmodMPL3115A2 |
35 | VDD |
3 | GND |
6 | SCL |
7 | SDA |
Fot. 9. Połączony układ sensora zdalnego
Kompletny rozkład wyprowadzeń płytki Pico W można znaleźć na stronie z dokumentacjami produktów Raspberry Pi pod nazwą picow/PicoW-A4-Pinout.pdf
Połączenie płytki z platformą Micropython oraz instalacja pakietu umqtt.simple
Zanim przejdziemy do programowania płytki należy wgrać do pamięci Flash firmware z interpreterem języka Micropython. Najnowsza wersja tego pliku znajduje się na stronie https://micropython.org/download/rp2-pico-w/. Aby wgrać plik należy podczas podłączania pliku do komputera przytrzymać przycisk BOOTSEL, co sprawi, że zostanie odczytana jako pamięć przenośna. Wtedy wystarczy skopiować plik .uf2 z firmware, a płytka sama zrestartuje się i uruchomi kod. Jeśli wszystko poszło zgodnie z planem, to w środowisku Thonny Raspberry Pi Pico będzie wykrywane jako urządzenie MicroPython.
Rys. 10. Raspberry Pi Pico wykrywane w środowisku Thonny
Tak jak wspomnieliśmy płytka Pico W będzie komunikować się z chmurą za pomocą protokołu MQTT. W tym celu wykorzystamy moduł umqtt.simplejęzyka Micropython. Aby jednak móc korzystać z niego w swoim kodzie należy go pobrać na płytkę Pico. W tym celu należy wpisać w interpreterze następujące linijki kodu:
import network ssid = 'SSID' password = 'HASŁO' wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) import upip upip.install('umqtt.simple')
Powyższy kod działa w sposób następujący: importuje moduł network, a następnie aktywuje i łączy się z podaną siecią Wi-Fi. Do zmiennych ssid oraz password należy wpisać nazwę oraz hasło do naszej sieci (pomiędzy apostrofami). Po połączeniu z siecią kod importuje moduł upip, a więc instalator pakietów dla języka Micropython (odpowiednik pip dla Pythona), a na koniec instaluje pakiet umqtt.simple.
Po wykonaniu wszystkich instrukcji powinniśmy otrzymać komunikat o rozpoczęciu instalacji pakietu. Po instalacji można zrestartować połączenie przyciskiem Stop/Restart.
Rys. 11. Komunikat o zainstalowanym module umqtt.simple
Kod przykładu
Kod przykładu ma za zadanie połączyć się z siecią oraz chmurą Azure, a następnie cyklicznie pobierać dane o pomiarze ciśnienia z czujnika i wysyłać je do chmury.
W pierwszej kolejności importujemy wszystkie niezbędne moduły języka Micropython:
import network import time import machine import struct from umqtt.simple import MQTTClient
Następnie definiujemy dane sieci Wi-Fi oraz podejmujemy próbę połączenia. Oczywiście do zmiennych ssid oraz password przypisujemy nazwę i hasło naszej sieci. Po wykonaniu komendy connect sprawdzamy w pętli stan połączenia. Dalsza cześć skryptu wykona się dopiero, gdy nawiązane zostanie połączenie z siecią.
import network ssid = 'SSID' password = 'HASŁO' wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) max_wait = 10 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') time.sleep(1) if wlan.status() != 3: raise RuntimeError('network connection failed') else: print('connected') status = wlan.ifconfig() print( 'ip = ' + status[0] )
Kolejnym krokiem jest konfiguracja interfejsu I2C oraz sensora. Czujnik jest dołączony do portu I2C0 na pinach domyślnych (6 i 7). Natomiast konfiguracja czujnika obejmuje włączenie pomiaru, a także ustawienie nadpróbkowania. Więcej informacji o sensorze znajdziesz w dokumentacji dostępnej na stronie modułu KAmodMPL3115A2 w zakładce zasoby.
i2c=machine.I2C (0, freq=400000) i2c.writeto_mem (96,0x26,b'\x39')
Zmienne i funkcje do obsługi MQTT
Następnie definiujemy zmienne używane do obsługi komunikacji z chmurą Azure oraz do konfiguracji MQTT. Mają one następujące znaczenie:
- hostname: adres naszego huba IoT,
- clientid: nazwa urządzenia,
- user_name: nazwa dla uwierzytelniania połączenia z hubem,
- passw: token SAS używany do uwierzytelniania,
- topic_pub: temat publikacji używany wysyłania wiadomości telemetrycznych,
- port_no: numer portu używanego do publikacji wiadomości MQTT; w tym przypadku 0 oznacza port domyślny, a więc 8883, ponieważ używamy SSL
- subscribe_topic: temat wiadomości zasubskrybowanych, a więc wysyłanych z chmury do urządzenia,
- topic_msg: treść wiadomości wysyłanych do chmury (ta zmienna zostanie zdefiniowana w dalszej części kodu).
hostname = 'NAZWA_HOSTA.azure-devices.net' clientid = 'picow' user_name = 'NAZWA_HOSTA.azure-devices.net/picow/?api-version=2021-04-12' passw = 'TOKEN_SAS' topic_pub = b'devices/picow/messages/events/' port_no = 0 subscribe_topic = "devices/picow/messages/devicebound/#"
Generacja tokena SAS
Zmienne należy uzupełnić w następujący sposób: NAZWA_HOSTA to nazwa wpisywana podczas rejestracji huba; w naszym przypadku to MikrokontrolerPL. Natomiast token SAS należy uzyskać z narzędzia Explorer. Po wejściu do urządzenia picow widzimy ekran Device identity, gdzie znajdziemy m.in. klucze uwierzytelniające i parametry łączności. Poniżej znajdziemy sekcję „Connection string with SAS token”, którą należy rozwinąć.
Rys. 12. Generowanie tokena SAS w Azure IoT Explorer
Potrzebujemy klucza typu Primary Key. Warto zmienić czas obowiązywania tokena na więcej niż 5 minut, gdyż będzie to wygodne podczas testów. Przyciskamy przycisk Generate i tworzymy token, który należy skopiować do edytora tekstowego.
Token wygląda podobnie do poniższego:
HostName=MikrokontrolerPL.azure-devices.net;DeviceId=picow;SharedAccessSignature=SharedAccessSignature sr=MikrokontrolerPL.azure-devices.net%2Fdevices%2Fpicow&sig=0000000000000000000000000000000000000000000%3D&se=1665498192
Należy zmodyfikować ten łańcuch znaków: początkowa część zawierająca hosta i nazwę urządzenia jest zbędna i trzeba ją usunąć, tak samo jak początkową nazwę zmiennej „SharedAccessSignature=”. Końcowa forma odpowiednia do wklejenia do kodu wygląda następująco:
SharedAccessSignature sr=MikrokontrolerPL.azure-devices.net%2Fdevices%2Fpicow&sig=0000000000000000000000000000000000000000000%3D&se=1665498192
Funkcja mqtt_connect odpowiada za łączenie z brokerem (serwerem) MQTT. Rozpoczyna się załadowaniem certyfikatu Baltimore SSL, który umożliwia łączenie się z hubem w sposób bezpieczny i szyfrowany. Następnie definiowane są parametry klienta MQTT. Na koniec funkcja łączy się z brokerem i zwraca obiekt klienta MQTT do użycia w dalszej części kodu.
def mqtt_connect(): certificate_path = "baltimore.cer" print('Loading Blatimore Certificate') with open(certificate_path, 'r') as f: cert = f.read() print('Obtained Baltimore Certificate') sslparams = {'cert':cert} client = MQTTClient(client_id=clientid, server=hostname, port=port_no, user=user_name, password=passw, keepalive=3600, ssl=True, ssl_params=sslparams) client.connect() print('Connected to IoT Hub MQTT Broker') return client
Kolejną funkcją jest funkcja ponownego łączenia. Jest wywoływana, gdy płytka straci połączenie z brokerem MQTT. Wtedy wysyła informację na konsolę, a następnie resetuje układ, co ma taką wadę, że zrywa także połączenie ze środowiskiem Thonny.
def reconnect(): print('Failed to connect to the MQTT Broker. Reconnecting...') time.sleep(5) machine.reset()
Dalej mamy funkcję odpowiedzialną za obsługę wiadomości przychodzących. Ponieważ nie jest to przedmiotem projektu, funkcja ta jest maksymalnie uproszczona i wypisuje jedynie tekst wiadomości na konsolę. Oczywiście jest to miejsce, w którym można zaimplementować bardziej zaawansowane sterowanie z chmury.
def callback_handler(topic, message_receive): print("Received message") print(message_receive)
Połączenie z brokerem MQTT
W tym momencie kodu łączymy się z brokerem MQTT. Kod ten jest w ramce try, a więc z obsługą wyjątków. Program łączy się z brokerem MQTT, definiuje funkcję callback dla wiadomości przychodzących, a następnie uruchamia subskrypcję wiadomości z chmury. W razie wystąpienia błędu połączenia kod uruchomi procedurę ponownego łączenia.
try: client = mqtt_connect() client.set_callback(callback_handler) client.subscribe(topic=subscribe_topic) except OSError as e: reconnect()
Główna pętla programu rozpoczyna się od sprawdzenia wiadomości przychodzących. Następnie z sensora odczytywane jest ciśnienie atmosferyczne (3 bajty pod adresami 0x01-0x03). Dane są konwertowane na hektopaskale i lądują w zmiennej pressure. Na koniec tworzymy łańcuch znaków topic_msg do którego wpisujemy nazwę danych („pressure”) oraz wartość pomiaru. Całość wysyłamy do chmury.
Pętla wykonuje się co 15 sekund. Pakiet darmowy chmury umożliwia wysłanie 8000 wiadomości dziennie, a więc jak łatwo policzyć program mieści się w tym zakresie.
while True: client.check_msg() data = i2c.readfrom_mem(96, 0x01, 3) pres = ((data[0] * 65536) + (data[1] * 256) + (data[2] & 0xF0)) / 16 pressure = (pres / 4.0) / 100.0 topic_msg = '{"pressure":"' + str(pressure) + '"}' client.publish(topic_pub, topic_msg) time.sleep(15)
Plik z pełnym kodem znajduje się w sekcji „Do pobrania”
Utworzenie certyfikatu Baltimore SSL
Przed uruchomieniem programu należy jeszcze zapisać w pamięci płytki certyfikat Baltimore SSL, który umożliwia szyfrowanie komunikacji. Aby to zrobić trzeba stworzyć w środowisku Thonny nowy plik o następującej treści:
-----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp -----END CERTIFICATE-----
Ten plik należy zapisać w pamięci Raspberry Pi Pico pod nazwą “baltimore.cer“.
Odbiór wiadomości w Azure IoT Explorer
Uzupełniony kod można uruchomić w środowisku Thonny. Na konsoli ukażą się wiadomości o połączeniu z Wi-Fi, załadowaniu certyfikatu oraz połączeniu z brokerem MQTT. Po otrzymaniu tej ostatniej wiadomości urządzenie powinno już wysyłać do chmury wiadomości telemetryczne.
UWAGA!! W przypadku otrzymania błędu “MQTTException: 5” najprawdopodobniej powodem jest wygaśnięcie tokena SAS. W takim wypadku należy wygenerować nowy token i wprowadzić go do kodu. Jeśli to nie pomoże, sprawdź poprawność wszystkich pozostałych zmiennych dotyczących połączenia z brokerem.
Teraz można już monitorować wiadomości telemetryczne wysyłane do chmury. W tym celu w Explorerze wejdź do zakładki Telemetry i wciśnij przycisk Start.
UWAGA!! W przypadku powtarzającego się błędu Failed to start monitoring device telemetry: Error: „The messaging entity could not be found” zainstaluj starszą wersję 0.14.12.
Rys. 13. Odbiór wiadomości telemetrycznych w Azure IoT Explorer
Podsumowanie
W artykule zaprezentowałem sposób łączenia zdalnego czujnika opartego o płytkę Raspberry Pi Pico z chmurą Microsoft Azure. Wiedząc jak przesłać dane z czujnika do chmury, a także mając wiedzę o możliwości przesyłania komend w drugą stronę można zaprojektować aplikację IoT opartą o przetwarzanie danych w chmurze. Pod tym kątem Azure ma duże możliwości programowania oraz wykorzystywana strumieni danych, co jednak wykracza poza zakres tego artykułu. Tym niemniej funkcja wysyłania danych z sensorów do chmury jest podstawową koncepcją w systemach IoT, niezależnie od użytych komponentów, usług czy skomplikowania projektu.