1

I want to make two Raspberry Pi send message to each other using ZigBee protocol. I have connected XBee S2C (ZigBee) module to Raspberry Pi using USB Explorer (CH430g). I had written a python script which will do the desired work,

import serial

ser = serial.Serial('/dev/ttyUSB0', 9600)

while True:
    incoming = ser.readline().strip()
    print ('%s' %incoming.decode())
    string = input("") + '\n'
    ser.write(string.encode())

But I need a C program to do the same, I looked into libserial library for C and C++, found that it's buggy and never compiled for me.

I tried this thread it works pretty well, but at the receiver side I need to keep read(fd, &buffer, sizeof(buffer)); in a while loop to continuously open for listening unlike a C socket program where read() function will halt till it receives the data just like my python script will wait in line incoming = ser.readline().strip() till it receives some message.

Is there any solution for it without using while loop ?

Edit 1:

In aforementioned python code, while loop is used to receive multiple messages. The line incoming = ser.readline().strip() will catch the message, process it and waits for next message since its in a while loop.

In C if my code is something like this:

while(1){
    str = read();
    //some processing
    }

it throws error since read is not halting till it gets the data, it's just returning read fail. Since the read data is NULL the post processing of the data will throw an error. To make it work I have introduce another while loop like this:

 while(1){
    while(1){
        str = read();
        if(str!=NULL)
            break;
        }
    //some processing
    }

I want to eliminate this extra loop and make read() to wait for the message. PS: I'm opening serial device like this: uart0_filestream = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);

Vinayaka S P
  • 177
  • 3
  • 13
  • Why is this question C++, if you ask specificly for C? – hellow Jul 31 '18 at 09:23
  • The `read()` in the mentioned thread is *blocking*, will put the process in sleep waiting for data, so yes, you can use it within a loop (careful with the `&buffer`, it is the bug, look at the answer too). May I ask why if the python script is working do you need it in C? – Alex Jul 31 '18 at 09:28
  • @hellow I prefer C, but solution is available in C++ the I'm fine with it so. – Vinayaka S P Jul 31 '18 at 09:40
  • @Alex Actually I don't want to use `read()` in a while loop since the code is just a part of other vast project. So I'm asking how I can avoid while loop or any library available for C/C++. As I said, its a part of a project, which should be in C/C++ I cant use python script in that. – Vinayaka S P Jul 31 '18 at 09:41
  • @VinayakaSP I see now, sorry, you can configure the serial as *non-blocking*, you can use `select()` to do some other stuff while waiting for data, but I don't see how you can get rid of a loop if you are expecting more data – Alex Jul 31 '18 at 09:44
  • @Alex could you please elaborate on `select()` I'm new to serial programming :( – Vinayaka S P Jul 31 '18 at 09:46
  • You can use [select()](http://man7.org/linux/man-pages/man2/select.2.html) to check if a number of file descriptors have received some data, read it only when you know there's data, but you still need a loop – Alex Jul 31 '18 at 09:48
  • @Alex oh. So it doesn't actually solve my issue. – Vinayaka S P Jul 31 '18 at 09:49
  • How do you expect your program to continue to read from the serial connection without a loop of some kind? – Chris Turner Jul 31 '18 at 10:03
  • @VinayakaSP, I don't see how you can get rid of a loop somewhere, but you can move the serial processing in another thread and let the main process continue its execution – Alex Jul 31 '18 at 10:05
  • @ChrisTurner @Alex I think I was not clear in question to explain my point. Suppose I have a receiver and sender, Sender might send data anytime, but receiver should listen to it always and grab the message whenever transmitted. Now refer to my python cod, There i used only one while loop, `incoming = ser.readline().strip()` this will halt program till it get message and after receiving message, it will process it and comes back to `incoming = ser.readline().strip()` for next message. To achieve this in C/C++ i have to use an extra while loop around `read()` function. i need to eliminate this – Vinayaka S P Aug 01 '18 at 03:32
  • @ChrisTurner I have appended edit 1 to my question, I have tried hard to explain the problem. Please go through it and comment your suggestions – Vinayaka S P Aug 01 '18 at 03:47

2 Answers2

0

Get some inspiration from this code. This is very generic canonical serial programming using C.

NOTE: Canonical input processing can also handle the erase, delete word, and reprint characters, translate CR to NL, etc..


I suggest you to read this article in order to know more about the serial programming settings, different mode.

HowTo serial programming.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>

/* baudrate settings are defined in <asm/termbits.h>, which is
included by <termios.h> */
#define BAUDRATE B38400            
/* change this definition for the correct port */
#define SERIAL_DEVICE "/dev/ttyS1"


#define FALSE 0
#define TRUE 1

volatile int STOP=FALSE; 

int main(void)
{
  int fd,c, res;
  struct termios oldtio,newtio;
  char buf[255];
/* 
  Open modem device for reading and writing and not as controlling tty
  because we don't want to get killed if linenoise sends CTRL-C.
*/
 fd = open(SERIAL_DEVICE, O_RDWR | O_NOCTTY ); 
 if (fd <0) {perror(SERIAL_DEVICE); exit(-1); }

 tcgetattr(fd,&oldtio); /* save current serial port settings */
 memset(&newtio, sizeof(newtio)); /* clear struct for new port settings */

/* 
  BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
  CRTSCTS : output hardware flow control (only used if the cable has
            all necessary lines. See sect. 7 of Serial-HOWTO)
  CS8     : 8n1 (8bit,no parity,1 stopbit)
  CLOCAL  : local connection, no modem contol
  CREAD   : enable receiving characters
*/
 newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

/*
  IGNPAR  : ignore bytes with parity errors
  ICRNL   : map CR to NL (otherwise a CR input on the other computer
            will not terminate input)
  otherwise make device raw (no other input processing)
*/
 newtio.c_iflag = IGNPAR | ICRNL;

/*
 Raw output.
*/
 newtio.c_oflag = 0;

/*
  ICANON  : enable canonical input
  disable all echo functionality, and don't send signals to calling program
*/
 newtio.c_lflag = ICANON;

/* 
  initialize all control characters 
  default values can be found in /usr/include/termios.h, and are given
  in the comments, but we don't need them here
*/
 newtio.c_cc[VINTR]    = 0;     /* Ctrl-c */ 
 newtio.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
 newtio.c_cc[VERASE]   = 0;     /* del */
 newtio.c_cc[VKILL]    = 0;     /* @ */
 newtio.c_cc[VEOF]     = 4;     /* Ctrl-d */
 newtio.c_cc[VTIME]    = 0;     /* inter-character timer unused */
 newtio.c_cc[VMIN]     = 1;     /* blocking read until 1 character arrives */
 newtio.c_cc[VSWTC]    = 0;     /* '\0' */
 newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */ 
 newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
 newtio.c_cc[VSUSP]    = 0;     /* Ctrl-z */
 newtio.c_cc[VEOL]     = 0;     /* '\0' */
 newtio.c_cc[VREPRINT] = 0;     /* Ctrl-r */
 newtio.c_cc[VDISCARD] = 0;     /* Ctrl-u */
 newtio.c_cc[VWERASE]  = 0;     /* Ctrl-w */
 newtio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
 newtio.c_cc[VEOL2]    = 0;     /* '\0' */

/* 
  now clean the modem line and activate the settings for the port
*/
 tcflush(fd, TCIFLUSH);
 tcsetattr(fd,TCSANOW,&newtio);

/*
  terminal settings done, now handle input
  In this example, inputting a 'z' at the beginning of a line will 
  exit the program.
*/
 while (STOP==FALSE) {     /* loop until we have a terminating condition */
 /* read blocks program execution until a line terminating character is 
    input, even if more than 255 chars are input. If the number
    of characters read is smaller than the number of chars available,
    subsequent reads will return the remaining chars. res will be set
    to the actual number of characters actually read */
    res = read(fd,buf,255); 
    buf[res]=0;             /* set end of string, so we can printf */
    printf(":%s:%d\n", buf, res);
    if (buf[0]=='z') STOP=TRUE;
 }
 /* restore the old port settings */
 tcsetattr(fd,TCSANOW,&oldtio);
 return 0;
}
danglingpointer
  • 4,708
  • 3
  • 24
  • 42
0

If you don't want the connection to be non-blocking, you probably want to remove the O_NDELAY option you've added to your call to open that turns on non-blocking. Just like this...

uart0_filestream = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);

Also, read doesn't return a string, it returns the number of bytes read in so your calls to read should look more like

bytecount = read(uart0_filestream, str, 20);
if(bytecount>0)
   {
   str[bytecount]='\0';
   }
else
   {
   // Something bad happened?
   }
Chris Turner
  • 8,082
  • 1
  • 14
  • 18