piixel
Version:
Control WS281X / NeoPixel LEDs from a Raspberry Pi using Node.js and TypeScript
266 lines (237 loc) • 5.9 kB
text/typescript
import {bindings} from './bindings'
/**
* @public
* LED strip type identifiers
* See {@link https://github.com/jgarff/rpi_ws281x/blob/master/ws2811.h#L46}
*/
export enum StripType {
// 4 color R, G, B and W ordering
SK6812_STRIP_RGBW = 0x18100800,
SK6812_STRIP_RBGW = 0x18100008,
SK6812_STRIP_GRBW = 0x18081000,
SK6812_STRIP_GBRW = 0x18080010,
SK6812_STRIP_BRGW = 0x18001008,
SK6812_STRIP_BGRW = 0x18000810,
// 3 color R, G and B ordering,
WS2811_STRIP_RGB = 0x00100800,
WS2811_STRIP_RBG = 0x00100008,
WS2811_STRIP_GRB = 0x00081000,
WS2811_STRIP_GBR = 0x00080010,
WS2811_STRIP_BRG = 0x00001008,
WS2811_STRIP_BGR = 0x00000810,
// predefined fixed LED types,
WS2812_STRIP = WS2811_STRIP_GRB,
SK6812_STRIP = WS2811_STRIP_GRB,
SK6812W_STRIP = SK6812_STRIP_GRBW,
}
/**
* @public
* PWM0, which can be set to use GPIOs 12, 18, 40, and 52.
* Only 12 (pin 32) and 18 (pin 12) are available on the B+/2B/3B
* See {@link https://github.com/jgarff/rpi_ws281x?tab=readme-ov-file#pwm}
*/
export type PWM0Gpio = 12 | 18 | 40 | 52
/**
* @public
* PWM1 which can be set to use GPIOs 13, 19, 41, 45 and 53.
* Only 13 is available on the B+/2B/PiZero/3B, on pin 33
* See {@link https://github.com/jgarff/rpi_ws281x?tab=readme-ov-file#pwm}
*/
export type PWM1Gpio = 13 | 19 | 41 | 45 | 53
/**
* @public
* PCM_DOUT, which can be set to use GPIOs 21 and 31.
* Only 21 is available on the B+/2B/PiZero/3B, on pin 40.
* See {@link https://github.com/jgarff/rpi_ws281x?tab=readme-ov-file#pcm}
*/
export type PCMGpio = 21 | 31
/**
* @public
* SPI0-MOSI is available on GPIOs 10 and 38.
* Only GPIO 10 is available on all models.
* See {@link https://github.com/jgarff/rpi_ws281x?tab=readme-ov-file#spi}
*/
export type SPIGpio = 10 | 38
/**
* @public
*
* Valid GPIOs to use with the Ws281x library
*/
export type ValidGPIO = PWM0Gpio | PWM1Gpio | PCMGpio | SPIGpio
/**
* @public
* Configuration options for the Ws281x library
*/
export interface Ws281xConfig {
/**
* @public
* Number of LEDs to control
*/
leds: number
/**
* @public
* Set the GPIO number to communicate with the Neopixel strip (default 18, PWM0)
*/
gpio?: ValidGPIO
/**
* @public
* Reset the LEDs on process exit (default false)
*/
resetOnExit?: boolean
/**
* @public
* DMA channel to use (default 10)
* See {@link https://github.com/jgarff/rpi_ws281x/blob/master/README.md#important-warning-about-dma-channels}
*/
dma?: number
/**
* @public
* Strip type (default {@link StripType.WS2811_STRIP_GRB})
*/
type?: StripType
}
/**
* @public
* Options for rendering pixels to the LED strip
*/
export interface RenderOptions {
/**
* @public
* Pixels to render to the LED strip
*/
pixels?: Uint32Array
/**
* @public
* Brightness of the LED strip (0.0 - 1.0)
*/
brightness?: number
}
/**
* @public
* The Ws281x API
*/
export interface Ws281xAPI {
/**
* @public
* Configure the library
*
* @param options - configuration options
*/
configure(options: Ws281xConfig): void
/**
* @public
* Clear the LEDs (set all to off)
*/
clear(): void
/**
* @public
* Render the given pixels to the LED strip
*/
render(renderOptions: RenderOptions): void
/**
* @public
* Render the given pixels to the LED strip
*/
render(pixels: Uint32Array): void
/**
* @public
* Reset the library and release resources
*/
reset(): void
}
/**
* @internal
* The Ws281x class
*/
class Ws281xImpl implements Ws281xAPI {
#leds?: number = undefined
#resetOnExit = false
constructor() {
process.once('exit', () => {
if (this.#resetOnExit) {
this.clear()
}
})
}
configure({resetOnExit, ...options}: Ws281xConfig) {
if (this.#leds !== undefined) {
throw new Error(
'ws281x is already configured. Call ws281x.reset() first!',
)
}
this.#resetOnExit = resetOnExit ?? false
this.#leds = options.leds
bindings.configure(options)
}
reset() {
if (this.#leds !== undefined) {
this.clear()
bindings.reset()
}
this.#leds = undefined
}
clear() {
if (this.#leds !== undefined) {
this.render(new Uint32Array(this.#leds))
}
}
render(pixelsOrOpts: Uint32Array | RenderOptions) {
if (pixelsOrOpts instanceof Uint32Array) {
this.#render({pixels: pixelsOrOpts})
} else {
this.#render(pixelsOrOpts)
}
}
#render(options: RenderOptions) {
if (this.#leds === undefined) {
throw new Error('Must call configure() before render()')
}
const {pixels, brightness} = options
const ops: (() => void)[] = []
if (typeof pixels !== 'undefined') {
if (pixels.length !== this.#leds) {
throw new Error(
`Size of pixels array must match number of LEDs (expected: ${this.#leds}, got: ${pixels.length})`,
)
}
ops.push(() => bindings.setPixels(pixels))
}
if (brightness !== undefined) {
ops.push(() => bindings.setBrightness(brightness))
}
if (ops.length === 0) {
// nothing to do
return
}
ops.forEach(op => op())
bindings.render()
}
}
/**
* @public
* Singleton instance of Ws281x
* @example
*
* ```typescript
* import {colorwheel, StripType, ws281x} from 'piixel'
*
* const LEDS = 16
*
* // Configure the library. Must be called before calling `render`.
* ws281x.configure({
* gpio: 18,
* leds: LEDS,
* type: StripType.WS2811_STRIP_GRB,
* })
*
* const pixels = new Uint32Array(LEDS)
* for (let i = 0; i < LEDS; i++) {
* pixels[i] = colorwheel((i * 256) / LEDS)
* }
* // Render pixels to the LED strip
* ws281x.render(pixels)
* ```
*
* See more examples here: {@link https://github.com/bjoerge/piixel/tree/main/examples}
*/
export const ws281x: Ws281xAPI = new Ws281xImpl()