Sterowanie ledami za pomocą klawiatury – ASCII i (znowu) tablice

Na ostatnich zajęciach kontynuowaliśmy naukę obsługi ledów przez monitor szeregowy. Tym razem nie ograniczaliśmy się do sterowania wszystkimi ledami jednocześnie, a dążyliśmy do możliwości dowolnego włączania i wyłączania pojedynczych lampek. Podczas tych zajęć uczyliśmy się sprytnego (zaawansowanego?) wykorzystania tablic.

Switch … case

Na zajęciach wykorzystaliśmy układy przygotowane tydzień temu. Podłączyliśmy je w taki sam sposób. Do naszego pierwszego programu wykorzystaliśmy warunek wielokrotnego wyboru, czyli składnię switch … case.

Na początku kodu standardowo  podłączamy każdy z ledów do pinu cyfrowego. Następnie tworzymy pętlę if, która sprawdza, czy są dane (bajty) na porcie szeregowym do odczytania przez Arduino – linia #11. W 12 linii wczytujemy jeden bajt i przypisujemy go do zmiennej znakowej c (omawialiśmy to tydzień temu). W celu umożliwienia sterowania pojedynczymi lampkami za pomocą konkretnych liter w 13 linii tworzymy przełącznik switch zależny właśnie od zmiennej c. W liniach 14-23 tworzymy warunki, czyli przypisujemy do wybranych znaków (u nas: a b c d e A B C D i E)  włączanie lub wyłączanie danego LED-a. Fajne jest to, że do bufora możemy od razu wpisać całe „zdanie” a nie tylko pojedynczy znak. Arduino będzie odczytywać znak-po-znaku (linia 12), a my zobaczymy daną sekwencję włączania/wyłączania LED-ów na płytce. Aby uatrakcyjnić ten fragment etap zabawy z Arduino i LED-ami dodaliśmy specjalny case z wartośćią #, który tworzy przerwę – pauzę (linia 24).

#define MAX 5
int piny[5]={2,3,4,5,6};
int i;
char c;
void setup(){
  for(i=0;i<MAX;i++)
  pinMode(piny[i],OUTPUT);
  Serial.begin(9600);
}
void loop(){
  if(Serial.available()>0){
  c=Serial.read();  
   switch(c){
      case 'a': digitalWrite(piny[0],HIGH);break;
      case 'A': digitalWrite(piny[0],LOW);break;
      case 'b': digitalWrite(piny[1],HIGH);break;
      case 'B': digitalWrite(piny[1],LOW);break;
      case 'c': digitalWrite(piny[2],HIGH);break;
      case 'C': digitalWrite(piny[2],LOW);break;
      case 'd': digitalWrite(piny[3],HIGH);break;
      case 'D': digitalWrite(piny[3],LOW);break;
      case 'e': digitalWrite(piny[4],HIGH);break;
      case 'E': digitalWrite(piny[4],LOW);break;
      case '#': delay(200);break; 
}}}

Właśnie dzięki nowemu symbolowi # (pauza) było możliwe wpisywanie sekwencji (=”zdań”) typu abcde#####A#B#C#D#E (jednoczesne włączenie wszystkich 5-ciu LEDów, odczekanie sekundy a następnie wyłączenie, z krótkimi przerwami, po kolei LED-ów). Inne sekwencje to, np. abcde##E##D##C##B##A##abcde###ABCDE###abcde###ABCDE (włączenie wszystkich, wyłączenie po kolei wszystkich w odwrotnej kolejności a na koniec dwukrotne „mrygnięcie” wszystkimi na raz LED-ami).

Obserwacje/uwagi

Powyższy program jest prosty ale ciągle efektowny – dzięki wprowadzeniu pauzy (#). Jednak z informatycznego punktu widzenia cierpi na następujące problemy:

  • chcąc dodać więcej LED-ów musimy jak „małpa” skopiować linie 14-15 dodając nowe literki do sterowania. Zauważamy jednak pewną regularność w oprogramowaniu każdej literki (linie 14-23 niewiele różnią się od siebie). Może nie ma w tym nic złego, ale czy nie da się tego jakoś lepiej zaprogramować? 
  • jak rozbudować program o możliwość sterowania literkami w ten sposób, że dana literka włącza LED-a gdy był on wyłączony, a wyłącza gdy był on włączony? Jak na razie do włączania używamy małych liter a do wyłączania dużych – to chyba zbyteczna rozrzutność.

Zaczynamy od rozwiązania pierwszego problemu i przechodzimy do wykorzystania tablic. Tablice pojawiają się tu w sposób naturalny – przyglądając się liniom 14-23 zauważamy, że włączamy/wyłaczamy LED-y podpięte do portów cyfrowych Arduino zapisanych w tablicy piny[0,1,2,3,4]. Przy czym pierwszy LED podłączony jest do portu piny[0], drugi do portu piny[1] i tak dalej. Jak więc dobrać odpowiedni indeks tablicy do konkretnego LED-a?

ASCII

Dobranie indeksu tablicy piny[] będzie bazować na kodowaniu znaków ASCII. Dane wczytywane przez Serial.read() to w rzeczywistości bajty, które możemy interpretować jako literki (typ char) lub jako liczby (typ int). Możemy więc patrzeć na literkę d jak znak 'd’ (typ char) lub jak na kod ASCII wynoszący 100 (liczba całkowita, typ int). Dlaczego liczba 100? Przypatrz się uważnie tablicy kodów ASCII z poprzedniego linku (kolumna DEC) lub tutaj. Kolejna literka po d to e – czyli kod ASCII 101 i tak dalej. Co więcej, możemy odejmować literki od siebie, bo to będzie zrozumiałe jako… odejmowanie liczb całkowitych! Tak więc d-a oznacza 100-97, czyli 3. Jesli więc umówimy się, że pod literką a mamy sterowanie pierwszego LEDa, pod b drugiego i tak dalej – to właśnie różnica wczytanej literki i znaku a da nam dobrze określenie indeksu tablicy dla konkretnego LED-a! Zapisane jest to w linii #6 poniższego kodu:

void loop(){
  if(Serial.available()>0){
    c=Serial.read();
    Serial.print("Wczytałem znak = ");
    Serial.println(c);
    int idx=c-'a';
    Serial.print("indeks =");
    Serial.print(idx);
    if(idx>=0&&idx<5){ //gdy mamy 5 LEDow
      Serial.print("Włączam / wyłączam LED-a nr");
      Serial.print(idx);
      //dalsza część kodu
    }
}

Na małą uwagę zasługuje jeszcze linia #9 – sprawdzenie, czy indeks nie jest większy niż liczba podłączonych LED-ów (oczywiste) oraz czy indeks jest większy od zera. To drugie może nastąpić  gdy, np. omyłkowo wpiszem z klawiatury znak [ (o numerze ASCII 91), więc w wyniku odejmowania mamy nieistnieący element tablicy o indeksie 91-97=-6.

Tablica – zapamiętanie stanów pinów

Drugi problem na naszej liście to zapamiętanie stanów portów cyfrowych Arduino. Chodzi o to, aby po odczytaniu danej literki włączyć LED-a gdy był on w stanie wyłączonym, i wyłączyć – gdy  był on włączony. Arduino nie ma jakiejś specjalnej funkcji do „zapytania się” o aktualny stan portu, dlatego więc musimy zrobić do samodzielnie. Wykorzystamy pamięć operacyjną płytki Arduino, czyli stany portów będziemy zapisywać w zmiennych. Może do tego służyć zmienna typu logicznego bool. Przechowuje ona tylko dwie wartości: 0, czyli fałsz (false), oraz liczbę różną od zera, czyli prawdę (true). Pewnie nie ma nic złego w utworzeniu pięciu takich zmiennych dla naszych pięciu LED-ów, ale gdy podłączymy ich 20? 30? Dlatego ponownie używamy tablice:

bool stan[]={false,false,false,false,false};
void loop(){
  if(Serial.available()>0){
  c=Serial.read();
  int idx=c-'a';
  if(idx>=0&&idx<5)//gdy mamy 5 LEDow
    if(stan[idx]==true){
      digitalWrite(piny[idx],HIGH);
      stan[idx]=false;
    }
    else{
      digitalWrite(piny[idx],LOW);
      stan[idx]=true;
    }
  else//jesli idx rozny od zera to moze... nasza pauza?
  if(c=='#') delay(200);

Na początku (linia #1) wprowadzamy zmienną tablicową stan z informacją o wyłączeniu wszystkich LED-ów (pięć false-ów). Kluczowe są linie #7-14, gdzie sprawdzamy stan portu i jeśli jest on włączony (wartość true) to wyłączamy LED-a i zmieniamy stan na false (linia #9), a gdy jest wyłączony (wartość false – u nas „w przeciwnym przypadku” linia #11) to włączamy LED-a i także zmieniamy stan portu – tym razem na true (linia #13). 

Program ponownie akceptuje całe sekwencje rozkazów („zdania”) a dodatkowo nie potrzebuje już oddzielnej literki dla włączenia (poprzednio mała literka) i wyłączenia (poprzednio duża literka) LED-a. Piszemy więc zdania typu   abcde######edcba##a##a##b##b##c##c##b##b  i obserwujemy płytkę stykową.

Podsumowanie

Wszystko dało się tak prosto zapisać dzięki tablicom oraz sprytnie wyliczonemu indeksowi tablicy (kodowanie ASCII). Jak widać indeks wykorzystaliśmy dwukrotnie – raz w odniesieniu do włączania/wyłączania LED-a (funkcja digitalWrite i LEDy podłączone do portów zapisanych w tablicy piny) a drugi raz do zapisu stanu portu cyffrowego Arduino (tablica stan). Siłę tego programy docenimy wówczas, gdy podłączymy dużo LEDów (np. 20) i jedyne rozbudowanie tego kodu polegać będzie na… zmienie stałej MAX w pierwszej linii programu – a nie dopisywaniu prawie identycznych linii kodu dla każdego nowego LED-a (i jego stanu!).

Proszę przemyśleć dzisiejszą lekcję, bo za tydzień ponownie spotykamy się z tablicami (no i z wyświetlaczem siedmiosegmentowym).

(c) Ewelina & KG

Komunikacja z naszym Arduino – monitor szeregowy oraz funkcje.

Kolejne zajęcia Fibotu za nami!

Tym razem poruszyliśmy tematy, które pozwoliły nam poznać nieco bardziej techniki komunikacji użytkownik – komputer – kontroler.

Po utworzeniu znanego z początkowych zajęć układu równolegle podłączonych i adresowanych LEDów chcieliśmy móc nimi sterować ręcznie, a nie jedynie z pomocą gotowych algorytmów w pętli.

LEDy połączone płytką stykową

By spróbować czegoś zaawansowanego wróciliśmy do podstaw – każdy uczestnik stworzył znany już układ pięciu LEDów z czego każdy był podłączony do innego pinu cyfrowego w płytce Arduino. Ten układ pozwala niezależnie kontrolować każdą diodę.

#define MAX 5
int piny[5]={2,3,4,5,6}; // Tablica z numerami wyjść cyfrowych do których podłączone zostały diody

void setup(){
  for(i=0;i<MAX;i++) // pętla pozwalająca zdefiniować wyjście każdego z pinów cyfrowych
  pinMode(piny[i],OUTPUT);

}

Monitor szeregowy i komunikacja

Wzbogaciliśmy nasz program o funkcje pozwalające na komunikację przez port szeregowy, a następnie dodaliśmy możliwość wysyłania komend, które będą zapalać i gasić nasze diody.

Sterownik również na bieżąco informuje nas o tym, czy wczytał nasz input – wyświetlał wszystkie znaki które wprowadzimy do monitora szeregowego. By mieć możliwość wczytania więcej niż znaku (char – 1 bajt) zastosowaliśmy funkcję parseInt() pozwalającą na wczytanie ciągu znaków, który zostanie zamienony na liczbę całkowitą. Zmienna „ile” była wprowadzana przez użytkownika i definiowała ile razy lampki mają zamrugać.

#define MAX 5 // liczba diod
int piny[MAX]={2,3,4,5,6};
int i,j;
//char znak;
byte znak;

void setup(){
  for(i=0;i<MAX;i++)
     pinMode(piny[i],OUTPUT);
  Serial.begin(9600);
}
void loop(){
  if(Serial.available()>0){
    int ile=Serial.parseInt();
    Serial.print("Wczytalam ");
    Serial.println(ile);
    mig(ile);
  }

Warto zwrócić uwagę na linijkę trzynastą Serial.available() zwraca liczbę bajtów czekających na odczytanie (a aktualnie przechowywanych w buforze portu szeregowego), gdy zostanie wprowadzona przez użytkownika jakaś dana wejściowa. Czytając jeden bajt (np. Serial.read()) zabieramy z tego bufora jeden bajt a tym samym zmniejszamy licznik danych (bajtów) czekających na odczytanie.

Adnotacja: podczas zajęć modyfikowaliśmy nasz program na bieżąco. W kodzie możecie znaleźć 'przestarzałe’ metody które wprowadziliśmy w ramach zapoznania się z ideologią zadania. Najczęściej będą one zakomentowane w pełnym kodzie, który znajdziecie na samym dole tego wpisu. Warto zwrócić uwagę, że przy wczytywaniu zmiennej char będącej jednym znakiem musimy stosować tłumaczenie na tablicę ASCII, gdyż właśnie w tym formacie zapisane są zmienne char.

Funkcje: szkoda życia na robienie w kółko tego samego !

Stworzywszy program pozwalający na dwukierunkową komunikację z naszym sterownikiem utworzyliśmy funkcję o wdzięcznej nazwie „mig()”. Tworzenie takich funkcji jest podstawą programowania strukturalnego – chodzi o „zamykanie” logicznych części programu (tu: włączanie/wyłączanie wszystkich LEDów) w pewną całość, którą następnie będziemy wielokrotnie używać.

Funkcja „mig()” przechodziła kolejne stadia swojego rozwoju, od najprostrzej – bezargumentowej:

void mig(){
  Serial.println("ON");
  for(i=0;i<MAX;i++)
     digitalWrite(piny[i],HIGH);
   delay(400);
   Serial.println("OFF");
   for(i=0;i<MAX;i++)
      digitalWrite(piny[i],LOW);
   delay(400);
}//mig

Zadaniem powyższej jest jednokrotne włączenie/wyłączenie wszystkich LED-ów. Aby zrobić to kilkukrotnie należy wielokrotnie wykonać stworzoną funkcję mig() – lub wykonać ją w pętli n-razy. Dlatego kolejna modyfikacja polegała na dodaniu argumentu do funkcji:

void mig(int ile){
  for (int jj=0;jj<ile;jj++){
    Serial.println("ON");
    for(i=0;i<MAX;i++)
      digitalWrite(piny[i],HIGH);
    delay(400);
    Serial.print("OFF x");
    Serial.println(jj+1);
    for(i=0;i<MAX;i++)
      digitalWrite(piny[i],LOW);
    delay(400);
  }//jj
}//mig

jednoargumentową, która umożliwia nam wielokrotne włączenie/wyłączenie LED-ów (dodatkow pętla po zmiennej jj). Mając takią funkcję możemy kazać migać, np. czterokrotnie przez wywołanie mig(4) – wówczas następuję przekazanie liczby 4 dla parametru ile w definicji funkcji mig(int ile). Kolejna modyfikacja polegała na dodaniu dodatkowego, drugiego parametru czas określającego ile ms mają być włączone/wyłączone LED-y.

void mig(int ile, int czas){
  for (int jj=0;jj<ile;jj++){
    Serial.println("ON");
    for(i=0;i<MAX;i++)
      digitalWrite(piny[i],HIGH);
    delay(czas);
    Serial.print("OFF x");
    Serial.println(jj+1);
    for(i=0;i<MAX;i++)
      digitalWrite(piny[i],LOW);
    delay(czas);
  }//jj
}//mig

Ten dodatkowy parametr umożliwia nam szybkie miganie (np. czterorkotne) przez wywołanie mig(4, 100) lub wolne przez wywołanie mig(4,2000). Podobnie jak poprzednio wywołując naszą funkcję przypisujemy wartości 4 do zmiennej ile, oraz 100 (lub 2000 w drugim przykładzie) do zmiennej czas. Ostatnia modyfikacja to parametry domyślne w języku C++ (nie ma tego w „czystym” C), czyli zamiana prototypu funkcji (=nagłówka) na następujący: 

void mig(int ile, int czas=400){
  for (int jj=0;jj<ile;jj++){
    Serial.println("ON");
    for(i=0;i<MAX;i++)
      digitalWrite(piny[i],HIGH);
    delay(czas);
    Serial.print("OFF x");
    Serial.println(jj+1);
    for(i=0;i<MAX;i++)
      digitalWrite(piny[i],LOW);
    delay(czas);
  }//jj
}//mig

Powyższa zmiana umożliwia wywołanie dwuargumentowej funkcji mig(int, int) nie wtylko w postaci mig(4,100) ale także mig(4) – wówczas parametr czas przyjmie domyślną wartość 400.   

Funkcja ta robi dokładnie to, o czym wspomniałem przy zmiennej „ile”. Wartość ukryta pod tą zmienną była kierowana do funkcji. Pętla zapalająca (zaznaczona linijka 2) zapala i gasi (linijki 5 i 10) lampki za pomocą znanej już nam pętli wewnętrznej (zapalającej każdą diodę jedną po drugiej w odstępie czasu rzędu milisekund – linijka 4).

Funkcja miała też dodatkową, opcjonalną zmienną wejściową „czas” regulującą odstępy między zapaleniem i zgaszeniem diod za pomocą wbudowanej funkcji „delay()”.

Podsumowanie:

Na tych zajęciach zamiast poznać nowe elementy elektroniczne jak np. znana z poprzednich zajęć czujka szczelinowa, poznaliśmy niezwykle kluczowe możliwości sterownika Arduino – komunikację dwukierunkową przez monitor szeregowy. Możliwość bezpośredniego wysyłania sterownikowi danych wejściowych pozwala na ręczne sterowanie i otwiera nas na nowe możliwości.

Stworzyliśmy swoją własną funkcję istniejącą poza pętlą główną, co zwiększa przejrzystość kodu i daje wygodę stosowania gotowych funkcji.

To nie koniec przygód z komunikacją za pomocą monitora szeregowego. Możliwości implementacji tak kluczowej metody są niezwykle szerokie.

Do zobaczenia na następnych zajęciach!
Maciej (c) 2017 & KG

 

 

Pełny kod:

#define MAX 5
int piny[5]={2,3,4,5,6};
int i,j;
//char znak;
byte znak;

void setup(){
  for(i=0;i<MAX;i++)
  pinMode(piny[i],OUTPUT);
  Serial.begin(9600);
}

void mig(int ile, int czas=400){
  for (int jj=0;jj<ile;jj++){
  Serial.println("ON");
  for(i=0;i<MAX;i++)
    digitalWrite(piny[i],HIGH);
    delay(czas);
   Serial.print("OFF x");
   Serial.println(jj+1);
   for(i=0;i<MAX;i++)
    digitalWrite(piny[i],LOW);
    delay(czas);
  }//jj
}//mig

void loop(){
  if(Serial.available()>0){
    int ile=Serial.parseInt();
    //znak=Serial.read(); //przechowuje 1 bajt
    Serial.print("Wczytalam ");
    Serial.println(ile);
 //   mig(znak-48); //0 w tabeli ASCII to 48
   mig(ile);
 //   if (znak=='3')mig(3);
 //  if (znak=='5')mig(5,500);
    }
    
    

 

Czujka szczelinowa — pomiar wartości przyspieszenia ziemskiego

Czujka szczelinowa

Na ostatnich zajęciach za pomocą czujki szczelinowej i wahadła wyznaczaliśmy wartość g (przyspieszenia ziemskiego). Podzieliliśmy się na dwie grupy, z których każda miała inny pomysł na stworzenie mechanizmu oraz napisanie odpowiedniego programu.

Zasada działania czujki jest bardzo prosta. Na wyjściu cyfrowym OUT z modułu czujki pojawia się sygnał wysoki, jeśli szczelina jest przesłonięta, oraz sygnał niski, gdy szczelina jest odsłonięta. Wahadło wykonując swoje ruchy harmoniczne będzie przechodziło przez szczelinę zasłaniając ją – my musimy zmierzyć czas pomiędzy tymi sygnałami.

Wahadła

Jedna grupa korzystała ze specjalnego statywu, na którym przymocowała cały mechanizm składający się ze sznurka i nakrętki na śrubkę. Ułatwiło to pracę i zminimalizowało niepewności pomiarowe.

Druga grupa musiała wykazać się inwencją twórczą i improwizacją.

Podłączanie czujki

Czujnik łączymy z Arduino za pomocą trzech kabelków. Vcc podłączamy do napięcia 5V, GND do GND, a OUT to dowolny pin cyfrowy, który jest niezbędny do przekazywania informacji.

Kod programu

int czujka=5;
unsigned long t1,t2;

void setup(){
 Serial.begin(9600);
 pinMode(czujka, INPUT);
 Serial.println("jestem gotowy! ");
 Serial.print(millis());
  t1=millis();
  Serial.println(sizeof(t1));
}
int i=1;

void loop(){
 if(digitalRead(czujka)==1)
 {t2=millis();
 Serial.print(i);
 Serial.print(" ");

 Serial.println(t2-t1);
 t1=t2;
 i++;

 delay(200);
}
}

Czujnik podłączyliśmy do cyfrowego pinu nr 5, można to zmienić w pierwszej linijce kodu.

Dążąc do wyznacznia wartości przyspieszenia ziemskiego, musimy poznać okres drgań wahadła. W tym celu napisaliśmy program korzystający z funkcji millis (więcej informacji znajduje się w jej manualu). Używamy jej dwa razy: za pierwszym razem w linii #9 zapamiętujemy jej wartość do zmiennej t1 (czyli czas włączenia programu Arduino), natomiast w linii #16 przypisujemy jej wartość do zmiennej t2 w celu późniejszego obliczenia różnicy pomiędzy nimi. Różnica ta oznacza czas pomiędzy dwoma następującymi po sobie przesłonięciami czujki, czyli momentem przejścia przez „bramkę” i ponownego powrotu. Pamiętajmy, że nie jest jeszcze okres wahadła (okres to czas pełenego ruchu „tam i spowrotem”) dlatego obrabiając dane pamiętamy o mnożeniu tych czasów przez dwa. W linii #21 równamy wartości zmiennych t1 i t2, dzięki czemu w następnej iteracji pętli zostanie obliczona różnica pomiędzy kolejnymi dwoma ruchami wahadła. W przypadku braku tej linii, otrzymywane wyniki byłyby różnicą czasu danego ruchu wahadła i czasu od włączenia Arduino (uruchomienia programu). Wypisane wartości sprawdzaliśmy w Monitorze szeregowym, podobnie jak w przypadku przycisku.

Istotna jest linia #24 powyższego kodu – te opóźnienie (dobrane metodą prób-i-błędów) potrzebne jest po to, aby otrzymać tylko jedną informację o przesłonięciu czujki naszym wahadłem. Gdy nie ma tej linii, to w czasie przejścia przez szczelinę wahadła czujka „produkuje” setki zdarzeń, które są nam zbyteczne (a nawet przeszkadzają).

Wyznaczanie wartości g

Po otrzymaniu wielu pomiarów, w celu dalszych obliczeń użyliśmy arkusza kalkulacyjnego. Skorzystaliśmy ze wzoru: G=(4π²)*L/T², przy czym G to przyspieszenie ziemskie, L to długość wahadła, a T to okres drgań. Wykonaliśmy obliczenia (w programie LibreOffice Calc), a wyniki wyszły nam dość imponujące. Średnia wartość pomiarów dała nam wynik 9,73(±0,12)m/s²,  przy czym ogólnie przyjęta wartość to 9,81m/s².

 (c) Ewelina 2017

Przycisk (modeuł) + ekran

Dziś poznalismy trzy rzeczy: wypisywanie komunikatów przez Arduino na ekran PC-ta, funkcję „zegara” millis() oraz obsługę przycisku (a właściwie modułu z przyciskiem, niedługo poznamy różnice).

Komunickacja z PC-tem

Chodzi o komunikację szeregową UART z wykorzystaniem dwóch pinów cyfrowych Rx/Tx (piny numer zero i jeden). Uruchamiamy komunikację poleceniem Serial.begin(szybkość). W tym momencie tracimy dwa wspomniane piny(zero i jeden) – no cóż, coś za coś. Parametr szybkość musi być na obu urządzeniach taki sam, w przeciwnym przypadku urządzenia się „nie dogadają”. W środowisku Arduino IDE wybieramy Monitor szeregowy (lub w angielskiej wersji: Serial Monitor) i nasłuchujemy to, co nadaje do nas Arduino – to jest właśnie drugie urządzenie, do którego Arduino nadaje (prawy, górny róg okienka z kodem programu)

Jak wspomniałem należy sprawdzić, czy w okienku Monitora (portu) szeregowego parametry tramisji (szybkość) jest taka sama, jak w programie Arduino – prawy, dolny róg tego okienka.

W przyszłości opowiem więcej o tych parametrach transmisji oraz jak wysyłać dane do Arduino – ale to później.

Trzeba zapamiętać dwie funkcje wysyłające napisy z Arduino:

  • Serial.print(„napis”) — wysyłamy tekst napis do urządzenia nasłuchującego; tekst musi być w cudzymsłowiu (aka. psie uszy)
  • Serial.println(„napis”) — wysyłamy napis i znak przejścia do nowej linii – dzięki temu kolejny napis pojawi się w nowej linii.

Te dwie funkcje w zupełności wystarczają, aby Arduino coś do nas „mówiło” 😉

millis()

Bardzo przydatna funkcja zwracająca liczbę milisekund od czasu rucuhomienia programu (włączenia/zrestartowania Arduino). Do zapisywania danych zwracanych przez tą funkcję należy użyć typu unsigned long – wystarczy na około 50 dni. Więcej info tutaj.

Dzięki tej funkcji możemy mierzyć czas wykonania jakiejś czynności. Aby to zrobić, musimy zapisać „zegar” (aktualną wartość milisekund, która sama w sobie nie jest istotna) do jednej zmiennej, powiedzmy t1, wykonać czasochłonne czynności, a po nich ponownie zapytać o „zegar” – i zapisać do drugiej zmiennej, np. t2. Różnica t2 i t1 jest właśnie czasem wykonania konkretnej czynności.

Przycisk (moduł).

Dzięki temu, że to jest przycisk z modułem, to jego obsługa jest bardzo prosta – musimy podłączyć zasilanie do modułu (Vcc do Arduino 5V, GND do Arduino GND, a pin sygnałowy S – do dowolnego, wybranego przez nas pinu cyfrowego w Arduino – np. 7).

Następnie pamiętajmy o poleceniu pinMode(7, INPUT), które spowoduje, że będziemy mogli odczytywac stan przycisku za pomocą digitalRead(7). Jeśli przycisk jest wciśnięty, to funkcja ta zwróci wartość 1, w przeciwnym przypadku – wartość 0 (zero).

Koszt takiego modułu to około 3 zł, należy szukać hasła w stylu Moduł przycisku tack switch z LED ARDUINO (np. w serwisie Allegro).

Fale… z ledów… także wirtualnych…

Fala… z LED-ów…. prawdziwych! Oprócz fal pojawiła się TABLICA i pętla FOR. To sporo – kto nadążał – gratuluję!

Prezentacja Pana Macieja – proste, ale… całkiem efektowne! Gratuluję. Bez pętli zaprogramować coś takiego to koszmar.

Następnie zachęcałem do wirtualnego Arduino ze stronki tinkercad.com (jest to znany projekt cicuits.io, który przejęła firma Autodesk):

Stronka ta umożliwia zabawę w Arduino bez posiadania płytki Arduino – wystarczy się zarejestrować (za darmo!) i można działać.

Jak zacząć? Już po rejestracji wybieramy CIRCUITS:

a następnie zielony przycisk Create new circuits:

Kolejnym krokiem jest już dodawanie elementów po naciśnięciu +Components (prawy pasek, na górze)

gdzie zaczynamy od płytki stykówki, LED-a, rezystorka i bateryjki. Uruchamiamy symulację przyciskiem Start Simulation (potem ją zatrzymujemy Stop). Gdy już wszystko rozumiemy (tyle-o-ile) to czas na dodanie płytki Arduino i… tu się zaczyna zabawa!

Kto zrobi pracę domową z „odbijającą się” falą? Ze zmienną czas? Na następnych zajęciach wgramy programiki na prawdziwe płytki i zobaczymy kolorowe efekty! Proszę śmiało działać!

 

Pierwsze spotkanie 2017 r.

No i rozpoczęliśmy nowy rok akademicki 2017/2018 a tym samym i Fi-BOTa. Na pierwszych zajęciach pojawiło się 9 śmiałków – zobaczymy, ilu będzie kontynuować…

Co robiliśmy? Dla większości była to pierwsza przygoda z Arduino więc było sporo gadania, ale finalnie udało nam się błyskać jednym LED-em podłączonym na płytce stykowej. To dużo jak na godzinkę z hakiem i moją skłonność do gadania 😉 Ale pokazałem też motywację – ta sama płytka Arduino podłączona do żarówki 230V – i działa!

Spotkania w roku 2017/18 będą odbywać się zawsze we wtorki o godz. 16:00. Zapraszam!

Przerwa wakacyjna

Ponownie spotykamy się w październiku 2017 r.

Podsumowując rok akademicki 2016/17: przeprowadziliśmy 31 spotkań w ramach Fi-BOTa, w tym jedno festiwalowe (XV Podlaski Festiwal nauki i Sztuki). Dziękuję za wytrwałość i zapraszam w roku akademickim 2017/18 (termin będzie ustalony w październiku). Zachęcam do samodzielnej pracy z Arduino i… pochwaleniem się własnymi projektami w nowym semestrze!

K.G.

Followliner (FL) – starcie 1

  1. Prosty FL – zbudowany na 2 czujnikach TCRT5000 ustawionych blisko siebie tak, by oba leżały nad czarną linią. Położenie czujek – z przodu, przy napędzie (wiem wiem…).
  2. Instalacja modułu w pojeździe.
  3. Pierwszy program – jedź prosto, gdy oba czujniki „widzą” linię.
  4. Modyfikacja – skręcaj (zatrzymując jedno koło) gdy jedna czujka „gubi” linię.
  5. Modyfikacja – silniki STOP, gdy obie czujki zgubiły linię.

Do zrobienia:

  1. (chwilowo) zostaniemy przy 2 czujkach, za to dodamy regulację szybkości kół w zależności od sygnału na czujce (aby nie było szarpania, a ruch był płynny).
  2. gdy pojazd wyjechał poza linię, to ma się cofnąć (może nawet kilka razy, a jeśli po tych kilku razach dalej nie widać linii – to dopiero wówczas STOP).
  3. zmodyfikujemy tor testowy – łagodniejsze łuki (łatwiejsze, ale z czasem zmienimy).

Liczymy obroty (TCRT5000) – sukces!

Liczymy obroty silniczka D65:

Znalezione obrazy dla zapytania d65 zestaw napedowytutaj nasz program:

 

bool stan, poprz;
unsigned long int t1;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  stan=false;
}

int odczyt;
int licz=0;
void loop() {
  poprz=stan;
  odczyt=analogRead(A0);
  if (odczyt<50)
    stan=true;
  else
    stan=false;
//  Serial.print(odczyt);
//  Serial.print("   ");
//  Serial.print(stan?"jasne":"ciemne");
//  Serial.print("   ");
  if (stan!=poprz){
    licz++;
    Serial.print(" KLIK ");
    Serial.print(licz);
    Serial.print(" ");
    if (licz%2==0){
      Serial.print(1000./(millis() - t1)*60);
      Serial.println(" rpm");
      t1= millis();
    }
  }
//  else
//    Serial.println();
}

I działa bardzo fajnie, nawet z tylko jednym znacznikiem! Wyniki są bardzo powtarzalne (przy zasilaniu około 7V – jeden obrót w 230-240ms, czyli 4 obroty w 1 sekundę, a to daje 4*60=240 obrotów na minutę – rpm).

Rozbudowa?

  1. więcej znaczników (już 2 polepszają sprawę, 4 to zdecydowanie OK).
  2. inny algorytm – zliczanie liczny obrotów i aktualizowanie wartości rpm co określony czas, np. 2 sek

Liczymy obroty (TCRT5000)

Liczymy obroty silniczka D65:

Znalezione obrazy dla zapytania d65 zestaw napedowy

który przy zasilaniu 6V powinien mieć 100 rpm (=obrotów na minutę). My wykorzystujemy poznaną czujkę odbiciową TCRT5000:

 

Znalezione obrazy dla zapytania tcrt5000

Pierwsze zgrubne wyniki dały 1 pełen obrót w czasie 250ms, czyli 240 rpm (trochę dużo, ale to dlatego, że zailalem silniczek 8V – „nielegalnie”). Zrzut ekranu:

oraz program (wyjaśnienia i dyskusja później, jak też i omówienie dokładności pomiaru):

int pomiar[3],i=0,maksior;
unsigned long t1,delta;

void setup() {
  Serial.begin(9600);
  t1=millis();    
}
void loop() {
//  Serial.print(millis());
//  Serial.print("   ");
  pomiar[0]=pomiar[1];
  pomiar[1]=pomiar[2];
  pomiar[2]=analogRead(A0);
  //Serial.println(pomiar[2]);  
  if ((pomiar[1]<950)&&(pomiar[1]>300)&&(pomiar[1]>pomiar[0])&&(pomiar[1]>pomiar[2])){
    delta=millis()-t1;
    if(delta>50){
      Serial.print("1 obrot w ");
      Serial.print(delta);
      t1=millis();    
      Serial.print("ms, czyli rpm=");  
      Serial.println(60*1000.0/delta); 
    }
  }  
  //delay(100);
}