1

(Edited 2/1/14 - Paired down code to be specific to this issue. Tested with smaller code. Edited this pose to show entire new code which still has the same issue.)

I'm working on a beagle bone black in the Angstrom Linux distribution kernel 3.8 using the POSIX serial guide.

I have a set of functions to handle serial communication defined in serial.c I want to use these as a library for other programs to use. I am declaring many of the common variables for these functions as static at the top of serial.c before the function definitions. My intent with this is that these will be like global variables but just for the functions in serial.c and also that they will keep their value between function calls. It does appear as if this working as I intended.

One of the functions is send_serial(). It does just what it says, sends data to the serial port. If I write a main() function inside of serial.c then and call it from inside the same file it is defined then send_serial() works as expected. As soon as the program hits the line with send_serial(), data is written to the serial port immediately. I can see the send and receive lights flash and I get back the expected data.

However when I try to use send_serial() from a different file it behaves oddly. It executes ok and it returns the correct number of bytes written and the program continues to the next line, except there is no actual serial port communication at all. Not until the program ends or I call close on the serial port, does is the data actually sent out on the serial port.

Why will it work fine from inside the same file, but it works incorrectly from a different file. I'm using the exact same functions to setup and open the serial port in both cases.

serial.h

#ifndef _PUP_SERIAL_   /* Include guard */
#define _PUP_SERIAL_ 

#define MAXBUF      1024

typedef struct byte_array {
    unsigned char byte[MAXBUF];
    int count;
} byte_array;

void set_serial_debug(int debug_value);
void close_serial();
int setup_serial_port(char * port_name);
int send_serial(byte_array message);
byte_array read_serial(); 
byte_array test_message();

#endif

serial.c

//#define _SERIAL_IS_MAIN_ // comment out this line for this file to be a library instead of main function

#include <stdio.h>   /* Standard input/output definitions */
#include <fcntl.h>   /* File control definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <sys/time.h>
#include "serial.h"


static int            debug;
static int            fd;
static struct termios options;


void print_bytes_as_hex(char * str, byte_array message)
{
    int n;
    int i;
    printf(str);
    if (message.count > 0)
        for (i = 0; i < message.count; i++)
        {
            n = (int) message.byte[i];
            printf("0x%.2x ", n);
        }
    else
        printf("-x--");
    printf("\n");
} // end of print bytes as hex


int setup_serial_port(char * port_name)
{
    fd = open(port_name, O_RDWR | O_NOCTTY | O_NDELAY);
    printf("Opened up port %s as number %d",port_name,fd);
    tcgetattr(fd, &options);

    if (port_name == "/dev/ttyUSB0")<--Fixed by changing to: if (strcmp(port_name,"/dev/ttyUSB0")==0)
        options.c_cflag &= ~CRTSCTS;  // set NO RTS control for USB
    else
        options.c_cflag |= CRTSCTS;  // set RTS control for ttyO#

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

    // set No parity (8N1):
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;

    options.c_iflag |= IGNBRK;
    options.c_iflag &= ~(IXON | IXOFF | IXANY); // No software handshake

    options.c_lflag = 0;
    options.c_oflag = 0;

    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Raw Input
    //options.c_lflag |= (ICANON | ECHO | ECHOE); // Canonical input

    options.c_cc[VMIN] = 1; // 1 means read dos not block
    options.c_cc[VTIME] = 1; // 1 means 0.1 seconds read timeout

    options.c_cflag |= (CLOCAL | CREAD); // Enable the receiver and set local mode

    /* Make raw */
    cfmakeraw(&options);

    /* Flush Port, then applies attributes */
    tcflush( fd, TCIFLUSH );
    fcntl(fd, F_SETFL, 0);
    tcsetattr(fd, TCSANOW, &options); // Set the new options for the port
    if (fd == -1)
    { //Could not open the port.
        perror("open_port: Unable to open");
        printf("Could not open port %s\n",port_name);
    }
    else // port is open
        printf("Port %s is open as # %d\n",port_name, fd);

    return fd;
} // End of setup_serial_port

void set_serial_debug(int debug_value)
{
    debug = debug_value;   
}

void close_serial()
{
    close(fd);
}

int send_serial(byte_array message)
{
    int n;
    printf("about to send to serial port...\n");
    n = write(fd, message.byte, message.count);
    printf("sent %d bytes to serial port %d\n",n,fd);
    return n;
}

byte_array read_serial()
{
    static byte_array message;
    int n;
    int i;

    printf("Attempting to read from serial port %d...\n",fd);
    for (i = 0; i < 8; i++)
        n = read(fd, &message.byte[i], 1);
    message.count = i;
    printf("read %d bytes from the serial port %d\n",message.count,fd);
    return message;
}

byte_array test_message()
{
    int i;
    unsigned char st[] = {0x02, 0x10, 0x27, 0x0D, 0x02, 0x01, 0xFB, 0x43, 0x56, 0x00, 0x00, 0x00, 0x00, 0xDC};
    byte_array return_message;

    for (i = 0; i < 14; i++)
        return_message.byte[i] = st[i];
    return_message.count = 14;
    return return_message;
}

#ifdef _SERIAL_IS_MAIN_
int main(int Count, char *Strings[])
{
    setup_serial_port("/dev/ttyUSB0");

    byte_array out_message = test_message();
    byte_array in_message;

    print_bytes_as_hex("sending: ",out_message);
    send_serial(out_message);
    in_message = read_serial();
    print_bytes_as_hex("recieved: ",in_message);

}
#endif

main.c

#include <stdio.h>
#include "serial.h"

byte_array in_message;
byte_array out_message;

int main(int Count, char *Strings[])
{
    setup_serial_port("/dev/ttyUSB0");

    byte_array out_message = test_message();
    byte_array in_message;

    print_bytes_as_hex("sending: ",out_message);
    send_serial(out_message);
    in_message = read_serial();
    print_bytes_as_hex("recieved: ",in_message);

}

If I call send_serial(message); from inside serial.c it works.

# gcc -o serial serial.c
# ./serial      
Opened up port /dev/ttyUSB0 as number 3Port /dev/ttyUSB0 is open as # 3
sending: 0x02 0x10 0x27 0x0d 0x02 0x01 0xfb 0x43 0x56 0x00 0x00 0x00 0x00 0xdc 
about to send to serial port... 
sent 14 bytes to serial port 3  <-- Send and Receive Lights blink
Attempting to read from serial port 3...
read 8 bytes from the serial port 3
recieved: 0x02 0x10 0x27 0x07 0x81 0x02 0x0e 0xbc

If I call send_serial(message); from outside of serial.c it does not send until the program ends.

(edit serial.c to comment out //#define _SERIAL_IS_MAIN_)
# gcc -o main main.c serial.c
# ./main
Opened up port /dev/ttyUSB0 as number 3Port /dev/ttyUSB0 is open as # 3
sending: 0x02 0x10 0x27 0x0d 0x02 0x01 0xfb 0x43 0x56 0x00 0x00 0x00 0x00 0xdc 
about to send to serial port...          
sent 14 bytes to serial port 3           <-- comm lights do not blink at all
Attempting to read from serial port 3... <-- Program waits here for ever until CTRL^C
^C                                       <-- Once I press CTRL^C snd and rec comm lights blink

When I run this program from main.c, it waits at the read_serial() function because it is waiting for a reply from my device. The device is not sending back a reply, because my program never sent the message to the serial port. It said it sent it, but it did not. Once I press CTRL^C, the program (or maybe os) sends the message, the device receives the message and it responds back to me. However I never get the message because the program is no longer running.

Timothy Vann
  • 2,537
  • 2
  • 20
  • 23
  • Suspect that outside of serial.c different buffering/handshaking is employed. – chux - Reinstate Monica Feb 01 '14 at 04:04
  • 1
    There's too much missing here to tell what is going on - include the serial port opening and configuration, the call from another file, and whatever you are doing to get the prototype and declarations for these functions available in the other file. Are you writing exactly the same data in both cases? What does the output of strace look like in each case? – Chris Stratton Feb 01 '14 at 04:35
  • I'm using the same serial port setup routine in both cases. I've reduced the code so that I could narrow it down to just this issue, then I've posted the entire code here. – Timothy Vann Feb 02 '14 at 03:08

1 Answers1

2

I finally figured out what the issue was. Chux was correct. Run as a stand alone program serial.c was NOT using RTS control. Run as part of main.c it was using RTS control. Since I have it connected to the USB port it should not be using RTS. The cause of the confusion was this line in setup_serial_port(char * port_name):

if (port_name == "/dev/ttyUSB0")

where port_name is a char *. I'm not sure why this worked (returned true) inside serial.c but it returned false inside main.c. When I changed it to:

if (strcmp(port_name,"/dev/ttyUSB0")==0)

The program works as expected in both cases.

Can anyone explain why if (port_name == "/dev/ttyUSB0") returned true inside serial.c? (The port name is /dev/ttyUSB0 but after investigating this issue, I see that you can't compare strings with ==. So why does it return true in one case an not the other?

Timothy Vann
  • 2,537
  • 2
  • 20
  • 23
  • Sometimes C compilers, when the exact same literal string is mentioned two or more times in one ".c" file, store it only once in RAM -- so all the pointers to those strings end up pointing to the same address. Nearly all C compilers, when the exact same literal string is mentioned in two different ".c" files, store that string twice in RAM -- so pointers to those strings end up pointing to different addresses. See http://stackoverflow.com/questions/4843640/why-is-a-a-in-c , http://stackoverflow.com/questions/8222495/how-to-compare-strings-in-an-if-statement for details. – David Cary Jun 23 '14 at 03:19