Zajęcia nr 5 – sygnały cyfrowe, analogowe…

Sygnały cyfrowe – czujka ruchu PIR

Prosta w obsłudze czujka ruchu. Trzy piny  – zasilanie, masa oraz wyjście. W przypadku wykrycia ruchu wyjście jest w stanie wysokim (5V) przez chwilę (można sterować tym czasem),  a gdy ruchu brak – wyjście przechodzi w stan niski (0V). Aby to odczytać przy pomocy Arduino poznaliśmy dwie nowe rzeczy:

  • pinMode(7, INPUT) – czyli właśnie informujemy Arduino, że będziemy odczytywać wartość napięcia na konkretnym pinie (w tym przypadku: pinie numer 7),
  • digitalRead(7) – funkcja odczytująca napięcie i zwracająca wartość 1 (jedynka) gdy napięcie jest wysokie (2.4 – 5V) oraz 0 (zero) gdy napięcie jest niskie (0 – 0.8V).

Warto pamiętać, że Arduino UNO ma 14 pinów cyfrowych, a inne modele – patrz specyfikacja (kupując płytkę dla siebie weź to pod uwagę).

Sygnały analogowe

Czyli czytanie napięcia z przedziału od zera do 5V z rozdzielczością 10 bitów – a więc 1024 poziomów napięć (bo 2 do potęgi 10-tej to właśnie 1024). Nasze płytki są już lekko zdezelowane i wcale na wyjściu pinu 5V z Arduino nie mamy 5V a np. 4.57V, dlatego warto to mieć na uwadze czytając konkretną wartość napięcia. Poznaliśmy:

  • wejścia analogowe: A0, A1, A2, …, A5 (6 sztuk w UNO)
  • analogRead(A0 – funkcja odczytująca napięcie i zwracająca wartości od 0..1023 w zależności od napięcia na wejściu A0.
  • wejście AREF na płytce Arduino do podawania napięcia referencyjnego z przedziału 0..5V oraz funckję analogReference(EXTERNAL) uruchamiającą tę funkcję. Dzięki temu można wykorzystać całą rozdzielczość 10-ciu bitów na czytanie napięcia w przedziale 0..AREF V – ale tego nie testowaliśmy w praktyce.
  • inne możliwości ustawienia punktu odniesienia w funkcji analogReference, np. INTERNAL1v1 w zależności od płytki Arduino daje to pożliwość wykorzystania całej rozdzielczości 10-ciu bitów w zakresie 0..1V.

Galeria:

dsc_2177

Na zdjęciu: 4-ka wspaniałych czyli mała lecz silna grupa, która soboty ma wolne 😉 A ten piąty? widać, że nie z grupy, taki na peryferiach… soboty ciekawie spędza 😉 zresztą nie tylko On, inni (spoza kadru) też.

Potencjometr liniowy – dzielnik napięć (3 piny, nie „bolce” !!!)

potencjometr-osiowy-liniowy-5kBardzo przydatny element elektroniczny – będziemy go wielokrotnie wykorzystywać. Dlatego przypominam, że działanie oparte jest na dzielniku napięć, i należy pamiętać roli 3 nóżek tego elementu (proszę nie nazywać pinów „bolcami” – to mało profesjonalne):

  • pierwsza – zasilanie (np. 5V)
  • druga – napięcie na wyjściu, czyli zmodyfikowane konkretnym ustawieniem gałki potencjometru (np. 1.2V)
  • trzecia – masa (0V)

Oczywiście nóżki piersza i trzecia mogą być zamienione rolami. Przy pomocy miernika uniwersalnego sprawdziliśmy w praktyce działanie tego potencjometru, a już po chwili wczytywaliśmy do Arduino to napięcie i pisaliśmy na ekranie – dzięki funkcji  Serial.println().

Ważne: zauważyliśmy, że ustawiony potencjometr w jednej pozycji i nie poruszany daje lekko różne wartości, np. 600, 600, 601, 600, 600, 600, 598, 600, 600, 601, 600… Niby widać, że odczytana wartość wynosi 600, a te drobne odstępstwa nazywamy fluktuacjami. Dzieje się tak gdyż przetwornik DAC w Arduino nie jest doskonały (a niby co jest?!), wrażliwy na szumy i inne rzeczy. Warto o tym pamiętać.

Sygnały PWM – sygnały cyfrowe

Czyli Pulse Width Modulation – sygnały szybkozmienne w czasie (Arduino UNO – 2ms, czyli 500Hz), o dwóch wartościach – 0V i 5V. Mamy możliwość wybrania wypełnienia sygnału 5V – czyli jak długo wartość 5V utrzymuje się w okresie 2ms (a następnie wszystko się powtarza). Mamy 8 bitów do sterowania wypełnieniem – a więc wartości z przedziału 0..255.

Ku pamięci:

  • PWM jest dla sygnałów cyfrowych, oznaczonych tyldą w Arduino (a więc ~3, ~5, ~6, ~9, ~10 – aż 6 takich wyjść!)
  • pinMode(7, OUTPUT)
  • analogWrite(3, 127) – funkcja sterująca wypełnieniem (w tym przykładzie 50%, gdyż podałem 127, a maksymalna wartość to 255)

Przykłady -> 01 Basics -> Fade

Omówienie przykładu, zbudowanie układu i uruchomienie.

Pamiętaj:

wybierając płytkę Arduino dla siebie pamiętaj o jej parametrach: z dzisiejszych zajęć już wiesz, że musisz zwracać uwagę na liczbę pinów cyfrowych, analogowych oraz pinów cyfrowych z możliwością PWM. Zajrzyj na specyfikację Arduino Mega i porównaj z UNO – zobaczysz różnicę. A kolejne istotne parametry poznasz na dalszych zajęciach.

Praca domowa

Proszę zaprogramować w wirtualnym Arduino (przypominam: 123d.circuits.io) układ potencometr 10k + LED sterowany PWM tak, by LED rozjaśniał się wskutek nastwień potencjometu. W tym celu trzeba 1) czytać potencjometr analogowo, 2) sterować jasnością LEDa przez PWM (podobnie do omawianego przykładu Fade).

Podpowiedź: analogRead() zwróci nam wartości od 0..1023, a PWM potrzebuje wartości od 0..255. Jak widać te wartości do siebie nie pasują… rozwiązanie najprostrze to podzielenie przez 4 wczytanej wartości i ustawienie właśnie na tyle PWMa. Lepsze rozwiązania tego „problemu” poznamy na kolejnych zajęciach.

 

Zajęcia nr 6 – fotorezystor, dzielnik napięć, map(), serwo silnik i znowu map()

Dzielnik napięć
dzielnik_napiec

Bardzo podstawowa wiedza, ale niezbędna podczas zabawy z Arduino i podobnymi. Dlatego zajęcia rozpoczęliśmy od dwóch rezystorów o tej samej wartości, wówczas ze wzoru na dzielnik Uwy= Uwe*R/(R+R1)= 0.5*Uwe i przy pomocy miernika uniwersalnego mierzyliśmy napięcie Uwy. Jako źródło mieliśmy do dyspozycji baterie AAA (różnie – jedni 2 sztuki, inni 4) o różnych napięciu. Dlatego aby prawidłowo wykonać to ćwiczenie trzeba było najpierw zmierzyć napięcie źródła. Dzielnik napięć zbudowaliśmy na płytce stykowej, o tak:

dzielnik1

Gdy już prawidłowo zbudowaliśmy dzielnik napięć i rozumieliśmy co się dzieje z mierzonym napięciem, zastąpiliśmy fotorezystorem.

Fotorezystor

fotorezystor

Oświetlenie fotorezystora powoduje zmniejszenie jego rezystancji (a tym samym zwiększenie płynącego przez niego prądu, jeśli mamy stałe napięcie zasilania). Oświetlenie zmienialiśmy albo zasłaniając ręką fotorezystor, albo oświetlając go latarką z telefonu komórkowego. Dalej zamieniliśmy jeden z rezystorów z naszego dzielnika napięć na fotoopornik i przeprowadziliśmy pomiary napięcia. Układy doświadczalne prezentowały się w ten oto sposób:

dzielnik3 dzielnik2

Warto podkreślić, że istotne jest który rezystor zastępujemy fotoopornikiem. Rysunki poniżej przedstawiają dwa podobne układy dzielnika napięć – zwróć uwagę na wskazania napięcia przy zmianie oświetlenia:

dzielnik52 dzielnik51  Czyli w jednej konfiguracji napięcie rosło oświetlając dzielnik, w drugiej – napięcie malało. Było to przyczyną kilku wątpliwości na naszych zajęciach… Proponuję pobawić się tym w domu (na wirtualnym Arduino, jeśli nie posiadamy płytki).

Inteligentne oświetlenie

Do układu podłączyliśmy LEDa, którego jasnością sterowaliśmy poprzez Arduino z pinem PWM (poprzednie zajęcia + poprzednia praca domowa). Zabawa miała polegać na oprogramowaniu układu tak, aby LED gasł gdy jest dużo światła zastanego (mierzonego przez fotorezystor i wejście analogowe Arduino), oraz aby LED świecił mocniej i mocniej gdy światła zastanego braknie. Takie proste, ale inteligentne oświetlenie 😉

Tutaj poznaliśmy nową funkcję z biblioteki Arduino: map(). Funkcja ta przeskalowywała (liniowo) podaną wartość z pewnego zakresu (dziedziny, poniżej oznaczonej jako wartości od min_x do max_x), na inna wartość z innego zakresu (przeciwdziedzina, od min_y do max_y). Formalnie wygląda to następująco:

map(war,  min_x, max_x,  min_y, max_y)

co oznacza, że chcemy przeskalować wartość war z zakresu min_x do max_x, na wartość z przedziału min_y do max_y. W naszym przykładzie chodziło o przeskalowanie wartości odczytywanych przez analogRead (czyli wartości od 0 do 1023) do wartości podawanych do sterowania jasnością LEDa (przez PWM, czyli z zakresu 0..255). Dlatego skalowaliśmy

war2= map(war, 0, 1023, 0, 255);

To liniowe skalowanie przez funkcję map() nie ma „magii” w sobie, to proste wykorzystanie funkcji liniowej y=ax+b, znanej Wam z lekcji matematyki plus umiejętność rozwiązania układu równań. Dopowiadając: w liniowym skalowaniu mamy 2 nieznane parametry – współczynniki a i b prostej. Musimy więc podać dwa równania aby je wyznaczyć (chyba każdy pamięta, że do narysowania prostej potrzebne są tylko dwa punkty? więc stąd dwa równania…). Posługuję się wartościami krańcowymi, oczywistymi przy naszym zagadnieniu: chcę bowiem, by do PWMa trafiło 255 gdy na wejściu z analogRead-a było 1023 (pierwsze równanie: y=255 gdy x=1023), oraz chcę, by mieć wartość y=0 gdy podaję x=0 (drugie równanie). Oba punkty podstawiam do niewiadomego y=a*x+b i otrzymuję układ równań. Funkcja map() znajduje a i b za nas i wyznacza każdą inną wartość leżącą na tej prostej.

UWAGA: map() działa tylko na liczbach całkowitych!

Przyjrzyjcie się ponownie mojemu rysunkowi – to prosta matematyka w zastosowaniu 😉

map1

Okazało się, że  w wyniku różnego łączenia fotoopornika w układ niektórzy uczniowie musieli stosować:

war2 = map(war, 0, 1023, 255, 0);

co oznaczało taką sytuację:

map2

Serwo silnik (a właściwie mikro-serwo)

serwo1Czyli silnik, który obraca się od 0 do 180 stopni (ma blokadę na inne wychylenia). Potem utrzymuje swoją pozycję. Służy do tworzenia obrotowych ramion itd…

Trzy przewody – zasilanie (czerowny +5V, czarny/brązowy GND) oraz jeden sterujący – musi być PWM. Za dużo nie wnikałem o co chodzi w sterowaniu tym silnikiem, tylko wspomniałem o potencjometrze wewnątrz i o wypełnieniu sygnału sterującego… więcej może później? Zobaczymy.

 

Do sterowania tym silnikiem użyliśmy 2 nowych funkcji z nowej biblioteki:

  • #include <Servo.h> – na początku programu informujemy, że chcemy funkcje z tej nowej biblioteki
  • Servo silniczek; tworzymy zmienną typu silnik-serwo, czyli właśnie o to nam chodzi!
  • silniczek.attach(3); powoduje przekazanie informacji do Arduino, że sterujemy silnikiem przez pin numer 3 (przypominam: musi być to pin PWM, czyli jak nie 3, to 5,9…)
  • silniczek.write(133); ustawia nasz silnik w pozycji 133 stopni. Albo na dowolny inny z zakresu 0..180 stopni. Dziecinie proste 😉

Serwo sterowane potencjometrem

W tym przykładzie wróciliśmy do poprzednich zajęć i ponownie wykorzystaliśmy potencjometr – tym razem czytywaliśmy wartości napięcia na potencjometrze przez Arduino (i wejście analogowe, np. A0) a następnie ustawialiśmy serwo na konkretną wartość kąta. Ponownie użyliśmy funkcję map() w taki oto sposób:

kąt = map( potencjometr, 0, 1023, 0, 180);

gdzie kąt to właśnie wartość kąta, na jaką ma się ustawić serwo (z zakresu 0..180 stopni), a potencjometr to wartość napięcia na wyjściu z potencjometra (z zakresu 0..1023). Upewnij się, że rozumiesz kolejność przekazywania parametrów do funkcji map().

Program działał na prostej zasadzie:

  • potencjometr = analogRead(A0);
  • kąt = map( potencjometr, 0, 1023, 0, 180);
  • silniczek.write(kąt);

i ponownie od początku. Taki program miał jednak pewien problem, gdyż gdy nic nie zmienialiśmy na potencjometrze, to nasz program ciągle przeliczał wartość napięcia na kąt i ciągle ustawiał serwo w pozycji, w której już był! To głupie, prawda? Jak na pierwsze rozwiązanie OK, ale po dłuższym przyjrzeniu się widać, że nie jest dobrze. Dlatego zaproponowałem ulepszyć program tak, by serwo nie było ustawiane gdy pozycja potencjometru się nie zmieniła. W tym celu należało pamiętać poprzednie ustawienia potencjometru (lub poprzedni kąt) i porównywać tą wartość z nowymi ustawieniami. Zaproponowałem taki oto kod:

#include <Servo.h>
#define potencjometr A0

Servo silniczek;  
void setup() {
  Serial.begin(9600);
  silniczek.attach(3);
  pinMode(potencometr, INPUT);
}

int pot, kat, ost_kat;
void loop() {
  pot = analogRead(potencjometr);
  kat = map(pot, 0, 1023, 0, 180);
  if (kat != ost_kat){
    Serial.print(pot);
    Serial.print(" czyli  ");
    Serial.println(kat);
    silniczek.write(kat);           
    ost_kat=kat;
  }
  delay(100);                 
}

Gratuluję tym, którym udało się samodzielnie na to wpaść! Przy okazji: w powyższym programie jest o jedną zmienną za dużo… nie ma bowiem potrzeby tworzyć zmiennej pot. Można się jej pozbyć i zastąpić dwie linijki tak:

int kat, ost_kat;
void loop() {
  kat = analogRead(potencjometr);
  kat = map(kat, 0, 1023, 0, 180);
...
}

Kluczowa tutaj linijka to  kat = map(kat, 0, 1023, 0, 180); którą należy rozumieć tak, że nowa wartość zmiennej kat zostaje ustawioina na postawie funkcji map() ze starej wartości zmiennej kat. Symbol = („równa się”) jest tak zwanym w informatyce operatorem left-assign operator (czyli operator przypisania lewostronnego, tj. najpierw obliczamy wszystko z lewej strony, a dopiero potem obliczona wartość przekazana jest to prawej strony – zmiennej). Z matematycznego punktu widzenia jest to skomplikowana funkcja rekurencyjna…. ale tutaj symbol = trzeba rozumieć w sposób informatyczny.

Prąd „zjadany” przez serwo – mierzymy!

W skrajnych ustawieniach serwa (tj. w okolicy 0 stopni, oraz w okolicach 180 stopni) słyszymy buczenie/piszczenie serwo-silnika. Coś się dzieje. Amperomierz w garść i mierzymy prąd.

serwo1

Przyjrzyj się uważnie obrazkowi i zwróć uwagę, jak podłączony jest amperomierz.

Oczywiście w wirtualnym Arduino silniczek serwo jest idealny i nie widzimy tego, co było u nas na zajęciach….

Dodatkowo: w przypadku mierników uniwersalnych ustaw największą wartość prądu, jaką się spodziewasz dostać – nie odwrotnie! W przeciwnym przypadku zwiększając zakres przepalisz bezpiecznik w multimetrze…

Gratulacje

Wypada pogratulować jednemu z uczestników BTXXIw, który w międzyczasie zbudował układ sterujący serwem za pomocą… fotorezystora! To świetny przykład na to, że nie ma co się nudzić na moich zajęciach – jeśli wyprzedzasz grupę, wykombinuj coś samemu! A ten projekt nie jest bynajmniej głupi – może to być sterowanie jakimś silnikiem w kierunku światła… Gratuluję pomysłowości Mariuszowi Karpowiczowi z II LO. Kto zabłyśnie następnym razem? Nagrody czekają …

Praca domowa

Zbudować układ w wirtualnym Arduino z serwem i zrobić tak, aby czytać z klawiatury kąt, na jaki należy ustawić silniczek. Zajrzyj do poprzednich notatek z naszych spotkań aby przypomnieć sobie o prawidłowym czytaniu liczb z portu szeregowego Arduino.

Zajęcia nr 5 – sygnały cyfrowe, analogowe…

Sygnały cyfrowe – czujka ruchu PIR

Prosta w obsłudze czujka ruchu. Trzy piny  – zasilanie, masa oraz wyjście. W przypadku wykrycia ruchu wyjście jest w stanie wysokim (5V) przez chwilę (można sterować tym czasem),  a gdy ruchu brak – wyjście przechodzi w stan niski (0V). Aby to odczytać przy pomocy Arduino poznaliśmy dwie nowe rzeczy:

  • pinMode(7, INPUT) – czyli właśnie informujemy Arduino, że będziemy odczytywać wartość napięcia na konkretnym pinie (w tym przypadku: pinie numer 7),
  • digitalRead(7) – funkcja odczytująca napięcie i zwracająca wartość 1 (jedynka) gdy napięcie jest wysokie (2.4 – 5V) oraz 0 (zero) gdy napięcie jest niskie (0 – 0.8V).

Warto pamiętać, że Arduino UNO ma 14 pinów cyfrowych, a inne modele – patrz specyfikacja (kupując płytkę dla siebie weź to pod uwagę).

Sygnały analogowe

Czyli czytanie napięcia z przedziału od zera do 5V z rozdzielczością 10 bitów – a więc 1024 poziomów napięć (bo 2 do potęgi 10-tej to właśnie 1024). Nasze płytki są już lekko zdezelowane i wcale na wyjściu pinu 5V z Arduino nie mamy 5V a np. 4.57V, dlatego warto to mieć na uwadze czytając konkretną wartość napięcia. Poznaliśmy:

  • wejścia analogowe: A0, A1, A2, …, A5 (6 sztuk w UNO)
  • analogRead(A0 – funkcja odczytująca napięcie i zwracająca wartości od 0..1023 w zależności od napięcia na wejściu A0.
  • wejście AREF na płytce Arduino do podawania napięcia referencyjnego z przedziału 0..5V oraz funckję analogReference(EXTERNAL) uruchamiającą tę funkcję. Dzięki temu można wykorzystać całą rozdzielczość 10-ciu bitów na czytanie napięcia w przedziale 0..AREF V – ale tego nie testowaliśmy w praktyce.
  • inne możliwości ustawienia punktu odniesienia w funkcji analogReference, np. INTERNAL1v1 w zależności od płytki Arduino daje to pożliwość wykorzystania całej rozdzielczości 10-ciu bitów w zakresie 0..1V.

Potencjometr liniowy – dzielnik napięć

Bardzo przydatny element elektroniczny – będziemy go wielokrotnie wykorzystywać. Dlatego przypominam, że działanie oparte jest na dzielniku napięć, i należy pamiętać roli 3 nóżkek tego elementu:

  • pierwsza – zasilanie (np. 5V)
  • druga – napięcie na wyjściu, czyli zmodyfikowane konkretnym ustawieniem gałki potencjometru (np. 1.2V)
  • trzecia – masa (0V)

Oczywiście nóżki piersza i trzecia mogą być zamienione rolami. Przy pomocy miernika uniwersalnego sprawdziliśmy w praktyce działanie tego potencjometru, a już po chwili wczytywaliśmy do Arduino to napięcie i pisaliśmy na ekranie – dzięki funkcji  Serial.println().

Ważne: zauważyliśmy, że ustawiony potencjometr w jednej pozycji i nie poruszany daje lekko różne wartości, np. 600, 600, 601, 600, 600, 600, 598, 600, 600, 601, 600… Niby widać, że odczytana wartość wynosi 600, a te drobne odstępstwa nazywamy fluktuacjami. Dzieje się tak gdyż przetwornik DAC w Arduino nie jest doskonały (a niby co jest?!), wrażliwy na szumy i inne rzeczy. Warto o tym pamiętać.

Sygnały PWM – sygnały cyfrowe

Czyli Pulse Width Modulation – sygnały szybkozmienne w czasie (Arduino UNO – 2ms, czyli 500Hz), o dwóch wartościach – 0V i 5V. Mamy możliwość wybrania wypełnienia sygnału 5V – czyli jak długo wartość 5V utrzymuje się w okresie 2ms (a następnie wszystko się powtarza). Mamy 8 bitów do sterowania wypełnieniem – a więc wartości z przedziału 0..255.

Ku pamięci:

  • PWM jest dla sygnałów cyfrowych, oznaczonych tyldą w Arduino (a więc ~3, ~5, ~6, ~9, ~10 – aż 6 takich wyjść!)
  • pinMode(7, OUTPUT)
  • analogWrite(3, 127) – funkcja sterująca wypełnieniem (w tym przykładzie 50%, gdyż podałem 127, a maksymalna wartość to 255)

Przykłady -> 01 Basics -> Fade

Omówienie przykładu, zbudowanie układu i uruchomienie.

Pamiętaj:

wybierając płytkę Arduino dla siebie pamiętaj o jej parametrach: z dzisiejszych zajęć już wiesz, że musisz zwracać uwagę na liczbę pinów cyfrowych, analogowych oraz pinów cyfrowych z możliwością PWM. Zajrzyj na specyfikację Arduino Mega i porównaj z UNO – zobaczysz różnicę. A kolejne istotne parametry poznasz na dalszych zajęciach.

Praca domowa

Proszę zaprogramować w wirtualnym Arduino (przypominam: 123d.circuits.io) układ potencometr 10k + LED sterowany PWM tak, by LED rozjaśniał się wskutek nastwień potencjometu. W tym celu trzeba 1) czytać potencjometr analogowo, 2) sterować jasnością LEDa przez PWM (podobnie do omawianego przykładu Fade).

Podpowiedź: analogRead() zwróci nam wartości od 0..1023, a PWM potrzebuje wartości od 0..255. Jak widać te wartości do siebie nie pasują… rozwiązanie najprostrze to podzielenie przez 4 wczytanej wartości i ustawienie właśnie na tyle PWMa. Lepsze rozwiązania tego „problemu” poznamy na kolejnych zajęciach.