Unable to read/write serial port multiple times

Hi, I met a strange issue while communicating with Arduino Uno R3 serial port. Can you please help take a look? Thanks a lot.

In my use case, my host program on Ubuntu sends multiple commands to Arduino via serial port and reads responses from Arduino, via serial port also.
Arduino can successfully receive the 1st command and return the response. My host program on Ubuntu can receive the 1st response as expected.

However, when my host program sends the 2nd command, the communication always fails. Sometimes it looks like Arduino misses the 2nd command and sometimes host program cannot fetch the response.
My current workaround is to close and re-open the serial port in host program before sending the 2nd command. Then the communication runs well.

It is more strange that everything works well if I open a terminal tool to connect to Arduino and manually input those commands.

So I guess something is wrong in my host program or it doesn't match the implementation on Arduino side, for example timing or serial configuration.
May I ask for your suggestion to fix this issue?
My Arduino code is simplified as:

#define MAX_CMD_LEN    8
char cmd[MAX_CMD_LEN];

void setup(void)
{
    Serial.begin(BAUD_RATE);
    PRINTLN("Test board reset via Arduino");

    ...
}

void loop(void)
{
    char in_ch = '\0';
    static uint8_t idx = 0;

    while (Serial.available() > 0) {
        in_ch = Serial.read();
        delay(1);

        if (idx < MAX_CMD_LEN) {
                if (idx == 0)
                    continue;

                cmd[idx] = '\0';
                idx = 0;

                if (it is cmd1) {
                    cmd1_respond();
                } else if (it is cmd2) {
                    cmd2_respond();
                } else {
                    printf("wrong cmd\r\n");
                }
            } else {
                cmd[idx] = in_ch;
                idx++;
            }
        } else {
            // Deal with buffer overflow
        }
    }
}

My host program running in Ubuntu

/* Send command */
static void tx_msg(int port, const char *cmd, size_t len)
{
    char tx_msg[CMD_BUF_SIZE];

    strncpy(tx_msg, cmd, len);
    tx_msg[len] = '\r';
    write(port, tx_msg, len + 1);
}

/* Receive response */
static int rx_msg(int port, char *msg, size_t size)
{
    uint8_t nr_rd_byte;
    int8_t i;

    memset(msg, '\0', size);

    nr_rd_byte = read(port, msg, size);
    if (nr_rd_byte < 0)
        return -1;

    if (nr_rd_byte >= 2) {
        /* Replace \r\n with \0 */
        msg[nr_rd_byte - 2] = '\0';
        msg[nr_rd_byte - 1] = '\0';
    }

    return 0;
}

int main(void) {
    ...

    port_fd = open("/dev/ttyACM0", O_RDWR| O_NOCTTY | O_SYNC);
    if (port_fd < 0)
        ...

    memset(tty, 0, sizeof(tty));

    if (tcgetattr(port, &tty) != 0)
        ...

    /* Setup port. 8-N-1 */
    tty->c_cflag &= ~PARENB;
    tty->c_cflag |= CSTOPB;
    tty->c_cflag &= ~CSIZE;
    tty->c_cflag |= CS8;
    /* Turn on READ & ignore ctrl lines (CLOCAL = 1) */
    tty->c_cflag |= CREAD | CLOCAL;
    /* Non-canonical mode */
    tty->c_lflag &= ~ICANON;
    /* Wait for up to 2s, returning as soon as any data is received. */
    tty->c_cc[VTIME] = 20;
    tty->c_cc[VMIN] = 0;
    /* Baud rate */
    cfsetspeed(tty, ARDUINO_BAUD_RATE);

    /* Write back configuration */
    if (tcsetattr(port, TCSANOW, tty) != 0)
        ...

    /* Wait for a while.*/
    sleep(2);

    /* Send the 1st cmd */
    tx_msg(port_fd, cmd1, strlen(cmd1));
    /* Wait for the response of 1st cmd */
    err = rx_msg(port_fd, msg, MSG_BUF_SIZE);
    if (err)
        ...

    sleep(2);
    /* Send cmd2 */
    tx_msg(port_fd, cmd2, strlen(cmd2));
    /* Wait for the response of cmd2 */
    err = rx_msg(port_fd, msg, MSG_BUF_SIZE);
    if (err)
        ...

    return 0;
}

The Arduino code does not compile so I fail to see how it will work even once.

The serial input basics tutorial may be of interest.

Thanks. @groundFungus. I removed some piece of details from Arduino code.
Checked that post. Very helpful. Imo, my Arduino code is quite similar.

After that, your code became absolutely meaningless. It is impossible to see the problem from it. For example because of these lines you will never accept any character

Try to form a minimally representative example that can be compiled and run

Hi @b707 , thanks for the suggestion. Sorry for the over-simplification.
Can you please help check the updated version below?
I verified on my Arduino Uno R3. Manual terminal input works well while host program on Ubuntu still fails unless reopening serial port after 1st command completes.

// Arduino setting
#define BAUD_RATE      115200
#define MAX_CMD_LEN    8
#define RESET_CMD      "reset"
#define FIND_CMD       "find"

char cmd[MAX_CMD_LEN];

uint8_t nr_ch = 0;
bool new_cmd = false;

void setup(void)
{
    Serial.begin(BAUD_RATE);
}

static void reset(void)
{
    Serial.println("Done");
    Serial.flush();
}

static void respond(void)
{
    Serial.println("Hello");
    Serial.flush();
}

void handle_cmd(void)
{
    if (strcmp(cmd, RESET_CMD) == 0) {
        reset();
    } else if (strcmp(cmd, FIND_CMD) == 0) {
        respond();
    } else {
        Serial.println("Error: Unrecognized Command!");
    }
}

void loop(void)
{
    char in_ch = '\0';

    while (Serial.available() > 0) {
        in_ch = Serial.read();
        delay(1);

        if (nr_ch < MAX_CMD_LEN) {
            if (in_ch == '\r') {
                if (nr_ch == 0)
                    continue;

                cmd[nr_ch] = '\0';
                nr_ch = 0;
                new_cmd = true;
            } else {
                cmd[nr_ch] = in_ch;
                nr_ch++;
            }
        } else {
            Serial.println("Error: Invalid command. Buffer overflow.");
            nr_ch = 0;
        }
    }

    if (new_cmd) {
        handle_cmd();
        new_cmd = false;
    }
}

Are any of your error messages being sent to your Ubuntu program?

Hi @johnwasser , sorry for the delayed reply.

My host program didn't receive any error for Arduino.
It looks like Arduino just didn't receive the 2nd command as my original code requests Arduino to toggle a LED while receiving the 2nd command. The LED didn't shine after host sent out the 2nd command.

Some updates.

I figure out another workaround :slight_smile:
I put the 2nd command submission and response reception into a for()/while() loop.
The 2nd command will be sent several times until host receives Arduino's response.

It turns out that it always succeed in the second try:

  • Host sends 2nd command. No response.
  • Host repeats 2nd command. Response received.

Looks like a timing mismatch issue?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.