6 years, 10 months ago.

SOLVED - Sending string via UART - fscanf() loops at the first read and succeeds after next data

Hi all,

I am sending data over UART between two MCUs and I have an issue with scanf() function. It seems that it loops after receiving data for the first time (the program doesn't go any further) until I send the data second time - than it proceeds to display it on PC terminal, but the first character is doubled (I get "sstring" - unless I send something with a space as a first character, but it still displays on a second try).

Data is transmited similarly as I would send it to PC (where a terminal shows everything alright):

Serial uart(TX_PIN_NUMBER, RX_PIN_NUMBER);
...
// On button press:

if( uart.writeable() )
{
    uart.printf("string ");     // space added purpousefully to terminate the string with EOL
}

And the receiver code that probably makes problems:

char readData[10] = {0};    // Doesn't matter if filled with 0's or just declared

...

while(1)
{
  if( uart.readable() )
  {
    uart.scanf("%s", readData);

    pc.printf("UART data: ");
    pc.printf("%s", readData);
  }
else
   wait_ms(100);  // SOLVED: This statement created problems! I didn't show it here at first as I didn't copy all of the code and didn't even think it might be important.

}

Any idea on what I can be missing?

2 Answers

6 years, 10 months ago.

Hello Jakub,
There is no mbed platform indicated on your profile page. Which mbed boards did you use? I'm asking because I have tested the codes below with a NUCLEO-F103RB board as sender and an LPC1768 as receiver and it worked OK. I was always getting "Serial data: string" message on the PC's serial terminal any time I pushed the user button on the NUCLEO board. So the issue seems to be specific to the board(s) you have used.

NUCLEO-F103RB

#include "mbed.h"

Serial        serial(D8, D2);
InterruptIn   userButton(USER_BUTTON);

void sendSerial(void) {
    if(serial.writeable()) {
        serial.printf("string ");
        wait_ms(500);   // lag to debounce the button
    }
}

int main(void) {
    userButton.fall(callback(sendSerial));
        
    while(1) {}
}

LPC1768

#include "mbed.h"

Serial  pc(USBTX, USBRX);
Serial  serial(p13, p14);
char    data[10];

int main() {
    while(1) {
        if(serial.readable() ) {
            serial.scanf("%9s", data);
            pc.printf("Serial data: %s\r\n", data);
        }
    }
}

Accepted Answer

Thank you, Zoltan.

I am sending data from NRF51822 module to Nucleo-F103RB (will update it in my profile soon).

I have copied-pasted your code and it works!! I'll try to figure out what is the difference, I thought I had the same thing...

posted by Jakub K 14 Jun 2017

The problem was that I had a 100ms delay in the "else" statement after checking if UART is readable.

        if( uart.readable() )
        {
           uart.scanf("%s", readData);
           pc.printf("%s", readData);
        }

        else
        	wait_ms(100);

Reducing the delay to 1ms or deleting it solves the problem. I must have inserted it during testing to see if UART is unreadable at times. I wouldn't suppose that a statement outside scanf of function would have an impact on it.

So thank you very much for this exemplary code, it was probably a stupid mistake to make. Just cannot believe it.

posted by Jakub K 14 Jun 2017
6 years, 10 months ago.

End the string you send with a new line (\n) rather than a space and it should work fine. Scanf is intended for lines of input text rather than individual words, it reads until the next symbol starts or a the new line is found.

But I've no idea why you're getting "sstring"

If you want a more controlled parsing system which does stop at the first space then as long as all you need to do is read a string until a space is found it's fairly simple to write one.

And one minor note, your scanf has no buffer overflow protection, if someone sent a string that was 10 characters long it would overflow the readData buffer and start corrupting the system memory. That is exactly the bug that is behind more than half of the security vulnerabilities in internet connected devices. In this specific situation it's just a potential minor annoyance but it's a very bad habit to get into, especially since it's so simple to avoid, just use "%9s" in place of "%s".

Thank you for an answer, Andy.

"End the string you send with a new line (\n) rather than a space and it should work fine."

Just tried it, no difference at all to the behavior of my program. It must be something else.

"And one minor note, your scanf has no buffer overflow protection [...]"

I didn't care about such things when testing, but thank you very much for this tip! Will surely be very useful in the future.

posted by Jakub K 14 Jun 2017

The real problem was that I had a 100ms delay in the "else" statement after checking if UART is readable (see my comment under the next answer). It was a stupid mistake, so I should be sorry for bothering you.

posted by Jakub K 14 Jun 2017