XBee (MaxStream; Digi International) - EEE 802.15.4

XBee to modem bezprzewodowy z interfejsem szeregowym. Pozwala przesyłać dane z prędkościami od 1200 do 230400 bitów na sekundę w paśmie 2.4 Ghz. Prostota implementacji gotowego modułu okupiona jest jego ceną, ale za to komunikacja i podłączenie fizyczne jest uproszczone do maksimum. Moduły pracują w standardzie IEEE 802.15.4, który to opisuje bezprzewodowe sieci osobiste o niskiej przepustowości.

Moduły Xbee XB24, które widać na poniższych zdjęciach, występują w odmianach zwykłej i PRO. W sprzedaży znajduje się wiele układów różniących się: rodzajem anteny, wersją modelu, zasięgiem, prędkością, itp.

 Płytki developerskie zapewniające interface RS232 i USB, oraz moduły XBee XB24.

 Płytka z interface USB: dane i zasilanie jednym przewodem. Widać diody sygnalizacyjne i przyciski, które można wykorzystać przy testach konfiguracji.

Płytka z interface RS232 i gniazdem zasilania.


Moduły występują z różnymi antenami, a co za tym idzie różnią się też zasięgiem:
 Płytka z anteną "chip antenna" - nie wiem czy istnieje jakieś dobre polskie tłumaczenie.

Antena z kawałka drutu, zapewne o długości zbliżonej do 31,2mm bo tyle wynosi 1/4 fali 2.4GHz.

Moduł ze złączem U.FL.

 ... i odpowiednia antena do modułu ze zdjęcia powyżej.

By uniknąć zwarcia warto zamocować płytki z modułami. Jeden moduł zasilam z USB, a drugi z akumulatorka. 


Moduły trzeba podłączyć pod firmowy program XCTU (X-CTU) by sprawdzić, czy wszystkie moduły posiadają jednakową, najlepiej najnowszą, wersję firmware. Wygląd starszej wersji programu był dla mnie czytelniejszy.

Wybór urządzenia XBee podłączonego do komputera.

Gdybym odzyskiwał komunikację z modułem to wybrał bym dla posiadanych modułów następująca konfigurację.

W modułach mam skonfigurowaną większą prędkość transmisji i taką też ustawiam. 

Część konfiguracji modułu XBee. 

Sprawdzam, czy każdy moduł ma identyczną wersję firmware. 

 Jeśli jakiś moduł - jak na zdjęciu powyżej - ma inną wersję to go uaktualniam.

 Proces uaktualnienia firmware. Z zasady przeprowadzam taką operację na laptopie lub na komputerze z UPS (jeden z modułów zasilam w tym wypadku z USB, a drugi z akumulatorka)


 Przykładowe ustawienia minicom ( xminicom -oD /dev/ttyUSB1)
.
Przykładowe ustawienia minicom, cdn.

Moduły mogą pracować w dwóch topologiach sieci IEEE 802.15.4: 
1) Gwiaździstej (mesh). Komunikacja odbywa się przez koordynatora, a sieci pracujące na danym obszarze są niezależne od siebie, dzięki stosowaniu unikalnych identyfikatorów sieci.
2) Partnerskiej (multipoint). Komunikacja może odbywać się bezpośrednio pomiędzy urządzeniami, z pominięciem koordynatora. Można budować skomplikowane sieci, a komunikaty przekazywać do urządzeń znajdujących się poza zasięgiem nadawcy, z wykorzystaniem innych urządzeń (realizuje się to w warstwie sieci, czyli poza 802.15.4)

Komunikację z modułami można zrealizować również na dwa sposoby:
1) Z użyciem trybu transparentnego (tryb domyślny).
2) Z użyciem trybu API.
Można zdalnie przekazywać zmianę stanu wejść i ADC. 


***


Poniżej zamieszczam konfigurację trybu transparentnego, multipoint, z uproszczonym adresowaniem i przekazywaniem danych pomiędzy portami UART. Urządzenia można skonfigurować np. takimi komendami (w dokumentacji XBee te opcje są dokładnie opisane):

Urządzenie 1:
ce=0
a1=0
id=100
ch=b
my=1
dh=0
dl=2

Urządzenie 2:
ce=0
a1=0
id=100
ch=b
my=2
dh=0
dl=1
I to wszystko :-)


***


Poniżej publikuję ważniejsze fragmenty aplikacji realizującej wyszukiwanie innych stacji i odczyt stanu portów wejściowych i ustawianie stanu portów wyjściowych (programy pod FreeBSD i Windows). Nie umieszczę całego kodu, ponieważ te moduły wykorzystywane są na niektórych uczelniach w celach dydaktycznych. By uszanować wykładowców podaję wędkę, nie rybę :-) Przestrzegam też przed bezmyślnym kopiowaniem - wykładowcy, przynajmniej znani mi, zorientują się natychmiast :-)
Zachęcam do dokładnego zapoznania się z dokumentacją układów , która jest wykonana na wysokim poziomie.

Wersja FreeBSD:
#include <fcntl.h>
#include <termios.h>
#include <iostream>


void czekaj(const int sekundy, const int setne);
void openPort(int &portCom, struct termios &optCom, const char *portx);
void ustawPort(const int &portCom, struct termios &optCom, const int ustawCzas);
void wewy_ppp(const int &portCom);
void wewy(const int &portCom, const char *komenda, const int kdl, char *wynik, const int wdl);
void wewy_nd(const int &portCom, char *wyborDL);
bool wewy_przycisk(const int &portCom);
bool wewy_wyswietl(const int &portCom, const bool test);



int main(int argc, char *argv[])
{
int portCom,
licznik;
bool przycisk = false,
dioda = false,
dioda_znacznik = false;
char wyborDL[22] = {0};
struct termios optCom;
const int   kCzas  = 2,
   kCzas2 = 5,
   dCzas  = 20;

char  k_OK[] = {"OK"};
char  k_CH[] = {"ATCH=F\r"}; //kanal radiowy
char  k_DH[] = {"ATDH=0\r"}; //ustawia: Destination Address High
char  k_CN[] = {"ATCN\r"}; //koniec trybu command
char  k_CT[] = {"ATCT=0x64\r"}; //timeout dla trybu command
char  k_ID[] = {"ATID=3332\r"}; //ustawia: ID (Pan ID)
char  k_ATDL[] = {"ATDL "};
char  k_r[] = {"\r"};

char  port0[] = {"/dev/cuaU0"};
char  port1[] = {"/dev/cuaU1"};

char  k_NI[17] = {0};
char  k_MY[17] = {0};
char  k_DL[17] = {0};
char  portX[15] = {0};

char  k_NI_1[] = {"ATNI Stacja1\r"};
char  k_MY_1[] = {"ATMY=111\r"};

char  k_NI_2[] = {"ATNI Stacja2\r"};
char  k_MY_2[] = {"ATMY=222\r"};

    if(argc==1)
    {
printf("Nie podano parametrow w wywolaniu programu.\n");
return(129);
    };

    if(argc>2)
    {
printf("Za duzo parametrow, maksymalnie podaj jeden parametr (np.: -h)\n");
return(130);
    };

    switch (*argv[1])
    {
case 'v':
   printf("Versja 1.9\n");
   return(131);
case 'h':
   printf("Poprawne wywolanie programu: zad01 <numer portu 0 lub 1>\n");
   return(132);
case '0':
   strcpy(k_NI, k_NI_1);
   strcpy(k_MY, k_MY_1);
   strcpy(portX, port0);
   break;
case '1':
   strcpy(k_NI, k_NI_2);
   strcpy(k_MY, k_MY_2);
   strcpy(portX, port1);
   break;
    };

    srand(time(0));
    openPort(portCom, optCom, portX);
    // konfiguracja XBee   
    ustawPort(portCom, optCom, kCzas);
    wewy_ppp(portCom);
    wewy(portCom, k_CH, sizeof(k_CH), k_OK, sizeof(k_OK)); //ATCH
    wewy(portCom, k_NI, sizeof(k_NI), k_OK, sizeof(k_OK)); //ATNI
    wewy(portCom, k_MY, sizeof(k_MY), k_OK, sizeof(k_OK)); //ATMY
    wewy(portCom, k_ID, sizeof(k_ID), k_OK, sizeof(k_OK)); //ATID
    wewy(portCom, k_DH, sizeof(k_DH), k_OK, sizeof(k_OK)); //ATDH
   
    wewy(portCom, k_CT, sizeof(k_CT), k_OK, sizeof(k_OK)); //ATCT
    wewy(portCom, k_CN, sizeof(k_CN), k_OK, sizeof(k_OK)); //ATCN
   
    wewy_ppp(portCom);

    ustawPort(portCom, optCom, dCzas);
    
    strcpy(k_DL, k_ATDL); 
    strcat(k_DL,wyborDL);
    strcat(k_DL, k_r);

    // ustawiam adres DL
    ustawPort(portCom, optCom, kCzas);
    wewy_ppp(portCom);
    wewy(portCom, k_DL, sizeof(k_DL), k_OK, sizeof(k_OK)); //DL

    ustawPort(portCom, optCom, kCzas2);

    for (licznik=0; licznik<21; licznik++)
//    while (true)
    {
wewy(portCom, k_CN, sizeof(k_CN), k_OK, sizeof(k_OK)); //CN
// sprawdza czy przyszla komenda wyswietl
dioda = wewy_wyswietl(portCom, przycisk); 
wewy_ppp(portCom);
if (dioda)
{
    
   dioda_znacznik = true;
}
else
{
   if (dioda_znacznik)
   {
dioda_znacznik = false;
   };
};

// sprawdza nacisniecie przycisku
przycisk = wewy_przycisk(portCom); 
    };
    
    close(portCom);
    printf("Koniec programu.\n");
};




void czekaj(const int sekundy, const int setne)
// jednostka = 10ms
{
timespec    czas,
   czasPozostaly;

    czas.tv_sec = sekundy;
    czas.tv_nsec = (long)10000000 * setne;

    nanosleep(&czas,&czasPozostaly);
};




void openPort(int &portCom, struct termios &optCom, const char *portx)
{
    portCom = open(portx, O_RDWR | O_NOCTTY | O_NDELAY);
    if (portCom < 0) {perror(portx); return; }
    fcntl(portCom, F_SETFL, 0);

    tcgetattr(portCom, &optCom);

    cfsetispeed(&optCom, B9600);
    cfsetospeed(&optCom, B9600);

    optCom.c_cflag |= (CLOCAL | CREAD);
    optCom.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    optCom.c_oflag &= ~OPOST;

    tcsetattr(portCom, TCSANOW, &optCom);
};




void ustawPort(const int &portCom, struct termios &optCom, const int ustawCzas)
{
    optCom.c_cc[VMIN]  = 0;
    optCom.c_cc[VTIME] = ustawCzas;
    tcsetattr(portCom, TCSANOW, &optCom);
};




void wewy_ppp(const int &portCom)
{
char bufor[255]; 
char *bufptr;    
int   rbytes;       
int   proba;       

    czekaj(1,20);
    for (proba = 0; proba < 12; proba++)
    {
if (proba > 0)
{
   if (proba>2)
   {
czekaj(1,0);
   };
}
else
{
   printf("Wysylam do modemu: +++\n");
};

write(portCom, "+++", 3);
czekaj(1,20);
write(portCom, "AT\r", 3);

bufptr = bufor;

while ( (rbytes = read(portCom, bufptr, bufor + sizeof(bufor) - bufptr - 1)) > 0 )
{
   bufptr += rbytes;
   if (bufptr[-1] == '\r')
break;
};

if ( strncmp(bufor, "OK", 2) == 0 )
{
   bufor[3]='\0';
    
   return;
};
    }

    printf("Koncze program, nie moge wyslac: '+++'.\n");   
    abort();
};




void wewy(const int &portCom, const char *komenda, const int kdl, char *wynik, const int wdl)
{
char bufor[255],  
    *bufptr;     
int  rbytes,
    proba;       

    for (proba = 0; proba < 4; proba ++)
    {
if (proba > 0)
{

}
else
{
    
};

if (write(portCom, komenda, kdl) < kdl)
   continue;

bufptr = bufor;

while ( (rbytes = read(portCom, bufptr, bufor + sizeof(bufor) - bufptr - 1)) > 0 )
{
   bufptr += rbytes;
   if (bufptr[-1] == '\r')
break;
};

*bufptr = '\0';
wynik[wdl] = '\0';       

if ( strncmp(bufor, wynik, wdl-1) == 0 )
{
   
   return;
};

wewy_ppp(portCom);
    };
};




void wewy_nd(const int &portCom, char *wyborDL)
{
char bufor[255]={0},
  *bufptr,      
  *spis[20][5];
int   rbytes,    
  nrWpisu=0,
  nrPola=0,
wybor,
znacznik0D=0,
  licz;

    for (int i=0; i<20; i++)
    {
for (int j=0; j<5; j++)
{
   spis[i][j] = new char[22];        
};
    };

    for (licz=0; licz<7; licz++)
    {
bufptr = bufor;
while ( (rbytes = read(portCom, bufptr, 1)) > 0 )
{
   if ( (*bufptr==10) and (znacznik0D>0) )
   {
printf("======\n");
znacznik0D++;
   }
   else
   {
    if (*bufptr==10)
{
   znacznik0D++;
   if (bufptr != bufor+1)
   {
*bufptr = '\0';
printf("-- %s\n", bufor);

strcpy(spis[nrWpisu][nrPola], bufor);
bufptr = bufor;
if (nrPola==4)
{
   nrPola=0;
   if (nrWpisu==19)
   {
printf("Przekroczono zakres tablicy.\n");
abort();
   }
   else
   {
nrWpisu++;
   };
}
else
{    
   nrPola++;
};
   };
}
else
{
   znacznik0D=0;
   bufptr++;
};
   };
};
if (nrWpisu>0)
{
   break;
}
else
{
   
   czekaj(1,0);
};
    };

    if(nrWpisu==0)
    {
printf("Koncze dzialanie programu.\n\n");
abort();
    };

  
    for (int i=0; i<nrWpisu; i++)
    {
printf(" nr. %i: %s NI=%s\n",i+1, spis[i][4],spis[i][0]);
    };

    do
    {
if ( !scanf("%d",&wybor) )
{
   std::cin.clear();
   std::cin.ignore(10, '\n');
};
    }
    while ( wybor<1 or wybor>nrWpisu);
//    while ( !(wybor>0 and wybor<nrWpisu+1));
    
    strcpy(wyborDL, spis[wybor-1][0]);
    
   

    for (int i=0; i<20; i++)
    {
for (int j=0; j<5; j++)
{
   delete spis[i][j];
};
    };
};




bool wewy_przycisk(const int &portCom)
{
char bufor[255],
     *bufptr;     
int  rbytes,
     proba;       

    for (proba = 0; proba < 4; proba ++)
    {
if (proba > 0)
{
    
}
else
{
   
};

if (write(portCom, "ATIS\r", 5) < 5)
   continue;

bufptr = bufor;
while ( (rbytes = read(portCom, bufptr, bufor + sizeof(bufor) - bufptr - 1)) > 0 )
{
   bufptr += rbytes;
};

*bufptr = '\0';
if ( (bufor[6]=='0') and (bufor[7]=='0') and (bufor[8]=='0') )
{
   return(true);
}
wewy_ppp(portCom);
    };
    return(false);
};




bool wewy_wyswietl(const int &portCom, const bool test)
{
char bufor[255],
        *bufptr,
komunikat[] = {'*','G','R','1','*'};     
int rbytes,
komunikatDl = 5,
licznik;    

    if (test) 
    {
for (licznik=0; licznik<8; licznik++)
{    
   write(portCom, komunikat, komunikatDl);
   czekaj(0,25);
};
    };

    bufptr = bufor;

    while ( (rbytes = read(portCom, bufptr, bufor + sizeof(bufor) - bufptr - 1)) > 0 )
    {
bufptr += rbytes;
    };

    if (test) 
    {
   write(portCom, komunikat, komunikatDl);
    };

    *bufptr = '\0';

    if ( strncmp(bufor, komunikat, komunikatDl) == 0 )
    {



if (test) 
{
   write(portCom, komunikat, komunikatDl);
};

for (licznik=0; licznik<3; licznik++)
{    
   if (test) 
   {
czekaj(0,50);
write(portCom, komunikat, komunikatDl);
   };
};

return (true);
    };
    
    czekaj(0,(rand()%25));

    if (test) 
    {
   write(portCom, komunikat, komunikatDl);
    };

    return (false);
};


//end



Wersja Windows:

#include <windows.h>
//#include "stdio.h"
#include "string.h"
//#include <stdio.h>
//#include <iostream>
//#include <iomanip>
//#include <list>
//#include <time.h>
//#include <windows.h>
//#include <conio.h>
//#include <stdlib.h> 
//#include <cstdio>
//#include <ctime>
//#include <iostream> 
//#include <stdio.h>



char * k_OK = {"OK"};

char * k_ppp = {"+++"};
char * k_ppp_spr = {"OK"};

char * k_ATCT = {"ATCT=64"};
char * k_ATCT_spr = {"ATCT=64"};


//NAZWA
char * k_ATNI_1 = {"ATNI Unit1"};
char * k_ATNI_spr_1 = {"ATNI"};
char * k_ATNI_2 = {"ATNI Unit2"};
char * k_ATNI_spr_2 = {"ATNI"};


//KANAL
char * k_ATCH = {'A','T','C','H','=','B',13,0};
char * k_ATCH_spr = {"ATCH,",13};

char * k_ATDH = {"ATDH=0"};
char * k_ATDH_spr = {"ATDH"};


char * k_ATMY_1 = {"ATMY=1357"};
char * k_ATMY_spr_1 = {"ATMY"};

char * k_ATDL_1 = {"ATDL=2468"};
char * k_ATDL_spr_1 = {"ATDL"};


char * k_ATMY_2 = {"ATMY=2468"};
char * k_ATMY_spr_2 = {"ATMY"};

char * k_ATDL_2 = {"ATDL=1357"};
char * k_ATDL_spr_2 = {"ATDL"};



//KONIEC
char * k_ATCN = {"ATCN"};
char * k_ATCN_spr = {""};



HANDLE portCOM;    
DCB dcb;        
BOOL fSuccess;  
BYTE RS_buf;    
DWORD RS_ile;   
COMMTIMEOUTS ustawCOM={0, 0, 0, 0, 0};

int licznik_komunikatow = 0;
const byte czekaj_1   = 1;
const byte czekaj_10  = 10;
const byte czekaj_100 = 100;


void czekaj(byte czekaj)
// jednostka = 10ms
{
int i;
for (i = 0; i < czekaj; i++)
{
Sleep(10);
}
}


void rs_otworz_urzadzenie()
{
//port COM1 z prawami RW
portCOM = CreateFile( TEXT("COM1"), GENERIC_READ | GENERIC_WRITE,
0,    // exclusive access
NULL, // default security attributes
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (portCOM == INVALID_HANDLE_VALUE)
{
printf("Open - blad %d.\n", GetLastError());
}
}


int rs_ustaw_parametry()
{
int blad;
blad = GetCommState(portCOM, &dcb);
if (!blad)
{
printf ("GetCommState - blad %d.\n", GetLastError());
return 2;
}

dcb.BaudRate = CBR_9600;     
dcb.ByteSize = 8;             
dcb.Parity = NOPARITY;        
dcb.StopBits = ONESTOPBIT;    

blad = SetCommState(portCOM, &dcb);
if (!blad)
{
printf ("SetCommState - blad %d.\n", GetLastError());
return 3;
}
}


int rs_znak_pisz(char znak)
{
int blad;
RS_buf=znak;
printf("%c",znak);
ustawCOM.ReadTotalTimeoutConstant=0;
    blad = WriteFile( portCOM, &RS_buf, 1, &RS_ile, 0);
    if (!blad)
    {
    
      return 4;
    }
}


char rs_znak_czytaj()
{

}



void pisz_raw(char *wyslij, char *odczytaj, byte czekaj_przed, byte czekaj_pomiedzy, byte czekaj_po, byte ilosc_powt)
// string do wyslania, 
// string, ktory ma zwrocic urzadzenie
// czekaj przed wyslaniem w ms
// czekaj pomiedzy powtorzeniami wyslania w ms
// czekaj po wyslaniu w ms
// ilosc prob powtorzen operacji
{
int i;

//czekaj(czekaj_przed);

int len = strlen(wyslij);
for (i = 0; i < len; i++) 
{
//printf("%c",wyslij[i]);
rs_znak_pisz(wyslij[i]);

}


czekaj(czekaj_po);
}


void pisz(char *wyslij, char *odczytaj, byte czekaj_przed, byte czekaj_pomiedzy, byte czekaj_po, byte ilosc_powt)
{
}



char czytaj(int czekaj_max)
//jak dlugo ma czekac na znaki
{
int blad;
int testuj=1;

   while(testuj!=0)
   {         
czekaj(10);
  testuj--;

ustawCOM.ReadTotalTimeoutConstant=1100;

SetCommTimeouts(portCOM,&ustawCOM);
 
       
 
     
        //if(RS_ile==1)
       // {

          //  if(RS_buf == )
           // {
               
           //     testuj=0;
           //     break;
          //  };
//            printf("%c", RS_buf);
            printf("%s", &RS_buf);
FlushFileBuffers(portCOM);

      //  }
   }

}


void xbee_ustaw_parametry_stanowisko_1(void)
{
}


void xbee_ustaw_parametry_stanowisko_2(void)
{
}


void xbee_ustaw_parametry(void)
{
    xbee_ustaw_parametry_stanowisko_1();
}


int main()
{
    rs_otworz_urzadzenie();
    rs_ustaw_parametry();

//+++

   czekaj(110);
   czytaj(1);
   czekaj(5);
printf ("\n");

printf ("\n");
 czytaj(1);
   czekaj(5);

/*
//ATCH=B
pisz_raw(k_ATCH, k_ATCH_spr, 110, 120, 10, 100);
czytaj(1);
czekaj(10);
printf ("\n");
*/
/*
pisz_raw(k_ATCH_spr, k_ATCH_spr, 110, 120, 10, 100);
czytaj(1);
czekaj(10);
printf ("\n");
*/


//pisz_raw(k_ENTER, k_ENTER, 110, 120, 10, 100);
//pisz_raw(k_ATCH_spr, k_ATCH_spr, 110, 120, 10, 100);
//   czytaj(1);
//   czekaj(10);
//ATNI Unit1
//pisz_raw(k_ATNI_1, k_ATNI_spr_1, 110, 120, 10, 100);
//   czytaj(1);

//ATCH=B
//pisz_raw(k_ATCH, k_ATCH_spr, 110, 120, 10, 100);
//   czytaj(1);

//ATDH=0
//pisz_raw(k_ATDH, k_ATDH_spr, 110, 120, 10, 100);
//   czytaj(1);

//ATMY=1357
//pisz_raw(k_ATMY_1, k_ATMY_spr_1, 110, 120, 10, 100);
//   czytaj(1);

//ATDL=2468
//pisz_raw(k_ATDL_1, k_ATDL_spr_1, 110, 120, 10, 100);
//   czytaj(1);

printf ("\n");
system("pause");
return (0);   
}

//END


***


Użyłem adapterów:
- Z interface RS232: XBIB-R-DEV
- Z interface USB: XBIB-U-DEV



Brak komentarzy:

Prześlij komentarz