Konfiguracja i łącznie się z węzłem IoT Cayenne

1. Połączenie z przykładowym serwerem usług IoT i oprogramowanie urządzenia do komunikacji z chmurą obliczeniową

Do konfiguracji urządzenia NUCLEO-F401RE wyposażonego w Wi-Fi IDW01M1 oprzemy się na funkcjach przygotowanych w chmurze mbed.com przez dostawcę usług IoT Cayenne [1-3]. W tym celu definiuje się klienta MQTTClient do komunikacji z serwerem MQTT, sieć MQTTNetwork i interfejs SpwfSAInterface:

CayenneMQTT::MQTTClient<MQTTNetwork<SpwfSAInterface>, MQTTTimer> mqttClient(network, username, password, clientID);

Interfejsem SpwfSA będzie moduł Wi-Fi IDW01M1 obsługiwany w protokole szeregowym:

SpwfSAInterface interface(D8, D2); // linie TX, RX
MQTTNetwork<SpwfSAInterface> network(interface);

Ustawiamy parametry bezprzewodowej sieci lokalnej z dostępem do Internetu, z którą połączy się NUCLEO:

// Wi-Fi

char* ssid = "TP-LINK_9E4F"
char* wifiPassword = "31339301";

Ustawiamy dane odczytane we własnym profilu na cayenne.mydevices.com/cayenne/dashboard.

// Autoryzacja Cayenne

char* username = "xxxxxxxx-xxxxx-xxx-xxxx-xxxxxxxxxxxx";
char* password = "9793204867c9e1937f9836227d463dc6a333b5f1ag";
char* clientID = "1c82d230-5ad1-11e9-86v5-4fe3d2557533";

Połączenie z serwerem – węzłem IoT (IRz – Internetu Rzeczy) Cayenne odbywa się za pomocą funkcji connectClient [1] korzystającej z biblioteki Cayenne-MQTT-mbed [2]. Niektóre fragmenty programu "Cayenne-X-NUCLEO-IDW01M1-TMP36" [3] po nieznacznej modyfikacji, wykorzystującego tę bibliotekę i niezbędne dla zrozumienia tego zagadnienia zamieszczono poniżej. Użyta funkcja printf() wyświetli komunikaty na konsoli, np. TeraTerm w standardowej konfiguracji portu szeregowego (Baud rate = 9600).

WYDRUK 1
  
/**

* Łączenie z węzłem IoT Cayenne.

* @return Funkcja zwraca CAYENNE_SUCCESS jeśli połączenie zostało ustanowione lub
* kod błędu w przeciwnym przypadku.

*/

int connectClient(void)
{
    int error = 0;

    // Połącz z serwerem.

    printf("Laczenie z %s:%d\n", CAYENNE_DOMAIN, CAYENNE_PORT);

    while ((error = network.connect(CAYENNE_DOMAIN, CAYENNE_PORT)) != 0) {

        printf("Blad polaczenia TCP: %d\n", error);

        wait(2);
    }

    if ((error = mqttClient.connect()) != MQTT::SUCCESS) {

        printf("Blad polaczenia z MQTT: %d\n", error);

        return error;
    }

    printf("Polaczono\n");

    // Subskrypcja tematów.

    if ((error = mqttClient.subscribe(COMMAND_TOPIC, CAYENNE_ALL_CHANNELS)) != CAYENNE_SUCCESS) {

        printf("Nieudana subskrypcja tematu Command, blad: %d\n", error);
    }

    if ((error = mqttClient.subscribe(CONFIG_TOPIC, CAYENNE_ALL_CHANNELS)) != CAYENNE_SUCCESS) {

        printf("Nieudana subskrypcja tematu Config, blad:%d\n", error);
    }

    // Wyślij informacje o urządzeniu - dane systemowe. Można usunąć ten fragment, jeśli takie nie będą wysyłane.
    mqttClient.publishData(SYS_VERSION_TOPIC, CAYENNE_NO_CHANNEL, NULL, NULL, CAYENNE_VERSION);
    mqttClient.publishData(SYS_MODEL_TOPIC, CAYENNE_NO_CHANNEL, NULL, NULL, "mbedDevice");

    return CAYENNE_SUCCESS;

}


WYDRUK 2
  
/**

* Obsługa wiadomości przychodzących z serwera Cayenne.

* @param[in] message - wiadomość z serwera.

*/

void messageArrived(CayenneMQTT::MessageData& message)
{
    int error = 0;

    // Tutaj można dodać kod obsługujący otrzymaną wiadomość.
    // Wiadomość poniżej jest zwyczajnie wyświetlana.

    outputMessage(message);


    if (message.topic == COMMAND_TOPIC) {

        switch(message.channel) {

        case 0:

            // Ustaw stan diody LED dla sygnalizacji stanu  
            led1 = atoi(message.getValue());

            // Publikuj stan diody
            if ((error = mqttClient.publishData(DATA_TOPIC, message.channel, NULL, NULL, message.getValue())) != CAYENNE_SUCCESS) {

                printf("Publikacja stanu diody LED zakończona niepowodzeniem, blad: %d\n", error);
            }
            break;
        }    

        // W przypadku odebrania wiadomości - polecenia, publikuje się odpowiedź. Poniżej wysyła się domyślną odpowiedź 'OK'.       

        if ((error = mqttClient.publishResponse(message.id, NULL, message.clientID)) != CAYENNE_SUCCESS) {

            printf("Odpowiedz zakonczona niepowodzeniem, blad: %d\n", error);
        }
    }
}

Funkcja loop() jest odpowiedzialna za powtarzanie odczytu z czujnika temperatury co 5 sekund.

WYDRUK 3
  
void loop(void)
{
    // Start odliczania co 5 sekund.
    MQTTTimer timer(5000);

    // Definicja zmiennej przechowującej odczyt temperatury na pinie A5
    TMP36 tmpSensor(A5);   

    while (true) {

        // Umożliwienie przetwarzania wiadomości MQTT
        mqttClient.yield(1000);

        // Sprawdź połączenie i połącz ponownie w razie potrzeby

        if (!network.connected() || !mqttClient.connected()) {

            network.disconnect();
            mqttClient.disconnect();

            printf("Ponowne laczenie\n");

            while (connectClient() != CAYENNE_SUCCESS) {

                wait(2);

                printf("Proba ponownego polaczenia\n");
            }
        }

        // Publikuj wartości temperatury co kilka sekund.
        // UNIT_CELSIUS można zmienić na UNIT_KELVIN wg typów zdefiniowanych
        // w pliku nagłówkowym CayenneTypes.h

        if (timer.expired()) {

            int error = 0;

            if ((error = mqttClient.publishData(DATA_TOPIC, 5, TYPE_TEMPERATURE, UNIT_CELSIUS, tmpSensor.read())) != CAYENNE_SUCCESS) {

                printf("Nieudana publikacja temperatury otoczenia modulu NUCLEO, blad: %d\n", error);
            }

            // Restart licznika.
            timer.countdown_ms(5000);
        }
    }
}

Główna funkcja programu przedstawia się następująco:

WYDRUK 4
  
/**

* Funkcja main.

*/

int main()
{  
    // Inicjalizacja interfejsu sieciowego.
    printf("Inicjalizacja interfejsu\n");

    interface.connect(ssid, wifiPassword, NSAPI_SECURITY_WPA2);

    // Ustawienie domyślnej funkcji przechwytującej wiadomości z Cayenne
    mqttClient.setDefaultMessageHandler(messageArrived);

    // Łączenie z Cayenne
    if (connectClient() == CAYENNE_SUCCESS) {
        loop();
    }

    else {
        printf("Blad w trakcie laczenia, konczenie...\n");
    }

    if (mqttClient.connected())
        mqttClient.disconnect();

    if (network.connected())
        network.disconnect();

    return 0;
}
* kod sformatowano w kolorach C++ (profil default) za pomocą narzędzia konwersji kodu do HTML na stronie hilite.me

2. Programowanie platformy NUCLEO

Na stronie mbed.com (Ilustracja 1) tworzymy konto i przechodzimy do kompilatora (Compiler). Podłączamy płytkę rozwojową z modułami rozszerzeń do komputera przez port USB i dodajemy urządzenie do swojego profilu, wybierając w prawym górnym narożniku "Add Board" (model F401RE jest obsługiwany).

Ilustracja 1. mbed.org udostępniający mbed OS do tworzenia IoT i programowania urządzeń na użytek chmury obliczeniowej.

Następnie w lewym górnym narożniku wybieramy "Import" i wyszukujemy program o nazwie Cayenne-X-NUCLEO-IDW01M1-TMP36" [3]. W prawym górnym narożniku klikamy ikonę Import! by po chwili w lewym oknie eksploratora projektów pojawił się program zaimportowany wraz z niezbędnymi bibliotekami.

Kod numeryczny udostępniony przez dostawcę usług IoT Cayenne modyfikujemy do swoich potrzeb wg opisu w punkcie 1. W zadaniu dla studentów (pkt. 4) modyfikacja będzie dotyczyła dodania kolejnych wartości pomiarowych z innych czujników, co wiąże się z przygotowaniem kolejnych kanałów wymiany danych Channels do odbioru wartości przychodzących na serwer Cayenne.

Ilustracja 2. Widok okna z kodem numerycznym opartym na bibliotekach: mbed, obsługi modułu rozszerzeń Wi-Fi i platformy Cayenne usług dostępu do chmury obliczeniowej w IoT.

Po zalogowaniu (Ilustracja 3), używając "New App" tworzymy aplikację, dodajemy urządzenie przez "Add New/Device", klikamy przycisk "Bring Your Own Thing". Nasz moduł musi być podłączony do komputera przez port USB i zaprogramowany kodem opisanym powyżej, gdzie w polach username, password i clientID wpisuje się dane (wyświetlone na Ilustracji 4 w Kroku 2) potrzebne do autoryzacji naszego urządzenia podczas łączenia się z węzłem IoT Cayenne.

Ilustracja 3. cayenne.mydevices.com logowanie na platformę Cayenne.

Ilustracja 4. cayenne.mydevices.com – odczyt danych potrzebnych do autoryzacji przy tworzeniu nowego urządzenia.

Po skompilowaniu programu na stronie mbed (np. w pliku głównym https://ide.mbed.com/compiler/#nav:/Cayenne-X-NUCLEO-IDW01M1-TMP36/main.cpp) i zaprogramowaniu mikrokontrolera przez zapisanie pliku wynikowego "Cayenne-X-NUCLEO-IDW01M1-TMP36.bin" w pamięci, urządzenie powinno połączyć się z serwerem MQTT przez lokalną siecią Wi-Fi i rozpocząć przesyłanie zmierzonych wartości temperatury. W efekcie, serwer zacznie przyjmować wartości pomiarowe, które można wyświetlać: na wykresie w funkcji czasu, w postaci tabelki lub na wyświetlaczu. Dane aktualizują się natychmiast w trybie Live po odczytaniu wartości lub z większym interwałem czasowym (minuta, godzina, itd.) jak na Ilustracji 5.

Ilustracja 5. cayenne.mydevices.com – obserwacja zmian temperatury na wykresie.


Powyżej zilustrowano prostą przykładową metodę użycia jednego z kanałów komunikacji do wyświetlania wartości odbieranych przez wybrany serwer IoT. Należy zaznaczyć, że po zainstalowaniu aplikacji mobilnej Cayenne, dostępnej w Sklepie Play i zalogowaniu się, można zdalnie podejrzeć stan na czujniku temperatury.

3. Zadanie dla studentów

Na zajęciach laboratoryjnych z Mechatroniki studenci otrzymają płytki rozwojowe NUCLEO-F401RE z modułami rozszerzeń, samodzielnie je skonfigurują zgodnie z informacjami zawartymi powyżej i wykonają pomiary czujnikami z modułu rozszerzeń Motion MEMS Multi-Sensor Board (czujnik inercyjny, kompas, barometr, czujnik temperatury i wilgotności). Do zaczytania danych tekstowych można wykorzystać kolejny moduł Dynamic NFC TAG. Wskazówki pomocne przy realizacji tego zadania można znaleźć na tym blogu. W szczególności, przydatny będzie program opublikowany w poście pt. "Odczyt i wizualizacja danych pomiarowych w LabVIEW" i opracowania I i II z projektów zakończonych w roku akademickim 2018/2019.

4. Wnioski

Internet Rzeczy to prężnie rozwijająca się dziedzina zastosowań mechatroniki i informatyki w życiu codziennym i przemyśle. Rozmaitość chmur obliczeniowych i podłączonych do nich rzeczy pozwala na twórcze kreowanie otoczenia. Programowanie urządzeń wykorzystywanych hobbistycznie lub na użytek publiczny pod względem dostępu do zasobów IoT może być ciekawą alternatywą nauki informatyki w szkołach średnich, uczelniach wyższych, a nawet w badaniach naukowych. Oprócz opisanej, istnieją inne usługi serwowane np. przez IBM Watson IoT, BMC Truesight Pulse lub Zerynth and Google Cloud IoT Core. Pierwsza spośród wymienionych usług została opisana tutaj [4] i pozwala na podobne do metody opisanej wyżej skorzystanie z węzła IoT.


Źródła i odnośniki Internetowe

[1] https://github.com/myDevicesIoT/Cayenne-MQTT-mbed
[2] https://os.mbed.com/teams/myDevicesIoT/code/Cayenne-MQTT-mbed/
[3] https://os.mbed.com/teams/myDevicesIoT/code/Cayenne-X-NUCLEO-IDW01M1-TMP36/
[4] "Quick Start Guide STM32Cube Function Pack for connecting 6LoWPAN IoT Nodes to the Internet through a Wi-Fi Network (FP-NET-6LPWIFI1)". Opis konfiguracji i łączenia się z węzłem IoT (w formacie PDF), serwowanym przez IBM Watson IoT.

Komentarze