Skip to content

Commit 1c5584b

Browse files
committed
Demonstrate using the status led API
1 parent 9b4c332 commit 1c5584b

File tree

6 files changed

+202
-0
lines changed

6 files changed

+202
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ add_subdirectory(pio)
8585
add_subdirectory(pwm)
8686
add_subdirectory(reset)
8787
add_subdirectory(rtc)
88+
add_subdirectory(status_led)
8889
add_subdirectory(spi)
8990
add_subdirectory(system)
9091
add_subdirectory(timer)

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,13 @@ App|Description
371371
[max7219_8x7seg_spi](spi/max7219_8x7seg_spi) | Attaching a Max7219 driving an 8 digit 7 segment display via SPI.
372372
[max7219_32x8_spi](spi/max7219_32x8_spi) | Attaching a Max7219 driving an 32x8 LED display via SPI.
373373

374+
### Status Led
375+
376+
App|Description
377+
---|---
378+
[status_blink](status_led) | Blink the onboard LED using the status LED API.
379+
[color_blink](status_led) | Blink the onboard colored (WS2812) LED using the status LED API if supported by the board.
380+
374381
### System
375382

376383
App|Description

status_led/# Overview.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Overview
2+
3+
This example demonstrates how to implement an Over The Air (OTA) software update mechanism using facilities provided by the RP2350 bootrom.
4+
5+
A python script runs on a host machine to _push_ a new (UF2 format) software image to a Pico 2 W running the `ota_update` example image. The incoming image is received via the LwIP IP stack and programmed into Pico 2 W flash memory.
6+
7+
On successful completion of the flash programming, the Pico 2 W will be rebooted and the updated operating image will be selected and executed by the RP2350 bootrom. This process can be repeated as required.
8+
9+
## More detail
10+
11+
The Pico 2 W listens on TCP port 4242. The host [python_ota_update.py](python_ota_update.py) script transmits the update image in a series of fixed sized chunks. Each chunk contains an integer number of UF2 image blocks. If the update image UF2 block count is not an exact sub-multiple of the number of chunks the script will pad the last chunk as required.
12+
13+
On receipt of pushed chunks, the ota_update image will program the UF2 block stream into flash using the APIs provided by the RP2350 bootrom. In addition to programming, each chunk is 'hashed', using SHA256, and the calculated hash transmitted to the host as an acknowledgement of the received chunk. The host script compares the local and remotely computed hashes and if data corruption has occurred the update will halt.
14+
15+
The flash must be appropriately partitioned for this example to work. Two ` IMAGE_DEF` partitions are required, one for the currently running software and the other which is updated by the `ota_update` example.
16+
17+
Note: This example _also_ demonstrates how the CYW43 Wi-fi firmware can be stored in a separate flash partition(s). This means the Pico 2 W application can be updated separately which reduces the size of the update download.
18+
19+
For more information flash partitioning and boot image selection please see section 5 of [RP2350 datasheet](https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf)
20+
21+
## How to run the example
22+
23+
### Flash partitioning
24+
25+
Before the example can be run, the Flash on the Pico 2 W must be appropriately partitioned.
26+
27+
Flash partitioning only needs to be done once for this example but, as written below, will completely remove any previous flash contents.
28+
29+
The required partition data can be loaded by creating a UF2 from the partition table JSON in this folder:
30+
31+
```
32+
picotool partition create main.json pt.uf2
33+
```
34+
then dragging & dropping this UF2 onto the device, or loading it using `picotool` and rebooting:
35+
```
36+
picotool load pt.uf2
37+
picotool reboot -u
38+
```
39+
40+
> **NOTE**
41+
> `reboot -u` reboots back to bootsel mode so we can send more commands to the device
42+
43+
Once the partition table is loaded, you then need to load the Wi-Fi firmware UF2 (`picow_ota_update_wifi_firmware.uf2`) followed by loading and executing the main program (`picow_ota_update.uf2`) - either by dragging and dropping them in order, or using `picotool`:
44+
```
45+
picotool load picow_ota_update_wifi_firmware.uf2
46+
picotool load -x picow_ota_update.uf2
47+
```
48+
49+
> **NOTE**
50+
> `load -x` attempts to execute the uf2 after the load
51+
52+
The device should now have the following firmware layout. You can confirm this by running "`picotool info`
53+
54+
| Partition | Purpose |
55+
| ----------|-------- |
56+
| 0 | Partition A for the application firmware |
57+
| 1 | Partition B for the application firmware |
58+
| 2 | Partition A for the wifi firmware |
59+
| 3 | Partition B for the wifi firmware |
60+
61+
### Operation
62+
63+
This example will send debug output text on the default UART. On startup it displays the current boot partition (from where the firmware is running) and IP address of the Pico 2 W
64+
65+
```
66+
Boot partition was 0
67+
Starting server at 192.168.0.103 on port 4242
68+
```
69+
70+
Once running, you can use [python_ota_update.py](python_ota_update.py) to upload new UF2s from the host to the Pico 2 W using its IP address. For example:
71+
```
72+
python ./python_ota_update.py 192.168.0.103 picow_ota_update.uf2
73+
```
74+
This will update the Pico 2 W at `192.168.0.103` with the specified image.
75+
76+
```
77+
Boot partition was 1
78+
...
79+
Starting server at 192.168.0.103 on port 4242
80+
```
81+
82+
The update is downloaded into the free boot partition that's not currently in use, before rebooting the Pico 2 W to take the new software into use. After the restart the boot partition should have changed.
83+
84+
### Try before you Buy
85+
86+
The `picow_ota_update.uf2` application is marked with the "Try Before You Buy" (TBYB) attribute by specifying `PICO_CRT0_IMAGE_TYPE_TBYB=1` in its compile definitions.
87+
88+
This means that if a newly downloaded image partition is not explicitly "bought" by calling the `rom_explicit_buy` function within 16.7 seconds, the device will reboot and revert to the previously used partition. This gives some protection against a bad OTA update being applied. See section 5.1.17 of [RP2350 datasheet](https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf) for more details about Try Before You Buy.
89+
90+
#### Wifi Firmware
91+
92+
This example also demonstrates how TBYB can be applied to other partitions - in this case the Wifi firmware. In debug builds you should see debug output which shows the partition that's currently being used for the wifi firmware.
93+
94+
The example uses the `pico_use_wifi_firmware_partition` keyword to make the Wifi firmware appear in its own partition rather than embedded in the application as is usually the case.
95+
96+
```
97+
Chosen CYW43 firmware in partition 2
98+
```
99+
100+
Two UF2 files are generated for the wifi firmware, `picow_ota_update_wifi_firmware.uf2` and `picow_ota_update_wifi_firmware_tbyb.uf2`. These images are the same except only the latter image has the TBYB attribute set.
101+
102+
You can update the current Wifi partition with these UF2 files using [python_ota_update.py](python_ota_update.py).
103+
104+
If you use `picow_ota_update_wifi_firmware_tbyb.uf2` for the update then if something goes wrong before the partition is explicitly "bought" by calling the `rom_explicit_buy` function, then the device will switch back to the previous working Wifi firmware partition after a reboot. This does not happen if you use `picow_ota_update_wifi_firmware.uf2` for the update. In this way the TBYB attribute gives you some protection against an OTA update applying bad Wifi firmware.

status_led/CMakeLists.txt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Blink the "status" LED connected to the GPIO defined by PICO_DEFAULT_LED_PIN for your board
2+
add_executable(status_blink
3+
status_blink.c
4+
)
5+
# You can define PICO_DEFAULT_LED_PIN yourself to add a led to a different GPIO
6+
#target_compile_definitions(status_blink PRIVATE
7+
# PICO_DEFAULT_LED_PIN=15
8+
#)
9+
target_link_libraries(status_blink
10+
pico_stdlib
11+
pico_status_led
12+
)
13+
pico_add_extra_outputs(status_blink)
14+
example_auto_set_url(status_blink)
15+
16+
# Blink the colored "status" LED connected to the GPIO defined by PICO_DEFAULT_WS2812_PIN for your board
17+
add_executable(color_blink
18+
color_blink.c
19+
)
20+
# You can define PICO_DEFAULT_WS2812_PIN yourself to add a WS2812 led to a normal GPIO
21+
target_compile_definitions(color_blink PRIVATE
22+
PICO_DEFAULT_WS2812_PIN=16
23+
)
24+
target_link_libraries(color_blink
25+
pico_stdlib
26+
pico_status_led
27+
)
28+
pico_add_extra_outputs(color_blink)
29+
example_auto_set_url(color_blink)

status_led/color_blink.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* Copyright (c) 2025 Raspberry Pi (Trading) Ltd.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
#include "pico/stdlib.h"
8+
#include "pico/status_led.h"
9+
10+
#if !PICO_COLORED_STATUS_LED_AVAILABLE
11+
#warning The color_blink example requires a board with a WS2812 LED
12+
#endif
13+
14+
#ifndef LED_DELAY_MS
15+
#define LED_DELAY_MS 250
16+
#endif
17+
18+
int main() {
19+
bool rc = status_led_init();
20+
hard_assert(rc);
21+
hard_assert(colored_status_led_supported()); // This assert fails if your board does not have WS2812 support
22+
uint32_t count = 0;
23+
while (true) {
24+
// flash red then green then blue
25+
uint32_t color = PICO_COLORED_STATUS_LED_COLOR_FROM_RGB(count % 3 == 0 ? 0xaa : 0, count % 3 == 1 ? 0xaa : 0, count % 3 == 2 ? 0xaa : 0);
26+
colored_status_led_set_on_with_color(color);
27+
count++;
28+
sleep_ms(LED_DELAY_MS);
29+
assert(colored_status_led_get_state());
30+
colored_status_led_set_state(false);
31+
sleep_ms(LED_DELAY_MS);
32+
assert(!colored_status_led_get_state());
33+
}
34+
status_led_deinit();
35+
}

status_led/status_blink.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Copyright (c) 2025 Raspberry Pi (Trading) Ltd.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
#include "pico/stdlib.h"
8+
#include "pico/status_led.h"
9+
10+
#ifndef LED_DELAY_MS
11+
#define LED_DELAY_MS 250
12+
#endif
13+
14+
int main() {
15+
bool rc = status_led_init();
16+
hard_assert(rc);
17+
while (true) {
18+
status_led_set_state(true);
19+
sleep_ms(LED_DELAY_MS);
20+
assert(status_led_get_state());
21+
status_led_set_state(false);
22+
sleep_ms(LED_DELAY_MS);
23+
assert(!status_led_get_state());
24+
}
25+
status_led_deinit();
26+
}

0 commit comments

Comments
 (0)