Skip to content

rozek/raspi-pico-kaluma-neopixel

Repository files navigation

raspi-pico-kaluma-neopixel

Neopixel/WS2812 driver for Raspberry Pi Pico running Kaluma

The Raspberry Pi Pico is a cheap (but powerful) microcontroller board from Raspberry Pi (important: the Raspberry Pi Pico does not run Linux). Kaluma is a JavaScript runtime for the RasPi Pico with a web-based IDE, which may be run on any modern browser that supports the "Web Serial API" (important: you do not have to sign-in in order to use the IDE)

Surprisingly, Kaluma does not yet have a library that may be used to drive Neopixel displays (or other strips of WS2812 LEDs) - this little contribution shall fill this gap.

Just a small note: if you like this work and plan to use it, consider "starring" this repository (you will find the "Star" button on the top right of this page), so that I know which of my repositories to take most care of.

Technical Background

This code uses the first of the "Serial Programming Interfaces" (SPI) the RasPi Pico provides. With proper timing, such an SPI can be used to drive Neopixel displays (or compatible LED strips) - ioprog has a good article describing the technical background.

MOSI only

From the possible signals an SPI provides, only "Master Out Slave In" (MOSI) is used. The RasPi Pico allows that signal to be routed to multiple output pins (but not to any of them): by default, pin GP3 wil be used - alternatively, you may also choose pin GP19 if you modify the examples accordingly.

Nota bene: the Pimoroni Tiny 2040 already uses pin GP19 for its own built-in RGB LED - thus, on such a device, the default pin GP3 should be preferred.

Connecting a LED Stripe

Sometimes, LED stripes (which assume to be powered with 5V) may be directly wired to the RasPi Pico output pin (which provides 3v3 levels only) but usually, such a connection does not work reliably and may produce wrong LED patterns from time to time.

In such a case, a level shifter should be inserted between RasPi Pico and LED Stripe. This device does not have to be bidirectional but definitely must be fast since communication effectively runs with 800kHz. For that reason, a dedicated level shifting chip should be used (instead of the brilliant and, thus, widely used circuit consisting of a simple MOSFET and two resistors only). If it still has to be the latter one, you may try to "short-circuit" the MOSFET with a small ceramic capacitor (e.g., 22pF) but success is not guaranteed.

Memory Consumption

Internally, the driver allocates 3 bytes per Neopixel data byte in order to prepare the bit pattern to be sent through the SPI - thus, for each RGB LED a total of 9 bytes is used internally.

Byte Order

The library directly supports linear stripes as well as matrices wired in zigzag fashion. The assumed byte order of connected LEDs is GRB - but you do not have to care and may specify any color values in RGB order.

API

The "library" consists of a single function SPIDisplay which should be invoked to setup a driver for a given MOSI pin and LED geometry:

  • SPIDisplay (Pin, Width, Height)
    prepares an internal display storage for a LED matrix with the given dimension (omit Height if you have a linear stripe only) which is connected to the given Pin (set Pin to null or 3 if you want to use pin GP3 or 19 for pin GP19)

The output of this function is an object containing a few methods which may be used to prepare a display and send it to the LED stripe.

  • clear ()
    fills the internal display storage with the SPI bit pattern for dark LEDs
  • setPixelRGB (x,y, R,G,B)
    sets the LED at the given coordinate (x = 0...Width-1, y = 0...Height-1) to the given RGB values (R = 0...255, G = 0...255, B = 0...255)
  • setPixelHSL (x,y, H,S,L)
    sets the LED at the given coordinate (x = 0...Width-1, y = 0...Height-1) to RGB values which correspond to the given "Hue" (H = 0...1), "Saturation" (S = 0...1) and "Luminosity" (L = 0...1) values. Internally, this method uses the same conversion as HSLtoRGB and has been provided for your convenience only
  • HSLtoRGB (H,S,L)
    converts the given "Hue" (H = 0...1), "Saturation" (S = 0...1) and "Luminosity" (L = 0...1) values to corresponding RGB values (R = 0...255, G = 0...255, B = 0...255) and returns them as an array. The same conversion is also used by setPixelHSL, but has been provided as a separate method in case that you want to process the resulting RGB values further
  • show ()
    sends the current contents of the internal display storage to the connected LED stripe

Usage

In the simplest case, the contents of file SPIDisplay.js may just be copied into the Kaluma Web IDE and your code added below:

// <<<< insert SPIDisplay.js here

let Display = SPIDisplay(null, 16,16);
  Display.clear();

  Display.setPixelRGB(0,0, 16,0,0); // red
  Display.setPixelRGB(2,0, 0,16,0); // green
  Display.setPixelRGB(4,0, 0,0,16); // blue
Display.show();

Tests and Examples

The following image illustrates the expected wiring (without level shifter) for the tests and examples that come with this driver.

Please note, that the shown Neopixel display is just an example - please, consider the individual code examples for their actually expected LED geometry.

The following tests assume a LED string connected to Pin 19. Just copy them into your clipboard, paste them into the Kaluma Web IDE and "Upload":

  • RGB-Test.js
    sets pixel 0 to red, pixel 2 to green, 4 to blue and pixel 10 to yellow, 12 to magenta, 14 to cyan
  • HSL-Test.js
    fills pixels 0...15 with different colors

All examples assume a 16x16 RGB LED matrix connected to Pin 19. Just copy them into your clipboard, paste them into the Kaluma Web IDE and "Upload":

  • Intensity-Test-linear-non-dimmed.js
    fills a 16x16 LED matrix with a variety of colors with linearly increasing RGB values ranging from 0 to 255. Neopixel LEDs usually shine quite bright, perhaps too bright - try this example to see what I mean
  • Intensity-Test-linear-dimmed.js
    fills a 16x16 LED matrix with a variety of colors and linearly increasing RGB values ranging from 0 to 1/16th of the potential maximum. This example is like the previous one, but does not exhaust the complete intensity range of Neopixel LEDs
  • Intensity-Test-non-linear-non-dimmed.js
    fills a 16x16 LED matrix with a variety of colors and quadratically increasing RGB values ranging from 0 to 256. Linearly increasing the RGB values of a Neopixel LED does not lead to a linearly increasing brightness - this example therefore uses quadratically increasing RGB values which seem to do a better job
  • Intensity-Test-non-linear-dimmed.js
    fills a 16x16 LED matrix with a variety of colors and quadratically increasing intensity ranging from 0 to 1/16th of the potential maximum. This example is like the previous one, but does not exhaust the complete intensity range of Neopixel LEDs

License

MIT License

About

Neopixel driver for Raspberry Pi Pico running Kaluma

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published