skip to Main Content

I got my program running fine as explained at: How can you make a micropython program on a raspberry pi pico autorun?

I’m installing a main.py that does:

import machine
import time

led = machine.Pin('LED', machine.Pin.OUT)
# For Rpi Pico (non-W) it was like this instead apparently.
# led = Pin(25, Pin.OUT)

i = 0
while (True):
    led.toggle()
    print(i)
    time.sleep(.5)
    i += 1

When I power the device on by plugging the USB to my laptop, it seems to run fine, with the LED blinking.

Then, if I connect from my laptop to the UART with:

screen /dev/ttyACM0 115200

I can see the numbers coming out on my host terminal correctly, and the LED still blinks, all as expected.

However, when I disconnect from screen with Ctrl-A K, after a few seconds, the LED stops blinking! It takes something around 15 seconds for it to stop, but it does so every time I tested.

If I reconnect the UART again with:

screen /dev/ttyACM0 115200

it starts blinking again.

Also also noticed that after I reconnect the UART and execution resumes, the count has increased much less than the actual time passed, so one possibility is that the Pico is going into some slow low power mode?

If I remove the print() from the program, I noticed that it does not freeze anymore after disconnecting the UART (which of course shows no data in this case).

screen -fn, screen -f and screen -fa made no difference.

Micropython firmware: rp2-pico-w-20221014-unstable-v1.19.1-544-g89b320737.uf2, Ubuntu 22.04 host.

Some variants follow.

picocom /dev/ttyACM0 instead of screen and disconnect with Ctrl-A Ctrl-Q: still freezes like with screen.

If I exit from picocom with Ctrl-A Ctrl-X instead however, then it works. The difference between both seems to be that Ctrl-Q logs:

Skipping tty reset...

while Ctrl-X doesn’t, making this a good possible workaround.

The following C analog of the MicroPython hacked from:

did not show the same problem, tested on https://github.com/raspberrypi/pico-sdk/tree/2e6142b15b8a75c1227dd3edbe839193b2bf9041

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

int main() {
    stdio_init_all();
    if (cyw43_arch_init()) {
        printf("WiFi init failed");
        return -1;
    }
    int i = 0;
    while (true) {
        printf("%in", i);
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, i % 2);
        i++;
        sleep_ms(500);
    }
    return 0;
}

Reproduction speed can be greatly increased from a few seconds to almost instant by printing more and faster as in:

import machine
import time

led = machine.Pin('LED', machine.Pin.OUT)

i = 0
while (True):
    led.toggle()
    print('asdf ' * 10 + str(i))
    time.sleep(.1)
    i += 1

This corroborates people’s theories that the problem is linked to flow control: the sender appears to stop sending if the consumer stops being able to receive fast enough.

Also asked at:

Possibly related:

2

Answers


  1. Chosen as BEST ANSWER

    I don't know why it works, but based on advie from larsks:

    sudo apt install picocom
    picocom /dev/ttyACM0
    

    and then quit with Ctrl-A Ctrl-X (not Ctrl-A Ctrl-Q) does do what I want. Not sure what screen is doing differently exactly.

    When quitting, Ctrl-Q shows on terminal:

    Skipping tty reset...
    

    and Ctrl-X does not, which may be a major clue.


  2. What appears to be happening here is that exiting screen (or exiting picocom without the tty reset) leaves the DTR line on the serial port high. We can verify this by writing some simple code to control the DTR line, like this:

    #include <unistd.h>
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <termios.h>
    #include <sys/types.h>
    #include <sys/time.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #include <sys/ioctl.h>
    #include <signal.h>
    
    int main(int argc, char **argv)
    {
        int fd;
    
        int dtrEnable;
        int flags;
    
        if (argc < 2) {
            fprintf(stderr, "Usage: ioctl <device> <1 or 0 (DTR high or low)>n");
            exit(1);
        }
    
        if ((fd = open(argv[1], O_RDWR | O_NDELAY)) < 0) {
            perror("open:");
            exit(1);
        }
    
        sscanf(argv[2], "%d", &dtrEnable);
    
        ioctl(fd, TIOCMGET, &flags);
    
        if(dtrEnable!=0) {
            flags |= TIOCM_DTR;
        } else {
            flags &= ~TIOCM_DTR;
        }
    
    
        ioctl(fd, TIOCMSET, &flags);
    
        close(fd);
    }
    

    Compile this into a tool called setdtr:

    gcc -o setdtr setdtr.c
    

    Connect to your Pico using screen, start your code, and then disconnect. Wait for the LED to stop blinking. Now run:

    ./setdtr /dev/ttyACM0 0
    

    You will find that your code starts running again. If you run:

    ./setdr /dev/ttyACM0 1
    

    You will find that your code gets stuck again.


    The serial chip on the RP2040 interprets a high DTR line to mean that a device is still connected. If nothing is reading from the serial port, it eventually blocks. Setting the DTR pin to 0 — either using this setdtr tool or by explicitly resetting the serial port state on close — avoids this problem.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search