Skocz do zawartości

Kurs Arduino - #9 - Czujnik odległości HC-SR04, funkcje


Pomocna odpowiedź

(edytowany)

 

Dnia 2.04.2021 o 07:24, Gieneq napisał:

@Forseti super, fajnie że robisz zadania samemu, ale mam radę 😉 nie używaj zmiennych a,b,c,d,e itp. bo jest to bardzo niejasny zapis. 

Czy o to chodziło? 

#define trigPin 12
#define echoPin 11

void setup() {
 Serial.begin (9600);
 pinMode(trigPin, OUTPUT); //Trig jako wyjście - pomiar odległości
 pinMode(echoPin, INPUT); //ECHO jako wejście - odczyt zmierzonej odległości
 pinMode(2, OUTPUT); //Wyjście na LED zielona 1
 pinMode(3, OUTPUT); //Wyjście na LED zielona 2
 pinMode(4, OUTPUT); //Wyjście na LED czerwona 1
 pinMode(5, OUTPUT); //Wyjście na LED czerwona 2
 }

void loop() {
zakres(10, 15, 20, 25, 30 ); //Włącz LEDY jeśli odległość 10,15,20,30 - poniżej 10 migaj 2x LED czerwony
  delay(100);
}
    
  int zmierzOdleglosc(){
long czas, dystans;

digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

czas = pulseIn(echoPin, HIGH);
dystans = czas/58;

return dystans;

}

void zakres(int od_10cm, int od_15cm, int od_20cm, int od_25cm, int od_30cm) {
  int jakDaleko = zmierzOdleglosc();
  if ((jakDaleko >od_30cm) ){
    digitalWrite(3,LOW); //Odpal LED zielona 1
  }else{
    digitalWrite(3,HIGH);//Wyłączamy LED zielona 1 gdy obiekt poza zakresem
  }
  if ((jakDaleko >od_25cm) ){
    digitalWrite(2,LOW); //Odpal LED zielona 2
    
  }else{
    digitalWrite(2,HIGH); //Wyłącz LED zielona 2
  }
  if ((jakDaleko >od_20cm) ){
    digitalWrite(4,LOW); //Odpal LED czerwona 1
  }else{
    digitalWrite(4,HIGH); //Wyłącz LED czerwona 1
  }
   if ((jakDaleko >od_15cm)){
    digitalWrite(5,LOW); //Odpal LED czerwona 2
  }else{
    digitalWrite(5,HIGH); //Wyłącz LED czerwona 2
  }
 if ((jakDaleko <od_10cm)){
    digitalWrite(4,LOW);
    digitalWrite(3,LOW);  
    digitalWrite(2,LOW);      
    digitalWrite(5,HIGH); //Miganie LED czerwona 2
    digitalWrite(4,HIGH); //Miganie LED czerwona 1
    delay(100);
    digitalWrite(5,LOW); //Miganie LED czerwona 2
    digitalWrite(4,LOW); //Miganie LED czerwona 1
    delay(100);
    }
} 

Pracuję teraz nad rozbudową kombinacji o przycisk startowy (ON/OFF). Chcę podpiąć go tak aby to on włączał program. Czyli:

  1. Podłączenie Arduino UNO do zasilania - Przycisk w pozycji OFF nic się nie dzieje
  2. Przycisk w pozycji ON - uruchamia się program.
  3. Przycisk w pozycji OFF - wyłącza się program

Mam pewne wątpliwości co do podpięcia. Czy przycisk powinien być wpięty jako separator zasilania całości czy samo Arduino to określi - Przełącznik połączony z całością tylko za pomocą GND ?

Męczę się z kodem ale nic nie wychodzi. Nie wiem jak zaprogramować ten układ aby działał tak jak opisałem wyżej :/. Jakaś sugestia, w którą stronę iść? Nie poddaję się ! 🙂

Taką mam wizję.

1544282505_Podczenie.thumb.jpg.368f2a589e2f6c79b6db2dfbf741864a.jpg

 

Edytowano przez Forseti
Dodany schemat
Link to post
Share on other sites

@Forseti Możesz zrobić to na dwa sposoby:

  • albo jak piszesz odłączyć zasilanie - Arduino po podłączenia zasilania zresetuje program i zacznie od nowa.
  • albo zrobić to w programie.

Pierwsza opcja nie wymaga raczej komentarza.

Druga opcja jest bardziej rozwojowa, bo możesz zmienić funkcjonalność. W pętli loop() tworzysz maszynę stanów (if lub swtch-case), a przyciskiem ustawiasz jakąś zmienną. Poczytaj mój wpis o zatrzasku i złóż z tego całość 🙂 

  • Pomogłeś! 1
Link to post
Share on other sites
(edytowany)

@Gieneq Dziękuję bardzo za odpowiedź i link do super poradnika - korzystam i dokształcam się.

Ale odnośnie zagadnienia chyba nie do końca się zrozumieliśmy. Próbuję się nauczyć jak wykonać mechaniczne przełączenie pomiędzy programami. Chcę zbudować robota, który będzie miał min. dwie funkcje, które będzie można przełączać za pomocą np. przełącznika suwakowego 3- wyjściowego. Zasilanie główne robota ma swoje oddzielne wyłączenie. Zatem po podaniu napięcia na układ (wyłącznik główny) żaden z programów nie startuje dopóki nie wybiorę odpowiedniej pozycji na przełączniku (PIN wyzwalający pracę programu).

Na szybko machnąłem "schemat" o co mi chodzi:

652879452_Diagramsterujcy.thumb.jpg.336aaebc5c303588c7209c6cec8c82fe.jpg

Chciałbym aby odczyt stanu na PINie (wywołany przełącznikiem) był nadrzędny nad pętlą pracy danego programu. Chcę mieć możliwość przełączania jego pracy w trakcie. Prześledziłem kurs 1 oraz Twoje porady (zabieram się z wolna za kurs 2) i nadal czuję, że jestem w lesie ze swoją wiedzą. Mam problem z zaimplementowaniem "pollingu" do kodu.  Popraw mnie proszę ale wydaje mi się, że do mojej aplikacji najrozsądniej będzie korzystać właśnie z tej metody przerwania programu (zamiast zerwania- ograniczenie tylko do 2 PINów). 

Sam pooling w prostych kodach idzie mi całkiem sprawnie ale nie wiem jak to przełożyć na bardziej zaawansowany kod. Dopóki nie opanuję tego na etapie prostych LEDów to nie ma po co iść dalej :/. Nie mniej walczę 🙂

Edytowano przez Forseti
Link to post
Share on other sites
(edytowany)

@Forseti Czyli masz przycisk z 3 pozycjami - ma pewnie 4 wyprowadzenia w tym jedno wspólne. 3 z nich dajesz do Arduino, jedno np do masy. Wejścia Arduino ustawiasz z PULL-UP. Sprzęt gotowy. W kodzie traktujesz ten 3 pozycyjny przełącznik jak 3 osobne przyciski, które nigdy nie będą włączone w tym samym momencie.

Do obsługi możesz podejść na przynajmniej 3 sposoby:

  • typowy polling - 3 warunki w stylu jeżeli przycisk A (pozycja przełączonika) wciśnięty to część 1  itd.
  • przerwania - 3 zewnętrznych przerwań nie masz, masz tylko 2 - ale nawet na 2 zrobisz co chcesz, bo 3 stan będzie domniemany na podstawie 2 poprzednich. Ja bym jednak radził Ci najpierw zrobić ładny polling, a później przerwania, żeby sie czegoś więcej nauczyć.
  • programowe przerwania - to co opisałem w poradniku. Sprawdzasz co było na przycisku kiedyś i co jest teraz i z tego wyznaczasz typ przejścia - włączony/wyłączony. W tym przypadku potrzebujesz dodatkowe zmienne określające stan przycisku.

Pisze tu przycisk bo to niczym się nie różni od dźwigienki. Uznaj dźwigienkę za 3 przyciski, które wyglądają tak jakby ktoś caly czas trzymał jeden z nich. Wtedy przyłożenie kodu z kursu na twoje zadanie będzie prostsze.

 

Edytowano przez Gieneq
  • Pomogłeś! 1
Link to post
Share on other sites

Dziękuję za wskazówki. Udało się za pomocą opcji 1 - poolingu. 

Działa to bardzo dobrze.  Wstawiam kod:

//Program symulujący pracę dwóch programów przełączanych za pomocą przełącznika ON/OFF/ON lub suwakowego. Nie dotyczy przycisków typu microswitch
#define LED_1 8 //Symbolizuje Program 1
#define LED_2 9 //Symbolizuje Program 1
#define LED_3 10 //Symbolizuje Program 2
#define LED_4 11 //Symbolizuje Program 2
#define Przycisk_1 2
#define Przycisk_2 3
#define Przycisk_3 4


void setup() {
pinMode(LED_1, OUTPUT); //Wyjście na LED 1
pinMode(LED_2, OUTPUT); //Wyjście na LED 2
pinMode(LED_3, OUTPUT); //Wyjście na LED 3
pinMode(LED_4, OUTPUT); //Wyjście na LED 4


pinMode(Przycisk_1, INPUT_PULLUP); //Wejście przycisku 1 w trybie PULLUP
pinMode(Przycisk_2, INPUT_PULLUP); //Wejście przycisku 2 w trybie PULLUP
pinMode(Przycisk_3, INPUT_PULLUP); //Wejście przycisku 3 w trybie PULLUP

digitalWrite(LED_1, LOW); //Stan diody LED1 - wyłączona 
digitalWrite(LED_2, LOW); //Stan diody LED2 - wyłączona 
digitalWrite(LED_3, LOW); //Stan diody LED1 - wyłączona 
digitalWrite(LED_4, LOW); //Stan diody LED2 - wyłączona
}

void loop() {
   
if (digitalRead(Przycisk_1)==LOW) {//Jeśli przycisk 1 jest włączony - Program 1 miganie ledów 1,2 na przemian
  digitalWrite(LED_1, HIGH); //Stan diody LED1 - włączona 
  delay(100);
  digitalWrite(LED_2, HIGH); //Stan diody LED2 - włączona 
  delay(100);
  digitalWrite(LED_1, LOW); //Stan diody LED1 - wyłączona
  delay(100);
  digitalWrite(LED_2, LOW); //Stan diody LED2 - wyłączona 
  delay(100);
}else if(digitalRead(Przycisk_2)==LOW){ //Jeśli przycisk 2 jest włączony - Program 2 miganie ledów 3,4 jednocześnie
  digitalWrite(LED_3, HIGH); //Stan diody LED3 - włączona 
  digitalWrite(LED_4, HIGH); //Stan diody LED4 - włączona 
  delay(100);
  digitalWrite(LED_3, LOW); //Stan diody LED3 - wyłączona  
  digitalWrite(LED_4, LOW); //Stan diody LED4 - wyłączona 
 delay(100);
}else if(digitalRead(Przycisk_3)==LOW){//Tutaj wszystko off - LEDY dla efektu w linię
   digitalWrite(LED_1, HIGH); //Stan diody LED1 - włączona 
  delay(100);
  digitalWrite(LED_2, HIGH); //Stan diody LED2 - włączona 
  delay(100);
  digitalWrite(LED_1, LOW); //Stan diody LED1 - wyłączona
  delay(100);
  digitalWrite(LED_2, LOW); //Stan diody LED2 - wyłączona 
  delay(100);
   digitalWrite(LED_3, HIGH); //Stan diody LED1 - włączona 
  delay(100);
  digitalWrite(LED_4, HIGH); //Stan diody LED2 - włączona 
  delay(100);
  digitalWrite(LED_3, LOW); //Stan diody LED1 - wyłączona
  delay(100);
  digitalWrite(LED_4, LOW); //Stan diody LED2 - wyłączona 
  delay(100);
}
else{
  
}}
  

Schemat.thumb.png.239d11063705e6cea67127a1af81df13.png

Teraz próbuję powyższe zaimplementować do kodu bardziej zaawansowanego - z czujnikiem HC-SR04 jako jednym z programów ale póki co idzie czerstwo 🙂

 

Link to post
Share on other sites

Witam,

Zmienne "czas" oraz "dystans" zostały zadeklarowane jako "long" podczas gdy funkcja "zmierzOdleglosc" zwraca dystans jako "int". Czy mamy tu do czynienia z rzutowaniem? Kiedy konkretnie należy stosować polecenie ".toInt"? Czy tylko w przypadku komunikacji przez UART? Z góry dzięki za odpowiedź. 

Link to post
Share on other sites
(edytowany)

@Vuko dobre pytania, zacznę od najprostszego:

Dnia 2.05.2021 o 23:47, Vuko napisał:

Kiedy konkretnie należy stosować polecenie ".toInt"?

funkcja toInt konwertuje ciąg znaków na liczbę int - odbierasz po UARTcie znaki* i wrzucasz to do funkcji dostajesz typ całkowity long.

Pierwsze pytanie jest trudniejsze. Jesteśmy w obrębie liczb całkowitych i najważniejsze aby nie wyjść poza zakres. Czy to long, int, czy unsigned long - grunt żeby nie przekroczyć zakresu, chyba że interesują nas różnice, wtedy zakresy nie mają tak dużego znaczenia; ale to inny temat...

Zacznijmy od tego jak reprezentowane są te zmienne. Patrzymy na tabelkę dla Arduino UNO:

image.thumb.png.205f6cb4b4375f54d0d905bf62de4962.png

Int ma 16 bity, long 32 bity. Możesz to sprawdzić operatorem sizeof() na typie danych:

  size_t rozmiar_long_w_bajtach = sizeof(long);
  size_t rozmiar_int_w_bajtach = sizeof(int);

I tu ciekawostka, nie wszędzie tak jest. Np. mikrokontroler STM32L4 ma 4 B (32 b) dla int i long.

Druga sprawa funkcja pulseIn - zwraca unsigned long. Więc czas powinien mieć taki typ, nie jest to wielki błąd, bo czas jest zwracany w microsekundach (10^-6s) więc przepełnienie longa nastąpi po 35 godzinach 🙂 

Wracając do UNO, czyli jak z inta robimy long to jest ok, a w drugą stronę już niekoniecznie 😞 Czy zrzutujesz, czy przypiszesz, jeżeli przekroczysz zakres to wartość zmiennej się "przekręci", tu masz przykład:

  int zmienna1_int = 32767;
  Serial.println(zmienna1_int);
  
  long zmienna1_long = zmienna1_int;
  Serial.println(zmienna1_long);

  Serial.println("inkremetuje...");

  zmienna1_int += 1;
  zmienna1_long += 1;
  
  Serial.println(zmienna1_int);
  Serial.println(zmienna1_long);
  
  Serial.println("rzutuje...");

  int jakbylong = zmienna1_long;
  int jakbylong2 = (int)(zmienna1_long);
  
  Serial.println(zmienna1_int);
  Serial.println(zmienna1_long);
  Serial.println(jakbylong);
  Serial.println(jakbylong2);

Wynik:

32767
32767
inkremetuje...
-32768
32768
rzutuje...
-32768
32768
-32768
-32768

Wystarczyło dorzucić 1 i już jesteśmy na ujemnym biegunie zmiennej 😉 

Więc jeżeli poczekasz ponad 35 godzin to faktycznie typ long zepsuje wynik funkcji pulseIn.

A co z tym intem w returnie? Sprawdzamy po jakim czasie zwróci głupoty. Tu już gorzej, ale nie będzie problemów - masz tam dzielenie przez stałą, wiec mnożymy i wychodzi 1,9 sekundy. Mało ale ten pomiar tyle nie trwa i nie zepsuje nic.

Zgodnie ze sztuką wewnątrz funkcji powinniśmy operować na unsigned long i taki też typ powinna mieć cała funkcja, ale dozwolony jest ten int, zwłaszcza gdy poza funkcją interesuje cię nie long ale int.

Też w ramach ciekawostki możesz sprawdzić dokumentację delay'a jaki ma typ argumentów 😉 

Edytowano przez Gieneq
Link to post
Share on other sites

@Gieneq dzięki wielkie za szczegółowe wyjaśnienie. Sprawa jasna!

 

Rozumiem, że 1 impuls musiałby trwać te 35h w przypadku pulseIn aby 'przekrecic' wynik? 

Sprawdziłem typ zmiennej dla parametru w funkcji delay: unsigned long. Czyli wychodzi mi na to że gdybym z jakiegoś powodu chciał użyć tej funkcji dłużej niż 49dni 16h i 48min musiałbym ją powtórzyć? Pytanie czysto teoretyczne oczywiście 😉

 

Pozdrawiam

Link to post
Share on other sites
(edytowany)
20 minut temu, Vuko napisał:

Rozumiem, że 1 impuls musiałby trwać te 35h w przypadku pulseIn aby 'przekrecic' wynik? 

Tak, ale sama funkcja zwraca typ unsigned long, czyli jeżeli zostawić funkcję włączoną ponad 71 godzin to nastąpi w niej przepełnienie. A w przykładzie zrzutowanie wyniku z unsigned long na long skraca ten czas właśnie do tych około 35 godzin.

20 minut temu, Vuko napisał:

Sprawdziłem typ zmiennej dla parametru w funkcji delay: unsigned long.

Tak zgadza się 🙂 I właśnie z tym powtarzaniem jest pewien trik. Jak spojrzysz do wnętrza delaya:

void delay( unsigned long ms )
{
  if (ms == 0)
  {
    return;
  }

  uint32_t start = micros();

  while (ms > 0)
  {
    yield();
    while (ms > 0 && (micros() - start) >= 1000)
    {
      ms--;
      start += 1000;
    }
  }
}

To widać jakieś zliczanie i odejmowanie. Jest to prosty sposób na mierzenie interwałów - jakiś okresów czasu. I wiesz że to jest odporne na przepełnienia? Przykład: 

  int zmienna1_int = 32767;
  int zmienna1_int2 = 32767;
  zmienna1_int2 += 1;
  int zmienna1_int3 = zmienna1_int2 - zmienna1_int;

Zmienna "zmienna1_int2" po zwiększeniu będzie mieć wartość -32768, a wynik operacji odejmowania to: -32768 - 32767 = ... 1 😄 

Można wyobrazić sobie że liczby w takim zapisie są na okręgu, tak jak tu dla 3 bitów

image.thumb.png.632a49e8d4b4b4c22dc48bf058999883.png

jak do 3 dodasz 1 to trafisz na -4, wtedy operacja -4  -  3 da w wyniku odległość pomiędzy nimi czyli 1.

Niestety choć wygląda to pięknie, to gdy przepełnisz zmienną kilka razy to już stracisz tę informację. Więc jakbyś robil coś co ma długo zliczać, to trzeba pomyśleć jak realne jest przepełnienie i jak sobie z tym poradzić.

Edytowano przez Gieneq
  • Lubię! 1
Link to post
Share on other sites

@Gieneq Dzięki za kolejną porcję wyjaśnień. Wniosek z tego taki, że trzeba uważać na przepełnienia i mieć świadomość kiedy mogą nastąpić.

Pozdrawiam!

 

Link to post
Share on other sites
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.