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.