4 edycja
warsztatów zakończona

Sterowanie Arduino przy pomocy Pythona cz.1

Published: Mon 13 July 2015

Artykuł ten jest pierwszą częścią mini-cyklu poświęconego sposobom sterowania platformą Arduino przy pomocy języka Python. W części tej omówione zostaną zarówno podstawy korzystania z samej platformy Arduino, jak i podstawowe sposoby sterowania nią przy pomocy Pythona.

Na wstępie chciałbym napisać parę słów o tym, dlaczego w ogóle zabrałem się do napisania artykułu z pogranicza robotyki i elektroniki, podczas gdy z obiema tymi dziedzinami tak na prawdę mam niewiele wspólnego, a na co dzień zajmuje się tworzeniem bogatych systemów webowych przy pomocy języków JavaScript oraz Python. Wyjaśnię także do kogo artykuł jest skierowany.

Otóż spróbowanie swoich sił ze stworzeniem czegoś na platformie Arduino chodziło mi po głowie od bardzo długiego czasu, a kiedy w końcu zakupiłem swój zestaw, to już przy pierwszych próbach natrafiłem na wiele problemów i masę pytań. Chciałem po prostu szybko i w prosty sposób tchnąć życie w kawałek elektroniki przy pomocy jednego ze swoich ulubionych języków, a tym czasem trafiłem w małe piekiełko pytań i wątpliwości. Oczywiście odpowiedzi na wszystkie te pytania znalazłem ostatecznie w internecie, jednak w przypadku niektórych z nich, często banalnych i wynikających z totalnej nieznajomości tematów elektroniki, musiałem się nieźle naszukać. Nie udało mi się też znaleźć żadnego artykułu czy tutorialu wprowadzającego od A do Z w podstawy platformy bez zmuszania do nurkowania w schematy i szukania uzupełnień wiedzy “na zewnątrz”.

I właśnie głównie po to powstał ten artykuł. Są to doświadczenia laika, który chciałby chociaż trochę ułatwić życie osobom niezaznajomionym z tematami elektroniki i robotyki. Które tak jak i ja chciałyby posmakować świata Arduino, którym tak bardzo wydają się fascynować ostatnimi czasy wszyscy dookoła. Oczywiście osoby mające zasady działania Arduino w jednym palcu, ale chcące dopiero spróbować wykorzystać Pythona, także powinny znaleźć tutaj coś dla siebie.

Arduino. Z czym to się je

A czym tak naprawdę jest Arduino? Jest to otwarta platforma składająca się z hardware’u, czyli całej tej płytki, do której podpinać będziemy wszelką elektronikę, a także software’u pozwalającego na programowanie owej płytki.

Obecnie na rynku jest dostępna cała masa oryginalnych płytek Arduino różniących się wszelkimi parametrami, takimi jak różna moc obliczeniowa zależna od zastosowanego układu, wielkość pamięci określająca jak duży program sterujący możemy wgrać, czy ilością pinów, czyli po prostu dostosowanych do danego przeznaczenia. Płytki te mogą być rozszerzane poprzez zastosowanie tzw. Shield’ów, czyli modułów dodających dodatkowe funkcjonalności, takie jak interfejs Ethernet, GPS, Bluetooth, WiFi, oraz sporo innych. Oprócz oryginalnych i certyfikowanych przez Arduino płytek i rozszerzeń, na rynku jest także pełno produktów innych firm stworzonych na ogólnodostępnych, bo przecież otwartych, schematach Arduino. Kopie takie są zazwyczaj w pełni kompatybilne z Arduino (także oprogramowaniem) i często nie różnią się absolutnie niczym poza nazwą.

Jedną z takich kopii posiadam ja i jest to kopia Arduino Mega oparta na układzie ATmega2560. W artykule korzystać będziemy jednak z podstawowych pinów wspólnych dla większości rodzajów płytek, więc nie powinno być problemu, gdy ktoś korzystać będzie np. z Arduino Uno, który jest chyba najpopularniejszą wersją.

A jak tego się właściwie używa?

Podczas gdy w samym wgrywaniu programów na płytkę Arduino wielkiej filozofii nie ma, to kiedy się okryje, że jest wiele sposobów sterowania płytką, a różnych bibliotek do komunikacji (napisanych w różnych językach i przez różne społeczności) jest jeszcze więcej, to zaczynają się problemy i pojawiają pytania m.in. co wybrać? I chyba najważniejszą rzeczą jest zrozumienie właśnie tych dwóch, głównych sposobów sterowania Arduino.

Pierwszy sposób to zwyczajnie wgranie skompilowanego programu bezpośrednio do pamięci płytki Arduino po podłączeniu przez USB. Program taki może być teoretycznie napisany w jakimkolwiek języku posiadającym kompilator do języka maszynowego, oczywiście z uwzględnieniem architektury Arduino. Samo oprogramowanie Arduino posiada masę przykładowych programów napisanych w C i C++, którymi możemy się pobawić, zanim zaczniemy pisać coś swojego, lub na których możemy się wzorować. Program taki zostaje wgrany do pamięci płytki na stałe i nawet po jej odłączeniu od USB, a podłączeniu zasilacza, będzie dalej wykonywać swoje operacje.

A jak wgrać dany program do pamięci płytki? W większości przypadków wszystko sprowadza się do wykonania dosłownie paru czynności w IDE dostarczonym w oprogramowaniu ze strony Arduino. Należy:

  • wybrać typ płyty jaki posiadamy poprzez menu Narzędzia -> Płyta, w moim przypadku jest to “Arduino Mega or Mega 2560”; czasem, ale bardzo rzadko, wybrać także trzeba odpowiedni procesor z menu Narzędzia -> Procesor,
  • wybrać port, który będziemy używać do łączenia się z Arduino, robimy to poprzez Narzędzia -> Port; w większości przypadków będzie tam dostępny tylko jeden port,
  • po napisaniu swojego programu, lub wybraniu z puli dostępnych przykładów, wgrywamy go poprzez wybranie z menu Plik -> Wgraj, lub po prostu poprzez naciśnięcie okrągłego przycisku ze strzałką w IDE. Polecenie to skompiluje program, a następnie wgra do pamięci naszej płytki. Ot cała filozofia.

Sposób drugi - komunikacja z zewnętrznego urządzenia poprzez określony protokół. Drugim sposobem jest użycie gotowego programu określającego protokół komunikacji, lub napisanie swojego (niektórzy zapaleńcy to robią), który wgrywamy do pamięci Arduino i komunikujemy się z nim za pomocą dowolnego, zewnętrznego programu, który używa tego samego protokołu komunikacji.

Najpopularniejszym i chyba najprostrzym przykładem jest tutaj protokół Firmata, którego właśnie będziemy używać w tym artykule. Protokół ten w kilku wariantach (my użyjemy standardowego) jest dostępny w przykładach i wgrywamy go poprzez wybranie Plik -> Przykłady -> Firmata -> Standard Firmata. Po wgraniu protokołu nie musimy więcej już wgrywać żadnego programu do pamięci płytki, a sam protokół będzie wykonywać dokładnie te polecenia, które mu prześlemy dowolną drogą komunikacji. I tutaj pojawia się niestety minus tego rozwiązania: aby mieć możliwość sterowania naszym urządzeniem, musimy mieć zapewnioną z nim stałą komunikcję. Możemy to zrobić poprzez podłączenie Arduino do komputera poprzez USB, a także rozszerzając płytkę o moduł Ethernet, WiFi, czy nawet Bluetooth. Oznacza to tyle, że nasze urządzenie nie ruszy nawet kablem, jeżeli nie prześlemy mu odpowiedniego polecenia z programu uruchomionego na innym urządzeniu hoście.

Obydwa te sposoby oczywiście można łączyć, a nawet trzeba, w przypadku np. budowania robotów. Czyli wgrywamy do naszego urządzenia zbudowanego na bazie Arduino rdzeń oprogramowania wykonujący określone czynności, a wraz z nim cały protokół komunikacji, aby być w stanie przesyłać dodatkowe polecenia do samodzielnie działającego urządzenia.

Piny, wszędzie piny

Przejdźmy teraz do opisu tego, co jest na samej płytce Arduino. Zagłębiać się w to, co robi każdy z pinów nie będziemy, ponieważ jest to temat tak naprawdę na osobny artykuł, ale opiszę tutaj główne grupy pinów występujące na chyba wszystkich płytkach i wyjaśnię do czego służą.

Na samej płytce jest wszystko ładnie oznaczone grupami i na chyba wszystkich płytach występują 3 główne rodzaje pinów: cyfrowe (DIGITAL), analogowe (ANALOG IN) oraz POWER. Dodatkowo na wersjach Arduino posiadających więcej pinów (np. Arduino Mega) dochodzą jeszcze grupy takie jak COMMUNICATION, czy PWM, ale są to tak naprawdę też piny cyfrowe, działające w większości tak samo, ale przeznaczone czasem do innych zastosowań i dające dodatkowe możliwości.

Piny cyfrowe (DIGITAL) - są to piny działające zazwyczaj w obu trybach: jako wyjściowe i jako wejściowe.

Piny te są łatwe w użyciu i działają zero-jedynkowo, czyli np. w przypadku użycia takiego pinu jako wyjściowego ustawiamy zawsze jeden z dwóch stanów: 1 (HIGH) dający napięcie 5V (volt), albo 0 (LOW) dający napięcie 0V. Efektem tego jest to, że np. gdy podepniemy pod taki pin diodę LED uziemiając ją drugą nóżką w pinie z oznaczeniem GND (Ground), to gdy nadamy stan HIGH dioda się zaświeci.

W przypadku działania takiego pinu w trybie odczytu (DIGITAL IN), zwyczajnie dostajemy stan danego pinu także zero-jedynkowo, czyli np. w przypadku podpięciu przycisku dostajemy stan mówiący o tym, czy jest on wciśnięty, czy też nie.

Piny analogowe (ANALOG) - w przeciwieństwie do cyfrowych pinów, analogowe potrafią operować na pełnym zakresie napięcią pomiędzy 0V, a 5V. Oznacza to, że oprócz trudniejszego użycia oferują także możliwość precyzyjniejszego operowania danym elementem, co jest przydatne zwłaszcza w przypadku odczytu stanu owego elementu. Piny te zazwyczaj są używane do odczytu stanów wszelkiego rodzaju czujników itp.

Piny POWER - w tej grupie znajdują się głównie piny oferujące zwyczajnie stałe napięcie (5V, 3,3V) oraz piny zwrotne GND do uziemienia. Takiego dodatkowego napięcia można użyć, gdy dany element wymaga stałego napięcia otrzymując dodatkowo z innych pinów sygnały sterujące 0/1 itp.

Oczywiście zazwyczaj nie podłącza się elementów bezpośrednio do płytki Arduino. Aby testować swój prototyp czy zestawienie elementów zazwyczaj używa się tzw. breadboarda. Oferuje on możliwość wygodnego “wtykania” elementów w taki sposób, że nie musimy ich razem lutować. Osiągnięte zostało to dzięki połączeniu danych rzedów i kolumn razem, co oznacza, że wpinając kabel pomiędzy pin w arduino, a np. najwyższy rząd oznaczony zazwyczaj plusem lub X-em na breadboardzie, propagujemy napięcie danego pinu na cały ten rząd, lub odczytywać będziemy napięcie zwrotne będące sumą wszystkich elementów wpiętych pod ten rząd. Sposób, w jaki są na breadboardzie złączone dane rzędy i kolumny obrazuje poniższy obrazek 1:

złączone rzędy i kolumny Obrazek 1. Sposób, w jaki są na breadboardzie złączone dane rzędy i kolumny

Arduino a Python.

Mamy chyba już solidne podstawy odnośnie działania Arduino, czas teraz wpleść w to wszystko Pythona. Nie natknąłem się na żaden kompilator pozwalający na przełożenie programu napisanego w tym języku, na taki zrozumiały dla Arduino. Oznacza to, że nie ma obecnie (a przynajmniej ja nie znalazłem) dobrego sposobu na wgrywanie programów napisanych w Pythonie bezpośrednio do pamięci Arduino. Pozostaje nam komunikacja z urządzenia hosta za pomocą jakiegoś protokołu i tu podstawową biblioteką jest pySerial, biblioteka w Pythonie, która umożliwia komunikację z dowolnym urządzeniem poprzez dowolny serial port. Z biblioteki tej korzystają praktycznie wszystkie biblioteki wyższego poziomu implementujące protokoły dla Arduino, używa się jej także przy pisaniu swoich metod komunikacji z Arduino.
Ale nie po to masa mądrych ludzi się namęczyła, żeby wydać na świat, w dodatku za darmo, swoje biblioteki i implementacje protokołów, abyśmy teraz pisali coś swojego od podstaw. Tak jak wcześniej wspominałem użyjemy protokołu Firmata do komunikacji z naszą płytką Arduino. Opis protokołu oraz spora lista różnych bibliotek do komunikacji z Firmatą, napisanych w wielu różnych językach, są dostępne w repozytorium protokołu:

Jeśli chodzi o Pythona, to wymienione są tak aż cztery biblioteki implementujące ten protokół dla Arduino, a parę innych można też wyszukać w google, wszystkie korzystają z pySerial. Jednak jak się chwilę przypatrzeć to tylko dwie z bibliotek są aktywnie rozwijane, a są to pyMata i pyFirmata. Ja wybrałem tę drugą i na niej się skupimy w dalszej części artykułu.

Bibliotekę instalujemy standardowo pip’em, czyli mając zainstalowany w systemie Python 2.7.x lub 3.3/3.4, wykonujemy poniższe polecenie:

pip install pyfirmata

Na systemach linuxowych oczywiście wykonujemy powyższe polecenie z sudo, jeżeli chcemy zainstalować bibliotekę globalnie. W przypadku problemów z instalacją polecam spojrzeć na README w repozytorium na githubie, jest tam opis instalacji bezpośrednio z repozytorium.

Następnym krokiem jest oczywiście podłączenie naszej płytki Arduino do komputera poprzez USB i wgranie protokołu Standard Firmata we wcześniej opisany sposób. Urządzenie oczywiście zostawiamy podpięte. Gdy już to zrobimy, następnym krokiem jest stworzenie nowego pliku z naszym testowym programem w Pythonie, np. led.py gdzie umieszczamy poniższy kod:

from pyfirmata import Arduino, util

board = Arduino('\.\COM3')
board.digital[13].write(1)

W powyższym kodzie tworzymy nową instancję klasy Arduino, czyli tak naprawdę obiekt naszej płytki Arduino, dostępnej pod wskazanym serial portem. Podany port dotyczy mojego przypadku, gdy Arduino zostało podpięte pod USB widoczne jako COM3 pod Windowsem. W przypadku systemów Linuxowych ścieżka ta będzie przypominała coś w stylu:'/dev/tty.usbserial-A6008rIF'

Po stworzeniu obiektu płytki odwołujemy się do cyfrowego pinu nr. 13, którego używamy jako wyjściowego poprzez użycie metody write i ustawiamy dla niego stan 1 (HIGH) dając tym samym napięcie 5V. Efektem tego działania będzie zapalenie się diody LED wbudowanej na płytce Arduino, kiedy uruchomimy nasz program.

I tu ważna uwaga: każda płytka Arduino posiada pod pinem nr 13 wlutowaną testową diodę LED oraz rezystor. Oznacza to, że cokolwiek będziemy podpinać pod pin 13, musimy zawsze uwzględnić, że jest tam także podpięta dioda i rezystor. Ogólnie w przypadku podpinania pod inne piny diod LED, należy pamiętać, aby przed diodą zawsze wstawić dla bezpieczeństwa rezystor. np 10K ohm.

Przy uruchamianiu naszych programów pisanych w Pythonie często się zdarzy, że po wykonaniu programu płytka Arduino zostanie w ostatnim stanie, czyli w przypadku wykonania powyższego kodu dioda LED ciągle będzie się świecić. Aby przywrócić płytkę do poprzedniego stanu, można wcisnąć przycisk opisany na płycie jako RESET, który nie resetuje pamięci urządzenia, a tylko je restartuje.

Co dalej?

Powyższy kod jest oczywiście zupełną podstawą pokazującą jak działa biblioteka i tak naprawdę nie oferuje niczego ciekawego. To, co możemy z nim zrobić, to dodać drugą zmianę stanu diody na 0 (LOW) tak, aby w interwale stany te się zmieniały, zamykając wszystko w dowolną pętlę. Możemy tym sposobem zrobić sobie np. stroboskop.

W przypadku gdy chcemy używać danego pinu wcześniej, trzeba go przypisać do zmiennej z uwzględnieniem trybu w jakim chcemy go używać. Używamy do tego metody “get_pin” z biblioteki pyFirmata. Nasz program teraz będzie wyglądać tak:

from pyfirmata import Arduino, util
from time import sleep

board = Arduino('\.\COM3')
pin_13 = board.get_pin('d:13:o')

while True:
pin_13.write(1)
sleep(1)
pin_13.write(0)
sleep(1)

W powyższym kodzie zmieniło się tylko to, że w nieskończonej pętli włączamy i wyłączamy diodę LED na płytce Arduino. Aby to uzyskać przypisaliśmy pin cyfrowy, na którym operujemy w trybie ‘output’, do zmiennej. Dodatkowo aby uzyskać sekundowy interwał pomiędzy stanami, użyliśmy metody ‘sleep’ z wbudowanej w Pythona biblioteki ‘time’.

A co można z tym zrobić więcej? W zasadzie można to rozwijać w nieskończoność dodając kolejne diody wpięte pod kolejne piny. Możemy np. napisać sobie program, który będzie z naszego firmowego bugtrackera (Jira, Bugzilla itp.), czy np. z Gmaila pobierał poprzez API informację o nowych bugach do nas przypisanych, lub o nowych wiadomościach, i będzie włączać zieloną, żółtą, lub czerwoną diodę zależnie od ważności buga lub wiadomości. Jeżeli popuścimy wodze fantazji, to możemy nawet podpiąć pod to syrenę, aby na pewno nie przegapić krytycznej wiadomości przeznaczonej dla nas.

Zadanie domowe

A teraz małe zadanie domowe i wyzwanie: użyć wiedzę z tego artykułu, aby dodać kilka dodatkowych diod LED, ale nie wpinając ich bezpośrednio do płytki Arduino, tylko używając do tego breadboarda. Należy pamiętać, aby przed każdą diodą wstawić rezystor ok. 10K ohm. Zwrócić trzeba uwagę także na nóżki diody: jedna jest przeznaczona do pinu, a druga do wpięcia w masę oznaczoną na płytce jako GND. Tak na marginesie, z reguły odpowiednie rezystory oraz diody dodawane są do zestawów Arduino lub pochodnych, jeżeli takie się zakupiło. Jeżeli nie, to warto wybrać się do pobliskiego sklepu elektrycznego i zaznajomić się z tamtejszym sprzedawcą, bo będziemy go od teraz częściej odwiedzać.

Podsumowanie

To by było tyle, jeżeli chodzi o pierwszą część. W kontekście Pythona nie ma tutaj zbyt zaawansowanych rzeczy, ale są podstawy, wymagane aby w pełni zrozumieć jak działają zarówno Arduino, jak i wszelkie biblioteki się z nim komunikujące. W następnej części skupimy się już na czymś trochę bardziej szalonym i oczywiście zaawansowanym, włączając w to reagowanie na czynniki zewnętrze, czyli używanie pinów w trybie INPUT.
Ale to w następnej części, a tym czasem czas się pożegnać. Mam nadzieję, że artykuł okazał się chociaż trochę przydatny. W razie pytań zawsze chętnie pomogę, więc można śmiało się ze mną kontaktować mailowo.
Do zobaczenia w następnej części!

Michał Janiszewski Michał Janiszewski

Jestem JavaScript oraz Python Developerem tworzącym złożone aplikacje webowe wraz z zespołem świetnych ludzi w poznańskim STX Next. Z zamiłowania jestem także UI Designerem, po godzinach lubię czasem pogrzebać przy grach HTML5, a w przeszłości także pyGame. Czasem można mnie posłuchać na mniejszych i większych eventach, takich jak meet.js

comments powered by Disqus
Współpraca: programista