0

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
Kağan Kayal
  • 2,303
  • 1
  • 19
  • 30
  • Is stdout implemented as line buffered or as something else on this platform? You could try to add a `fflush(stdout);` after each printf and see if it changes anything. – Lundin Apr 13 '23 at 10:54
  • The most likely reason is that you are not running the code you think you're running – klutt Apr 13 '23 at 10:54
  • The serial terminal is connected before the program starts running? – Some programmer dude Apr 13 '23 at 10:56
  • 1
    @Lundin Was thinking that too, but if that was the issue, wouldn't Let's start get printed as soon as Hello World is? – klutt Apr 13 '23 at 10:56
  • 1
    @klutt Yeah it should... although the various stdio implementations for microcontrollers tend to be skunky, so I wouldn't assume that it is standard compliant. – Lundin Apr 13 '23 at 11:01
  • 4
    I'd add a numeric identifier to each `hello world` to see if you really print all of them – maybe the first (few) `hello world`(s) get(s) lost already as well. – Aconcagua Apr 13 '23 at 11:02
  • 1
    @Someprogrammerdude If that's the case, a `sleep_ms(1000)` before the print would do the trick – klutt Apr 13 '23 at 11:03
  • @klutt Well that depends on how long the OP is taking between running the program and connecting the terminal... :) – Some programmer dude Apr 13 '23 at 11:14
  • Thanks for all the comments. I have added the answers as update to the question – Kağan Kayal Apr 13 '23 at 11:19
  • @KağanKayal Did you try my suggestion (below)? – Ted Lyngmo Apr 13 '23 at 11:22
  • @Aconcagua: Solved! After adding the counter, I have seen that the first two printfs in the loop did not make it. So I experimented with several sleep_ms(x) before the first printf. x = 1000ms reliably fails. x = 1200ms sometimes fails. x = 1500 worked 10 times in a row. – Kağan Kayal Apr 13 '23 at 12:10
  • @Someprogrammerdude `sleep_ms(INT_MAX)` :D – klutt Apr 13 '23 at 12:35

1 Answers1

1

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);
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • I have tried this solution with defining PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS, but it didn't make any difference. Neither 5000 nor -1. However, adding the counter in the for loop made the difference. I have seen that the first two printfs in the loop did not make it. So I experimented with several sleep_ms(x) before the first printf. With x = 1000ms reliably fails. x = 1200ms sometimes fails. x = 1500 worked 10 times in a row. So, the solution in my case is just to add sleep_ms(1500) after the `stdio_usb_init()` – Kağan Kayal Apr 13 '23 at 12:10
  • If you could adapt your answer, I would click it as the accepted answer. – Kağan Kayal Apr 13 '23 at 12:16
  • @KağanKayal I see. Ok, then, the proper way is probably to do what I added to the answer just now. – Ted Lyngmo Apr 13 '23 at 12:20
  • Sorry for the misunderstanding. I mean defining `PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS` didn't help in my case at all. It may help others. So I propose to add a third code snippet without this definition, but **only** with a sleep_ms(1500) just after the `stdio_usb_init()` in compare to the code in my question. I have no explanation, just the experimental evidence ;-) – Kağan Kayal Apr 13 '23 at 12:30
  • @KağanKayal Did you define both macros? The first to `(-1)` and the second to `(1500)`? – Ted Lyngmo Apr 13 '23 at 13:59
  • @KağanKayal And can you also verify that both macros were actually defined as I suggested? `printf("%d %d\n", PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS, PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS);` should print `-1 1500`. – Ted Lyngmo Apr 13 '23 at 14:41
  • "should now wait for up to 5 seconds" Are you serious, what kind of **** library is this??? Find that library on your HD, delete it and take things from there. – Lundin Apr 13 '23 at 14:44
  • @Lundin :-) I don't actually have the library. I just read the doc and suggested a very long time. Perhaps only suggesting to wait until it's ready (`-1`) would be enough, but I wanted to show that one can use a timeout. – Ted Lyngmo Apr 13 '23 at 14:47
  • There's no reason why any lib in a MCU should need to busy-wait for anything taking longer than 1ms or so. Any lib needing longer than that should provide a non-blocking polling function or interrupt callback. And there's no excuses for not providing that. So if this library was implemented otherwise, it needs to be deleted and rewritten from scratch by at least semi-competent programmers... – Lundin Apr 13 '23 at 14:55
  • @Lundin _"... no reason ... to busy-wait ..."_ - Yet, that's exactly what the pico-sdk does. It polls every 10ms to check if the connection has been successfully made (by checking the `DTR` pin). – Ted Lyngmo Apr 13 '23 at 14:59
  • And that's why we shouldn't let hobbyist PC programmers write embedded systems libraries, I guess... or well, they could write as many as they like, just don't upload the crap to public github. – Lundin Apr 13 '23 at 15:04
  • @Lundin :-) It's odd that they're setting up IRQs etc but then checking if the connection over the wire is established is done via polling. I found another guy who measured `while (!tud_cdc_connected()) {}` and got 2.5 seconds. "cdc" sounds like carrier detect to me, but the comment says DTR which I interpret as data terminal ready. – Ted Lyngmo Apr 13 '23 at 15:07
  • @TedLyngmo: Yes, I have tried it now again. In both cases, I do not see 'Let's start.'. In both cases the while loop starts with `Hello, world! Init: 1 id: 2`. It makes no difference at all and I always miss the first printf before the while loop. In both cases it feels like it goes immediately into the while loop. There is no significant delay that I can feel. Certainly not 5000ms. In short, this mechanism doesn't work for me. What works is the blocking `sleep_ms(1500)` before the first printf. – Kağan Kayal Apr 13 '23 at 20:10
  • @KağanKayal As I wrote at the end of the answer, for `PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS` to actually work, it's the pico-sdk you need to recompile and what it does internally is `sleep_ms(PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS);` after the connection has been established. So, use `PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS (-1)` and recompile the pico-sdk with `PICO_STDIO_USB_POST_CONNECT_WAIT_DELAY_MS (1500)` - or don't recompile and do the sleep manually. – Ted Lyngmo Apr 13 '23 at 20:53
  • @TedLyngmo: Thanks for the explanation. It seems to me, just adding a simple `sleep_ms(1500)` is the easiest way. – Kağan Kayal Apr 14 '23 at 20:54
  • @KağanKayal You're welcome! Yes, unfortunately, yes. That's how I've interpreted the code too. – Ted Lyngmo Apr 15 '23 at 01:32