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

RP2350

Waveshare RP2350-Matrix

  • waveshare_rp2350_matrix/rp2350a/m33

  • waveshare_rp2350_matrix/rp2350a/hazard3

RP2040

Waveshare RP2040-Plus

  • waveshare_rp2040_plus/rp2040

RP2040

Waveshare RP2040-Matrix

  • waveshare_rp2040_matrix/rp2040

RP2040

Raspberry Pi Pico (BBE)

  • rpi_pico/rp2040/bbe

RP2350

Raspberry Pi Pico 2 (BBE)

  • rpi_pico2/rp2350a/m33/bbe

  • rpi_pico2/rp2350a/hazard3/bbe

Building and Running

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

    west build -b waveshare_rp2350_matrix/rp2350a/m33 -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 -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.4.99 ***
[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