Pojazd sterowany – nRF24L01
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ą).
Tekstowy protokół — kodowanie
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.
Tekstowy protokół — odczyt
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:
- indexOf(napis) — zwracająca pozycję napisu w danym stringu (u nas napis to średnik, którym oddzielaliśmy liczby)
- length() — zwracająca długość stringu
- remove(od, do) — ucinająca napis od pozycji od do pozycji do
- substring(od, do) — zwracająca podciąg w danym napisie, od pozycji od do pozycji do
- toInt(napis) — zamienia napis na liczbę całkowitą (int)
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
Tekstowy protokół — szybkość dekodowania
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ń).
Tekstowy protokół: modyfikacje — suma kontrolna
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ą.
Tekstowy protokół: modyfikacje — protokół binarny
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.