[FEATURE] raspberrypi pico w/2/2w firmware flashing automation with make flash
Is your feature request related to a problem? Please describe.
- For now in order to flash rPI-Pico(-W/-2/-2W) we need to manually press Boot button on USB attach then copy-paste firmware file.
- That blocks automation testing.
Describe the solution you'd like
- Use
make flashto have firmware flashed and ready for runtime testing. - Enable full automation of firmware flashing.
Describe alternatives you've considered
@keever50 reported that picotool (something like esptool for ESP32) can be used to flash firmware but still requires manual Boot button interaction, and PlatformIO can switch to BootROM by some magic sequence. That may indicate flashing automation should be possible.
Verification
- [x] I have verified before submitting the report.
@linguini1 as you are most familiar with rPI-Pico can we assign you this task to research in a free moment? :-) Having this single usb port flashing just as with esptool would enable automated testing on a real world hardware :-)
@linguini1 as you are most familiar with rPI-Pico can we assign you this task to research in a free moment? :-) Having this single usb port flashing just as with esptool would enable automated testing on a real world hardware :-)
I can certainly try to research how this is done!
TANK U SIR! :-)
After doing more research on this topic I haven't been able to find any mention of automatic flashing of RP2040 chips. All sources I've found said that the chip requires the "bootsel" button to be held to enter boot mode, or people have created custom boot loaders to flash without holding the button.
It was also suggested on this forum to use the SWD pins: https://forums.raspberrypi.com/viewtopic.php?t=328795
The pico SDK is meant to have some utilities for rebooting into bootsel, so it may be possible for the purposes of mass testing to create some bootloader/helper program that reboots the pico into bootsel, uploads the configuration to be tested, tests it, and reboots into bootsel again to repeat.
I can confirm platformio (https://github.com/earlephilhower/arduino-pico) is including a bootloader. Might be a nice addition for NuttX to add it as well.
@keever50: I can confirm platformio (https://github.com/earlephilhower/arduino-pico) is including a bootloader. Might be a nice addition for NuttX to add it as well.
Hmm this seems to be whole Arduino SDK.. we would prefer to have our own bootloader and stay external SDK independent right? :-P
If we were to write such a bootloader for NuttX, would it be preferable to:
- Have a bootloader that puts the RP2040 into BOOTSEL when it detects something like 1200 baud on a UART line
- Have an application/NSH command that when run reboots the RP2040 into bootsel
- Expect some kind of special bytes over USB to enter BOOTSEL
My thought is that with 2) we could flash the Pico manually once, and then always be able to reboot into bootsel to flash more configurations, which would be suitable for creating test-farm setups. I think 1) will be inconvenient because it requires a UART connection (as opposed to just a USB connection which is used for both powering and interacting with the RP2040 in a lot of these boards) and I think 3) would be complicated to get right/integrate + require some special byte sequence known in advance and sent by the host device.
Just a thought but you can use a raspberry pi debug probe (or another pico) to program the pico over SWD. The probe firmware is open source so you can tweak it to give it an unique identifier. https://github.com/raspberrypi/debugprobe (I think its also possible to get an unique ID of the RP2040 to be flashed over SWD?)
Or create a microcontroller dedicated to reset the pico into bootloader and push firmware onto it via usb, or via SWD. (or mod the debugprobe to do this)
Those are dumb but easier solutions. We dont want to make it too complex of course. Maybe worth a thought.
Yeah but the goal (and need) is to have rpi using only single usb connection to flash and run.. to be honest its quite funny it cannot by default :D
Status update:
- @shtirlic added NuttX RISC-V core support for rPI Pico2(W) in https://github.com/apache/nuttx/pull/16424, yeah!
picotoolgrows with functions, nowload(firmware flashing) andreboot(reset to bootrom, arm core, riscv core) commands are now possible, we are getting closer to full automation, as discussed in https://github.com/raspberrypi/picotool/issues/270, this however requires api stub in firmware to perform controlled reset over usb protocol, hardware reset/bootom is not possible as in ESP family :-)- Here is initial implementation of usb-protocol-reset-vector for rPI Pico2 https://github.com/apache/nuttx/pull/16848.
- So far we are working on RP2350, but maybe this will be also possibe for RP2040.
@cederom I must clarify that this is NOT usb-protocol-reset-vector it's calling internal pico bootrom functions to reboot normally or reboot to bootsel mode, so you can just type/send the reboot bootloader command to nsh and it goes to reboot. The only problem I see if the NuttX crashed or not booting at all so the board will be stuck.
Do we have make flash target implemented @shtirlic ? :-)
Today I have some free moment will test the reset vector and flashing :-) Thanks again :-)
Not yet implemented. make flash only produces nuttx.uf2 while the goal is to automate flashing of the physical chip like in other targets. Reopening :-)
@linguini1 Have a bootloader that puts the RP2040 into BOOTSEL when it detects something like 1200 baud on a UART line
In my RP2040 board I have implemented this over USB CDC-ACM instead of UART. This comes at the cost of having a background demon that listens for LINE_CODING events (let me improperly call them "baud rate changes"), though.
Example code, ran as part of bringup
Note: I have removed all error handling, comments, and mangled the control structure of this code for maximum brevity. Consider it pseudocode at this point :)
The BOARDIOC_RESET_BOOTSEL ioctl does what the name suggests and is backed by a ROM lookup like in pico-sdk.
static sem_t g_board_bootsel_on_baudrate_change_daemon_sem;
static void cdcacm_daemon_callback(enum cdcacm_event_e event) {
if (event == CDCACM_EVENT_LINECODING) {
sem_post(&g_board_bootsel_on_baudrate_change_daemon_sem);
}
}
static int cdcacm_daemon(int argc, char *argv[]) {
while (true) {
sem_wait(&g_board_bootsel_on_baudrate_change_daemon_sem);
int fd = open("/dev/ttyACM0", O_RDONLY);
struct cdc_linecoding_s linecoding;
int ret = ioctl(fd, CAIOC_GETLINECODING, &linecoding);
unsigned long baud = (linecoding.baud[3] << 24 | linecoding.baud[2] << 16 | linecoding.baud[1] << 8 | linecoding.baud[0]);
close(fd);
if (baud == 1200) {
boardctl(BOARDIOC_RESET_BOOTSEL, (uintptr_t)NULL);
}
}
}
static int board_cdcacm_daemon_setup(int argc, char *argv[]) {
uint8_t retry = 0;
uint8_t retry_delay_lut[] = { 1, 2, 3, 5, 10, 15, 30, 60 };
int fd;
while (true) {
fd = open("/dev/ttyACM0", O_RDONLY);
if (fd >= 0) break;
if (retry < ARRAY_SIZE(retry_delay_lut)) {
uint8_t delay = retry_delay_lut[retry++];
usleep(delay * USEC_PER_SEC);
} else {
return EXIT_FAILURE;
}
}
ioctl(fd, CAIOC_REGISTERCB, &cdcacm_daemon_callback);
close(fd);
sem_init(&g_board_bootsel_on_baudrate_change_daemon_sem, 0, 0);
sem_setprotocol(&g_board_bootsel_on_baudrate_change_daemon_sem, SEM_PRIO_NONE);
task_create("cdcacm_daemon", sched_get_priority_min(SCHED_FIFO), 512, cdcacm_daemon, NULL);
return EXIT_SUCCESS;
}
int board_app_finalinitialize(uintptr_t arg) {
int pid = task_create("board_cdcacm_daemon_setup", sched_get_priority_min(SCHED_FIFO) + 1, 1024, board_cdcacm_daemon_setup, NULL);
return EXIT_SUCCESS;
}
nsh> ps
PID GROUP PRI POLICY TYPE NPX STATE EVENT STACK USED FILLED COMMAND
0 0 0 FIFO Kthread - Ready 0001008 0000560 55.5% Idle_Task
1 1 100 RR Task - Running 0003032 0001664 54.8% nsh_main
3 3 1 RR Task - Waiting Semaphore 0000472 0000408 86.4%! cdcacm_daemon
This way I can run stty -F /dev/serial/by-id/usb-<your_device_id> 1200 on the host and have the board reboot to bootloader, and then it's just a matter of a few shell commands to wait for the USB drive to appear and copy the new UF2 file to it. I've wrapped all these actions in a couple of Just recipes for ease of use, so I can get away with things like just build && just bootsel-flash :)
@cederom What do you think of this approach? Would it be interesting to upstream it or is it too niche? I typically use a debug probe while developing, but this alternative way of flashing still comes in handy at times.
@linguini1 Have a bootloader that puts the RP2040 into BOOTSEL when it detects something like 1200 baud on a UART line
In my RP2040 board I have implemented this over USB CDC-ACM instead of UART. This comes at the cost of having a background demon that listens for LINE_CODING events (let me improperly call them "baud rate changes"), though.
This way I can run
stty -F /dev/serial/by-id/usb-<your_device_id> 1200on the host and have the board reboot to bootloader, and then it's just a matter of a few shell commands to wait for the USB drive to appear and copy the new UF2 file to it. I've wrapped all these actions in a couple of Just recipes for ease of use, so I can get away with things likejust build && just bootsel-flash:)
I really like this approach! If it can be Kconfig selected as whether to run at boot or not for the RP2040s, I think it's great. That way the Pico can have a single connection (USB shell) for testing and booting in a hardware test setup. Thanks for sharing that!
Thanks @nmaggioni :-) Yes I saw that 1200 baudrate magic on the picotool but did not use that yet myself. So it needs some in-firmware stub anyways? Would it reset board under segfault?
We also have reset to bootloader recently merged but this 1200 baudrate seems simpler, true :-)
Lets keep make flash command to flash as with the other boards that may use different underlying mechanisms too :-)
The pico boards will not reset with the magic baud when its stuck, such as in a seg fault. Usually for development, i put code in the start of my code that Waits for a serial connection before continuing. This way baud resets are still possible because bootloops are way less likely. Assuming you have a watchdog.
On Mon, 8 Sept 2025, 15:05 CeDeROM, @.***> wrote:
cederom left a comment (apache/nuttx#16271) https://github.com/apache/nuttx/issues/16271#issuecomment-3266222254
Thanks @nmaggioni https://github.com/nmaggioni :-) Yes I saw that 1200 baudrate magic on the picotool but did not use that yet myself. So it needs some in-firmware stub anyways? Would it reset board under segfault?
— Reply to this email directly, view it on GitHub https://github.com/apache/nuttx/issues/16271#issuecomment-3266222254, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACJ3DKDCRRPH43XUQVXVKND3RV5JRAVCNFSM6AAAAAB35Y6UQKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTENRWGIZDEMRVGQ . You are receiving this because you were mentioned.Message ID: @.***>