Color wheel

Animate a circular HSV color space projection that spins around the centre of an RGB LED matrix, using the Display driver API.

Overview

This sample paints a color wheel onto an RGB LED matrix: the hue of each pixel follows its polar angle around the centre, so a full rainbow wraps once around the middle, while saturation rises with the radius. A bright white core in the centre blooms into fully saturated color towards the corners, and the whole wheel slowly spins. It drives the panel through the Display driver API rather than the LED strip API of the LED subsystem, so the same binary runs on panels of different geometry, wiring order and pixel format without any board specific code.

The panel is taken from the chosen { zephyr,display = ...; }; node, which on all supported boards resolves to a led-strip-matrix device on top of a WS2812 compatible LED strip. That driver maps a rectangular frame buffer onto the serpentine or circulative pixel order of the physical strip.

Color model

A color wheel is much easier to express in HSL and HSV [1] than in RGB: hue is the angle, saturation is the radius, and value sets the brightness of the whole panel. The sample computes every pixel in HSV and converts it to RGB in samples/display/colorwheel/src/hsv.c.

Each pixel is placed in a polar coordinate system centered on the panel, and the HSV values computed as follows:

hue(x, y)        = atan2(y - cy, x - cx) + phase
saturation(x, y) = 255 * dist((x, y), center) / (corner_radius * scale)
value(x, y)      = CONFIG_COLORWHEEL_MAX_BRIGHTNESS

where (cx, cy) is the center of the panel and the radius is normalised against a fraction of the corner distance, in percent, set by CONFIG_COLORWHEEL_SAT_SCALE, at which the ramp reaches full saturation.

Configuration options

The following sample-specific Kconfig options are used in this sample (located in samples/display/colorwheel/Kconfig):

CONFIG_COLORWHEEL_HUE_STEP

Hue rotation per frame [degrees]

How far the color wheel rotates around the centre of the panel between two consecutive frames. Set to 0 to render a static wheel.

CONFIG_COLORWHEEL_FRAME_INTERVAL

Delay between two frames [milliseconds]

Time to sleep between two consecutive frames. Together with COLORWHEEL_HUE_STEP this defines the angular speed of the wheel. The default of 40 ms gives 25 frames per second.

CONFIG_COLORWHEEL_SAT_SCALE

Radius of full saturation [% of corner distance]

Radius at which the saturation ramp reaches full saturation, as a percentage of the distance from the centre to a corner. Beyond this radius the color stays fully saturated all the way into the corners.

CONFIG_COLORWHEEL_MAX_BRIGHTNESS

Brightness ceiling

The brightness every pixel is driven at. The radial ramp is carried by saturation alone, so this option sets the brightness of the whole panel directly.

Important

Keep CONFIG_COLORWHEEL_MAX_BRIGHTNESS low. A WS2812B pixel draws up to 60 ㎃ at full white according to the WS2812B datasheet [2], so an 8×8 panel alone can draw more than 3.5 A, far beyond what a typical board’s USB supply can deliver.

Requirements

An RGB LED matrix assigned to the zephyr,display chosen node, with a pixel format of either RGB_888 or ARGB_8888. Either one of the following development boards with an on-board matrix:

or a Raspberry Pi Pico compatible board carrying the following shield:

The sample supports the following platforms (located in samples/display/colorwheel/tests.yaml):

Hardware platforms

Order number

Board name

Board target

Shields

STM32C5A3CG

XIAO STM32C5 (BBE)

xiao_stm32c5/stm32c5a3xx/bbe

seeed_xiao_6x10_rgb_matrix

STM32C5A3CG

XIAO STM32C5

xiao_stm32c5

xiao_stm32c5/stm32c5a3xx

seeed_xiao_6x10_rgb_matrix

ATSAMD21G18A

XIAO SAMD21 (Seeeduino XIAO)

xiao_samd21

xiao_samd21/samd21g18a

seeed_xiao_6x10_rgb_matrix

RP2350

XIAO RP2350 (BBE)

xiao_rp2350/rp2350a/m33_0/bbe

xiao_rp2350/rp2350a/hazard3_0/bbe

seeed_xiao_6x10_rgb_matrix

RP2350

XIAO RP2350

xiao_rp2350/rp2350a/m33_0

xiao_rp2350/rp2350a/hazard3_0

seeed_xiao_6x10_rgb_matrix

RP2040

XIAO RP2040 (BBE)

xiao_rp2040/rp2040/bbe

seeed_xiao_6x10_rgb_matrix

RP2040

XIAO RP2040

xiao_rp2040

xiao_rp2040/rp2040

seeed_xiao_6x10_rgb_matrix

RA4M1

XIAO RA4M1

xiao_ra4m1

xiao_ra4m1/r7fa4m1ab3cne

seeed_xiao_6x10_rgb_matrix

EFR32MG24

XIAO MG24

xiao_mg24

xiao_mg24/efr32mg24b220f1536im48

seeed_xiao_6x10_rgb_matrix

ESP32-S3

XIAO ESP32S3

xiao_esp32s3/esp32s3/procpu/sense

xiao_esp32s3/esp32s3/procpu

seeed_xiao_6x10_rgb_matrix

ESP32-C3

XIAO ESP32C3

xiao_esp32c3

xiao_esp32c3/esp32c3

seeed_xiao_6x10_rgb_matrix

nRF52840

XIAO BLE (Sense)

xiao_ble/nrf52840/sense

xiao_ble

seeed_xiao_6x10_rgb_matrix

RP2350

Waveshare RP2350-Matrix

waveshare_rp2350_matrix/rp2350a/m33_0

waveshare_rp2350_matrix/rp2350a/hazard3_0

RP2350

Waveshare RP2350-CAN

waveshare_rp2350_can/rp2350a/m33_0

waveshare_rp2350_can/rp2350a/hazard3_0

waveshare_pico_rgb_led

RP2040

Waveshare RP2040-Plus

waveshare_rp2040_plus

waveshare_rp2040_plus/rp2040

seengreat_pico_rgb_matrix_pes1

waveshare_pico_rgb_led

RP2040

Waveshare RP2040-Matrix

waveshare_rp2040_matrix

waveshare_rp2040_matrix/rp2040

RP2040

Waveshare RP2040-LCD-0.96

waveshare_rp2040_lcd_0_96

waveshare_rp2040_lcd_0_96/rp2040

waveshare_pico_rgb_led

ATSAMD21G18A

Seeeduino XIAO (BBE)

seeeduino_xiao/samd21g18a/bbe

seeed_xiao_6x10_rgb_matrix

ATSAMD21G18A

Seeeduino XIAO

seeeduino_xiao

seeeduino_xiao/samd21g18a

seeed_xiao_6x10_rgb_matrix

RP2040

Raspberry Pi Pico (BBE)

rpi_pico/rp2040/w/bbe

rpi_pico/rp2040/bbe

seengreat_pico_rgb_matrix_pes1

waveshare_pico_rgb_led

RP2350

Raspberry Pi Pico 2 (BBE)

rpi_pico2/rp2350a/m33_0/w/bbe

rpi_pico2/rp2350a/m33_0/bbe

rpi_pico2/rp2350a/hazard3_0/bbe

seengreat_pico_rgb_matrix_pes1

waveshare_pico_rgb_led

Building and Running

  • On RP2350-Matrix board, on ARM Cortex-M33:

    west build -b waveshare_rp2350_matrix/rp2350a/m33_0 -p -S "usb-console" -d build/colorwheel-waveshare_rp2350_matrix bridle/samples/display/colorwheel
    west flash -r uf2 -d build/colorwheel-waveshare_rp2350_matrix
    
  • On RP2350-Matrix board, on Hazard3 RISC-V (RV32IMAC+):

    west build -b waveshare_rp2350_matrix/rp2350a/hazard3_0 -p -S "usb-console" -d build/colorwheel-waveshare_rp2350_matrix bridle/samples/display/colorwheel
    west flash -r uf2 -d build/colorwheel-waveshare_rp2350_matrix
    
  • On Waveshare Pico RGB LED shield, on a Raspberry Pi Pico:

    west build -b rpi_pico/rp2040/bbe -p -S "usb-console" --shield waveshare_pico_rgb_led -d build/colorwheel-rpi_pico bridle/samples/display/colorwheel
    west flash -r uf2 -d build/colorwheel-rpi_pico
    

Sample output

The following output is logged on the UART console, here for the 8×8 panel of the RP2350-Matrix board:

*** Booting Zephyr OS build v4.5.0… ***
[00:00:00.003,000] <inf> colorwheel: Color wheel on a 8x8 RGB matrix, 4 bytes per pixel, 40 ms per frame

Troubleshooting

The panel stays dark

Check that the board assigns a matrix to the zephyr,display chosen node. The build fails when no such node exists, but a board that chooses a different kind of display reports an unsupported pixel format at run time instead.

The panel flickers or the board resets

The LED strip is almost certainly browning out the supply. Lower CONFIG_COLORWHEEL_MAX_BRIGHTNESS, or feed the panel from a supply that can carry the current.

The centre of the wheel is not white

The centre should be a bright white core on any panel. If it is not, the matrix is most likely cropped by a width or height smaller than the physical panel, which moves the geometric centre off the true middle. Check the width and height properties of the matrix node against the panel.

Dependencies

This sample uses the following Zephyr libraries:

Known issues and limitations

The sample redraws and rewrites the whole panel on every frame, even when CONFIG_COLORWHEEL_HUE_STEP is 0 and nothing has changed. This keeps the code simple at the cost of some unnecessary traffic to the LED strip.

References