Kurs ESP8266 & MicroPython #3: PWM i ulepszony LED blink

W tej krótkiej lekcji zajmiemy się PWM (Pulse Width Modulation), czyli modulacją szerokości impulsów. Jest to metoda regulacji sygnału polegającą na zmianie jego wypełnienia. Żeby było łatwiej to wyjaśnić, posłużę się grafiką z tutoriala do Arduino.

pwm

W dużym skrócie polega to na naprzemiennym podawaniu stanów 0 i 1 na wyjście. Stosunek czasu trwania stanu wysokiego do całego okresu wyrażony w procentach jest właśnie wspomnianym wcześniej wypełnieniem. Częstotliwość sygnału PWM powinna być na tyle wysoka, żeby np. przy zaświecaniu diody LED ludzkie oko nie mogło dostrzec, że w rzeczywistości dioda naprzemiennie zapala się i gaśnie. Sygnału PWM używa się między innymi też do sterowania prędkością obrotową silników DC, ale nie próbuj tego robić bez dodatkowego układu, tzw. mostka H lub chociażby tranzystora (jeśli nie chcesz zmieniać kierunku obrotów silnika).

Program
Moduł Micropythona machine, z którego korzystaliśmy poprzednio pozwala na generowanie sygnału PWM na pinach 0, 2, 4, 5, 12, 13, 14 i 15. Jak pewnie pamiętasz z ostatniej lekcji, wbudowana dioda podłączona jest do pinu 16, więc nie sprawdzimy na niej działania PWM. Podłączmy ją więc do pinu GPIO0 (D3 na płytce) zgodnie z poniższym rysunkiem (dla diody czerwonej idealnie jest zastosować rezystor o wartości około 100Ω):

esp8266_led

Na ESP8266 wgrajmy zaś taki skrypt o nazwie main.py:

1
2
3
4
5
import machine
led = machine.Pin(0)
led_pwm = machine.PWM(led)
led_pwm.freq(10)
led_pwm.duty(512)

Jeśli już kiedyś miałeś do czynienia z PWM to zapewne wiesz, co się stanie. Dioda będzie migać z częstotliwością 10 Hz i jest to zbyt niska częstotliwość, żeby ludzkie oko potraktowało to jako ciągłe świecenie. Stanie się tak dopiero gdy ustawisz częstotliwość 50 Hz lub nawet więcej (nawet przy 50 Hz można zaobserwować zmiany jasności). Za pomocą metody duty ustawić możesz wypełnienie sygnału od 0 (ciągły sygnał 0) do 1023 (ciągły sygnał 1). Zauważ też, że obiekt PWM tworzy się inicjując go obiektem Pin.

Dopiszmy teraz kod odpowiedzialny za płynną zmianę jasności diody – rozwiniemy w ten sposób poprzednią lekcję dotyczącą migania diodą.

6
7
8
9
10
11
12
while True:
    for i in range(1024):
        led_pwm.duty(i)
        time.sleep_us(500)
    for i in range(1023,-1,-1):
        led_pwm.duty(i)
        time.sleep_us(500)

Funkcja range(1024) tworzy nam listę elementów od 0 do 1023 (ostatni element nie jest wliczany) z domyślnym krokiem 1, zaś range(1023,-1,-1) listę od 1023 do 0 (drugi argument) z krokiem -1. Pętle for przypisują kolejne wartości z list do zmiennej i i wykonują swoją zawartość. A taki będzie efekt jego działania:

W kolejnej lekcji pokażę obsługę wejść cyfrowych i analogowych. Do zobaczenia!

Kurs ESP8266 & MicroPython #2: Migająca dioda LED

W tej lekcji dowiesz się, jak sterować portami ESP8266. Posłużymy się tutaj popularnym przykładem, jakim jest migająca dioda. Przybliżę Ci także nieco samego Pythona.

Układ portów
Na poniższym obrazku możesz zobaczyć, jak wyglądają wyprowadzenia modułu NodeMCU. Do dyspozycji jest ich całkiem pokaźna ilość, w tym jeden analogowy służący do pomiaru napięcia z rozdzielczością 10-bitów (1024 wartości).

nodemcu-pins

Tak się składa, że do wykonania tego przykładu nie musisz wykorzystać żadnego wyprowadzenia. Obok konwertera CP2102 znajduje się dioda LED połączona z pinem GPIO16, którą będziemy sterować. Nie oznacza to, że powyższy obrazek jest niepotrzebny – będziesz do niego często wracał, ponieważ nazwy pinów na płytce nie odpowiadają rzeczywistym numerom portów modułu ESP.

Program
Do sterowania pinami wymagane jest zaimportowanie modułu machine. No właśnie, czym jest moduł? Jest to odpowiednik bibliotek np. z języka C, zawierający między innymi funkcje oraz ich definicje (jako, że Python jest językiem obiektowym, mogą się tam znajdować także klasy i metody). Ogromna ilość modułów odpowiada za stale rosnącą popularność Pythona i jest jego największą siłą. Wracając do tematu – w module machine znajdują się funkcje oraz klasy odpowiadające za obsługę części hardware-owej ESP8266, w tym właśnie sterowanie portami. Program, który zaświeci nam diodę, będzie wyglądał następująco:

1
2
3
import machine
led = machine.Pin(16, machine.Pin.OUT)
led.low()

Jak można było się domyślić, nie jest zbyt skomplikowany. W drugiej linijce stworzyliśmy obiekt led klasy Pin z modułu machine, odpowiadający za pin GPIO16. Ustawiliśmy także tryb pracy jako wyjście. Chcemy zaświecić diodą, a jednak ustawiamy stan pinu na niski? Robimy tak, ponieważ dioda połączona jest na stałe anodą do napięcia 3.3V, zaś katodą do pinu GPIO16 i aby ją włączyć stan tego pinu musi być niski.
Tak powstały plik zapisz pod nazwą main.py i wyślij do ESP używając WebREPL, o którym pisałem w poprzedniej lekcji. Po zresetowaniu płytki powinieneś zobaczyć świecącą diodę obok konwertera.

Aby dioda migała, należy ją teraz zgasić. Nie wystarczy samo napisanie led.high(), dioda wówczas zaświeciłaby się i od razu zgasła. Konieczne jest zaimportowanie modułu time i dodanie opóźnienia po każdej zmianie stanu pinu. Na sam koniec całą część odpowiadającą za miganie należy umieścić w nieskończonej pętli, inaczej dioda zaświeciłaby się, po chwili zgasła i na tym by się skończyło.

1
2
3
4
5
6
7
8
9
import machine
import time
led = machine.Pin(16, machine.Pin.OUT)
 
while True:
    led.low()
    time.sleep_ms(1000)
    led.high()
    time.sleep_ms(1000)

Po przesłaniu takiego pliku dioda będzie migać co sekundę. Odstęp czasu można zmienić poprzez zmianę wartości w nawiasie przy sleep_ms.

Ciągłe odwoływanie się do funkcji z modułu poprzez poprzedzanie jej nazwą modułu może być męczące, ale Python i na to ma rozwiązanie. Zamiast import time można napisać from time import sleep_ms. Teraz wystarczy napisać samo sleep_ms(1000) żeby uzyskać jednosekundowe opóźnienie. Minus jest taki, że w tym przypadku importujemy tylko jedną funkcję z modułu i tylko ona będzie dostępna. Nie ma to istotnego wpływu na rozmiar programu – ten zawsze zajmuje tyle miejsca, ile zajmuje plik .py. Można także użyć formułki from time import *, co zaimportuje cały moduł bez konieczności każdorazowego wpisywania jego nazwy. Nie powinieneś tego jednak robić, bo może to powodować konflikty nazw.

Kurs ESP8266 & MicroPython #1: Konfiguracja i pierwszy program

W poprzedniej lekcji tego kursu poznałeś podstawowe informacje o ESP8266 i dowiedziałeś się, jak zainstalować MicroPythona. Tym razem zajmiemy się jego konfiguracją.

Łączenie z siecią Wi-Fi
Otwórz jakikolwiek monitor portu szeregowego (ja używam Realterm), ustaw właściwy port COM i prędkość transmisji 115200 bodów. Po zresetowaniu płytki przyciskiem RST powinieneś ujrzeć informacje o module, numer wersji oraz krótką instrukcję łączenia się z siecią w trybie STA (moduł połączy się z istniejącą siecią, zamiast tworzyć swoją własną). Najwyższa pora to zrobić.

Aby uzyskać dostęp do funkcji służących do obsługi sieci trzeba najpierw zaimportować moduł, wysyłając komendę import network. Po wykonaniu tej czynności możemy włączyć obsługę trybu STA poprzez wysłanie sta_if = network.WLAN(network.STA_IF); sta_if.active(True). Jeśli chcesz, możesz teraz sprawdzić, które sieci są widziane przez moduł używając sta_if.scan() lub od razu połączyć się ze swoją siecią wpisując sta_if.connect('nazwa_sieci', 'hasło'). Żeby sprawdzić, czy poprawnie połączyłeś się z siecią możesz wpisać sta_if.isconnected() lub sta_if.ifconfig(), dzięki czemu otrzymasz jeszcze dane dotyczące konfiguracji potrzebne później.

Struktura plików
Wszystko, co wykonałeś do tej pory było tymczasowe. Ustawienia znikną w przypadku zaniku zasilania lub zresetowania płytki. Co więc zrobić, żeby układ automatycznie łączył się z siecią po resecie?
Odpowiedzią jest plik boot.py, który – jak można wnioskować po nazwie – wykonywany jest w momencie uruchamiania układu. Po wykonaniu tego pliku jest wykonywany plik main.py. Nic nie stoi na przeszkodzie, żeby dodać więcej plików, ale póki co te dwa w zupełności wystarczą. Do tych plików można się dostać z poziomu linii komend, ale ich edytowanie nie jest wtedy zbytnio wygodne. Z pewnością jeszcze do tego wrócimy, gdy zechcemy napisać program, który będzie coś zapisywał do plików, zaś do ich wgrywania z poziomu komputera użyjemy narzędzia WebREPL.

WebREPL
WebREPL jest wygodnym narzędziem do komunikacji z modułem, ponieważ pozwala na pobieranie oraz wysyłanie plików bez używania komend. Można się do niego dostać na dwa sposoby: wchodząc na tę stronę lub ściągając go z GitHuba i otwierając plik webrepl.html w przeglądarce. Jeśli wybrałeś drugą opcję, polecam od razu zamienić w pliku webrepl.html ciąg ws://192.168.4.1:8266/ na ciąg zawierający adres IP Twojego modułu, czyli pierwszy z adresów uzyskanych poprzez użycie sta_if.ifconfig(), dzięki temu nie będziesz musiał go za każdym razem zmieniać. Jeśli w polu znajduje się prawidłowy adres, wciśnij Connect. Za pierwszym razem zostaniesz poproszony o utworzenie hasła, które będzie wymagane do każdego kolejnego połączenia przez WebREPL. Teraz możesz pobrać plik boot.py. Jego zawartość powinna wyglądać następująco:

1
2
3
4
5
6
7
# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
import gc
import webrepl
webrepl.start()
gc.collect()

# jest w Pythonie oznaczeniem komentarza, wszystko w danej linii jest niewidoczne dla interpretera i nie będzie wykonywane. Czwarta i piąta linia odpowiada za zaimportowanie modułów gc (garbage collector) oraz webrepl, zaś kolejne dwie uruchamiają WebREPL oraz odśmiecanie pamięci. Za nimi dopisz kod odpowiedzialny za łączenie z siecią, czyli:

8
9
10
11
12
13
14
15
16
17
18
19
20
21
import network
import time
network.WLAN(network.AP_IF).active(False)
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
sta_if.connect('ssid', 'hasło')
count = 0
while not sta_if.isconnected():
	time.sleep_ms(1)
	count += 1
	if count==10000:
		print('Not connected')
		break
print('Config: ', sta_if.ifconfig())

Zwróć uwagę na wcięcia – w Pythonie zastępują one nawiasy klamrowe znane z innych języków. W tym przypadku w pętli while wykonywane są kolejno: odczekanie jednej milisekundy, zwiększenie licznika o 1 oraz sprawdzenie warunku, czy licznik osiągnął wartość 10000. Jest to zabezpieczenie przeciwko pozostaniu w pętli, jeśli moduł nie będzie mógł nawiązać połączenia przez 10 sekund (instrukcja break przerywa pętlę). Dzięki temu będziesz mógł połączyć się ręcznie poprzez monitor portu szeregowego, jeśli z powodu np. złych danych nie stanie się to automatycznie. Pamiętaj też o podaniu danych swojej sieci WLAN. Po wszystkim zapisz plik boot.py i wyślij go do modułu ponownie korzystając z WebREPL. Po zresetowaniu modułu powinien on automatycznie połączyć się z siecią i wypisać na port szeregowy informacje o połączeniu. Będzie robił to samo za każdym razem, gdy wciśniesz reset lub ponownie podłączysz zasilanie. Kod zawiera także linijkę do wyłączenia pracy w trybie Access Pointa – domyślnie jest on bowiem włączony, co pozwala praktycznie każdemu połączyć się z modułem.

Jeśli uważnie przeczytasz informacje wysyłane przez ESP, zobaczysz jeszcze wzmiankę o braku pliku main.py. Faktycznie, nie ma go domyślnie i moduł po połączeniu się z siecią nie zrobi nic więcej. Plikiem tym zajmiemy się w kolejnej części kursu i napiszemy klasyczny program migający diodą.

Kurs ESP8266 & MicroPython #0: Słowo wstępu i instalacja MicroPythona

W tym kursie zajmiemy się programowaniem modułu ESP8266. Jeśli jeszcze nie wiesz, co to jest, to spieszę z wyjaśnieniem. Jest to tani układ obsługujący komunikację po Wi-Fi oraz posiadający porty we/wy którymi rzecz jasna można sterować. Moduły te występują w kilku wersjach, różniących się pojemnością pamięci, ilością portów oraz wymiarami. Najtańszy, a zarazem posiadający najmniej możliwości jest układ ESP-01. Wyposażony jest w pamięć o pojemności 512Kb (niebieski) lub 1Mb (czarny) i jedynie 2 porty GPIO, co szybko stałoby się problemem przy próbie użycia go do bardziej skomplikowanego projektu. Istnieje jeszcze kilka modeli, różniących się wielkością pamięci i ilością wyprowadzeń. Jedynym odstępstwem od reguły jest ESP-14, który posiada także mikrokontroler ST8S i to on jest właściwym urządzeniem, które się w tym przypadku programuje.

W takim razie który moduł wybrać?
Sprawa jest prostsza, niż mogłoby się wydawać. Wszystkie moduły wymagają zasilania 3.3V i pracy w takiej logice, do komunikacji zaś wymagany jest konwerter USB-UART. To wszystko powoduje, że już uruchomienie samego modułu objawi się plątaniną kabli. Na szczęście jest rozwiązanie – płytka NodeMCU, która zawiera wszystko, co potrzebne, czyli stabilizator napięcia i konwerter USB-UART i wystarczy ją tylko podłączyć kablem USB do komputera. Dostępna jest w dwóch wersjach, z modułem ESP-12 (512 kB) lub ESP-12E (4MB). Obie kosztują około 15 zł na eBayu, ale polecam kupić wersję z modułem ESP-12E ze względu na większą pojemność pamięci i liczbę portów. Można ją rozpoznać po antenie bez soldermaski. Płytki mogą się także różnić użytym konwerterem USB-UART, będzie to CP2102 lub CH340G. Pierwszy wymaga samodzielnej instalacji sterowników na Windowsie, natomiast drugi nie. Sam konwerter jest sprawą drugorzędną, możesz po prostu wybrać płytkę, która bardziej Ci „pasuje”. Ja wybrałem tę z CP2102.

nodemcu

Korzystanie z modułu
Pewnie teraz zastanawiasz się „z czym to się je”. Tutaj też jest kilka możliwości. Podstawową jest używanie tzw. komend AT. Są to tekstowe komendy wysyłane przez port szeregowy, na które moduł odpowiada w ten sam sposób. To rozwiązanie jest toporne i wymaga dodatkowego mikrokontrolera do wysyłania komend i interpretacji odpowiedzi. Ma to sens w zasadzie tylko przy module z małą ilością wyprowadzeń, jak ESP-01. Druga możliwość to użycie firmware NodeMCU (tak, nazwa płytki pochodzi właśnie od tego firmware). Programuje się wówczas w skryptowym języku Lua, który nie jest zbytnio popularny. Ciężko wypowiadać mi się na temat możliwości tego firmware, bo nigdy go nie używałem. Znalazłem ciekawszą alternatywę – MicroPythona, czyli implementację Pythona na mikrokontrolery. Projekt ten jest bezustannie rozwijany i w obecnej wersji (1.8.4) ma już całkiem spore możliwości. Obsługa ESP8266 przy użyciu MicroPythona będzie tematem tego kursu.
Wszystkie poprzednie możliwości łączy jedna cecha – korzystają z firmware, które pozwalają na wprowadzanie danych na wyższym poziomie, bez przejmowania się tym, jak odbywa się to na poziomie sprzętowym. I tu ukryta jest czwarta możliwość, czyli programowanie w C. Wspominam to jedynie jako ciekawostkę, bo jest to niepraktyczne i po prostu wygodniej użyć któregoś z dostępnych firmware.

Wgrywanie firmware
Aby wgrać firmware, trzeba je najpierw przede wszystkim pobrać. Na stronie micropython.org znajdują się wszystkie wersje skompilowane do pliku binarnego. Jak widać, nowe wydania pojawiają się co miesiąc, warto więc czasem przeprowadzać aktualizację, aby zyskać nowe funkcjonalności albo pozbyć się bugów. Póki co, po prostu pobierz najnowszą wersję. Będziesz jeszcze potrzebował Pythona w wersji 2.7.x do pobrania stąd oraz modułu pyserial. Jeśli wszystko już zainstalujesz, pobierz narzędzie esptool z GitHuba, które pozwoli Ci wgrać firmware do ESP8266. Następnie skopiuj plik firmware do folderu z esptoolem i uruchom w tym folderze wiersz poleceń (możesz to zrobić poprzez wpisanie w pasku adresu „cmd”). W wierszu poleceń wpisz

esptool.py --port COM1 --baud 115200 write_flash --flash_size=32m 0 esp8266-20160909-v1.8.4.bin

wpisując poprawne parametry, tj. numer portu COM, baudrate (standardowo 9600 lub 115200), rozmiar pamięci w megabitach i nazwę pliku. Po wciśnięciu enter powinien pokazać się taki widok:

programming

Oznacza to, że firmware jest wgrywany. Jeśli nic się nie wgrywa, sprawdź, czy poprawnie wpisałeś dane i spróbuj z innym baudrate. Możesz także spróbować uprzednio wyczyścić pamięć komendą

esptool.py --port COM1 erase_flash

Jeśli będziesz chciał kiedyś zaktualizować firmware, zrób to bez czyszczenia pamięci. Zachowasz wtedy swoje skrypty .py.

Po wgraniu firmware moduł powinien działać w trybie AP, tzn. pojawi się nowa sieć Wi-Fi o SSID MicroPython-xxxxxx, gdzie xxxxxx będzie fragmentem adresu MAC modułu.
W kolejnej części kursu pokażę, jak napisać pierwszy program, a właściwie skrypt, wykonywany przez właśnie zainstalowany interpreter.