Skip to main content

Neopixel LED and Button Sample

Introduction

The Neopixel LED and Button sample cycles through WS2812 Neopixel LED colors on each button press. The LED and button are initialized using the board's device tree. This sample is compatible with the following boards:

Download

Neopixel LED and ButtonNCS v3.2.1
NOTE

The Neopixel LED stores its last state even after removing this sample from the board. This can cause the LED to stay on when flashed with other firmware. To turn it off, press the reset button while this sample is on the board, or re-flash this sample (the LED is turned off at startup).

Project configuration

The application is configured in the prj.conf file:

CONFIG_GPIO=y
CONFIG_LED_STRIP=y
CONFIG_WS2812_STRIP_SPI=y
CONFIG_SPI=y

CONFIG_GPIO enables the GPIO driver, needed to read button input.

CONFIG_LED_STRIP and CONFIG_WS2812_STRIP_SPI enable the LED strip driver and the WS2812-compatible SPI driver.

CONFIG_SPI enables the SPI bus, which is used to communicate with the LED strip.

The sysbuild.conf file enables MCUBoot so the sample can be flashed over USB:

SB_CONFIG_BOOTLOADER_MCUBOOT=y

Code explanation

Device tree references

The GPIO controller is obtained using its node label. The button and LED strip are accessed through their device tree aliases:

#define GPIO_NODE		DT_NODELABEL(gpio0)
#define BUTTON_NODE DT_ALIAS(sw0)
#define STRIP_NODE DT_ALIAS(led_strip)
#define STRIP_NUM_PIXELS DT_PROP(DT_ALIAS(led_strip), chain_length)

static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(BUTTON_NODE, gpios);
static const struct device *const strip = DEVICE_DT_GET(STRIP_NODE);

STRIP_NUM_PIXELS is read from the device tree chain_length property, which is 1 for the single on-board Neopixel.

Button configuration

The button is configured as an input with an interrupt callback in the init_button function:

bool init_button(void)
{
int ret = gpio_pin_configure_dt(&button, GPIO_INPUT);

if (ret != 0) {
printk("Error %d: failed to configure %s pin %d\n", ret,
button.port->name, button.pin);
return false;
}

ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE);
if (ret != 0) {
printk("Error %d: failed to configure interrupt on %s pin %d\n", ret,
button.port->name, button.pin);
return false;
}

gpio_init_callback(&gpio_cb, button_pressed_callback, BIT(button.pin));
gpio_add_callback(button.port, &gpio_cb);

return true;
}

The callback sets a flag that the main loop checks:

void button_pressed_callback(const struct device *gpiob, struct gpio_callback *cb,
gpio_port_pins_t pins)
{
button_pressed = true;
}

LED strip initialization

Since the Neopixel LED is connected via SPI, it does not need GPIO configuration. A readiness check is performed, and the LED is turned off at startup:

if (!device_is_ready(strip)) {
printk("LED strip device %s is not ready\n", strip->name);
return -1;
}

memset(&pixels, 0x00, sizeof(pixels));
rc = led_strip_update_rgb(strip, pixels, STRIP_NUM_PIXELS);

Color cycling

Six colors are defined using RGB values:

#define RGB(_r, _g, _b) { .r = (_r), .g = (_g), .b = (_b) }

static const struct led_rgb colors[] = {
RGB(0x0f, 0x00, 0x00), /* red */
RGB(0x00, 0x0f, 0x00), /* green */
RGB(0x00, 0x00, 0x0f), /* blue */
RGB(0x0f, 0x00, 0x0f), /* magenta */
RGB(0x00, 0x0f, 0x0f), /* cyan */
RGB(0x0f, 0x0f, 0x00), /* yellow */
};

Main loop

On each button press, the pixel buffer is cleared, the next color is copied in, and the LED strip is updated:

while (1) {
if (button_pressed) {
memset(&pixels, 0x00, sizeof(pixels));
memcpy(&pixels, &colors[color], sizeof(pixels));
rc = led_strip_update_rgb(strip, pixels, STRIP_NUM_PIXELS);

if (rc) {
printk("Couldn't update strip: %d\n", rc);
}

color++;
if (color >= ARRAY_SIZE(colors)) {
color = 0;
}
button_pressed = false;
}

k_sleep(K_MSEC(100));
}

The color index wraps back to 0 after cycling through all six colors.

Was this page helpful?