Ethernet i AVR–y, cz.6.pdf
(
1004 KB
)
Pobierz
102-104_enut_cz6.indd
KURS
Ethernet i AVR–y
Ethernet od podstaw, część 6
W związku z niedawnymi wydarzeniami w polityce,
w szóstym odcinku kursu zajmiemy się niegodziwymi
lecz przydatnymi w praktyce technikami inwigilacji.
Pokażę bowiem jak stworzyć program zmieniający
płytkę ZL9AVR w narzędzie szpiegowskie, którego
nie powstydziliby się agenci najlepszych wywiadów.
Umieszczona w dyskretnym miejscu, nagrywa
prowadzone w pomieszczeniu rozmowy i przesyła
je odpowiednim „służbom” za pomocą sieci Ethernet.
A wszystko pod przykrywką demonstracji korzystania
z protokołu UDP w aplikacjach dla systemu Nut/OS :)
TCP vs UDP
Protokół UDP (
User Datagram Pro-
tocol
) jest nieco odmienny od dotych-
czas omawianego w kursie protokołu
TCP. Zasadniczą różnicą jest bezpołą-
czeniowość UDP. Aby przeprowadzić
transmisję danych z wykorzystaniem
protokołu TCP należało najpierw połą-
czyć się z serwerem, który mógł przy-
jąć lub odrzucić połączenie. Dopiero
potem była możliwa wymiana infor-
macji. Protokół UDP nie posiada me-
chanizmów nawiązywania połączenia
– transmisja danych polega na wymia-
nie luźnych, niepowiązanych ze sobą
pakietów zwanych w języku angielskim
datagramami
. O ile sesję TCP można
porównać do rozmowy telefonicznej –
ktoś dzwoni, druga strona odrzuca lub
odbiera połączenie i następuje wymia-
na danych (rozmowa), o tyle transmi-
sję danych przez UDP – do wysyłania
sobie wzajemnie SMS–ów (przy czym
nie wiadomo, czy druga strona prze-
czyta wiadomość).
Konsekwencją bezpołączeniowości
UDP jest brak mechanizmów kontroli
przepływu danych. Stos protokołu UDP
nie potwierdza odebrania pakietu od
nadawcy – nie ma zatem żadnej gwa-
rancji, że wysłany pakiet na pewno
dotrze do adresata. Jeśli pakiet zostanie
zagubiony, nie jest możliwa jego auto-
matyczna retransmisja jak w przypadku
protokołu TCP (o ile nie zostanie to
zaimplementowane samodzielnie).
Prostota UDP niesie ze sobą wiele
korzyści: zmniejszenie opóźnienia mię-
dzy wysłaniem pakietu, a jego odebra-
niem przez adresata oraz wzrost pręd-
kości przesyłu danych. Z tego względu
jest on często używany przez gry sie-
ciowe, oprogramowanie do wideokonfe-
rencji, telefony internetowe (VoIP) i in-
ne aplikacje przesyłające dźwięk i obraz
„na żywo”.
ma wdzięczną nazwę –
inwigila-
cja().
Rozpoczyna ona pracę od
utworzenia nowego gniazda protokołu
UDP. Ponieważ nie będziemy odbierać
żadnych pakietów UDP, parametr
port
jest równy 0.
Następnym krokiem jest alokacja
pamięci dla dwóch buforów przecho-
wujących nagrywany dźwięk. Bufory
te mają rozmiar 256 próbek, czyli 512
bajtów (próbki z przetwornika A/C są
10–bitowe, najprościej jest je zapisać
w postaci liczb 16–bitowych). Alokuje-
my również 514–bajtowy bufor prze-
chowujący nadawany pakiet UDP. Za-
stosowanie dwóch buforów umożliwia
ciągłą rejestrację i transmisję sygnału –
w chwili gdy dane z przetwornika A/C
są wpisywane do jednego z buforów,
zawartość drugiego jest wysyłana przez
sieć. Gdyby wykorzystać tylko jeden
bufor, podczas transmisji pakietu UDP
rejestracja sygnału akustycznego nie by-
Transmisja danych przez UDP
Schemat transmisji danych z wyko-
rzystaniem protokołu UDP w systemie
Nut/OS przedstawiono na
rys. 8
. Jak
zwykle zaczynamy od utworzenia no-
wego gniazda sieciowego, tym razem
jednak będzie to gniazdo UDP – dla-
tego wywołujemy funkcję
NutUdp-
CreateSocket()
. Ponieważ protokół
UDP jest bezpołączeniowy, transmisję
danych możemy rozpocząć natychmiast
po zainicjalizowaniu
socketa
. Aby wy-
słać
datagram
UDP należy skorzystać
z funkcji
NutUdpSendTo().
Funkcja
NutUdpReceiveFrom()
oczekuje na
nadejście pakietu UDP, zapisuje jego
treść w podanym buforze oraz zwraca
adres i port hosta, który wysłał pakiet.
Po zakończeniu wymiany pakietów
powinniśmy zwolnić zasoby zajmowa-
ne przez gniazdo sieciowe za pomocą
funkcji
NutUdpDestroySocket().
Prosty przykład
Jak wspomniałem na wstępie, zbu-
dujemy dziś prosty „podsłuch” – apli-
kację rejestrująca sygnał z mikrofonu
dołączonego do wbudowanego w mikro-
kontroler przetwornika A/C i wysyłającą
go do komputera o wybranym przez
nas adresie. Najistotniejsze fragmenty
kodu źródłowego programu pokazano
na
list. 10
.
Główna funkcja programu
Rys. 8. Algorytm transmisji danych
z wykorzystaniem protokołu UDP
102
Elektronika Praktyczna 5/2007
KURS
łaby możliwa. Spowodowałoby to dość
denerwujące „przerywanie” nagranego
dźwięku.
Kolejną czynnością jest uruchomie-
nie przetwornika analogowo–cyfrowego
wbudowanego w mikrokontroler przez
ustawienie odpowiednich rejestrów ste-
rujących. Przetwornik pracuje w trybie
ciągłym (
free running
) próbkując sygnał
z wejścia ADC0 (pin PF0). Napięcie
odniesienia jest pobierane z pinu AVCC
procesora. Dzielnik zegara ADC równy
128 wymusza częstotliwość próbkowa-
nia (16 MHz/128/13 cykli zegara ADC
na próbkę) =ok. 9,6 kHz wystarczającą
do poprawnej rejestracji mowy.
Ostatnim etapem inicjalizacji jest
ustawienie wektora przerwania ADC,
wywoływanego za każdym razem gdy
przetwornik zarejestruje próbkę sy-
gnału. W systemie Nut/OS odpowiada
za to funkcja
NutRegisterIrqHan-
dler()
– nie jest zalecany tradycyj-
ny sposób obsługi przerwań stosowa-
ny w AVR–GCC. Wektorem przerwania
ADC jest w naszym przypadku funkcja
audio_record_irq()
. Odczytuje ona
przetworzoną próbkę z rejestru ADCW
i wpisuje ją do bufora o indeksie
ac-
tive_buf
. Gdy bufor zostanie całko-
wicie wypełniony, zmieniamy indeks
active_buf
tak, by wskazywał na
drugi bufor (
active_buf = 1–ac-
tive_buf
). Dzięki temu jest możliwa
ciągła rejestracja sygnału, a jeden z bu-
forów zawsze zawiera jego kompletny
fragment. Po zainicjowaniu wektora
przerwania ADC możemy uruchomić
próbkowanie ustawiając bit ADSC w re-
jestrze ADCSR.
Po inicjalizacji przechodzimy do
głównej, nieskończonej pętli programu.
Oczekuje ona na przetworzenie kolejnej
porcji sygnału przez przetwornik A/C –
gdy to nastąpi, zmieni się numer obec-
nie wypełnianego bufora (
active_buf
).
Wówczas kopiujemy zarejestrowany frag-
ment sygnału do bufora
tx_buffer
,
który posłuży do przygotowania zawar-
tości kolejnego
datagramu
UDP. Struktu-
ra pakietów wysyłanych przez program
jest bardzo prosta – składają się one
z 16–bitowego numeru pakietu (kolejne
liczby całkowite), służącego do wyzna-
czania liczby zgubionych
datagramów
i 256–próbkowego bloku danych. Kom-
pletny pakiet zajmuje zatem (2 bajty +
256 próbek * 2 bajty) = 514 bajtów.
Po zbudowaniu pakietu, wysyłamy go
do komputera tajnego agenta :) wywo-
łując funkcję
NutUdpSendTo().
Jego
adres IP i port można ustawić za po-
mocą makrodefinicji
AGENT_IP
oraz
AGENT_PORT.
List. 10. Prosty program ilustrujący sposób korzystania z protokołu UDP
// adres IP komputera tajnego agenta, do ktorego bedzie transmitowana nagrana
rozmowa
#define AGENT_IP „192.168.0.2”
// port komputera tajnego agenta, do ktorego bedzie transmitowana nagrana
rozmowa
#define AGENT_PORT 12666
// liczba probek w buforz dzwieku
#define BUF_SIZE 256
// 2 bufory dla nagrywanego dzwieku. W chwili, kiedy jeden jest wypelniany
przez wektor prerwania ADC
// drugi jest wysylany przez siec
static volatile short *buffers[2];
// bufor, do ktorego obecnie zapisuje ADC oraz bufor juz wypelniony (last_ac-
tive)
static volatile int active_buf = 0, last_active_buf = 0;
// pozostala liczba probek w aktywnym buforze i indeks bufora
static volatile int samples_remaining = BUF_SIZE, buf_pos = 0;
// bufor nadawczy
unsigned short *tx_buffer;
// wektor przerwania od ADC. wpisuje kolejna probke do aktywnego bufora
static void audio_record_irq(void *arg)
{
// odczytujemy probke z rejestru przetwornika A/C
volatile short val = inw (ADCW);
// wpisujemy ja do aktywnego bufora
(buffers[active_buf])[buf_pos] = val;
// aktualizujemy liczniki
buf_pos ++;
samples_remaining ––;
if(!samples_remaining) // koniec aktualnego bufora
{
active_buf = 1–active_buf; // zmieniamy bufor na drugi
samples_remaining = BUF_SIZE;// aktualizujemy liczniki
buf_pos = 0;
}
sbi(ADCSR, ADIE); // czyscimy flage przerwania
}
// glowna funkcja programu. Tworzy gniazdo UDP, inicjalizuje przetwornik A/C
i w nieskonczonej petli wysyla nagrane dane.
void inwigilacja()
{
int i;
// adres IP komputera docelowego
u_long agent_addr;
// nr obecnie przetwarzaniego pakietu
unsigned short packet_no = 0;
// struktura gniazda UDP
UDPSOCKET *sock;
printf(„Rozpoczynam inwigilacje...\n”);
// tworzymy gniazdo sieciowe UDP
sock = NutUdpCreateSocket(0);
active_buf = 0;
samples_remaining = BUF_SIZE;
// przetwarzamy adres odbiorcy ze stringa na liczbe 32–bitowa
agent_addr = inet_addr(AGENT_IP);
// alokujemy pamiec na bufory do nagrywania dzwieku
buffers[0] = NutHeapAlloc(BUF_SIZE * sizeof(short));
buffers[1] = NutHeapAlloc(BUF_SIZE * sizeof(short));
// ... i bufor przechowujacy aktualnie wysylany pakiet
tx_buffer = NutHeapAlloc((BUF_SIZE + 1)* sizeof(short));
// Inicjalizacja ADC
// wylaczamy konwersje ADC
cbi(ADCSR, ADSC);
// ustawienie zrodla napiecia odniesienia z AVCC
cbi(ADMUX, REFS1);
sbi(ADMUX, REFS0);
// ustawienie trybu pracy (konwersja ciagla)
// sbi(ADCSR, ADFR);
// wybor zrodla sygnalu (kanal ADC0, pin PF0)
outb(ADMUX, inb(ADMUX) & 0xf8);
Elektronika Praktyczna 5/2007
103
KURS
List. 10. c.d.
// ustawiamy wektor przerwania ADC.
NutRegisterIrqHandler(&sig_ADC, audio_record_irq, NULL);
sbi(ADCSR, ADFR);
// ustawiamy preskaler zegara ADC ( ADCCLK = MCLK / 128). Uzyskujemy w ten
sposob czestotliwosc probkowania ok. 9.6 kHz
sbi(ADCSR, ADPS2);
sbi(ADCSR, ADPS1);
sbi(ADCSR, ADPS0);
// wlaczamy ADC
sbi(ADCSR, ADEN);
// wlaczamy przerwanie ADC
sbi(ADCSR, ADIE);
// rozpoczynamy prace przetwornika
sbi(ADCSR, ADSC);
for(;;) {
// jesli jeden bufor wypelnil sie, kopiujemy jego zawartosc do bufora zawie-
rajacego tarnsmitowany pakiet
ethernutową aplikację pakietów UDP
i odtwarzanie ich treści za pomocą
karty dźwiękowej. Przebieg sygnału
akustycznego od mikrofonu do głośni-
ka ilustruje rys. 9. Ponieważ mikrofon
jest dołączony bezpośrednio do wej-
ścia przetwornika analogowo–cyfrowe-
go, konieczne jest usunięcie z sygnału
składowej stałej, realizowane przez
prosty filtr górnoprzepustowy.
Drugim, poważniejszym proble-
mem jest niestandardowa wartość czę-
stotliwości próbkowania przetwornika
A/C (9,6 kHz). Aby móc poprawnie
odtworzyć zarejestrowany dźwięk,
program wykonuje konwersję często-
tliwości próbkowania z 9,6 kHz do
22,050 kHz (standardowa częstotliwość
obsługiwana przez wszystkie karty).
Ze względu na małą moc obliczenio-
wą mikrokontrolera, zadania te (fil-
trację i konwersję) wykonuje program
na PC. Program powinien rozpocząć
odtwarzanie dźwięku natychmiast po
uruchomieniu Ethernuta.
Aplikacja PC–towa została napisa-
na w języku C++ z wykorzystaniem
następujących bibliotek:
− FLTK (
Fast Light Toolkit
) – interfejs
użytkownika,
− PortAudio – obsługa karty dźwię-
kowej,
− libsamplerate – konwerter częstotli-
wości próbkowania,
− pthreads – obsługa wątków.
Program pracuje pod kontrolą sys-
temów operacyjnych Linux (kompi-
lator GCC) oraz Windows (2000/XP,
kompilator Microsoft Visual C++).
Do wersji windowsowej zostały do-
łączone odpowiednie biblioteki DLL.
Źródła i binaria aplikacji dla Nut/OS
oraz programu PeCetowego opubliku-
jemy na płycie CD-EP6/2007B oraz na
stronie
http://wlostowski.ep.com.pl
.
Tomasz Włostowski, EP
tomasz.wlostowski@ep.com.pl
memcpy(tx_buffer+1, buffers[last_active_buf], sizeof(short) * BUF_SIZE);
while(last_active_buf == active_buf);
// dodajemy na poczatku pakietu jego numer (uzywany przez program PeCetowy
do obliczania ilosci zgubionych pakietow)
tx_buffer[0] = packet_no++;
// wysylamy pakiet do PC
NutUdpSendTo(sock, agent_addr, AGENT_PORT, tx_buffer, (BUF_SIZE + 1)* si-
zeof(short));
}
}
Aby cokolwiek nagrać, do prze-
twornika A/C należy dołączyć źródło
sygnału – w najprostszym przypadku
mikrofon elektretowy z rezystorem po-
laryzującym o wartości około 10
kV
(patrz
rys. 9
).
Oprogramowanie na PC
Do zbudowania naszego systemu
podsłuchowego jest potrzebny jeszcze
odbiornik – w tym przypadku prosty
program dla komputera PC. Jego za-
daniem jest odbiór wysyłanych przez
Rys. 9. Tor transmisji sygnału
Ważniejsze funkcje używane w przykładach
UDPSOCKET *NutUdpCreateSocket(u_short port);
Tworzy nowe gniazdo sieciowe protokołu UDP. Jeżeli gniazdo będzie służyć do odbierania pakietów,
parametr
port
określa numer lokalnego portu, z którego dane trafiają do gniazda. Na przykład
z gniazda
sock = NutUdpCreateSocket(4000)
będzie można odczytać wszystkie
pakiety UDP jakie trafią na port 4000. Funkcja zwraca wskaźnik do stuktury opisującej nowo
utworzonego
socketa
, albo NULL w razie niepowodzenia
int NutUdpDestroySocket(UDPSOCKET *sock);
Zwalnia zasoby zajmowane przez gniazdo
sock
.
int NutUdpSendTo(UDPSOCKET *sock, u_long addr, u_short port,
void *data, u_short len);
Wysyła za pomocą gniazda
sock
pakiet UDP o zawartości znajdującej się pod adresem
data
i długości
length
bajtów do portu
port
hosta o adresie IP
addr
.
int NutUdpReceiveFrom(UDPSOCKET *sock, u_long *addr, u_short
*port, void *data, u_short size, u_long timeout);
Oczekuje na nadejście pakietu UDP do gniazda
sock
. Po odebraniu pakietu jego dane zapisywane
są pod adresem
data
. Maksymalna długość odebranego bloku danych podawana jest przez
parametr
size
. Do wskaźników
addr
i
port
zapisywane są odpowiednio adres i port nadawcy
pakietu. Parametr
timeout
określa po jakim czasie (w milisekundach) funkcja przerywa działanie
jeśli nie odbierze żadnego pakietu. Funkcja zwraca rozmiar odebranych danych w bajtach, 0 – gdy
przekroczono czas oczekiwania na odbiór pakietu lub –1 – gdy wystąpił błąd.
int NutRegisterIrqHandler(IRQ_HANDLER irq, void (*)(void *)
handler, void *arg);
Ustawia adres funkcji
handler
(zwykła funkcja w C, bez atrybutów!) wywoływanej po wystąpie-
niu przerwania
irq.
Parametr
arg
jest przekazywany jako argument funkcji
handler
. Pełna
listę obsługiwanych przerwań można znaleźć w pliku nagłówkowym
dev/irqreq_avr.h.
Rys. 10. Okno aplikacji do odsłuchi-
wania sygnału rejestrowanego przez
Ethernuta
Projekty przedstawione w cyklu artykułów
o implementacjach Ethernuta uruchomiono na
zestawach składających się z płyty bazowej
ZL9AVR oraz modułów ZL1ETH i ZL7AVR.
104
Elektronika Praktyczna 5/2007
Plik z chomika:
XelosPL
Inne pliki z tego folderu:
Ethernet i AVR–y, cz.1.pdf
(532 KB)
Ethernet i AVR–y, cz.2.pdf
(886 KB)
Ethernet i AVR–y, cz.3.pdf
(509 KB)
Ethernet i AVR–y, cz.4.pdf
(988 KB)
Ethernet i AVR–y, cz.5.pdf
(814 KB)
Inne foldery tego chomika:
AVR_ASM
c_avr
java
kody
KURS C & C++
Zgłoś jeśli
naruszono regulamin