0

I want to connect/bridge two serial ports in C. I have 2 threads reading the ports, and writing to the other port. Here is one example:

void *rfid_to_uart_thread(void *) {
char rfid_read_buffer[100];
int writeCounter;
do {
    writeCounter = read(rfidCom, rfid_read_buffer, sizeof(rfid_read_buffer)-1);
    if (writeCounter > 0) {
        write(uartCom, rfid_read_buffer, writeCounter);
    } else
        usleep(25);



} while (!bKillBridgeThreads);

return NULL;}

The problem is, it seems that the writes are too slow. I often receive only half of the String on the other side. It seems like the write is asynchronously and thus the buffer is overwritten again in the next loop and overwrites the last 'write', so that the data is crippled?! Is that right?

The ports are opened NON_BLOCKING and RW, Baudrate is and has to be 9600.

Tobi
  • 168
  • 1
  • 12
  • So, you have 2 COM ports with 2 threads, 1 port for thread. The thread writes to it's COM port or to a common one? – Raskayu Aug 10 '16 at 06:56
  • The code you given is single thread in which you are trying to read from one port(rfidcom) and write to other port(uartCom). So Inside the thread the code is sequential. so no way write is asynchronous. Write will get execute once read is done reading from rfid device(100 bytes or less than that). Is its the case that the buffer size what is given to read is small? – Sathish V Aug 10 '16 at 07:10
  • Sorry, maybe the question was misleading. It's ok for 'write' to write only what was received in read. At the UART side I i.e. have a TeraTerm running watching for the output of the RFID module, that has to be send over to the UART. Sometimes I receive via UART "2FEF0000" and the the next time I get "2FEF00001000\r". So maybe the problem is more like write is not writing everything at it is buffered? Is there a way to force write all characters still in the buffer? – Tobi Aug 10 '16 at 07:15
  • So you have a teraterm listening for RFID output, and your own application , that should Sent it to uartCom, is that right? – Sathish V Aug 10 '16 at 07:20
  • when you say "I receive via UART "2FEF0000"" through what you seeing the output of the UARTCom? – Sathish V Aug 10 '16 at 07:25
  • As stated into my answer you should loop receiving 1 char per time until `'\r'` char is received. So you can send data via UART. – LPs Aug 10 '16 at 07:37
  • That doesn't work sadly, because there also is a firmware write mode without \r's. – Tobi Aug 10 '16 at 07:58
  • If you used non-canonical blocking I/O, then you would not have to worry about adding a delay and/or draining the buffers. See http://stackoverflow.com/questions/25996171/linux-blocking-vs-non-blocking-serial-read/26006680#26006680 – sawdust Aug 10 '16 at 08:53

2 Answers2

0

Looking at the man:

read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.

In other wordsread does not grants to return all bytes send by other task, can give you a single byte up to sizeof(rfid_read_buffer)-1

What you can do is:

  1. loop reading from rfidCom until the number of chars matches the number of chars sent.
  2. You can use a specific terminator of messages and check for it to validate received message
  3. encapsulate chars into a protocol message with an header that embed the message length, so the receiver can count the received chars and stop reading when last char is received.

For example:

void *rfid_to_uart_thread(void *)
{
    char rfid_read_buffer[100] = {0};

    int writeCounter;
    char RXchar;
    ssize_t retVal;
    bool send = false;

    do
    {
        memset(rfid_read_buffer, 0x00, sizeof(rfid_read_buffer));
        send = true;

        do
        {
            retVal = read(rfidCom, &RXchar, 1);

            if (retVal > 0)
            {
                rfid_read_buffer[writeCounter] = RXchar;

                writeCounter++;
            }
            else if (retVal < 0)
            {
                send = false;
                RXchar = '\r'
                break;
            }
            else
            {
                usleep(25);
            }
        }
        while(RXchar != '\r');


        if (send)
        {
            write(uartCom, rfid_read_buffer, writeCounter);
        }
    }
    while (!bKillBridgeThreads);

    return NULL;
}
LPs
  • 16,045
  • 8
  • 30
  • 61
  • @sawdust true. It is just an example to explain better the concept. BTW you are free to post your efficient code as an answer... ;) – LPs Aug 10 '16 at 08:48
0

OK, I've found a solution to my problem I think.

void *rfid_to_uart_thread(void *) {
char rfid_read_buffer[10];
ssize_t writeCounter = -1;

do {
    writeCounter = read(rfidCom, &rfid_read_buffer, sizeof(rfid_read_buffer)-1);
    if (writeCounter>0){
        rfid_read_buffer[writeCounter] = 0;
        LOGE("RFID -> UART: %s", rfid_read_buffer);
        write(uartCom, rfid_read_buffer, writeCounter);
    }else{
        usleep(25);
    }
    tcdrain(uartCom);
} while (!bKillBridgeThreads);

return NULL;}

I've created my own define for a tcdrain, because the Android NDK I am using is not offering it in termios.h Now, all the values seem to get transmitted to the UART port.

tcdrain is now defined as:

#define tcdrain(fd) ioctl(fd, TCSBRK, 1)
Tobi
  • 168
  • 1
  • 12
  • I would really like to know how the ioctl to send a break character became the ioctl to wait until the transmit buffer has emptied. – rjp Aug 11 '16 at 18:57