Podstawy Arduino
LED-y podłączone do Arduino: pinMode(), digitalWrite(), delay() – trzy funkcje, a ile radochy!
Maskotka
Sterowanie bezprzewodowe (PP) – działa! Mimo to pojazd jeździ bokiem, bo silniki pracują nierówno oraz mechanika kuleje…
LED-y podłączone do Arduino: pinMode(), digitalWrite(), delay() – trzy funkcje, a ile radochy!
Sterowanie bezprzewodowe (PP) – działa! Mimo to pojazd jeździ bokiem, bo silniki pracują nierówno oraz mechanika kuleje…
1x LED + Arduino: setup(), loop(), pinMode(), digitalWrite(), delay() – zaczyna się zabawa…
Projekt wraca na trapetę – ale najpierw trzeba to spowrotem uruchomić, a tu przewody się rozłączyły, radiówka nie działa…. Brawo dla PP + BB za reaktywację Maskotki!
PP programował serwa: klasyczne (obrót 0..180 stopni) oraz pracy ciągłej… a tymczasem BB naprawiał popsute egzemplarze:
BB udoskonalił swój pojekt heXapod – robot zyskuje nowe, lepsze nogi!
Więcej o tym na stronie pojektu heXapod .
KG (c) 2020
PP zapoznaje się z czujką TCRT5000 do swojego projektu…
a BB udoskonalił swój pojekt heXapod. Tyle i aż tyle 😉
KG (c) 2020
Dziś zadziałało chodzenie/bieganie
Więcej szczegółów w stronie projektu heXapod.
KG (c) 2020
Pan Bartek zademonstrował poruszanie. Na dodatek heXapod został zerwany ze smyczy – sterowany jest bezprzewodowo (nRF24). Jak na pierwsze próby to wynik jest bardzo obiecujący!
KG (c) 2020
(Wpis ten jest reaktywacją wpisu z 2017 r. Okazało się, że warto podzielić oryginalny wpis na dwie mniejsze części – jedna dotycząca komunikacji I2C, druga SPI właśnie).
Tutaj https://www.arduino.cc/en/reference/SPI można poczytać czym jest protokół SPI. Nam wystarczy, że jest to szybka komunikacja synchroniczna na krótki dystans, wykorzystująca przynajmniej 3 linie nazwane MISO, MOSI i SCK. Dodatkowo są jeszcze linie CE i SCN. Jeśli więc w jakimś module zobaczymy tak nazwane piny – to znak, że działa w standardzie SPI właśnie.
Specjalne piny SPI w Arduino to MISO (12 w Arduino), MOSI (11 w Arduino) oraz SCK (13 w Arduin0). Inne piny – CE i CSN są dowolne cyfrowe (występuje różne nazewnictwo – SS to Slave Select i odpowiada CE, który musi działać jako OUTPUT, w odróżnieniu od CSN). W poniższym przykładzie wybrałem 9 i 10 – a dlaczego? okaże się to później (na kolejnych spotkaniach naszego koła).
Bardzo fajny moduł (no i bardzo tani – około 5 zł, co w porównaniu do modułów Bluetooth jest 1/5 ceny) – ale najpierw trzeba wiedzieć, jak podłączać go do Arduino:
UWAGA: zasilanie VCC jest z przedziału 3.3V-3.6V – podłączając do Arduino 5V USZKODZISZ moduł.
Oprogramowanie? Ponownie jest kilka bibliotek, ja użyłem nRF24L01.h by TMRh20. Jak poprzednio – doinstalowujemy do naszego Arduino IDE i tworzymy program odbierający dane:
#include <SPI.h> #include <nRF24L01.h> #include <RF24.h> RF24 radio(9, 10);//CE, CS const byte rxAddr[6] = "grzyb"; void setup() { Serial.begin(9600); Serial.print("nRF24 INIT="); bool ok=radio.begin(); Serial.println(ok); radio.openReadingPipe(0, rxAddr); radio.startListening(); } char text[32]; void loop() { if (radio.available()) { radio.read(&text, sizeof(text)); Serial.print("t="); Serial.print(millis()/1000); Serial.print("s, text="); Serial.println(text); } }
W linii 6 nadalismy nazwę naszemu strumieniowi – dowolne 5 znaków, niekoniecznie musi być tak jak u mnie… Ale nadajnik też musi nadawać na tym samym „paśmie”:
#include <SPI.h> #include <nRF24L01.h> #include <RF24.h> #include <printf.h> RF24 radio(9, 10);//CE, CS uint8_t rxAddr[6] = "grzyb"; void setup() { Serial.begin(9600); Serial.print("nRF24 INIT="); bool ok=radio.begin(); Serial.println(ok); printf_begin; radio.printDetails(); radio.setRetries(15, 15); radio.openWritingPipe(rxAddr); radio.stopListening(); } char text[] = "Fiza: OK!"; void loop() { radio.write(&text, sizeof(text)); text[7]=48+rand()%10; text[8]=48+rand()%10; text[9]=48+rand()%10; text[10]=48+rand()%10; text[11]=48+rand()%10; Serial.println(text); delay(3000); }
Maksymalna długość jednorazowo przesłanego napisu wynosi 32 bajty, a inne ważne rzeczy – w dokumentacji (zachęcam do czytania).
Np. problem ze zrywaniem połączenia – ludzie piszą o niestabilnym napięciu z pinu 3.3V Arduino oraz o problemie z prądem (pin 3.3V może maksymalnie dać tylko 50mA), dlatego dodają dodatkowy kondensator (mały, np 4.7 μF, 10 μF) do zasilania modułu:
Mogą pojawić się też problemy z anteną, więc albo kupujemy moduł z „wypasioną antenką”:
albo samodzielnie robimy sobie antenkę:
Polecam zajrzeć tutaj aby poczytać o problemach i różnych sposobach ich rozwiązywania, a także o oprogramowywaniu tego moduły.
(c) K.G. 2017, 2019
Pan Przemek oprogramował pojazd – działa przez radiówkę! Na razie sterowanie odbywa się przy pomocy 4-rech przycisków, a „grzybek” nie jest jeszcze oprogramowany. Od czegoś trzeba było zacząć 😉
Chwilowo nie zgłaszamy problemów z opóźnieniami spowodowanymi tekstowym protokołem – zobaczymy, co będzie dalej. Powyżej warsztat pracy Pana Przemka – jak widać kontroler jest minimalistyczny, a temat stworzenia fajniejszego pozostawiamy na później. Prace nad pojazdem sterowanym przez radio idą w bardzo dobrym kierunku. Za tydzień upgrade Maskotki, a potem nowe podwozie (+ modyfikacje serwomechanizmów do pracy ciągłej).
Przełączenie sterowników silników krokowych na 1/16 kroku (poprzednio: 1/8 kroku) poskutkowało cichszą pracą, lekko wolniejszą – ale za to dokładniejszą (mniej „chwiejnych” linii). Poniżej fajny wydruk:
Dodatkowo trwają prace nad kolejną zmianą konstrukcji – nowe wózki, wydrukowane w 3D, mają zastąpić te z profili Makerbeama. Zobaczymy co wyjdzie.
Więcej o projekcie PM2D3D na dedykowanej stronie.
(c) K.G.
Pan Przemek dalej kombinuje z komunikacją radiową na bazie układu nRF24. Jako nową zabawkę dostał JoyShield-a do Arduino, o takiego:
Jest to bardzo ciekawy układ, bo nie dość, że ma joystick oraz 7 przycisków (4 duże, kolorowe, jeden w joysticku, oraz 2 małe – mikrostyki), to ma jeszcze adaptery na radiówkę nRF24 (i inne też, ale tego nie tykamy). Wszystko złożone w „kanapkę” może pełnić funkcję kontrolera – po przyczepieniu bateryjki 9V (np. gumką recepturką).
Bazujemy na protokole tekstowym – shield odczytuje położenie joysticka, oraz 5 przycisków (chwilowo nie obsługujemy wszystkich). Dane wysyłane są przez nRF24 jako tekst, a poszczególne pola oddzielone są średnikiem. Jedna paczka danych wygląda więc tak:
507;512;0;1;1;0;0;
gdzie pierwsza liczba określa położenie joy-a na osi X, druga na osi Y, a kolejne zera i jedynki to stan logiczny 5-ciu przycisków. Bardzo proste w utworzeniu tego napisu — dzięki klasie String i jego licznie przeciążonych konstruktorach, oraz operatorowi „dodawania”.
#include <SPI.h> #include <nRF24L01.h> #include <RF24.h> RF24 radio(9, 10);//CE, CS uint8_t rxAddr[6] = "grzyb"; void setup(){ Serial.begin(9600); Serial.print("nRF24 INIT="); bool ok=radio.begin(); Serial.println(ok); radio.setRetries(15, 15); radio.openWritingPipe(rxAddr); radio.stopListening(); //dla modulu JoyShield pinMode(2, INPUT); pinMode(3, INPUT); pinMode(4, INPUT); pinMode(5, INPUT); pinMode(6, INPUT); } char bufor[32]; String napis; void loop(){ //tworzymy napis wedlug naszego protokolu napis = String(analogRead(A0)) + ";" + String(analogRead(A1)) + ";" + String(digitalRead(2)) + ";" + String(digitalRead(3)) + ";" + String(digitalRead(4)) + ";" + String(digitalRead(5)) + ";" + String(digitalRead(6)) + ";"; //przygotowujemy bufor -- tablice z napisem... napis.toCharArray(bufor, 32); //wazne! wysylamy bufor a nie napis! radio.write(&bufor, sizeof(bufor)); }
Ponieważ radio wysyła dane w postaci tablicy, nie możemy wysyłać obiektu napis. Dlatego korzystamy z metody toCharArray() klasy String i przekopiowujemy zawartość napisu do bufora – tablicy. W „eter” wysyłamy tablicę bufor.
No właśnie, prostota użycia napisów pociąga za sobą „problem” odczytywania takich danych. W grę wchodzą bardzo przydatne metody klasy String:
Poniżej program dekodujący nasz protokół – czyli zamieniający napis na
int x,y;
byte b1,b2,b3,b4,b5;
Napis wczytany z klawiatury, przez komunikację szeregową – dzięki temu możemy testować nasz program na różne sposoby (o przeniesieniu tego kodu na radio — będzie za tydzień).
void setup() { //dane wprowadzamy z klawiatury przez Serial Serial.begin(9600); } String tekst; String ciag; char znak = ';';//separator pola int x,y; //wspolrzedne x,y byte b1,b2,b3,b4,b5;//stan 5 przyciskow int k,l;//pomocnicze unsigned long int t1,t2; void loop() { if(Serial.available()>0){ tekst=Serial.readString(); Serial.println(tekst); t1=micros(); k=tekst.indexOf(znak); ciag=tekst.substring(0,k); x=ciag.toInt(); l=k+1; k=tekst.indexOf(znak,l); ciag=tekst.substring(l,k); y=ciag.toInt(); l=k+1; k=tekst.indexOf(znak,l); ciag=tekst.substring(l,k); b1=ciag.toInt(); l=k+1; k=tekst.indexOf(znak,l); ciag=tekst.substring(l,k); b2=ciag.toInt(); l=k+1; k=tekst.indexOf(znak,l); ciag=tekst.substring(l,k); b3=ciag.toInt(); l=k+1; k=tekst.indexOf(znak,l); ciag=tekst.substring(l,k); b4=ciag.toInt(); l=k+1; k=tekst.indexOf(znak,l); ciag=tekst.substring(l,k); b5=ciag.toInt(); l=k+1; t2=micros(); Serial.print("Pozycja X: "); Serial.println(x); Serial.print("Pozycja Y: "); Serial.println(y); Serial.print("PRZYCISK 1: "); Serial.println(b1); Serial.print("PRZYCISK 2: "); Serial.println(b2); Serial.print("PRZYCISK 3: "); Serial.println(b3); Serial.print("PRZYCISK 4: "); Serial.println(b4); Serial.print("PRZYCISK 5: "); Serial.println(b5); Serial.print("czas dekodowaia [mikrosekundy]= "); Serial.println(t2-t1); }//if }//loop
Wielokrotne użycie powyższych funkcji powoduje, że odkodowanie napisu 507;512;0;1;1;0;0; i zamiana go na
int x, y;
byte b1,b2,b3,b4,b5;
zajmuje dla Arduino UNO od 260 do 550 mikrosekund (czyli ~0.5ms). A dlaczego nie jeden, równy czas? Bo w zależności od postaci dekodowanego napisu mikrokotrolerek musi więcej lub mniej pracować. Mniej „męczy” się w przypadku ciągu 1;1;1;0;0;0;0; a więcej w przypadku 1012;1017;0;0;0;1;1; Dobra, zrozumiałe. A czy to duży czas? Dla człowieka to nic, dla elektroniki sporo… Czy to nam wystarczy – czy nie spowoduje opóźnień (tzw „lagów”) w sterowaniu pojazdem? O tym przekonamy się po zastosowaniu tego protokołu do pojazdu (za tydzień).
Można pomyśleć o rozbudowie naszego protokołu na dodatkowe „pole”, będące sumą kontrolą. Jeden z pomysłów to wysumowanie wszystkich danych (w końcu to liczby całkowite) i zapisanie tej sumy jako ostatni, dodatkowy element. Dla naszego przypadku wyglądałoby to tak: 507;512;0;1;1;0;0;1021; Po stronie odbiornika dekodujemy napis, liczymy sumę x+y+b1+b2+b3+b4+b5 i porównujemy z ostatnią wartością – nazwijmy ją sumak. A Jeśli nie ma równości… odrzucamy (ignorujemy) paczkę danych i czekamy na kolejną.
Lepiej by było stworzyć podobny protokół ale binarny, czyli nie bawić się w zapis liczb w postaci stringów, dodatkowo oddzielać je przecinkami tylko wysyłać x,y jako integer, a przyciski b1,b2,b3,b4 i b5 jako byte (nie 5 bajtów, a jeden – wszak 1 bajt = 8 bitów, mamy więc nadam zapas). Zyskujemy na tym mniejsze porcje danych – wszystkie informacje z JoyShielda to tylko 5 bajtów, a nie 14 (najlepszy przypadek) czy nawet 20 bajtów (najgorszy przypadek). No i nie ma zabawy w dekodowanie danych z wykorzystaniem metod klasy String… Ale to może przy innej okazji 😉
(c) K.G.