Do programowania będziemy potrzebować konwerter USB-UART oraz zintegrowane środowisko Arduino (do pobrania ze strony
https://www.arduino.cc/en/software).
Drobna uwaga zanim zaczniemy. Programowanie Arduino i programowanie w ogólności jest bardzo szeroką dziedziną wiedzy. Zatem w tym odcinku skupimy się na prezentacji potencjalnych zastosowań w modelarstwie. Siłą rzeczy będzie to przegląd pobieżny. Czytelników zainteresowanych głębszym poznaniem tematu zachęcam do sięgnięcia po specjalistyczną literaturę lub choćby internetowe kursy, często dostępne bezpłatnie.
Na początku zastanówmy się jakie funkcje ma realizować program. Moje założenia można sprowadzić do dwóch stwierdzeń:
- Krótkie naciśnięcie przycisku ma powodować przesunięcie iglic zwrotnicy
- Długie naciśnięcie przycisku ma przełączać pomiędzy oświetleniem portierni, a funkcją telewizora.
Takie ujęcie tematu powoduje, że jesteśmy gotowi, aby napisać tak zwany pseudo-kod. Czyli formę pośrednią pomiędzy językiem, w jakim my ludzie opisujemy problemy, a językiem zrozumiałym dla mikrokontrolera.
Przyjmijmy założenie, że instrukcje z pseudo-kodu są wykonywanie cyklicznie, od góry do dołu, co bardzo krótki czas. Powiedzmy co 2 milisekundy.
W każdym przebiegu naszego pseudo-kodu będziemy sprawdzać warunki, ustawiać flagi (przyjmujące wartości prawda, bądź fałsz) oraz wykonywać pożądane akcje. Przy czym akcje te mogą być rozbite na wiele przebiegów kodu. Przykładowo, ruch orczyka serwa przesuwającego iglice będzie odbywał się w każdym przebiegu o niewielką, zadaną odległość. Przemieszczenie się iglic z położenia zasadniczego w zwrotne lub na odwrót będzie więc wymagało wielokrotnego wykonania programu. Podczas tego ruchu program będzie mógł wykonać równolegle inne działania, np. symulować pracę telewizora.
Zacznijmy zatem. Na najwyższym poziomie abstrakcji forma naszego pseudo-kodu będzie reprezentować 3 konkretne funkcje:
- obsługę przycisku – krótkie (<0,5s) i długie (0,5s) naciśnięcie
- niewielki ruch ramieniem serwomechanizmu w kierunku nowej pozycji, jeżeli wydano wcześniej dyspozycję przestawienia zwrotnicy
- migotanie ekranu telewizora
Rozpatrzmy następnie jakie czynności musimy podjąć w każdym przebiegu w stosunku do każdej z nich. Najpierw przycisk.
W pierwszej kolejności sprawdzamy czy w obecnym przebiegu przycisk jest wciśnięty. Musimy być w stanie rozróżnić sytuację, w której przycisk dopiero co naciśnięto, od tej w której jest to już kolejny przebieg, gdy przycisk jest w takim stanie. Do tego celu użyjemy flagę „przycisk aktywny”. Jeżeli flaga jest jeszcze opuszczona, a wiemy, że stan przycisku odpowiada jego wciśnięciu, to musimy ją podnieść. Tak zatem robimy, a dodatkowo, jeżeli podnosimy flagę, to zapisujemy sobie również czas jej podniesienia w zmiennej pod nazwą „czas aktywacji”.
Code:
A)
JEŻELI przycisk wciśnięty TO:
JEŻELI flaga „przycisk aktywny” jest opuszczona TO:
Podnieś flagę „przycisk aktywny” ORAZ zmiennej „czas aktywacji” przypisz aktualny czas
Kolejny warunek posłuży do sprawdzenia, czy mamy do czynienia z długim naciśnięciem przycisku. W tym celu musimy najpierw sprawdzić, czy flaga „przycisk aktywny” jest podniesiona. Jeżeli nie jest, to nic tutaj nie robimy i przechodzimy do warunku C. Jeżeli jednak flaga jest podniesiona, to sprawdzamy czy od wciśnięcia przycisku upłynęło co najmniej 0,5 sekundy (wykorzystamy do tego wiedzę o „czasie aktywacji” oraz aktualny czas). Jeżeli nie, to nic nie robimy i przechodzimy do warunku C. Jeżeli jednak taki czas minął, to powinniśmy zmienić typ oświetlenia portierni. Tylko że chcemy to zrobić tylko raz po upłynięciu czasu 0,5 sekundy, a nie przy każdym przebiegu, gdy czas ten już zdążył upłynąć, a naciskający nie zwolnił jeszcze przycisku. W tym celu wprowadzimy kolejną flagę, którą nazwiemy „długa aktywacja”. Jej podniesienie będzie oznaczało, że mamy wykonać czynność związaną z długim naciśnięciem przycisku. Zatem nasz warunek w tym kroku przyjmuje postać – czy przycisk jest już naciśnięty co najmniej przez 0,5 sekundy i czy flaga „długa aktywacja” jest opuszczona. Jeżeli tak to mamy akcję do wykonania – musimy zmienić typ oświetlenia portierni.
W tym pomoże nam kolejna flaga nazwana „migotanie telewizora”. Załóżmy, że przy starcie programu flaga ta jest opuszczona co odpowiada oświetleniu pomieszczenia. Sprawdzając zatem stan tej flagi wiemy czy mamy zgasić światło i włączyć TV, czy może odwrotnie. W warunku tym musimy również odpowiednio włączyć lub wyłączyć oświetlenie portierni, lecz nie zajmujemy się tutaj miganiem ekranu telewizora.
W tym miejscu ważne jest również podniesienie flagi „długa aktywacja”, aby w kolejnym przebiegu nie zmienić gwałtownie typu oświetlenia portierni.
Code:
B)
JEŻELI flaga „przycisk aktywny” jest podniesiona TO:
JEŻELI aktualny czas – „czas aktywacji” >= 500ms ORAZ flaga „długa aktywacja” jest podniesiona TO:
ustaw flagę „długa aktywacja”
JEŻELI flaga „migotanie telewizora” jest opuszczona TO:
ustaw flagę „migotanie telewizora”
wyłącz oświetlenie portierni
JEŻELI flaga „migotanie telewizora” jest podniesiona TO:
wyłącz telewizor
włącz oświetlenie portierni
Ostatnim warunkiem sprawdzanym w tej części jest reakcja na zwolnienie przycisku. Jeżeli mamy podniesioną flagę „długa aktywacja”, oznacza to zwolnienie przycisku po jego długim naciśnięciu. W takiej sytuacji musimy jedynie opuścić tę flagę.
W przeciwnym wypadku przycisk był naciśnięty krótko i mamy jedną z dwóch sytuacji, które musimy umieć rozróżnić – orczyk serwomechanizmu jest obecnie w spoczynku, lub jest on już w ruchu. Jak się domyślamy, musimy wprowadzić kolejną flagę – „ruch orczyka”.
Jeżeli flaga jest podniesiona, to nie robimy nic. Po prostu ktoś omyłkowo nacisnął ponownie przycisk na krótko, gdy iglice już się poruszały.
Jeżeli jednak jest opuszczona, to musimy zrobić dwie rzeczy. Po pierwsze podnosimy tę flagę, aby zasygnalizować, że rozpoczynamy ruch orczyka (rzeczywistym ruchem zajmiemy się w sekcji mu przewidzianej) oraz ustalić kierunek obrotu – z pozycji zasadniczej do zwrotnej lub na odwrót.
Jak się zapewne domyślamy, obie te pozycje musieliśmy wcześniej wyznaczyć doświadczalnie. W przypadku sterowników dostępnych komercyjnie ustawia się je najczęściej za pomocą potencjometrów. My jednak zapiszemy je na stałe w programie.
Oprócz tych dwóch informacji musimy również mieć miejsce na zapisanie aktualnego położenia (pozycji) orczyka. Taką zmienną nazwijmy krótko „poz”. Zatem sprawdzamy jej wartość, która może być tożsama z „pozycją zasadniczą”, bądź „pozycją zwrotną”. W zależności od tego z którą sytuacją mamy do czynienia, zmiennej „poz docelowa” wpisujemy wartość, którą orczyk musi osiągnąć – „pozycję zasadniczą”, lub „pozycję zwrotną”.
Ostatnią czynnością, którą musimy tutaj wykonać jest opuszczenie flagi „przycisk aktywny”.
Code:
C)
JEŻELI zwolniono przycisk to:
JEŻELI flaga „długa aktywacja” jest podniesiona TO:
opuść flagę „długa aktywacja”
W INNYM WYPADKU JEŻELI flaga „ruch orczyka” jest opuszczona” TO:
podnieś flagę „ruch orczyka”
JEŻELI „poz” to „pozycja zasadnicza” TO:
ustaw „pozycję docelową” na wartość „pozycji zwrotnej”
W INNYM WYPADKU:
Ustaw „pozycję docelową” na wartość „pozycji zasadniczej”
opuść flagę „przycisk aktywny”
I to tyle, jeżeli chodzi o obsługę przycisku. Następna sekcja zajmuje się obsługą ruchu ramienia serwomechanizmu. Aby obsłużyć ten ruch musimy wprowadzić dwie dodatkowe stałe i jedną zmienną:
- interwał – niezmienna wartość określająca co, ile milisekund chcemy przesuwać ramię,
- krok – niezmienna wartość określająca, ile kroków pokona ramię w każdym interwale,
- czas ostatniego kroku – zgodnie z nazwą, tutaj zapisujemy, kiedy został wykonany ostatni krok.
Zatem w sekcji tej sprawdzamy czy flaga „ruch orczyka” jest podniesiona oznaczając ruch. Jeżeli nie, to nie mamy tutaj nic do roboty. W przeciwnym wypadku sprawdzamy, czy ramię osiągnęło pozycję docelową. Jeżeli tak, to mamy dwie możliwości. W pierwszej ramie jest teraz na pozycji zasadniczej, a w drugiej na pozycji zwrotnej. W każdej z nim ustawiamy odpowiednio przekaźnik. Na końcu opuszczamy flagę „ruch orczyka” aby zasygnalizować koniec ruchu.
Jeżeli ramię nie osiągnęło pozycji docelowej, to sprawdzamy czy od ostatniego kroku minął co najmniej jeden interwał. Jeżeli tak, to w zależności od strony, w którą porusza się ramię do jego pozycji dodajemy lub odejmujemy jeden krok, a następnie zapisujemy aktualny czas do zmiennej „czas ostatniego kroku”.
Code:
JEŻELI flaga „ruch orczyka” jest podniesiona TO:
JEŻELI „poz” ma wartość „pozycji docelowej” TO:
JEŻELI „poz” ma wartość „pozycji zasadniczej” TO:
ustaw odpowiednio przekaźnik
JEŻELI „poz” ma wartość „pozycji zwrotnej” TO:
ustaw odpowiednio przekaźnik
opuść flagę „ruch orczyka”
JEŻELI „poz” nie ma wartości „pozycji docelowej” TO:
JEŻELI od „czasu ostatniego kroku” minął co najmniej „interwał” TO:
JEŻELI „poz” < „pozycja docelowa” to dodaj „krok” do „poz”
JEŻELI „poz” > „pozycja docelowa” to odejmij „krok” od „poz”
dodaj wartość „interwału” do „czasu ostatniego kroku”
Ostatnia sekcja odpowiada za realizację funkcji migotania telewizora i jest bardzo prosta. Pomińmy więc komentarz i przejdźmy bezpośrednio do pseudo-kodu:
Code:
JEŻELI flaga „migotanie telewizora” jest podniesiona TO:
JEŻELI aktualny czas jest > „czasu zmiany” TO:
ustaw losową jasność telewizora z ustalonego przedziału
ustaw losową wartość nowego „czasu zmiany” z ustalonego przedziału
I to tyle, dobrnęliśmy do końca długiej litanii warunków. Ale zanim spojrzymy na rzeczywisty program musimy jeszcze wyjaśnić kilka spraw. Po pierwsze program dla Arduino składa się z dwóch sekcji:
- zamkniętej nawiasami klamrowymi sekcji „setup”, w której ustalamy stan początkowy po włączeniu mikrokontrolera. Ustalamy tam, które porty są wejściami, a które wyjściami. Przemieszczamy iglice w pozycję początkową (u mnie zasadniczą), czy włączamy oświetlenie. Sekcja ta jest wykonywana wyłącznie raz, tuż po włączeniu mikrokontrolera.
- Zamkniętej nawiasami klamrowymi sekcji „loop”, która określa czynności wykonywane przez program. Sekcja ta jest wykonywana przez cały czas pracy Arduino. Po każdorazowym przebiegu od góry do dołu wykonuje się ponownie. W tej sekcji umieścimy zapisywany formalnie pseudo-kod.
Przed sekcją
„setup” umieszcza się wszystkie stałe wykorzystywane przez program (np. pozycję zasadniczą i zwrotną iglic, definicję długiego naciśnięcia przycisku, itp.) oraz zmienne – flagi przyjmujące wartość prawa lub fałsz (nazywane
„boolean”) oraz zmienne liczbowe.
Za sprawdzenia warunków odpowiadają instrukcje
„if”,
„else if” oraz
„else”. Sprawdzane warunki zamykamy w nawiasy okrągłe, a akcje do wykonania w przypadku ich spełnienia w nawiasy klamrowe.
Arduino nie zawiera podtrzymywanego bateryjnie zegara. W związku z czym do informacji o zmianie czasu musimy wykorzystać ilość milisekund, które upłynęły od jego włączenia. Wartość ta jest zwracana przez funkcję
„millis”.
„millis” przepełnia się po około 50 dniach pracy mikrokontrolera i zaczyna ponownie liczyć od zera. W zastosowaniach modelarskich nie ma to praktycznego znaczenia.