[linux]Zdalne-sterowanie-Linuksem-za-pomoca-wiiremote'a.pdf
(
900 KB
)
Pobierz
273368584 UNPDF
Programowanie wiiremote’a
Zdalne sterowanie Linuksem za pomocą wiiremote'a
Linuksem za pomocą
wiiremote'a
Bartłomiej Burdukiewicz
Pewnie zdecydowana większość z was słyszała o rewolucyjnych kontrolerach marki Nintendo,
które zadebiutowały wraz z wydaniem konsoli Wii. Nie wiele osób zdaje sobie sprawę, że możemy
wykorzystać je do innych celów. Dzięki bibliotece libcwiid połączymy go z naszym komputerem.
jak w prosty sposób możemy napisać
oprogramowanie do zdalnego stero-
wania Linuksem wykorzystując wii-
remote’a, aby tego dokonać zapoznamy się z bibliote-
ką libcwiid dzięki której w łatwy sposób zaprogramu-
jemy nasz pilot, użyjemy także urządzenia znakowego
uinput, do stworzenia wirtualnej myszy. Będziemy do te-
go potrzebować skonigurowany interfejs bluetooth, mo-
duł uinput, bibliotekę libcwiid oraz dwie diody led (cho-
dzi tu głównie o dwa źródła podczerwieni, możemy użyć
świecznika). Omówione przeze mnie zostaną najważ-
niejsze funkcje, rozwiązania. Program będziemy budo-
wać w czterech stadiach, na koniec każdego, umieszcza-
ne będą listingi z kodem, który należy dodać (będzie to
oczywiście rozszerzona wersja, o różnego rodzaju wa-
runki sprawdzające, komunikaty, niektóre elementy z
powodzeniem można ignorować wg własnego uznania).
Wszystkie polecenia jakie będę używał w artykule są
oparte o dystrybucje Linuksa Debian w wersji Squeeze.
Należy pamiętać, że podczas instalacji pakietów wyma-
gane są prawa administratora.
Przygotowywanie interfejsu Bluetooth
Jeżeli chcemy nawiązać jakiekolwiek połączenie z na-
szym pilotem, będziemy potrzebować modułu bluetooth,
jeśli posiadamy komputer przenośny zazwyczaj jest już
wbudowany, w przeciwnym przypadku prawdopodobnie
musimy go dokupić. Cena adapterów bluetooth w dzisiej-
szych czasach jest naprawdę niewielka, można je nabyć
za niecałe 10zł, dobrym pomysłem jest uprzednie spraw-
dzenie kompatybilności urządzenia z naszym systemem,
chyba nikt nie lubi wyrzucać pieniędzy w błoto. Przejdź-
my teraz do koniguracji. Należy zainstalować usługę blu-
ez poleceniem
aptitude install bluez
zaraz po instalacji wystartuje nasza usługa, aby sprawdzić
czy poprawnie skonigurowała urządzenie bluetooth na-
leży wykonać polecenie hciconig, jeśli na liście znajdzie
się interfejs hci0 wszystko jest w porządku. Możemy do-
datkowo wytestować nasz moduł skanując sieć bluetooth
w poszukiwaniu urządzeń za pomocą polecenia
hcito-
ol scan
.
56
marzec 2010
Zdalne sterowanie
W
artykule chciałbym zaprezentować
Programowanie wiiremote’a
Zdalne sterowanie Linuksem za pomocą wiiremote'a
Wiiremote
Zanim przejdziemy do programowania, parę
słów o tym niezwykłym pilocie. Wiiremote
komunikuje się z konsolą Wii poprzez inter-
fejs bluetooth, składa się z 12 przycisków,
wliczam tu także “strzałki”, jeden z nich
to klawisz specjalny o nazwie power, słu-
ży do wyłączania/wyłączania konsoli, zry-
wa także połączenie bluetooth, co umożliwi
w kontrolowany sposób zakończenie naszej
aplikacji. Kontroler wyposażony jest także
w 4 diody led, służące do informowania o
stanie baterii lub id kontrolera, silnik które-
go głównym zadaniem jest wprawianie pilo-
ta w drganie, mały głośniczek nisko tonowy,
i najważniejsze kamerę oraz akcelerometr.
Dzięki kamerze możemy pobierać współ-
rzędne punktów podczerwieni, co posłuży
nam w aplikacji. Wbudowany akcelerometr
pozwala wykryć ruchy ręki, bazując na tych
danych możemy stworzyć prosty system ge-
stów. To by było na wszystko gdyby nie port
rozszerzeń, dzięki różnego rodzaju dodat-
kom takimi jak
nunchuk
,
classic controller
lub
wiimote motion plus
rozszerzamy funk-
cjonalność naszego kontrolera. Firma Nin-
tendo szykuje nawet pulsometr jako kolej-
ny dodatek.
Zaczynamy
Na początek napiszemy prosty szkielet, któ-
ry z czasem rozbudujemy, funkcjonalność
jaką będzie posiadał program w tym sta-
dium to możliwość połączenia się z kon-
trolerem oraz krótki raport o statusie po-
łączenia.
W pierwszej kolejności stwórzmy kata-
log w którym będziemy trzymać źródła. Do
edycji polecam program QtCreator domyśl-
nie służy on do tworzenia aplikacji w Qt, lecz
z powodzeniem można wykorzystywać do
kompilacji/edycji prostych oraz zaawanso-
wany programów w C/C++.
Pierwszym krokiem będzie dodanie do
sekcji include pliku nagłówkowego
cwiid.h
,
aby móc korzystać z dobrodziejstw jakie
udostępnia nam biblioteka
libcwiid
. Następ-
nie musimy określić pule adresów MAC jakie
będą dopuszczone do połączenia, każde urzą-
dzenie bluetooth jest identyi kowane przez
unikalny adres MAC, jak wcześniej wspo-
mniałem nasz pilot także posiada interfejs
bluetooth. Jeżeli chcemy uniknąć sprawdza-
nia adresu MAC dla naszego kontrolera pro-
ponuje użyć adresu, który dopuszcza wszyst-
kie urządzenia.
daniem jest zwolnienie połączenia, warto pa-
miętać, aby przed zakończeniem programu
ją wykonać, w przeciwnym przypadku mo-
że prowadzić to do trudnych w wykryciu pro-
blemów.
W tej chwili nasz program powinien wy-
glądać tak jak w pierwszym listingu, w kodzie
umieściłem funkcje
sleep()
, ponieważ czas
oczekiwania na połączenie jest stosunkowo
dość krótki, unikamy w ten sposób niepowo-
dzenia spowodowanego przez zbyt późno wci-
śnięte przyciski 1 oraz 2.
Callback
Callback, czyli wywołanie zwrotne, o któ-
rym wcześniej wspominałem, będzie nam
potrzebne do odbierania wiadomości z nasze-
go kontrolera. Aby wykorzystać ten mecha-
nizm musimy stworzyć specyi czną funkcje
na wzór typu
cwiid_mesg_callback_t
, któ-
ra powinna wyglądać, jak niżej
void wiimote_callback(cwiid_wiimote_t
*wiimote, int mesg_count, union
cwiid_mesg mesg_array[], struct
timespec *timestamp)
{
}
Libcwiid
Libcwiid jest to biblioteka napisana w C,
umożliwia ona nawiązanie połączenia z
pilotem, kontrole działania, pobieranie
informacji/statusów, obsługuje większość
rozszerzeń, które są dostępne dla naszego
kontrolera. Dzisiejszy program będzie opar-
ty prawie w całości o tą bibliotekę, wiec na-
leżało by ją zainstalować w naszym syste-
mie, będziemy potrzebować także źródeł bi-
blioteki, aby wszystko zainstalować wystar-
czy wykonać
bdaddr_t bdaddr;
bacmp(&bdaddr, BDADDR_ANY);
Należy teraz stworzyć pętle for, która zajmie
się analizowaniem wszystkich wiadomości,
na podstawie zmiennej mesg_count określi-
my jak długo pętla ma się wykonywać. Ana-
lizowana będzie tablica, która posiada infor-
macje pobrane z wiiremota.
W pierwszej kolejności definiujemy typ
bdaddr_t
, który będzie przechowywał
nasz MAC, następnie za pomocą
bacmp()
przypisujemy adres
BDADDR_ANY
, aby ze-
zwolić na połączenie wszystkim urządze-
niom bez względu na to jaki adres MAC
posiadają.
for (int i = 0; i < mesg_count; ++i)
switch (mesg_array[i].type)
cwiid_wiimote_t *wiimote = cwiid_
connect(&bdaddr, CWIID_FLAG_MESG_
IFC);
Musimy przeanalizować składnik tablicy o
nazwie type, dzięki niemu określimy jakie-
go rodzaju wiadomości odbieramy, czy są to
stany klawiszy, informacje o statusie/baterii
itp.
aptitude install libcwiid1-dev
libcwiid1
Przejdźmy teraz do funkcji głównej, od któ-
rej zależy sukces naszego połączenia. Funk-
cja zwraca nam wskaźnik do struktury
cwiid_wiimote_t
, jest to deskryptor nasze-
go urządzenia, jeśli wskaźnik przyjmie war-
tość różną od zera, połączenie zostało nawią-
zane. W pierwszym argumencie funkcji na-
leży przekazać wskaźnik do bdaddr, l aga
CWIID_FLAG_MESG_IFC
udostępni nam moż-
liwość obsługi wywołania zwrotnego, o któ-
rym szerzej w następnym nagłówku.
Jeśli w naszej dystrybucji nie znajdziemy
paczki dla libcwiid, należy pobrać samodziel-
nie źródła ze strony domowej cwiid'a, skom-
pilować i zainstalować.
Nasz program zajmie się jedynie ana-
lizą trzech rodzajów wiadomości – o błę-
dach, stanach klawiszy oraz punktach pod-
czerwieni.
Uwaga
Należy pamiętać aby podczas kompi-
lacji naszego programu zlinkować go
z biblioteką libcwiid, używając parame-
tru
-lcwiid
.
cwiid_disconnect(wiimote);
Została nam jeszcze do omówienia bardzo
prosta funkcja
cwiid_disconnect()
, jej za-
Rysunek 1.
Wiiremote oraz podstawowe akcesoria
www.lpmagazine.org
57
Programowanie wiiremote’a
Zdalne sterowanie Linuksem za pomocą wiiremote'a
case CWIID_MESG_ERROR:
disconnected = true;
break;
case CWIID_MESG_IR: break;
case CWIID_MESG_BTN: break;
stanie przerwane, dzieje się tak gdy odejdzie-
my za daleko z naszym pilotem (zasięg blu-
etooth to około 10m~, ale w praktyce bywa z
tym różnie, liczy się tu głównie jakość nasze-
go modułu) lub w razie przytrzymania przyci-
sku power. Zdeiniujmy zmienną
disconnect
,
która będzie posiadała status naszego połącze-
nia, użyjemy do tego typu logicznego, ponie-
waż istnieją tylko dwa stany (urządzenie roz-
łączone lub połączone). Dodajmy wiec linię
kodu, która zmieni wartość logiczną naszej
zmiennej
disconnected
– na prawdę w razie
wystąpienia błędu.
Nasza funkcja callback'a jest już gotowa,
ale wymaga inicjacji, należy powiązać nasze
urządzenie (deskryptor) z funkcją wywołania
zwrotnego.
W tym stadium programu wykorzystamy tyl-
ko
CWIID_MESG_ERROR
, wiadomość jest gene-
rowana, kiedy połączenie z wiiremotem zo-
Listing 1.
Szkielet aplikacji.
cwiid_set_rpt_mode(wiimote, CWIID_
RPT_STATUS | CWIID_RPT_BTN | CWIID_
RPT_IR);
cwiid_set_mesg_callback(wiimote,
&wiimote_callback);
#include
<iostream>
#include
<cwiid.h>
using
namespace
std
;
Zanim powiążemy nasz callback z urządze-
niem, warto byłoby ustalić jakie informacje
mają być “wysyłane”, służy do tego funkcja
cwiid_set_rpt_mode. Wytłumaczę teraz kolej-
no działania lag:
int
main
()
{
bdaddr_t
bdaddr
;
bacmp
(&
bdaddr
,
BDADDR_ANY
);
cout
<<
"Nacisnij 1+2 aby nawiazac polaczenie
\n
"
;
cout
.
lush
();
•
CWIID_RPT_STATUS
– dopuszcza do wy-
wołania zwrotnego, informacje o statusie
urządzenia
•
CWIID_RPT_BTN
– informacje o statusie
przycisków
•
CWIID_RPT_IR
– informacje o punktach
podczerwieni
sleep
(
3
);
cwiid_wiimote_t
*
wiimote
=
cwiid_connect
(&
bdaddr
,
CWIID_FLAG_MESG_IFC
|
CWIID_FLAG_REPEAT_BTN
);
if
(
wiimote
)
{
cout
<<
"Polaczono
\n
"
;
cout
.
lush
();
}
else
{
cout
<<
"Nie mozna ustanowic polaczenia
\n
"
;
cout
.
lush
();
return
0
;
}
cwiid_disconnect
(
wiimote
);
return
0
;
}
Pora na
cwiid_set_mesg_callback()
, w
drugim argumencie należy umieścić wskaźnik
do naszej funkcji wywołania zwrotnego. Nasz
callback jest gotowy, ale aplikacja nadal koń-
czy natychmiast działanie po ustanowieniu po-
łączenia, stworzymy zatem pętle która zablo-
kuje zakończenie programu.
do usleep(1000); while
(!disconnected);
Jako warunek zakończenia pętli użyjemy na-
szej zmiennej
disconnected
, gdy przyjmie
wartość prawdy, program zakończy działanie.
Efekt jaki uzyskaliśmy w tej chwili będzie wy-
glądał następująco, kiedy odejdziemy zbyt da-
leko program zakończy działanie lub jeśli wci-
śniemy przycisk power na naszym kontrole-
rze. W listingu nr 2 widzimy kod, który należy
dodać do naszego programu.
Rysunek 2.
QtCreator
Uinput
Wraz z powstaniem lini 2.6.x Linuksa do
głównego kodu jądra został wprowadzony
nowy sterownik o nazwie uinput. Udostępnia
dość prosty system komunikacji z jądrem sys-
temu dzięki czemu możemy tworzyć wirtual-
ne urządzenia wyjścia oraz zarządzać nimi bez
problemu. W większości dystrybucji Linuksa
sterownik jest dodawany w formie modułu,
58
marzec 2010
Programowanie wiiremote’a
Zdalne sterowanie Linuksem za pomocą wiiremote'a
aby go załadować do pamięci należy wykonać
polecenie (z prawami administratora)
je na wykonywaniu operacji bez blokowa-
nia wykonania (asynchronicznie) należy wy-
czyścić strukturę
uinput
z różnych pamię-
ciowych śmieci, które mogą się tam zna-
leźć,
memset()
będzie idealna funkcją do te-
go zadania
strncpy(uinput.name, "wiimote", 7);
uinput.id.vendor = 1;
uinput.id.product = 1;
uinput.id.version = 1;
uinput.id.bustype = BUS_USB;
modprobe uinput
Dobrym pomysłem byłoby dodanie go do listy
modułów automatycznie ładowanych podczas
startu systemu, aby unikać manualnego łado-
wania za każdym razem, gdy system zostanie
uruchomiony ponownie. Gdyby okazało się że
nie ma go w naszym systemie należy go do-
kompilować ręcznie najlepiej w formie modu-
łu używając źródeł naszego jądra, możemy go
znaleźć w sekcji
memset(&uinput, 0, sizeof(uinput));
za pomocą
strncpy()
przypiszemy nazwę
dla urządzenia wyjścia, kolejno
id.vendor
,
id.product
,
id.versation
nie są zbyt
ważne, ale należy je wypełnić najlepiej je-
dynkami, dla bustype przypiszmy
BUS_USB
można z powodzeniem użyć innych stałych
BUS_*
ale nie będzie miało to żadnego zna-
czenia, przynajmniej jeśli chodzi o funk-
cjonowanie urządzenia wyjścia. Należy te-
raz zdei niować/przekazać do jądra syste-
w pierwszym parametrze przekazujemy
wskaźnik do naszej struktury, w drugim nale-
ży podać wartość znaku, którym zapełnimy ją,
ostatni parametr to wielkość pamięci która zo-
stanie nadpisana.
Gdy mamy już czystą strukturę uinput
możemy przypisać jej własności
Device Drivers ---> Input device
support ---> Miscellaneous devices --
-> User level driver support
Listing 2.
Callback
Ok, rozbudujmy więc nasz program dodając
obsługę uinput, w pierwszej kolejności nale-
ży dodać nowe pliki nagłówkowe, które są
widoczne w listingu nr 3, następnie dodajmy
dwie zmienne
bool
disconnected
=
false
;
void
wiimote_callback
(
cwiid_wiimote_t
*
wiimote
,
int
mesg_count
,
union
cwiid_mesg
mesg_array
[]
,
struct
timespec
*
timestamp
)
{
for
(
int
i
=
0
;
i
<
mesg_count
;
++
i
)
switch
(
mesg_array
[
i
]
.
type
)
{
case
CWIID_MESG_ERROR
:
cout
<<
"Rozlaczono
\n
"
;
cout
.
l ush
();
disconnected
=
true
;
break
;
case
CWIID_MESG_IR
:
…
break
;
int uinput_fd;
struct uinput_user_dev uinput;
wstawmy je zaraz pod using namespace
std, tak aby były widoczne w całym progra-
mie, uinput_fd będzie przechowywał uchwyt
otwartego pliku, natomiast struktura uinput
podstawowe informacje dei niujące nasze no-
we urządzenie wyjścia.
uinput_fd = open("/dev/input/uinput",
O_WRONLY | O_NDELAY);
case
CWIID_MESG_BTN
:
…
break
;
}
}
Aby otworzyć plik użyjemy funkcji
open()
i
tu uwaga w moim przypadku ścieżka do pliku
uinput to
/dev/input/uinput
, ale może się
zdarzyć tak że będzie inna, należy sprawdzić
jeszcze /dev/uinput oraz /dev/misc/uinput.
Otwieramy plik do odczytu oraz zapisu więc
użyjemy l agi
O_WRONLY
,
O_NDELAY
wskazu-
int
main
(
int
argc
,
char
*
argv
[])
{
…
Problemy z utworzeniem
urządzenia
cwiid_set_rpt_mode
(
wiimote
,
CWIID_RPT_STATUS
|
CWIID_RPT_BTN
|
CWIID_RPT_IR
);
if
(
cwiid_set_mesg_callback
(
wiimote
,
&
wiimote_callback
))
{
cout
<<
"Nie mozna utworzyc wywolania zwrotnego
\n
"
;
cout
.
l ush
();
return
0
;
}
Jeśli mamy problemy z utworzeniem urzą-
dzenia prawdopodobnie jest to wina złych
uprawnień. Aby sprawdzić czy tak na-
prawdę jest wystarczy uruchomić nasz
program z prawami administratora, jeże-
li działa poprawnie należy zmienić upraw-
nienia dla pliku uinput za pomocą polece-
nia
chmod
.
do
usleep
(
1000
);
while
(!
disconnected
);
...
}
www.lpmagazine.org
59
Programowanie wiiremote’a
Zdalne sterowanie Linuksem za pomocą wiiremote'a
Listing 3.
Obsługa uinput
…
#include
<stdint.h>
#include
<string.h>
#include
<fcntl.h>
#include
<sys/ioctl.h>
#include
<sys/types.h>
#include
<unistd.h>
#include
<linux/input.h>
#include
<linux/uinput.h>
using
namespace
std
;
int
uinput_fd
;
struct
uinput_user_dev
uinput
;
void
uinput_send_event
(
int
input_fd
,
__u16
type
,
__u16
code
,
__s32
value
)
{
struct
input_event
event
;
memset
(&
event
,
0
,
sizeof
(
event
));
event
.
type
=
type
;
event
.
code
=
code
;
event
.
value
=
value
;
write
(
uinput_fd
,
&
event
,
sizeof
(
event
));
}
...
int
main
(
int
argc
,
char
*
argv
[])
{
uinput_fd
=
open
(
"/dev/input/uinput"
,
O_WRONLY
|
O_NDELAY
);
if
(!
uinput_fd
)
{
cout
<<
"Nie mozna otworzyc urzadzenia uinput"
;
cout
.
lush
();
return
0
;
}
memset
(&
uinput
,
0
,
sizeof
(
uinput
));
strncpy
(
uinput
.
name
,
"wiimote"
,
7
);
uinput
.
id
.
vendor
=
1
;
uinput
.
id
.
product
=
1
;
uinput
.
id
.
version
=
1
;
uinput
.
id
.
bustype
=
BUS_USB
;
ioctl
(
uinput_fd
,
UI_SET_EVBIT
,
EV_KEY
);
ioctl
(
uinput_fd
,
UI_SET_EVBIT
,
EV_ABS
);
ioctl
(
uinput_fd
,
UI_SET_ABSBIT
,
ABS_X
);
ioctl
(
uinput_fd
,
UI_SET_ABSBIT
,
ABS_Y
);
ioctl
(
uinput_fd
,
UI_SET_KEYBIT
,
BTN_LEFT
);
ioctl
(
uinput_fd
,
UI_SET_KEYBIT
,
BTN_RIGHT
);
uinput
.
absmax
[
ABS_X
]
=
512
;
uinput
.
absmin
[
ABS_X
]
=
-
512
;
uinput
.
abslat
[
ABS_X
]
=
0
;
uinput
.
absfuzz
[
ABS_X
]
=
0
;
uinput
.
absmax
[
ABS_Y
]
=
384
;
uinput
.
absmin
[
ABS_Y
]
=
-
384
;
uinput
.
abslat
[
ABS_Y
]
=
0
;
uinput
.
absfuzz
[
ABS_Y
]
=
0
;
write
(
uinput_fd
,
&
uinput
,
sizeof
(
uinput
));
if
(
ioctl
(
uinput_fd
,
UI_DEV_CREATE
))
{
close
(
uinput_fd
);
cout
<<
"Nie mozna stworzyc urzadzenia"
;
cout
.
lush
();
return
0
;
}
...
mu informacje z jakich komponentów zbu-
dujemy nasze urządzenie wyjścia. Dla na-
szej myszy będziemy potrzebować deini-
cji obsługi klawiszy (lewy, prawy myszy)
oraz osie X, Y.
ioctl(uinput_fd, UI_SET_EVBIT,
EV_KEY);
ioctl(uinput_fd, UI_SET_EVBIT,
EV_ABS);
Powyższe funkcje należy wykonać, aby
w jakiekolwiek sposób móc deiniować
osie/klawisze. (inaczej bez nich deinicje dla
UI_SET_ABSBIT, UI_SET_KEYBIT
nie mają
żadnego sensu)
ioctl(uinput_fd, UI_SET_ABSBIT,
ABS_X);
ioctl(uinput_fd, UI_SET_ABSBIT,
ABS_Y);
Zdeiniujmy teraz nasze osie X oraz Y, wy-
korzystamy do tego lag
ABS_X
oraz
ABS_Y
,
należy zwrócić uwagę, że nie korzystamy
już z
UI_SET_EVBIT
, tylko z
UI_SET_ABS-
BIT
.
ioctl(uinput_fd, UI_SET_KEYBIT,
BTN_LEFT);
ioctl(uinput_fd, UI_SET_KEYBIT,
BTN_RIGHT);
Zostały teraz tylko klawisze myszy należy
zdeiniować
BTN_LEFT
oraz
BTN_RIGHT
.
Nie należy zapomnieć o określeniu prze-
działów dla naszych osi
uinput.absmax[ABS_X] = 512;
uinput.absmin[ABS_X] = -512;
uinput.abslat[ABS_X] = 0;
uinput.absfuzz[ABS_X] = 0;
uinput.absmax[ABS_Y] = 384;
uinput.absmin[ABS_Y] = -384;
uinput.abslat[ABS_Y] = 0;
uinput.absfuzz[ABS_Y] = 0;
Rysunek 3.
Świecznik, wiiremote
60
marzec 2010
Plik z chomika:
isseau
Inne pliki z tego folderu:
100 Linux Tips And Tricks.pdf
(1217 KB)
Budowa Linuxa.zip
(1627 KB)
[linux]Zdalny-dostep-do-srodowiska-graficznego.pdf
(653 KB)
[linux]Zdalne-sterowanie-Linuksem-za-pomoca-wiiremote'a.pdf
(900 KB)
[linux]Wspolpraca-telefonow-z-systemem-linux.pdf
(538 KB)
Inne foldery tego chomika:
(Video2Brain) Adobe Flash CS5
3DS Max 2012
3DS MAX 2013
3ds Max 2014 64 bit + V-Ray 2.40.04
ABBY FINE READER
Zgłoś jeśli
naruszono regulamin