Światłolub: OCZY – dobór rezystora do dzielnika napięcia – OCZYWISTY?

Światłolub ma oczy… fotorezystory! Ale Arduino nie odczytuje rezystancji, tylko napięcia. Dlatego wykorzystujemy dzielnik napięcia według schematu:

Analizujemy prosty dzielnik napięcia, wykorzystywany do odczytu wartości z czujnika za pomocą Arduino. Napięcie odczytywane na pinie A0 będzie dane wzorkiem V_A0 (kolor czarny na obrazku powyżej). Celem jest znalezienie takiej wartości rezystora stałego (R_fixed), która zapewni najlepsze rozróżnienie między warunkami jasnymi a ciemnymi, tak aby Arduino mogło najlepiej wykrywać różnicę. Chcemy:

  • Zmierzyć opory R_foto w warunkach jasnych i ciemnych – miernik (multimetr)
  • Obliczyć V_A0 dla różnych wartości R_fixed w dwóch przypadkach: jasno i ciemno
  • Określić różnicę napięć (ΔV)
  • Przeskalować tę różnicę do zakresu analogRead() Arduino (0–1023)
  • Narysować odpowiednie wykresy, aby określić, która wartość rezystora działa najlepiej

Mierzymy opór czujnika w warunkach jasnych i ciemnych – następnie definiujemy stałe: napięcie wejściowe (5V) oraz opór czujnika w warunkach jasnych i ciemnych warunkach.

import matplotlib.pyplot as plt
# Stałe
V_in = 5 # Napięcie wejściowe w woltach
R_sensor_light = 60 # Opór czujnika w jasności (kiloomy)
R_sensor_dark = 180 # Opór czujnika w ciemności (kiloomy)

Przygotowujemy tablice (czyli listy w języku Python) do przechowywania wyników dla każdej testowanej wartości rezystora.

# Listy do przechowywania wyników
R_fixed_values = list(range(1, 1000))
V_out_light_values = []
V_out_dark_values = []
delta_V_values = []

Przechodzimy pętlą przez wartości R_fixed (od 1 do 999 kiloomy) i obliczamy:

  • Napięcie wyjściowe, gdy czujnik jest w jasności (V_out_light)
  • Napięcie wyjściowe, gdy czujnik jest w ciemności (V_out_dark)
  • Różnicę napięć między tymi dwoma wartościami (delta_V)

for R_fixed in R_fixed_values:
V_out_light = V_in * (R_sensor_light / (R_sensor_light + R_fixed))
V_out_dark = V_in * (R_sensor_dark / (R_sensor_dark + R_fixed))
delta_V = V_out_dark - V_out_light
V_out_light_values.append(V_out_light)
V_out_dark_values.append(V_out_dark)
delta_V_values.append(delta_V)

Znajdujemy taką wartość R_fixed, która daje największą różnicę napięć (ΔV). Oznacza to, że czujnik będzie w tym punkcie najbardziej „czuły”.

# Szukanie optymalej wartości R_fixed (maksymalna delta_V)
max_delta = max(delta_V_values)
max_delta_index = delta_V_values.index(max_delta)
optimal_R = R_fixed_values[max_delta_index]

Rysujemy napięcie wyjściowe dla warunków jasnych i ciemnych oraz „cieniujemy” obszar między dwiema funkcjami napięcia. To pokazuje w którym miejscu i jak bardzo zmienia się sygnał z czujnika.

# Wykres 1: V_out dla jasności i ciemności
plt.figure(figsize=(10, 5))
plt.plot(R_fixed_values, V_out_light_values, '--b', label='V_out (Jasność)')
plt.plot(R_fixed_values, V_out_dark_values, '--r', label='V_out (Ciemność)')


# Wypełnienie obszaru między dwiema krzywymi
where_condition = [dark > light for dark, light in zip(V_out_dark_values, _out_light_values)]
plt.fill_between(R_fixed_values, V_out_light_values, V_out_dark_values,where=where_condition,color='gray', alpha=0.3, label='Obszar ΔV')
plt.title('Napięcie wyjściowe dzielnika w warunkach jasnych i ciemnych')
plt.xlabel('R_fixed (kiloomy)')
plt.ylabel('V_out (V)')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()

Rysujemy różnicę napięć (ΔV) i zaznaczamy rezystor, który daje największe rozdzielenie sygnału

# Wykres 2: ΔV z zaznaczoną optymalną wartością R_fixed
plt.figure(figsize=(10, 5))
plt.plot(R_fixed_values, delta_V_values, color='red', label='ΔV (Jasność - Ciemność)')
plt.axvline(optimal_R, color='gray', linestyle=':', label=f'Optymalne R_fixed = {optimal_R} kΩ')
plt.scatter([optimal_R], [max_delta], color='black', zorder=5)plt.text(optimal_R + 10, max_delta, f'Max ΔV ≈ {max_delta:.2f} V\nprzy R = {optimal_R} kΩ', fontsize=9)
plt.title('Różnica napięć (ΔV) w zależności od R_fixed')
plt.xlabel('R_fixed (kiloomy)')
plt.ylabel('ΔV (V)')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()

Mapujemy różnicę napięć ΔV do skali analogRead() Arduino (0–1023)

# Szukanie najlepszej wartości R_fixed pod względem różnicy odczytu ADC
delta_ADC_values = [(v / V_in) * 1023 for v in delta_V_values]
max_delta_ADC = max(delta_ADC_values)
max_adc_index = delta_ADC_values.index(max_delta_ADC)
optimal_R_adc = R_fixed_values[max_adc_index]

Rysujemy ΔV przeskalowaną do zakresu analogRead oraz zaznaczamy rezystor dający największą różnicę odczytu cyfrowego

# Wykres ΔADC
plt.figure(figsize=(10, 5))
plt.plot(R_fixed_values, delta_ADC_values, color='purple', label='ΔADC (przeskalowane z ΔV)')
plt.axvline(optimal_R_adc, color='gray', linestyle=':', label=f'Optymalne R_fixed = {optimal_R_adc} kΩ')
plt.scatter([optimal_R_adc], [max_delta_ADC], color='black', zorder=5)
plt.text(optimal_R_adc + 10, max_delta_ADC, f'Maks. ΔADC ≈ {max_delta_ADC:.0f}\nprzy R = {optimal_R_adc} kΩ', fontsize=9)
plt.title('Różnica odczytu analogRead (ΔADC) w zależności od R_fixed')
plt.xlabel('R_fixed (kiloomy)')
plt.ylabel('ΔADC (0–1023)')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()

Wyświetlamy optymalną wartość rezystora dla skali Arduino, gdzie różnica odczytów analogRead() jest największa.

print(f"Optymalne R_fixed = {optimal_R_adc} kΩ, przy którym maks. ΔADC ≈ {max_delta_ADC:.0f}")
Optymalne R_fixed = 104 kΩ, przy którym maks. ΔADC ≈ 274

(c) Przemysław Baj, K.G. 2025

Światłolub – (znowu) programowanie

Trwa szlifowanie kodu… a raczej tworzenie go tak, by działał poprawnie 😉 Okazuje się bowiem, że w różnych miejscach testowanej podłogi jest różne oświetlenie (zastane) – o dziwo! 😀 Wbite na sztywno wartości odczytane z czujników nie pracują 😉 Na tym właśnie polega ta zabawa! Rozwiązanie? aktualizować światło zastane co 1s? Tak działa jeden zespół. Drugi kombinuje z hardware – wprowadza jakąś przeszkodę blokującą światło zastane – ciekawe, co z tego wyjdzie.

Zawody

Jeśli sterowanie będzie działać, to sprawdzimy to w ekstremalnej sytuacji- postawimy dwie przeszkody, będzie trzeba je objeżdźać – kręcąc tzw. ósemki. Zmierzymy czas trzech okążeń i się okaże, który zespół jest lepszy 😉

(c) K.G. 2025

Światłolub – programowanie

komunikacja I2C i hd44780

Aby widzieć odczyty z fotorezystorów wykorzystujemy ekran LCD16x2 — nie chcemy jednak tracić cennych portów cyfrowych (chyba trzeba użyć 7-miu do obsługi), więc stosujemy moduł I2C hd44780.

testy, testy, testy…

Czyli najprzyjemniejsza część zabawy – prototyp już mamy, teraz trzeba go mądrze oprogramować. Zaczynamy!

Trzeba odczytać wskazania obu fotorezystorów gdy światło jest przed pojazdem, z jednego boku, z drugiego… Zapisać do notepada.exe by następnie wpisać w ino 🙂

Kiszka. Stoi. Albo kręci się nie w tą stronę, co trzeba. A niby miało działać 😛 więc znowu do kompa i wprowadzamy zmiany w kodzie /w parametrach.

Nawet kłótni nie było 🙂 nikt nie zrzucał winy na drugiego – nie działa dobrze, więc trzeba poprawić! Świetny team-work !

Zawody

Za tydzień kody będą już dopieszczone, więc będzie można przystąpić do sprawdzenia algorytmów w akcji – postawimy dwie przeszkody, będzie trzeba je objeżdźać – kręcąc tzw. ósemki. Zmierzymy czas trzech okążeń i się okaże, który zespół jest lepszy 😉

(c) K.G. 2025

Oczy światłoluba

Ciągle budujemy pojazd sterowany latarką – wykorzystamy fotorezystory jako „oczy”. Dziś wracamy do konstrukcji pojazdu i będą pierwsze próby. Ale nie chcemy mieć płytki prototypowej na pokładzie więc… poznajemy lutownicę 😉

Przygotowujemy sobie wielokrotne wyjścia GND i 5V, a także lutujemy rezystor z fotorezystorem w szereg – z dodatkowym przewodem do pinu analogowego Arduino UNO. Wszystko proste, tylko trzeba trochę się pobawić… na tym to chyba polega, prawda? 😉

No i dobrze, że się ucząc się także się bawimy!

Udało się! Oczy skierowane są na podłogę (trochę za bardzo) więc rozpoczyna się faza testowa oprogramowania – część „hardwareowa” chwilowo zakończona.

(c) K.G. 2025

fotorezystor – oczy światłoluba

Czyli budujemy pojazd sterowany latarką – wykorzystamy fotorezystor jako „oczy”. Czyli muszą być dwa, aby porównywać ilość światła z jednego czujnika i drugiego i na tej podstawie jechać do przodu, zatrzymywać się czy skręcać. Niby proste – choć cała magia tkwi w odpowiednim doborze parametrów decyzyjnych.

analogRead()

Studenci jeszcze nie mieli omawianej tej funkcji na zajęciach z Arduino – więc zaczynamy od podstaw. Uczymy się więc co mierzymy, jakie są ograniczenia Arduino UNO, sprawdzamy odczyty napięcia 3.3V oraz 5V z płytki Arduino, a także baterie AAA.

Przechodzimy dalej do fotorezytora i uczymy się go najlepiej wykorzystać. Skoro Aruino idczytuje napięcie a nie opór – to wykorzystujemy dzielnik napięcia i odczytujemy napięcie. Ale jaka wartość stałego rezystora w dzielniku dla naszego fotorezystora? Testujemy z wykorzystaniem thinkercada!

Dziś bez podłączenia jeżdzącej platformy – to będzie za tydzień!

(c) K.G. 2025

Pierwsze spotkanie w nowym semestrze 2019/2020!

Nowy semestr w roku akademickim 2019/2020

Zapraszam zainteresowanych na zajęcia koła Fi-BOT w poniedziałki, o godz. 14:15 do sali 1064 (Wydział Fizyki, Kampus). Będziemy pracować nad rozpoczętymi projektami. Mile widziani studenci ze znajomością platformy Arduino, ale i tak liczy się zaangażowanie. Zapraszam!

Radiówka i „grzebek”

Pan Przemek zrealizował proste sterowanie „grzybkiem” (zamiast przycisków). Proste, bo wychylenie gałki (odczyt z analogRead() w zakresie 0..1023) powyżej ustalonej wartości (>700) oznacza ruch w jedną stronę z pełną prędkością, natomiast gałka w drugą stronę (<300) w przeciwną. Tak samo z drugą osią. W kolejnym podejściu prace nad uzależnieniem prędkości od wartości wychylenia (PWM silników skalowane analogReadem grzybka).

Jak zawsze coś się musiało rozlutować…

PM2D3D Nowe wózki i akceleracja

Pan Bartek udoskonalił nowe wózki i ciągle pracuje nad akceleracją… Więcej o projekcie PM2D3D na dedykowanej stronie.

(c) K.G.

Precyzyjna Maszyna oraz RPM

Obroty na minutę: RPM (czujka pola magnetycznego SS49E)

Pan Przemek ukończył kod, który zlicza obroty wirującego silniczka – brawo! Należy się pochwała, bo to jego pierwsze zmagania z Arduino.

Układ doświadczalny:

W układzie celowo zamontowano magnesy tak, by czujka SS49E „widziała” raz biegun północny (N) magnesu, a za drugim razem (gdy silniczek obróci sie o 180 stopni) biegun południowy (S). Ustawienie magnesów na końcach patyka nie jest więc przypadkowe 😉 Czujnik SS49E odczytuje zarówno biegun S jak i N (uwaga: nie wszystkie czujki pola magnetycznego, bazującego na efekcie Halla, tak mają – warto to sprawdzić przed zakupejm), dlatego widzimy dwa „piki” podczas obracania silniczka – jeden „do góry” (większe napięcie) oraz „do dołu” (napięcie mniejsze). Z dala od magnesów czujka zwraca napięcie ~2.5V informując, że wartość pola magnetycznego jest (około) zera. Poniżej wykres z Kreślarki

Program zliczający liczbę obrotów na sekundę (zmienna czas – aktualnie 1000ms, ale można zmienić, także przez krotność – zmienna krok). Algorytm polega na znajdowaniu maksimum i minimum napięcia – a zapisywane jest moment ich wystąpienia (do zmiennych t_1oraz t_2, odpowiednio). Różnica tych czasów do pół obrotu.

void setup() {
Serial.begin(9600);
}

int i,max_=518,min_=518,a=0,b=0,czas,n=0,krok=1;
float v;
long int t_1=0,t_2=0,t_k=0,t_3=0,t_p;

void loop(){
 t_p=millis();
 czas=1000;
 i=analogRead(A0);
 if(i>540){
   if(i>max_){
    max_=i;
    t_1=millis();
   }
   else if(i<max_){
    a=1;
   }
 }
 if(i<490){
   if(i<min_){
    min_=i;
    t_2=millis();  
    }
   else if(i>min_){ 
    b=1; 
  }
 }

 if(t_1>t_2){
  t_3=t_2;
 }
 else{
  t_3=t_1;
 }
 
 if(a==1&&b==1){
    t_k=abs(t_2-t_1);
    a=0;
    b=0;
    max_ = 518;
    min_ = 518;
    if(czas>t_3){
      n++;
    }
    else if(czas<t_3){
    Serial.print("Liczba pol-obrotow: ");
    Serial.println(n);
    n=0;
    krok++;
    czas=czas*krok;
    }
 }
 }

Zmienna n (małe n) zlicza wystąpienia „półobrotów”, a co ustalony czas wypisywany jest komunikat z tą liczbą. W ten sposób mamy właśnie pół-RPS (revolutions per second), z którego łatwo można już otrzymać RPM (revolutions per minute).

Precyzyjna Maszyna

Sprężynka nie wytrzymała – chyba była zbyt twarda 🙁

Nowy model powinien być lepszy – bo wydrukowany z Z-Ultratu:

No i mamy coraz lepszą pracę Maszyny:

Więcej o projekcie Maszyny na stronie projektu.

(c) K.G.

Precyzyjna Maszyna (chwytak!) oraz Wiatromierz

Chwytak do ołówka/mazaka zaprojektowany, wydrukowany w 3D i zamontowany! Działa z serwomechanizmem a kod BBcode obsługuje nowe instrukcje (up, down).

No i kolejny (próbny) precyzyjny rysunek:

Opis całego proejktu Maszyny pod tym adresem

Obroty na minutę (rpm) i czujka pola magnetycznego SS49E

Wakacje w trakcie, ale nie tylko Pan Bartek spędza wolny czas z pracą nad projektem. Pan Przemek, który nie miał czasu na zajęcia Fi-BOTa w roku akademickim, poznaje Arduino i „atakuje” temat liczenia obrotów silniczka, wykorzystując czujkę pola magnetycznego SS49E


A po co nam zliczanie tych obrotów? Zastosowań jest wiele, jednym z nich jest mierzenie prędkości wiatru takim prostym urządzeniem:

w środku którego znajdują się: czujka SS49E, dwa magnesy oraz łożysko kulkowe (typ 682ZZ).

O dalszych losach tego projektu niebawem…

(c) K.G.

Sterowanie serwem za pomocą joysticka

Na zajęciach sterowaliśmy serwomechanizmami za pomocą joysticka. Na początku używaliśmy niewielkiego modułu z joystickiem podłączanego bezpośrednio do Arduino. W drugiej części użyliśmy nakładki (shield) na Arduino z joystickiem oraz czterema przyciskami (podobnie jak na gamepadach).

Moduł z joystickiem

Moduł posiada pięć pinów. GND oraz 5V podłączamy do Arduino. VRx i VRy to piny sterujące odpowiednio osią OX (czyli lewo-prawo) oraz OY (czyli góra-dół). Podłączamy je do pinów analogowych. Ostatni pin odpowiada za przycisk, jednak nie używaliśmy go w tym zadaniu.

Prosty program wyświetlający położenie joystika

void setup(){
  Serial.begin(9600);
}
int x,y;
void loop(){
   x = analogRead(A0);
   y = analogRead(A1);
   Serial.print("x=");
   Serial.print(x);
   Serial.print(", y=");
   Serial.println(y);  
  }

Położenie na osi OX (także OY) to liczby z zakresu 0..1023. Położenie spoczynkowe powinno odpowiadać wartości 511 (liczby 0..510 to wychylenie w lewo, liczby 512..1023 to wychylenie w prawo). Użycie tego programu pozwoliło nam sprawdzić, że tak jednak nie jest – u nas joy w położeniu spoczynkowym miał wartości 514, 517 (oś OX i OY).

Podczas działania tego programu możemy postawić sobie następujące zadanie: ustawić pozycję joy-a w takim położeniu, aby odczyty były x=800 y=200. Okazuje się to jednak bardzo trudne! Widzimy więc, że sterowanie joy-em nie jest łatwe i wymaga sporo wprawy.

Wieżyczka

Używana przez nas wieżyczka (do której można przyczepić, np. kamerkę internetową by nią sterować) składała się z dwóch serw na podstawce. Umożliwia to poruszanie się mechanizmu na boki oraz w górę i dół.

Wszystkie potrzebne elementy połączyliśmy za pomocą płytki prototypowej. Zamiast zasilania z Arduino użyliśmy zewnętrznego koszyka na 4 baterie o łącznym napięciu 5V. Dlaczego tak zrobiliśmy? Chodziło nam o oddzielne zasilanie silników aby nie przeciążyć płytki Arduino – pojedyncze serwo może pobrać nawet 200mA prądu (co sprawdzaliśmy na poprzednich zajęciach), a wydajność prądowa Arduino UNO to około 200-400 mA. Dlatego dwa takie serwa mogą uszkodzić naszą płytkę. My użyliśmy oddzielnego zasilania silników aby temu zapobiec. WAŻNE: w przypadku używania kilku źródeł zasilania (u nas Arduino 5V i 4x baterie AAA) musimy uwspólnilić masy (GDN z Arduino, „minus” z bateryjki).

Użyliśmy płytki prototypowej, gdzie na jednej szynie (koloru niebieskiego) wetknęliśmy „-” z koszyka baterii oraz GND Arduino. Do tej „szyny” podłączone były masy serw (przewody koloru brązowego). Druga szyna (czerwona – z drugiej strony płytki, dla naszej wygody) doprowadzone miała przewód „+” z koszyka baterii i tam podłączone były zasilania silników serw (czerwone przewody serw). UWAGA: nie można łączyć pinu 5V Arduino z zewnętrznym zasilaniem baterii – może to spowodować uszkodzenie płytki! Łączymy (=uwspólniamy) jedynie masy. Przewody sterujące serwami (koloru żółtego) podłączyliśmy do pinów PWM Arduino UNO – u nas #3 i #5.

Program sterujący

Na początku standardowo dołączamy do programu bibliotekę umożliwiającą sterowanie serwami. Następnie dodajemy zmienne do obu serw, jedno odpowiedzialne za ruch góra-dół, a drugie lewo-prawo.

#include <Servo.h>
Servo GoraDol;
Servo LewoPrawo;

void setup (){
  Serial.begin(9600);
  GoraDol.attach(3);
  LewoPrawo.attach(5);
}
int x,y;

W kolejnej części deklarujemy zmienne odpowiadające za położenie obu osi joysticka – dokładnie tak, jak w pierwszym programie odczytującym położenia joy-a. Wyświetlamy wartości x oraz y w monitorze szeregowym. Joystick zwykle nie jest idealnie skalibrowany, jednak nie ma to znaczenia przy niewielkiej precyzji.

Funkcja map pozwala na łatwe proporcjonalne przeliczenie wartości. Używamy jej, ponieważ położenia joysticka są z zakresiu 0-1023, natomiast sterowanie serwem chcemy wyrażamy w stopniach 0-180. Wynik przypisujemy od razu do zmiennych x oraz y (używamy tych samych zmiennych, niszcząc ich poprzednie wartości). W argumentach funkcji wpisujemy zmienną do przeliczenia, następnie jej obecny zakres i na końcu zakres po zamianie. Po przeliczeniu wartości możemy je przekazać do serwomechanizmów.

   x=map(x,0,1023,0,180); //funkcja map
   LewoPrawo.write(x);
   y=map(y,0,1023,0,180);
   GoraDol.write(y);
   delay(10);

Nakładka (shield) na Arduino

Fajnym rozwiązaniem jest wykorzystanie specjalnej nakładki (shield), która poszerza możliwości Arduino. Piny obu komponentów pasują do siebie, więc nie da się pomylić przy wpinaniu nakładki. Joystick oraz przyciski są fabrycznie podłączone do pinów, więc nie musimy tego robić w programie. Na nakładce wszystkie piny są podpisane, więc później będziemy tylko operować odpowiednimi oznaczeniami pinów.

Modyfikacja programu – przycisk „zamrażający” położenie wieżyczki

Program sterujący jest bardzo podobny do poprzedniego. Jedyną różnicą jest brak podłączenia pinów cyfrowych. Dodatkową funkcją, którą wprowadzamy do naszego programu, jest zatrzymywanie serwa w ustawionej pozycji. Chcemy aby wciśnięcie wybranego przycisku (np. koloru czerwonego) zablokowywało dalsze sterowanie wieżyczką. Kolejne wciśnięcie tego przycisku powoduje odblokowanie sterowania. W tym celu do kodu wprowadzamy zmienną typu logicznego bool (nazwaną tryb) i ustawiamy ją na wartość true. Następnie wybieramy przycisk (np. ten czerwony), który ma sterować zatrzymaniem serwa i  sprawdzamy, który numer pinu u odpowiada. W naszym programie jest to pin 5.  Tworzymy instrukcję sterującą if, która wykona się po wciśnięciu przycisku. Do wartości zmiennej tryb, przypisujemy jej odwrotność. Czyli jeśli tryb jest true, zostanie zmieniony na false i odwrotnie. Funkcja delay() wprowadzamy aby zarejestrować tylko jedną zmianę ustawienia przycisku (przyciski lubią „drgać” co powoduje nie jeden „klik” a wiele takich „klików” – opóźnienie je zniweluje). Działa to w taki sposób, że jeśli tryb jest wartością false, czyli zostanie raz zmieniony we wcześniejszym if’ie, sterowanie joyem zostaje zablokowane. Natomiast po kolejnym użyciu przycisku, tryb zmieni się z false na true, a sterowanie zostanie odblokowane.

bool tryb=true;
void loop(){
  x=analogRead(A0);
  y=analogRead(A1); 
   if (digitalRead(5)==HIGH){
      tryb=!tryb;
      delay(50);
   }

   if(tryb==true){ 
   x=map(x,0,1023,0,180); //funkcja map
   LewoPrawo.write(x);
   y=map(y,0,1023,0,180);
   GoraDol.write(y);
  }
}

Podsumowanie

Jak można zauważyć programy umożliwiające sterowanie servami nie są skomplikowane ani długie. Można sprytnie wykorzystać komponenty posiadające więcej niż jedną oś ruchu, dzięki czemu nasze możliwości się poszerzają. Jednak precyzyjne sterowanie takimi joystikami nie jest łatwe…

(c) Ewelina, KG 2017

Sterowanie servem za pomocą potencjometru

Na ostatnich zajęciach łączyliśmy wiedzę nabytą na dwóch poprzednich spotkaniach, czyli działanie potencjometru oraz serva. Chcieliśmy wykorzystać możliwość zmiany nastawy potencjometru, w celu sterowania ramionami serwomechanizmu. Podobnie jak na zajęciach przedświątecznych, wykorzystamy do tego komendy biblioteki sterującej servami.

Sterowanie serva

#include <Servo.h>
Servo silnik;

void setup (){
  Serial.begin(9600);
  silnik.attach(3);
}

Servo podłączamy tak samo jak na przedświątecznych zajęciach, jednak w tym przypadku używamy 3 pinu, a sam mechanizm nazywamy silnikiem (linia #2 i #6).

Wykorzystanie potencjometru

int pot;
void loop(){
   pot=analogRead(A0);//odczytujemy liczby z zakresu od 0 do 1023
   pot=pot*180.0/1023;//zamienimy na liczby od 0 do 180
   Serial.print(pot);
   silnik.write(pot);
}

Na początku deklarujemy zmienną zapisującą stan potencjometru. W ciele funkcji void loop() podłączamy potencjometr do portu analogowego A0. Linia #4 przelicza zakres potencjometru. Jak pamiętamy, wynosi on od 0 do 1023, natomiast stopnie wychylenia serva chcemy wyrażać w zakresie 0-180. 180 konwertujemy na zmienną typu float dopisując do niej część dziesiętną w celu uniknięcia dzielenia całkowitego. W innym wypadku otrzymywalibyśmy nieprawdziwe wyniki, ponieważ wynikiem dzielenia 180/1023 zawsze będzie 0, przez co całe działanie również wyniesie 0. Przy zapisie 180.0/1023 mamy do czynienia z dzieleniem rzeczywistym, którego wynikiem będzie liczba rzeczywista. Następnie przy przemnożeniu przez zmienną całkowitą pot dostaniemy również liczbę rzeczywistą. Ostateczny wynik jest rzutowany na liczbę całkowitą w momencie przypisania operatorem równości.

Ulepszenie – dodatkowy if

W celu ulepszenia naszego kodu, chcieliśmy aby położenie ramion serwa było aktualizowane jedynie wtedy, kiedy zmienimy położenie potencjometru. Tym samym chcemy uniknąć sytuacji wydawania polecenia „ustaw serwo na pozycję XX” jeśli właśnie aktualną pozycją jest XX (nie ma to sensu, mimo tego, że to działa – jak w naszym pierwszym, prostym programie). Użyliśmy do tego instrukcji sterującej if. W linii #1 dopisaliśmy kolejną zmienną nazwaną old, która ma za zadanie zapisywać poprzedni stan położenia potencjometru (odczytanego napięcia). W warunkach if-a sprawdzamy, czy zmienna pot (czyli aktualny stan potencjometru), różni się od zmiennej old (czyli jego poprzedni stan).  Jeśli nie, funkcja Serial.print() nic nie wypisze i nie zmieniamy położenia serwa. W przeciwnym przypadku zostanie wypisane nowe napięcie, a ramiona serwa zmienią położenie. Na końcu przypisujemy zmienną old do pot, aby móc dokonać nowego porównania w kolejnej iteracji pętli. 

int pot, old; //old - zmienna zapisujaca poprzedni odczyt

void loop(){
  pot=analogRead(A0);
  pot=pot*180.0/1023.0; 
  if (pot!=old){ //aby zmieniac polozenie tylko wtedy, kiedy sie zmienilo, a nie wyswietlac polozenie ciagle
   Serial.print(pot);
   silnik.write(pot);
   old=pot; 
  }
}

Podsumowanie

Dzięki naszemu programowi możemy sterować ramionami serwa kręcąc potencjometrem. Przeliczenie wartości napięcia na stopnie umożliwia dość precyzyjne ustawienie serva.

2018, Ewelina (c)