Układ ESP operuje na napięciach 0…3,3 V, identycznie jak STM32F411 na używanej przeze mnie płytce rozwojowej KA-NUCLEO-F411CE. Domyślnie, wszystkie mikrokontrolery tej serii, mają wgrane oprogramowanie działające w roli karty sieci bezprzewodowej oraz implementacji stosu protokołów TCP/IP sterowanej przy pomocy poleceń AT Hayesa.
Dla układów z rodziny ESP8266 opracowano wiele ciekawych projektów, z czego, moim zdaniem – najbardziej ambitnym, jest programowalny nadajnik sygnału telewizyjnego – projekt „channel3”, autorstwa CNLohr (https://goo.gl/ti7V7C). Generuje on kolorowy obraz w standardzie NTSC i nadaje go falami radiowymi, poprzez jeden z pinów GPIO, do którego jest przyłączona antena. Generowanym obrazem są ruchome, również trójwymiarowe, wizualizacje. W trakcie emisji sygnału, mikrokontroler obsługuje także sieć Wi-Fi i pozwala na sterowanie nadawanym sygnałem przez stronę WWW.
Dlaczego więc, tak po prostu, nie zaprogramujemy tego układu? Programowanie mikrokontrolerów z serii ESP8266 nie jest tak łatwe, jak programowanie układów z rodziny STM32 z użyciem biblioteki HAL. Nie dysponujemy też tak zaawansowanym i jednocześnie łatwym w obsłudze narzędziem jak generator konfiguracji CubeMX. Cały czas pamiętać też musimy o tym, że ten sam rdzeń obsługuje zarówno nasz program, jak i wszystkie akcje związane z obsługą sieci Wi-Fi – programujemy więc zdarzenia wywoływane w odpowiednich momentach. Istnieją wprawdzie frameworki i środowiska, sprowadzające programowanie ESP8266 do programowania platformy „Arduino z Wi-Fi”, jednak znacznie ograniczają one możliwości tych układów. Następca serii ESP8266 – układ ESP32 posiada już dwa rdzenie CPU, z czego jeden może być przeznaczony tylko do wykonywania naszego kodu, a drugi zajmuje się obsługą sieci Wi-Fi.
Zestaw poleceń AT Hayesa
Wspomniane już wyżej polecenia Hayesa, są równie stare jak, omówiony w poprzedniej części kursu, interfejs RS232. Od dawna służyły one i wciąż są wykorzystywane do sterowania wszelkiego rodzaju modemami – początkowo akustycznymi i podłączanymi do linii telefonicznych, gdzie polecenia przesyłane były przez interfejs COM – RS232, a obecnie do sterowania modemami GSM, UMTS, czy nawet LTE, przez interfejs USB.
Polecenia AT interpretowane przez układ ESP8266 mogą zostać użyte na cztery sposoby:
1. Dopisując po identyfikatorze polecenia znak zapytania, możemy odczytać obecnie ustawioną wartość danego parametru, np. nazwę sieci Wi-Fi, poleceniem: AT+CWJAP?.
2. Dopisując znak równości i wartość parametru, możemy ją zmienić, np. AT+CWJAP=”NAZWA_SIECI”,”KLUCZ_SIECIOWY”.
3. Dopisując znaki równości i zapytania, możemy dowiedzieć się, jakie wartości przyjmuje dany parametr, np. tryb pracy urządzenia – AT+CWMODE=?.
4. Podając sam identyfikator, możemy wykonać polecenie, np. przeskanować sieci Wi-Fi w pobliżu i wyświetlić ich nazwy – AT+CWLAP.
Jedno polecenie może występować w kilku wariantach, lecz nie musi. Zazwyczaj wykonanie polecenia kończy się zwrotem kilku linii odpowiedzi, z których ostatnia zawiera ciąg „OK”, jeśli udało się wykonać polecenie lub „ERROR”, w przypadku błędu. To niestety również nie jest regułą i część odpowiedzi kończy się innymi ciągami, jak na przykład „no change”, w przypadku próby ponownego ustawienia takiej samej wartości dla danego parametru. Poniżej znajduje się spis popularnych poleceń, wraz z opisami ich działania.
AT – testuje komunikacje z układem ESP8266. Zwraca ciąg „OK”, jeśli mamy połączenie, a układ jest gotowy do pracy.
AT+RST – powoduje ponowne uruchomienie układu ESP8266. Ostatnim komunikatem po restarcie jest ciąg „ready”.
AT+CWMODE=ID_TRYBU_PRACY – wybiera tryb pracy układu: 1) urządzenie końcowe Wi-Fi, 2) punkt dostępowy Wi-Fi, 3) urządzenie końcowe + punkt dostępowy.
AT+CWLAP – skanuje sieci bezprzewodowe w okolicy i podaje ich parametry – rodzaj szyfrowania, nazwę sieci, adres MAC punktu dostępowego oraz siłę sygnału. Polecenie działa tylko w trybie pracy urządzenia końcowego.
AT+CWJAP=”NAZWA_SIECI”,”KLUCZ_SIECIOWY” – łączy się z siecią bezprzewodową o podanej nazwie i kluczu sieciowym (haśle). Polecenie działa tylko w trybie pracy urządzenia końcowego.
AT+CWQAP – rozłącza się z siecią bezprzewodową. Polecenie działa tylko w trybie pracy urządzenia końcowego.
AT+CWSAP=”NAZWA_SIECI”,”KLUCZ_SIECIOWY”,NR_KANAŁU,SZYFROWANIE – tworzy sieć bezprzewodową, rozgłaszaną przez urządzenie, w trybie pracy punktu dostępowego (polecenie działa tylko w tym trybie). Jako szyfrowanie możemy wybrać następujące wartości: 0) Sieć otwarta (bez szyfrowania); 2) Szyfrowanie WPA PSK; 3) Szyfrowanie WPA2 PSK; 4) Szyfrowanie WPA PSK + WPA2 PSK.
AT+CWLIF – wyświetla listę urządzeń przyłączonych do punktu dostępowego (polecenie działa tylko w tym trybie).
AT+CWDHCP=TRYB_PRACY,0/1 – włącza (1) lub wyłącza (0) klienta i/lub serwer usługi sieciowej DHCP, pozwalającej na automatyczne przydzielanie i pobieranie adresu IP oraz pozostałej konfiguracji urządzenia końcowego. Domyślnie, usługa DHCP jest włączona zarówno jako serwer w trybie punktu dostępowego (moduł przydziela konfigurację innym urządzeniom które przyłączą się do jego sieci), jak i klient w trybie pracy urządzenia końcowego (urządzenie samo pobiera sobie konfigurację).
AT+CIPSTA=”ADRES_IP” / AT+CIPAP=”ADRES_IP” – ustawia adres IP układu ESP8266, w trybie pracy urządzenia końcowego (AT+CIPSTA) i kolejno, punktu dostępowego (AT+CIPAP).
AT+CIPMUX=0/1 – włącza (1) / wyłącza (0) obsługę wielu połączeń równocześnie – maksymalnie pięciu, numerowanych od 0 do 4.
AT+CIPSERVER=0/1,PORT – uruchamia (1) lub zamyka (0) serwer TCP – gniazdo nasłuchujące, na podanym porcie. Polecenie działa jedynie w trybie wielopołączeniowym (AT+CIPMUX=1).
AT+CIPSTART=[NR_POŁĄCZENIA,]”TCP/UDP”,”ADRES_IP_SERWERA”,PORT – nawiązuje połączenie TCP lub UDP z serwerem o podanym adresie IP lub domenie, na wskazanym porcie. W trybie wielopołączeniowym, pierwszym parametrem jest numer połączenia (0-4), nie występuje on w trybie jednopołączeniowym.
AT+CIPSEND=[NR_POŁĄCZENIA,]LICZBA_BAJTÓW – wysyła strumień bajtów o podanym rozmiarze, przez nawiązane uprzednio, poleceniem AT+CIPSERVER lub AT+CIPSTART, połączenie. W trybie wielopołączeniowym należy podać numer połączenia, w trybie jednopołączeniowym, pomijamy pierwszy parametr.
AT+CIPCLOSE[=NR_POŁĄCZENIA] – zamyka nawiązane uprzednio, poleceniem AT+CIPSERVER lub AT+CIPSTART, połączenie. W trybie wielopołączeniowym należy podać jego numer.
AT+CIFSR – Zwraca obecny adres IP modułu ESP8266.
Po nawiązaniu połączenia TCP lub UDP, dane odebrane od strony przeciwnej, przesyłane są przez układ ESP8266 poprzedzone ciągiem +IPD[,NR_POŁĄCZENIA],ROZMIAR_DANYCH:.
Na podstawie powyższego spisu poleceń, możemy przygotować skrypt który prześlemy do modułu ESP8266 po uruchomieniu obu mikrokontrolerów. Sekwencja poleceń, która spowoduje przyłączenie się do sieci Wi-Fi, pobranie adresu IP oraz uruchomienie serwera-gniazda TCP na porcie 80, prezentuje się następująco:
AT+CWMODE=1
AT+CWJAP=”NAZWA_SIECI”,”KLUCZ_SIECIOWY”
AT+CIPMODE=1
AT+CIPSERVER=1,80
Jeśli chcielibyśmy utworzyć sieć Wi-Fi na kanale 11, obsługującą szyfrowania WPA i WPA2 PSK, musimy wykonać poniższe polecenia:
AT+CWMODE=2
AT+CWSAP=”NAZWA_SIECI”,”KLUCZ_SIECIOWY”,11,4
AT+CIPMODE=1
AT+CIPSERVER=1,80
Czym właściwie jest TCP, porty i jak w uproszczeniu przebiega komunikacja w Internecie?
W spisie poleceń użyłem określeń: połączenie i gniazdo TCP. Czym jednak jest to połączenie? Komputery wymieniają się w Internecie danymi w pakietach IP. Internet z natury jest siecią bezpołączeniową. Każdy pakiet może być wysłany pod dowolny adres IP bez zestawiania połączenia. Jest on przełączany indywidualnie przez routery, na trasie od źródła do celu. Z samych pakietów IP nie korzysta się jednak zbyt często.
Protokół TCP działa nad protokołem IP. Z jednej strony, udostępnia on aplikacjom połączeniowy kanał, którym przesyłane są strumienie danych między dwoma urządzeniami końcowymi, z drugiej, dzieli te dane i pakuje w pakiety IP, w celu przesyłu przez sieć. Zajmuje się też retransmisją zgubionych lub uszkodzonych na trasie pakietów i szeregowaniem ich we właściwej kolejności w miejscu odbioru. Serwer TCP to gniazdo nasłuchujące – oczekuje on na połączenia przychodzące na określonym porcie (w segmentach TCP przesyłane są 16-bitowe identyfikatory portów), nawiązywane również z określonego portu po stronie aplikacji klienckiej. Protokół TCP pozwala w ten sposób na używanie wielu serwerów i nawiązywanie wielu połączeń równocześnie.
Nad protokołem TCP (i UDP) działają jeszcze inne protokoły, standaryzujące sposób przesyłania danych w sieci. Jednym z tych protokołów jest HTTP, służący do przesyłania stron internetowych oraz plików z serwerów do przeglądarek WWW. Korzysta on z protokołu TCP i zwyczajowo działa na porcie 80.
W dalszej części tego artykułu, przedstawiona została bardzo uproszczona implementacja serwera tego protokołu. Nasz program, połączy się z siecią Wi-Fi, uzyska automatycznie adres IP oraz pozostałe parametry konfiguracyjne, uruchomi serwer TCP na porcie 80 i będzie oczekiwał na połączenia przychodzące. Gdy takie zostanie nawiązane, przez przeglądarkę internetową, sprawdzony zostanie adres URI, do którego odwołuje się żądanie HTTP przesłane w połączeniu, następnie serwer odpowie na nie przesyłając stronę WWW z formularze umożliwiającym wybór koloru i zamknie połączenie. Jeśli w żądaniu przesłane zostaną również dane z formularza, program zmieni także kolor świecenia diody RGB.
Jeśli chcemy przetestować działanie komend AT Hayesa i zobaczyć jak odpowiada na nie układ ESP8266, ale nie posiadamy adaptera USB<->UART, możemy w jego miejsce użyć płytki rozwojowej KA-NUCLEO lub dowolnej innej, z programatorem ST-LINK i wyprowadzonymi pinami RX/TX interfejsu UART, łączącego programator ST-LINK i programowany układ.
Na płytce KA-NUCLEO, dla zapewnienia kompatybilności wyprowadzeń z Arduino, są ta piny D0 (pin odbiorczy) i D1 (pin nadawczy), podłączone odpowiednio do wyprowadzeń PA3 i PA2 procesora. Za ich obsługę, po stronie układu STM32F411, odpowiada peryferial USART2.
Tworzymy w tym celu nowy projekt programu STM32CubeMX, wybieramy nasz mikrokontroler (dla przypomnienia, na płytce KA-NUCLEO, jest to układ STM32F411CEU6) i na pierwszej planszy konfiguratora – „Pinout”, odnajdujemy wyżej wspomniane piny, klikamy na każdy z nich lewym przyciskiem myszy i z listy funkcji alternatywnych, wybieramy opcję „GPIO_Input”, aby układ STM32 nie przeszkadzał nam w transmisji. Odszukujemy też dowolny inny pin dostępny, na naszej płytce rozwojowej, do dyspozycji użytkownika. Na płytce KA-NUCLEO, następny pin (obok pinów RX/TX) – D2, przyłączony jest do wyprowadzenia mikrokontrolera, o oznaczeniu PA10. Ustawiamy go w tryb wyjścia – „GPIO_Output”. Podłączony zostanie on do pinu „CH_PD” układu ESP8266 (rysunek 2).
Za pomocą kabli połączeniowych, łączymy płytkę KA-NUCLEO i układ ESP8266. Piny RX/TX interfejsów UART obu układów podłączamy na przemian, aby pin odbiorczy jednego z nich był połączony z pinem nadawczym drugiego. Pin ustawiony w tryb wyjścia (D2), łączymy z pinem „CH_PD” układu ESP. Nie zapominamy także o podłączeniu zasilania (3,3 V) i masy (rysunek 4).
Następnie generujemy nowy projekt, w sposób opisany w poprzednich częściach oraz importujemy go w środowisku System Workbench for STM32, kompilujemy źródła, wgrywamy program na mikrokontroler i uruchamiamy go. Teraz możemy już uruchomić, znany z poprzedniej części, program PuTTY i podobnie jak zostało to wcześniej opisane, wybrać w nim typ połączenia szeregowego – w polu „Connection Type” wybieramy opcję „Serial”, wybrać port szeregowy, który system operacyjny przydzielił programatorowi ST-LINK – np. „COM3” oraz ustawić szybkość połączenia (rysunek 5). Układy ESP z nowszym oprogramowaniem pracują domyślnie z szybkością transmisji 115200, ze starszym – 9600. Test komunikacji z modułem ESP8266 pokazano na rysunku 6.
Serwer HTTP i ustawianie koloru diody RGB przez stronę WWW
Przejdźmy teraz do właściwego projektu. Ponownie uruchamiamy generator konfiguracji STM32CubeMX, wybieramy mikrokontroler i przechodzimy do konfiguracji pinów (plansza „Pinout”). Tym razem nie skorzystamy już z tych samych wyprowadzeń interfejsu UART jak poprzednio, ponieważ chcemy umożliwić komunikację między mikrokontrolerem STM32 i układem ESP8266, a nie ESP8266 i programatorem ST-LINK. Na płytce rozwojowej KA-NUCLEO, możemy w tym celu skorzystać z interfejsu USART1 – jego wyprowadzenia nie są podłączone nigdzie indziej, poza samym mikrokontrolerem. Pinem nadawczym będzie pin D8, przyłączony do wyprowadzenia procesora o oznaczeniu PA9, a odbiorczym D2 – wyprowadzenie PA10. Ponieważ pin D2 zajęty został przez interfejs UART, musimy także przenieść pin połączony z wyprowadzeniem „CH_PD” układu ESP8266 – na płytce KA-NUCELO, skorzystamy teraz z pinu D3 – wyprowadzenie PB3. Podobnie jak poprzednio, ustawiamy na tym pinie stan domyślny wysoki (3,3 V).
Odszukujemy też wyprowadzenia, do których jest przyłączona dioda RGB i ustawiamy im funkcje alternatywne (klikamy na nie LPM) „TIM1_CHxN”, czyli wyjścia pierwszego licznika. Na płytce KA-NUCLEO są to piny PB13, PB14 i PB15, odpowiedzialne odpowiednio za kolory: niebieski, czerwony i zielony. Proponuję przy okazji nadać im nazwy (PPM) „LED_BLUE”, „LED_RED” i „LED_GREEN” – będą one wykorzystywane dalej w kodzie.
Musimy teraz jeszcze skonfigurować kanały licznika – na liście po lewej stronie odszukujemy pozycję „TIM1” i z pól „ChannelX”, wybieramy opcje „PWM Generation CHxN”, gdzie X/x to numer od 1 do 3 (numery kanałów). Ustawiamy również sygnał taktujący wchodzący na licznik – pole „Clock Source”, na wartość „Internal Clock” (rysunek 7). Konfiguracja liczników i generatora sygnału PWM została dokładniej opisana w drugiej części niniejszego kursu.
Przechodzimy do zakładki „Clock Configuration” i podobnie jak w pierwszej części kursu, konfigurujemy sygnał taktujący. W polu „PLL Source Mux” wybieramy opcję „HSE”, w polu „System Clock Mux”, wartość „PLLCLK”. Ustawiamy także częstotliwość naszego rezonatora kwarcowego – „Input frequency”, na płytce KA-NUCLEO jest to wartość 8 MHz, oraz pożądaną częstotliwość taktowania układu – „HCLK (MHz)”, na wartość 100 MHz (dla wykorzystywanej przeze mnie płytki rozwojowej jest to wartość maksymalna). Konfigurację generatora PLL pokazano na rysunku 8.
Następnie, po zapisaniu ustawień, z planszy „Configuration”, głównego okna programu, wybieramy pozycję „Control” -> „TIM1” i ustawiamy parametry generowanego sygnału PWM – dzielnik „częstotliwości” wejściowej – „Prescaler”, na 4, wartość do której zlicza licznik – „Counter Period”, na 49999 oraz odwracamy wyjście każdego z kanałów „PWM Generation Channel xN”: „CHN Polarity” – „Low” (rysunek 11). Dokładne znaczenie tych wartości oraz sposób obliczania pożądanej częstotliwości sygnału PWM opisany został w części drugiej tego kursu.
Teraz możemy już wygenerować projekt i zaimportować go w środowisku IDE System Workbench for STM32. Po zaimportowaniu projektu, potrzebujemy jeszcze ustawić go tak, aby na etapie linkowania/łączenia kodu, dodawana była do niego biblioteka math. Klikamy prawym przyciskiem myszy na nazwę projektu w panelu po lewej stronie, z menu kontekstowego wybieramy opcję „Properties”, dalej rozwijamy opcje: „C/C++ Build”, „Settings”, „Tool Settings”, „MCU GCC Linker”, „Libraries”, odznaczamy opcję „Use C math library (-lm)” i dodajemy tą bibliotekę – „m”, ręcznie, do listy „Libraries” (rysunek 12).
Po uruchomieniu, program wywołuje funkcję esp_setup(), która przesyła do układu ESP skrypt konfigurujący, za każdym razem czekając na odpowiedź potwierdzającą wykonanie danej komendy („OK”) lub informacje o błędzie („ERROR”). Polecenie przesyłane są przy pomocy funkcji esp_send_cmd(), ta z kolei wykorzystuje funkcje uart_write_line() i uart_read_line(). Po prawidłowym skonfigurowaniu chipu ESP8266, dioda LED RGB zapala się na zielono i uruchamiana jest obsługa przerwań, w przypadku niepowodzenia, dioda zapala się na czerwono.
W pętli głównej programu, po ustawieniu w przerwaniu flagi, wywoływana jest funkcja handle_request() obsługująca przychodzące żądanie HTTP. Na podstawie początku żądania, podejmowana jest decyzja, jaką stronę WWW zwrócić, oraz czy zmienić kolor ustawiony na diodzie RGB. Ten odczytywany jest z adresu URL jako trzy następujące po sobie liczby 8-bitowe zawierająca informacje o jasności kolejnych trzech kolorów składowych – czerwonego, zielonego i niebieskiego. Dalej, przy pomocy funkcji set_color() i set_led_brightness() kolor ten jest ustawiany.
W kolejnej części kursu, napiszemy program sterujący adresowalnymi paskami diod LED RGB, bazujących na chipie WS2812B. Kod przedstawionych powyżej projektów, jest dostępny na serwerze FTP.
Aleksander Kurczyk