Search code examples
c++linuxserial-portxbee

Sending Hexadecimal to a xbee radio module


I'm currently trying to send hexadecimal command to a xbee radio module (API mode).

Here's my code :

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <iostream>

using namespace std;

int main()
{

   int fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);


   if (fd < 0 ){
       cout << "Error " << errno << " opening /dev/ttyUSB0: " << strerror(errno) << endl;
   }    
   else
   {
       struct termios tty;
       struct termios tty_old;
       memset (&tty, 0, sizeof tty);

       if (tcgetattr (fd, &tty) != 0){
           cout << "Error " << errno << " from tcgetattr: " << strerror (errno) << endl;
       }

       tty_old = tty;

       cfsetispeed(&tty, B57600);
       cfsetospeed(&tty, B57600);

       tty.c_cflag &= ~PARENB;
       tty.c_cflag &= ~CSTOPB;
       tty.c_cflag &= ~CSIZE;
       tty.c_cflag |= CS8;
       tty.c_cflag &= ~CRTSCTS;
       tty.c_lflag = 0;
       tty.c_oflag = 0;
       tty.c_cc[VMIN] = 1;
       tty.c_cc[VTIME] = 50;

       tty.c_cflag |= CREAD | CLOCAL;

       cfmakeraw(&tty);

       tcflush(fd, TCIFLUSH);

       if (tcsetattr(fd, TCSANOW, &tty) != 0) {
           cout << "Error " << errno << " from tcsetattr" << endl;
       }

       unsigned char cmd1[] = {"\0x7E\0x00\0x04\0x08\0x69\0x43\0x54\0xF7"};

       sleep(1);
       int wr1 = write(fd, cmd1, 8);
       sleep(1);

       int rd;
       int spot = 0;
       char buff = '\0';


       char resp[128];
       memset(resp, '\0', sizeof(resp));

       do {
           rd = read(fd, &buff, 1);
           sprintf(&resp[spot], "%c", buff);
           spot += rd;
       } while (buff != '\r' && rd > 0);
       if (rd<0){
           cout << "Error reading: " << strerror(errno) << endl;
       }
       else if (rd==0) {
           cout << "Read nothing!" << endl;
       }
       else {
           cout << "Read: " << resp << endl;
       }

       close(fd);

    }
    return 0;
}

I also tried like this :

    unsigned char cmd1[8];

    cmd1[0] = 0X7E;
    cmd1[1] = 0X00;
    cmd1[2] = 0X04;
    cmd1[3] = 0X08;
    cmd1[4] = 0X69;
    cmd1[5] = 0X43;
    cmd1[6] = 0X54;
    cmd1[7] = 0XF7;


    sleep(1);
    int wr1 = write(fd, cmd1, 8);

When I use AT command mode it works perfectly fine, here's the code that do exactly the same in AT command mode (ask the bee how long it lasts in command mode after "+++") :

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <iostream>

using namespace std;

int main()
{

int fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);


if (fd < 0 ){
    cout << "Error " << errno << " opening /dev/ttyUSB0: " << strerror(errno) << endl;
}   
else
{
    struct termios tty;
    struct termios tty_old;
    memset (&tty, 0, sizeof tty);

    if (tcgetattr (fd, &tty) != 0){
        cout << "Error " << errno << " from tcgetattr: " << strerror (errno) << endl;
    }

    tty_old = tty;

    cfsetispeed(&tty, B57600);
    cfsetospeed(&tty, B57600);

    tty.c_cflag &= ~PARENB;
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;
    tty.c_cflag &= ~CRTSCTS;
    tty.c_lflag = 0;
    tty.c_oflag = 0;
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 50;

    tty.c_cflag |= CREAD | CLOCAL;

    cfmakeraw(&tty);

    tcflush(fd, TCIFLUSH);

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        cout << "Error " << errno << " from tcsetattr" << endl;
    }

    unsigned char cmd1[] = {"+++"};
    sleep(1);
    int wr1 = write(fd, cmd1, sizeof(cmd1) -1);
    sleep(1);
    //printf("%d \n", wr1);
    unsigned char cmd2[] = {"ATCT\r"};

    int rd;
    int spot = 0;
    char buff = '\0';

    char resp[32];
    memset(resp, '\0', sizeof(resp));

    do {
        rd = read(fd, &buff, 1);
        sprintf(&resp[spot], "%c", buff);
        spot += rd;
    } while (buff != '\r' && rd > 0);
    if (rd<0){
        cout << "Error reading: " << strerror(errno) << endl;
    }
    else if (rd==0) {
        cout << "Read nothing!" << endl;
    }
    else {
        cout << "Read: " << resp << endl;
    }


    int wr2 = write(fd, cmd2, sizeof(cmd2) -1);
    //printf("%d \n", wr2);

    spot = 0;
    buff = '\0';


    sleep(1);
    do {
        rd = read(fd, &buff, 1);
        sprintf(&resp[spot], "%c", buff);
        spot += rd;
    } while (buff != '\r' && rd > 0);
    if (rd<0){
        cout << "Error reading: " << strerror(errno) << endl;
    }
    else if (rd==0) {
        cout << "Read nothing!" << endl;
    }
    else {
        cout << "Read: " << resp << endl;
    }
    close(fd);

    }
    return 0;
}

The error I get is

"Error reading: Resource temporarily unavailable".

I'm sure the device is not used by something else because the code that uses AT command works fine... It's as if the Xbee was not able to understand hexadecimal...

I hope that someone else already encountered this problem...


Solution

  • Ok so it was a bit adiot...

    I tried to read a answer that finihed with \r but the real answer don't finish with it...

    I change with :

    do {
        rd = read(fd, &buff, 1);
        sprintf(&resp[spot], "%c", buff);
        spot += rd;
    } while (buff != 0x13 && rd > 0);
    

    And now it works fine. It as just trying to read non-existent characters.

    EDIT :

    @VenushkaT asked me a question about some problems in this code. Since I did something that works prerry well now, I post my new code :

    void R1logger::listenPort()
    {
    // Creation of a buffer to store data from radio module
    
    fill_n(buff, 2048, '\0');
    
    this->ind = 0;
    
    while(this->fd > 0)
    {
        // Creation of a buffer that stores data from serial port
        char mes[1024];
        fill_n(mes, 1024, '0');
        // read is a blocking call so this function will not return until it effectively reads some data or if there is a problem
        int rd = read(this->fd, &mes, sizeof(mes));
    
        /*struct timeval tv;
        gettimeofday(&tv, NULL);
        unsigned long long check1 = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000;*/
        if (rd > 0)
        {
            storeInBuff(mes, rd);
            fill_n(mes, 1024, '0');
    
            // If some data are read, we can have only a part of the frame so we call "poll" to wait for the rest of the data with a 10ms timeout
            struct pollfd fds;
            fds.fd = this->fd;
            fds.events = POLLIN | POLLPRI;
            int slct = 1;
    
            while (slct > 0)
            {
                slct = poll(&fds, 1, 10);
                if (slct > 0)
                {
                    rd = read(this->fd, &mes, sizeof(mes));
                    if (rd > 0)
                    {
                        storeInBuff(mes, rd);
                    }
                    else
                    {
                        close(this->fd);
                        serialConfig();
                    }
                }
            }
    
            /*gettimeofday(&tv, NULL);
            unsigned long long check2 = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000;
            unsigned long tmps = check2 - check1;
            cout << "Temps de lecture : " << tmps << endl;*/
    
            /*gettimeofday(&tv, NULL);
            check1 = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000;*/
    
            // Once the message is entirely read, we extract the radio frames
            findFrame(0);
    
            /*gettimeofday(&tv, NULL);
            check2 = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000;
            tmps = check2 - check1;
            cout << "Temps calcul + ecriture sur disque : " << tmps << endl;*/
    
    
            this->ind = 0;
            fill_n(buff, 2048, '\0');
        }
        else
        {
            close(this->fd);
            serialConfig();
        }
    }
    }
    

    This code uses less CPu since there are blocking call (read, select), and there is no O_NONBLOCKING option set. I also consider the case where read fails.

    In another post, some people game me advice for serial configuration (there was some bugs on some options and they gave me advice to be more POSIX compliant so here it is :

    void R1logger::serialConfig()
    {
    
    // Open Serial Port
    
    this->fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);
    
    if (this->fd < 0 )
    {
        cout << "Error " << errno << " opening /dev/ttyUSB0: " << strerror(errno) << endl;
    }
    else
    {
        //Configure Serial Port
        struct termios tty;
    
        if (tcgetattr (this->fd, &tty) != 0)
        {
            cout << "Error " << errno << " from tcgetattr: " << strerror (errno) << endl;
        }
    
        cfsetispeed(&tty, B57600);
        cfsetospeed(&tty, B57600);
    
        tty.c_cflag &= ~PARENB;
        tty.c_cflag &= ~CSTOPB;
        tty.c_cflag &= ~CSIZE;
        tty.c_cflag |= CS8;
        tty.c_cc[VMIN] = 1;
        tty.c_cc[VTIME] = 50;
    
        tty.c_cflag |= CREAD | CLOCAL;
    
        cfmakeraw(&tty);
    
        tcflush(this->fd, TCIFLUSH);
    
        if (tcsetattr(this->fd, TCSANOW, &tty) != 0) 
        {
            cout << "Error " << errno << " from tcsetattr" << endl;
        }
    }
    }