Klient IoT Amazon MQTT

Czym jest AWS IoT?

AWS IoT zapewnia bezpieczną, dwukierunkową komunikację między urządzeniami podłączonymi do internetu (takimi jak czujniki, siłowniki, mikrokontrolery lub inteligentne urządzenia) a chmurą AWS. Umożliwia to zbieranie danych telemetrycznych z wielu urządzeń, ich przechowywanie i analizę. Można również tworzyć aplikacje pozwalające użytkownikom sterować tymi urządzeniami z telefonów lub tabletów.

 

Broker wiadomości

Zapewnia bezpieczny mechanizm umożliwiający urządzeniom i aplikacjom AWS IoT wzajemne publikowanie i odbieranie wiadomości. Można korzystać zarówno z protokołu MQTT bezpośrednio, jak i MQTT przez WebSocket do publikowania i subskrybowania.

 

Broker wiadomości AWS IoT to usługa brokera publikuj/subskrybuj umożliwiająca wysyłanie i odbieranie wiadomości do i z AWS IoT. Podczas komunikacji z AWS IoT klient wysyła wiadomość zaadresowaną do tematu, takiego jak Sensor/temp/room1.

 

Broker wiadomości z kolei wysyła wiadomość do wszystkich klientów, którzy zarejestrowali się do odbierania wiadomości dla danego tematu. Czynność wysyłania wiadomości jest nazywana publikowaniem. Czynność rejestrowania się do odbierania wiadomości dla filtru tematu jest nazywana subskrybowaniem.

 

Przestrzeń nazw tematów jest izolowana dla każdej pary konta AWS i regionu. Na przykład temat Sensor/temp/room1 dla jednego konta AWS jest niezależny od tematu Sensor/temp/room1 dla innego konta AWS. Dotyczy to również regionów. Temat Sensor/temp/room1 na tym samym koncie AWS w us-east-1 jest niezależny od tego samego tematu w us-east-2. AWS IoT nie obsługuje wysyłania i odbierania wiadomości między kontami AWS i regionami.

 

Broker wiadomości przechowuje listę wszystkich sesji klientów wraz z subskrypcjami dla każdej sesji. Gdy wiadomość jest publikowana na temat, broker sprawdza sesje z subskrypcjami pasującymi do tego tematu. Broker przekazuje następnie opublikowaną wiadomość do wszystkich sesji z aktywnie połączonym klientem.

 

Klient MQTT

TsgcIoTAmazon_MQTT_Client to komponent służący do łączenia się z AWS IoT. Jeden klient może połączyć się tylko z jednym urządzeniem. Klient łączy się przy użyciu protokołu MQTT i uwierzytelnia się za pomocą certyfikatu klienta X.509.

 

Aby połączyć się z AWS IoT, klient musi mieć skonfigurowane następujące właściwości:

 

Amazon.ClientId: identyfikacja klienta, opcjonalna.

Amazon.Endpoint: nazwa serwera, z którym klient MQTT będzie się łączyć.

Amazon.Port: domyślnie używany jest port 8883. Jeśli port wynosi 443, do połączenia automatycznie stosowany jest ALPN (wymaga niestandardowej wersji Indy).

 

AWS IoT Core obsługuje urządzenia i klientów korzystających z protokołów MQTT i MQTT over WebSocket Secure (WSS) do publikowania i subskrybowania wiadomości. Poniższa tabela zawiera protokoły obsługiwane przez punkty końcowe urządzeń AWS IoT wraz z metodami uwierzytelniania i portami.

 

Protokół Uwierzytelnianie Port Nazwa protokołu ALPN
MQTT przez WebSocket Signature Version 4 443  
MQTT przez WebSocket Uwierzytelnianie niestandardowe 443  
MQTT Certyfikat klienta X.509 443 x-amzn-mqtt-ca
MQTT Certyfikat klienta X.509 8883  
MQTT Uwierzytelnianie niestandardowe 443 mqtt

 

 

Uwierzytelnianie certyfikatami

Należy utworzyć certyfikaty w konsoli Amazon AWS i ustawić ścieżkę, w której są przechowywane.

 

Korzystając z OpenSSL jako IOHandler, należy ustawić certyfikat w następujących ścieżkach

 

Certificate.Enabled: ustaw na True, jeśli chcesz używać certyfikatów.

Certificate.CertFile: ścieżka do certyfikatu klienta X.509.

Certificate.KeyFile: ścieżka do pliku klucza klienta X.509.

 

Używając SChannel jako IOHandler, należy najpierw przekonwertować certyfikat PEM wraz z kluczem do formatu PFX. Wymaga to plików binarnych OpenSSL:

 


openssl pkcs12 -inkey 884ccf73ff-private.pem.key -in 884ccf73ff-certificate.pem.crt -export -out 884ccf73ff-certificate.pfx

Następnie należy ustawić poniższe ścieżki (nie ma potrzeby ustawiania pliku klucza, ponieważ jest on już zawarty w certyfikacie).

 

Certificate.Enabled: ustaw na True, jeśli chcesz używać certyfikatów.

Certificate.CertFile: ścieżka do certyfikatu PFX

 

Uwierzytelnianie SignatureV4

Należy utworzyć użytkownika w konsoli Amazon AWS i zapisać klucze dostępu oraz klucze sekretne, które będą używane do podpisywania żądania WebSocket.

 

SignatureV4.Enabled: należy ustawić wartość True, aby korzystać z tego typu uwierzytelniania.

SignatureV4.Region: region, w którym znajduje się urządzenie (przykład: us-east-1).

SignatureV4.AccessKey: klucz dostępu utworzony w konsoli Amazon lub uzyskany jako tymczasowe poświadczenie.

SignatureV4.SecretKey: klucz tajny utworzony w konsoli Amazon lub uzyskany jako tymczasowe poświadczenie

SignatureV4.SessionToken: (warunkowe) w przypadku korzystania z tymczasowych poświadczeń bezpieczeństwa należy tutaj podać token bezpieczeństwa.

OpenSSL_Options: konfiguracja bibliotek OpenSSL.

APIVersion: pozwala określić, które API OpenSSL będzie używane.

oslAPI_1_0: używa API 1.0 OpenSSL; jest to najnowsza wersja obsługiwana przez Indy

oslAPI_1_1: używa interfejsu API 1.1 OpenSSL, wymaga niestandardowej biblioteki Indy i umożliwia korzystanie z bibliotek OpenSSL 1.1.1 (z obsługą TLS 1.3).

oslAPI_3_0: korzysta z API OpenSSL 3.0, wymaga naszej niestandardowej biblioteki Indy i umożliwia używanie bibliotek OpenSSL 3.0.0 (z obsługą TLS 1.3).

LibPath: tutaj można skonfigurować lokalizację bibliotek openSSL

oslpNone: jest to wartość domyślna; biblioteki OpenSSL powinny znajdować się w tym samym folderze co plik binarny lub w znanej ścieżce.

oslpDefaultFolder: automatycznie ustawia ścieżkę openSSL, w której powinny znajdować się biblioteki dla wszystkich osobowości IDE.

oslpCustomFolder: jeśli ta opcja jest wybrana, należy zdefiniować pełną ścieżkę we właściwości LibPathCustom.

LibPathCustom: gdy LibPath = oslpCustomFolder, należy podać pełną ścieżkę do folderu, w którym znajdują się biblioteki openSSL.

UnixSymLinks: włącz lub wyłącz ładowanie dowiązań symbolicznych w systemach Unix (domyślnie włączone, z wyjątkiem OSX64):

oslsSymLinksDefault: domyślnie jest włączone, z wyjątkiem platformy OSX64 (po MacOS Monterey próba załadowania biblioteki bez wersji kończy się błędem).

oslsSymLinksLoadFirst: Załaduj dowiązania symboliczne przed próbą załadowania bibliotek wersji.

oslsSymLinksLoad: Ładuj dowiązania symboliczne po próbie załadowania bibliotek wersji.

oslsSymLinksDontLoad: nie ładuj dowiązań symbolicznych.

 

*SignatureV4 wymaga Indy w wersji 10.5.7 lub nowszej

Uwierzytelnianie niestandardowe

Uwierzytelnianie niestandardowe umożliwia zdefiniowanie sposobu uwierzytelniania i autoryzacji klientów przy użyciu zasobów autoryzatora. Urządzenie przekazuje poświadczenia w polach nagłówka żądania lub parametrach zapytania (dla protokołów MQTT przez WebSockets) lub w polach nazwy użytkownika i hasła komunikatu MQTT CONNECT (dla protokołów MQTT i MQTT przez WebSockets).

 

CustomAuthentication.Enabled: należy ustawić na True, aby korzystać z tego rodzaju uwierzytelniania.

CustomAuthentication.Parameters: należy tu ustawić parametry zapytania, które będą przekazywane do serwera (domyślnie /mqtt)

CustomAuthentication.Headers: tu można umieścić niestandardowe pola nagłówka.

CustomAuthentication.WebSockets: jeśli ustawiono na true, połączenie będzie działać przez protokół WebSocket; w przeciwnym razie będzie działać przez zwykłe TCP.

 

MQTTAuthentication.Enabled: jeśli konieczne jest przekazywanie nazwy użytkownika i hasła w połączeniu MQTT, należy włączyć tę właściwość.

MQTTAuthentication.Username: nazwa użytkownika połączenia MQTT

MQTTAuthentication.Password: hasło połączenia MQTT.

 

 

Klient może opcjonalnie wysłać ClientId identyfikujący połączenie klienta; inne klienty mogą wówczas subskrybować powiadomienia o każdym zdarzeniu tego klienta (połączenie, subskrypcja, rozłączenie itd.).

 

Autoryzacja

Jeśli nie można połączyć się przy użyciu portu 8883 i protokołu TCP jako transportu (co jest ustawieniem domyślnym), Amazon stosuje "zasady AWS IoT Core" w celu przyznania lub odmowy autoryzacji klientom i subskrypcjom. Najprawdopodobniej konieczne jest autoryzowanie identyfikatora klienta.

Należy zalogować się do konsoli Amazon AWS, przejść do IoT Core i otworzyć menu „Secure/Policies", następnie wybrać zasadę przypisaną do urządzenia IoT (IoT Thing) i sprawdzić na końcu, jak skonfigurowane jest połączenie. Przykład:

 

{

"Effect": "Allow",

"Action": [

"iot:Connect"

],

"Resource": [

"arn:aws:iot:us-east-1:222178873557:client/sdk-java",

"arn:aws:iot:us-east-1:222178873557:client/basicPubSub",

"arn:aws:iot:us-east-1:222178873557:client/sdk-nodejs-*"

]

}

 

Ta konfiguracja oznacza, że połączyć się będą mogli wyłącznie klienci o identyfikatorach: sdk-java, basicPubSub i sdk-nodejs-*. Należy odpowiednio zmienić ustawienia i spróbować ponownie.

Jeśli problem nadal występuje, włącz dziennik i sprawdź w CloudWatch przyczynę braku możliwości połączenia.

 

Inne właściwości

 

MQTTHeartBeat: jeśli włączone, próbuje utrzymać połączenie MQTT wysyłając ping co x sekund.

 

Interval: liczba sekund pomiędzy kolejnymi pingami.

 

MQTTAuthentication: jeśli włączone, uwzględnia nazwę użytkownika i hasło w połączeniu MQTT

 

UserName: nazwa użytkownika

Hasło: tajny ciąg znaków

 

WatchDog: jeżeli jest włączony, w przypadku wykrycia nieoczekiwanego rozłączenia podejmuje automatyczną próbę ponownego połączenia z serwerem.

 

Interval: liczba sekund przed kolejnymi próbami ponownego połączenia.

 

Attempts: maksymalna liczba prób ponownego połączenia; zero oznacza bez ograniczeń.

 

LogFile: jeśli włączone, zapisuje wiadomości gniazda do pliku dziennika (przydatne do debugowania). Dostęp do pliku dziennika nie jest bezpieczny wątkowo, gdy jest dostępny z wielu wątków.

 

Enabled: jeśli włączone, każda wiadomość odebrana i wysłana przez gniazdo będzie zapisywana do pliku.

 

FileName: pełna ścieżka do pliku.

 

Implementacja

 

Implementacja Amazon MQTT opiera się na MQTT w wersji 3.1.1, jednak odbiega od specyfikacji w następujący sposób:

 

 

Połącz z AWS IoT

Najpierw należy zalogować się do konsoli AWS, zarejestrować nowe urządzenie i utworzyć certyfikat X.509 dla tego urządzenia. Po wykonaniu tych czynności można utworzyć nowy obiekt TsgcIoTAmazon_MQTT_Client i połączyć się z serwerem AWS IoT. Na przykład:

 


oClient := TsgcIoTAmazon_MQTT_Client.Create(nil);
oClient.Amazon.Endpoint := 'a2ohgdjqitsmij-ats.iot.us-west-2.amazonaws.com';
oClient.Amazon.ClientId := 'sgcWebSockets';
oClient.Certificate.CertFile := 'amazon-certificate.pem.crt';
oClient.Certificate.KeyFile := 'amazon-private.pem.key';
oClient.OnMQTTConnect := OnMQTTConnectEvent;
oClient.Active := True;
 
procedure OnMQTTConnect(Connection: TsgcWSConnection; const Session: Boolean; const ReturnCode: TmqttConnReturnCode);
begin
  ShowMessage('Connected to AWS');
end;

Tematy

Broker wiadomości używa tematów do kierowania wiadomości od klientów publikujących do klientów subskrybujących. Ukośnik (/) służy do oddzielania hierarchii tematów. Poniższa tabela zawiera listę symboli wieloznacznych, które można stosować w filtrze tematu podczas subskrypcji. Znak # musi być ostatnim znakiem tematu, do którego jest subskrypcja. Działa jako symbol wieloznaczny pasując do bieżącego drzewa i wszystkich poddrzew.

Na przykład subskrypcja tematu Sensor/# odbiera wiadomości publikowane w Sensor/, Sensor/temp, Sensor/temp/room1, ale nie wiadomości publikowane w Sensor.

+ Pasuje dokładnie do jednego elementu w hierarchii tematów. Na przykład subskrypcja Sensor/+/room1 odbiera wiadomości opublikowane w Sensor/temp/room1, Sensor/moisture/room1 i tak dalej.

 


oClient := TsgcIoTAmazon_MQTT_Client.Create(nil);
...
oClient.OnSubscribe := OnSubscribeEvent;
 
vPacketIdentifier := oClient.Subscribe('Sensor/moisture/room1');
  
procedure OnMQTTSubscribe(Connection: TsgcWSConnection; aPacketIdentifier: Word; aCodes: TsgcWSSUBACKS);
begin
  if vPacketIdentifier = aPacketIdentifier then
    ShowMessage('Subscribed to topic Sensor/moisture/room1'); 
end;
 
// Client, can send a message using Publish method.
oClient.Publish('Sensor/moisture/room1', '{"temp"=10}');
  
// Messages received from server, are dispatched OnMQTTPublishEvent.
// For extended payload access (string, bytes or stream), use OnMQTTPublishEx.
procedure OnMQTTPublish(Connection: TsgcWSConnection; aTopic, aText: string);
begin
  DoLog('Received Message: ' + aTopic + ' ' + aText);
end;

Zarezerwowane tematy

Następujące metody służą do subskrybowania i publikowania w zarezerwowanych tematach.

 

Subscribe_ClientConnected(const aClientId: String): AWS IoT publikuje do tego tematu, gdy klient MQTT o określonym identyfikatorze klienta łączy się z AWS IoT

Subscribe_ClientDisconnected(const aClientId: String): AWS IoT publikuje do tego tematu, gdy klient MQTT o podanym identyfikatorze klienta rozłącza się z AWS IoT

Subscribe_ClientSubscribed(const aClientId: String): AWS IoT publikuje do tego tematu, gdy klient MQTT o podanym identyfikatorze klienta subskrybuje temat MQTT

Subscribe_ClientUnSubscribed(const aClientId: String): AWS IoT publikuje w tym temacie, gdy klient MQTT z określonym identyfikatorem klienta anuluje subskrypcję tematu MQTT

 

Publish_Rule(const aRuleName, aText: String): Urządzenie lub aplikacja publikuje do tego tematu, aby bezpośrednio wyzwolić reguły

 

Publish_DeleteShadow(const aThingName, aText: String): Urządzenie lub aplikacja publikuje w tym temacie, aby usunąć cień

Subscribe_DeleteShadow(const aThingName: String): Urządzenie lub aplikacja subskrybuje ten temat w celu usunięcia cienia

Subscribe_ShadowDeleted(const aThingName: String): Usługa Device Shadow wysyła wiadomości do tego tematu po usunięciu shadow.

Subscribe_ShadowRejected(const aThingName: String): usługa Device Shadow wysyła wiadomości do tego tematu, gdy żądanie usunięcia cienia zostanie odrzucone.

Publish_ShadowGet(const aThingName, aText: String): Aplikacja lub urządzenie publikuje pustą wiadomość do tego tematu, aby pobrać shadow

Subscribe_ShadowGet(const aThingName: String): aplikacja lub urządzenie subskrybuje ten temat, aby pobrać cień

Subscribe_ShadowGetAccepted(const aThingName: String): Usługa Device Shadow wysyła wiadomości do tego tematu po pomyślnym zrealizowaniu żądania dotyczącego cienia

Subscribe_ShadowGetRejected(const aThingName: String): Usługa Device Shadow wysyła wiadomości do tego tematu, gdy żądanie dotyczące cienia zostaje odrzucone

Publish_ShadowUpdate(const aThingName, aText: String): Urządzenie lub aplikacja publikuje do tego tematu w celu aktualizacji cienia

Subscribe_ShadowUpdateAccepted(const aThingName: String): Usługa Device Shadow wysyła wiadomości do tego tematu po pomyślnym zastosowaniu aktualizacji cienia

Subscribe_ShadowUpdateRejected(const aThingName: String): Usługa Device Shadow wysyła wiadomości do tego tematu, gdy aktualizacja shadow zostaje odrzucona

Subscribe_ShadowUpdateDelta(const aThingName: String): Usługa Device Shadow wysyła wiadomości do tego tematu, gdy wykryta zostanie różnica między sekcjami reported i desired cienia

Subscribe_ShadowUpdateDocuments(const aThingName: String): AWS IoT publikuje dokument stanu w tym temacie za każdym razem, gdy aktualizacja cienia zostanie pomyślnie wykonana

 

Sesje trwałe

Sesja trwała reprezentuje bieżące połączenie z brokerem wiadomości MQTT. Gdy klient łączy się z brokerem wiadomości AWS IoT przy użyciu sesji trwałej, broker wiadomości zapisuje wszystkie subskrypcje dokonane przez klienta podczas połączenia. Po rozłączeniu klienta broker przechowuje niepotwierdzone wiadomości QoS 1 oraz nowe wiadomości QoS 1 publikowane w tematach, które subskrybuje klient. Po ponownym połączeniu klienta z sesją trwałą wszystkie subskrypcje zostają przywrócone, a wszystkie przechowywane wiadomości są wysyłane do klienta z maksymalną prędkością 10 wiadomości na sekundę.

 

Trwałą sesję MQTT tworzy się, ustawiając parametr cleanSession na False w zdarzeniu OnMQTTBeforeConnect. Jeśli dla klienta nie istnieje żadna sesja, tworzona jest nowa sesja trwała. Jeśli sesja już istnieje, zostaje wznowiona.

 

Urządzenia muszą sprawdzić atrybut Session w zdarzeniu OnMQTTConnect, aby ustalić, czy istnieje sesja trwała. Jeśli Session ma wartość True, sesja trwała jest obecna i przechowywane wiadomości są dostarczane do klienta. Jeśli Session ma wartość False, sesja trwała nie istnieje i klient musi ponownie subskrybować filtry tematyczne.

 

Trwałe sesje mają domyślny czas wygaśnięcia wynoszący 1 godzinę. Czas wygaśnięcia rozpoczyna się w momencie wykrycia przez brokera wiadomości rozłączenia klienta (rozłączenie MQTT lub przekroczenie czasu). Okres wygaśnięcia trwałej sesji można wydłużyć za pomocą standardowego procesu zwiększania limitów. Jeśli klient nie wznowi sesji w ciągu okresu wygaśnięcia, sesja zostaje zakończona, a wszelkie powiązane przechowywane wiadomości są odrzucane. Okres wygaśnięcia jest przybliżony — sesje mogą być przechowywane nawet o 30 minut dłużej (ale nie krócej) niż skonfigurowany czas trwania.

 

Tymczasowe dane uwierzytelniające

AWS IoT Core może pracować z tymczasowymi poświadczeniami uzyskanymi za pośrednictwem pul tożsamości; istnieją 2 typy tożsamości:

 

 

Nieuwierzytelniony

Jeśli używane są nieuwierzytelnione poświadczenia, należy dołączyć politykę do roli UnAuthenticated automatycznie tworzonej w menu IAM. Następnie należy skonfigurować klienta, ustawiając Access, Secret Key i Token zwrócone przez usługę Cognito.

Poniżej znajduje się kod w .NET umożliwiający uzyskanie nieuwierzytelnionych poświadczeń.

 


CognitoAWSCredentials credentials = new CognitoAWSCredentials(
    "us-east-1:cc3c9c48-646d-44ef-bfd5-0c5fb2f0882f", // Identity pool ID
    Amazon.RegionEndpoint.USEast1 // Region
);
 
var identityPoolId = credentials.GetCredentialsAsync();
 
AmazonCognitoIdentityClient cognitoClient = new AmazonCognitoIdentityClient(
    credentials, // the anonymous credentials
    Amazon.RegionEndpoint.USEast1 // the Amazon Cognito region
);
 
GetIdRequest idRequest = new GetIdRequest();
idRequest.AccountId = "222178873557";
idRequest.IdentityPoolId = "us-east-1:cc3c9c48-646d-44ef-bfd5-0c5fb2f0882f";
 
GetIdResponse idResp = cognitoClient.GetId(idRequest);
 
string AccessKey = identityPoolId.Result.AccessKey;
string SecretKey = identityPoolId.Result.SecretKey;
string SessionToken = identityPoolId.Result.Token;
 
string IdentityId = idResp.IdentityId;

Uwierzytelniono

Uwierzytelnione dane uwierzytelniające wymagają dołączenia polityki w roli uwierzytelnionej automatycznie tworzonej w menu IAM oraz dołączenia polityki użytkownika w zasadach AWS IoT Core.

Należy więc utworzyć nową politykę w menu polityk IoT Core i przy każdym uwierzytelnieniu nowego użytkownika dołączyć tę politykę do danego użytkownika.

Można użyć następującego polecenia AWS, aby dołączyć zasady lub utworzyć funkcję lambda.

 

aws iot attach-policy --policy-name PolicyName --target us-east-1:XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

 

 

Inicjowanie obsługi urządzeń

Usługa Fleet Provisioning obsługuje następujące operacje MQTT API:

 

 

CreateCertificateFromCsr

 

Należy użyć metody CreateCertificateFromCsr, przekazując CertificateSigningRequest jako parametr, aby utworzyć certyfikat. Aby otrzymać odpowiedź na to żądanie, należy najpierw zasubskrybować następujące metody: SubscribeCreateCertificateFromCsrResponse i SubscribeCreateCertificateFromCsrError.

 

CreateKeysAndCertificate

 

Należy użyć metody CreateKeysAndCertificate, aby utworzyć nowy certyfikat i klucze. Aby odebrać odpowiedź na to żądanie, należy najpierw zasubskrybować następujące metody: SubscribeCreateKeysAndCertificateResponse i SubscribeCreateKeysAndCertificateError

 

RegisterThing

 

Metodą RegisterThing należy zarejestrować nową rzecz, przekazując jako parametry nazwę szablonu oraz ładunek w formacie JSON. Aby otrzymać odpowiedź na to żądanie, należy najpierw zasubskrybować następujące metody: SubscribeRegisterThingResponse oraz SubscribeRegisterThingError.