Search code examples
csdkusbraspberry-pi-picominicom

Why does printf work in the while loop, but not before on a raspberry pi pico?


I have installed the C/C++ SDK for the Raspberry PI Pico on a Raspberry pi 4. Then I have compiled and executed the hello world example via USB successfully. I monitor the output using minicom and so far everything works fine.

However, when I add a printf before the while loop, it has no effect.

Here is the SDK Example with my 1 line addition:

#include <stdio.h>
#include "pico/stdlib.h"

int main() {
    stdio_init_all();

    printf( "Let's start\n" ); // <-- This is my addition

    while (true) {
        printf("Hello, world!\n");
        sleep_ms(1000);
    }
    return 0;
}

I do see Hello World! in minicom once a second as desired, but no Let's start. What could be the reason and how can I overcome this?

UPDATE: Answers to some questions in the comments:

  • I have also tried to add fflush(stdout); after the first printf. It didn't help
  • Usually, I connect minicom before I start the application. I restart the application several times with or without the debugger and it makes no difference. Minicom does not 'drop' between restarts. It seems the connection to the USB port is stable whatever I do. It shows the "hello world" part even if I launch minicom after the application on the pico has started
  • Adding a numeric identifier to see if some of the 'hello world' drops: I did this in a more complicated app with timers and various interrupts. They work just fine and they all get called after the while loop starts
  • I have also added a sleep_ms(1000) before the printf. This didn't help either

Solution

  • The most likely cause is that the USB initialization isn't done when stdio_init_all() returns. The first print outs will then be lost.

    Define PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS to make it wait for a certain amout of time. You must define it before including the pico headers, otherwise, pico/stdio_usb.h will define it to 0.

    // this must be done before indirectly including "pico/stdio_usb.h":
    #ifndef PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS
    #define PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS (5000)
    #endif
    
    #include <stdio.h>
    #include "pico/stdlib.h"
    
    int main(void) {
        _Bool result = stdio_init_all(); // should now wait for up to 5 seconds
    
        printf( "Let's start.\n" );
    
        for(unsigned co = 0;; ++co) {
            printf("Hello, world! %u\n", co); // Aconcagua's suggestion. Do you see "0"?
            sleep_ms(1000);
        }
        return 0;
    }
    

    If 5 seconds isn't enough, try waiting indefinitely and instead initialize USB explicitly with stdio_usb_init() which, according to the documentation, "is useful if you don't want any initial stdout output to be discarded before the connection is established".

    #ifndef PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS
    #define PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS (-1)
    #endif
    
    #include <stdio.h>
    #include "pico/stdlib.h"
    
    int main(void) {
        _Bool result = stdio_usb_init(); // init USB explicitly
    
        printf( "Let's start.\n" );
    
        for(unsigned co = 0;; ++co) {
            printf("Hello, world!  Init: %d  id: %u\n", (int)result, co);
            sleep_ms(1000);
        }
        return 0;
    }
    

    Some USB drivers seems to still loose I/O directly after the connection has been established, so the pico library has a define for a "post connect" delay too. It's set to 50 (ms) by default, but you could increase it:

    // this also needs to be done before including any pico headers:
    #ifndef PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS
    #define PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS (1500)
    #endif
    

    If defining both PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS (-1) and PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS (1500) doesn't help, then you'll have no other option than to sleep manually after stdio_usb_init returns:

    _Bool result = stdio_usb_init();
    sleep_ms(PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS);
    

    Note: PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS was added in

    commit 13be546dc393e6ae8154e127f2b5cc8f5ee8fd95
    Author: Graham Sanderson <[email protected]>
    Date:   Fri Oct 8 09:01:30 2021 -0500
    

    and requires that you recompile the pico sdk. What happens in the library when a connection is established is actually just:

    sleep_ms(PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS);