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:


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 <graham.sanderson@raspberrypi.com>
    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);