LedStrip
A controller for strips of individually controlled RGB LEDs.
About
Light programs
With 1 mbit Jacdac, we can transmit under 2k of data per animation frame (at 20fps). If transmitting raw data that would be around 500 pixels, which is not enough for many installations and it would completely clog the network.
Thus, light service defines a domain-specific language for describing light animations and efficiently transmitting them over wire. For short LED displays, less than 64 LEDs, you can also use the LED service.
Light commands are not Jacdac commands.
Light commands are efficiently encoded as sequences of bytes and typically sent as payload
of run
command.
Definitions:
P
- position in the stripR
- number of repetitions of the commandN
- number of pixels affected by the commandC
- single color designationC+
- sequence of color designations
Update modes:
0
- replace1
- add RGB2
- subtract RGB3
- multiply RGB (by c/128); each pixel value will change by at least 1
Program commands:
0xD0: setall C+
- set all pixels in current range to given color pattern0xD1: fade C+
- set pixels in current range to colors between colors in sequence0xD2: fadehsv C+
- similar tofade()
, but colors are specified and faded in HSV0xD3: rotfwd K
- rotate (shift) pixels byK
positions away from the connector0xD4: rotback K
- same, but towards the connector0xD5: show M=50
- send buffer to strip and waitM
milliseconds0xD6: range P=0 N=length W=1 S=0
- range from pixelP
,N
pixels long (currently unsupported: everyW
pixels skipS
pixels)0xD7: mode K=0
- set update mode0xD8: tmpmode K=0
- set update mode for next command only0xCF: setone P C
- set one pixel atP
(in current range) to given colormult V
- macro to multiply current range by given value (float)
A number k
is encoded as follows:
0 <= k < 128
->k
128 <= k < 16383
->0x80 | (k >> 8), k & 0xff
- bigger and negative numbers are not supported
Thus, bytes 0xC0-0xFF
are free to use for commands.
Formats:
0xC1, R, G, B
- single color parameter0xC2, R0, G0, B0, R1, G1, B1
- two color parameter0xC3, R0, G0, B0, R1, G1, B1, R2, G2, B2
- three color parameter0xC0, N, R0, G0, B0, ..., R(N-1), G(N-1), B(N-1)
-N
color parameter0xCF, <number>, R, G, B
-set1
special format
Commands are encoded as command byte, followed by parameters in the order from the command definition.
The setone()
command has irregular encoding to save space - it is byte 0xCF
followed by encoded
number, and followed by 3 bytes of color.
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
Commands
run
Run the given light "program". See service description for details.
ledStrip.run(program: Buffer): Promise<void>
Registers
intensity
Set the luminosity of the strip.
At 0
the power to the strip is completely shut down.
type:
Register<number>
(packing formatu0.8
)read and write
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
const value = await ledStrip.intensity.read()
await ledStrip.intensity.write(value)
- track incoming values
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
ledStrip.intensity.subscribe(async (value) => {
...
})
write
and read
will block until a server is bound to the client.
actualBrightness
This is the luminosity actually applied to the strip.
May be lower than brightness
if power-limited by the max_power
register.
It will rise slowly (few seconds) back to brightness
is limits are no longer required.
type:
Register<number>
(packing formatu0.8
)read only
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
const value = await ledStrip.actualBrightness.read()
- track incoming values
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
ledStrip.actualBrightness.subscribe(async (value) => {
...
})
write
and read
will block until a server is bound to the client.
lightType
Specifies the type of light strip connected to controller. Controllers which are sold with lights should default to the correct type and could not allow change.
type:
Register<number>
(packing formatu8
)read and write
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
const value = await ledStrip.lightType.read()
await ledStrip.lightType.write(value)
- track incoming values
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
ledStrip.lightType.subscribe(async (value) => {
...
})
write
and read
will block until a server is bound to the client.
numPixels
Specifies the number of pixels in the strip. Controllers which are sold with lights should default to the correct length and could not allow change. Increasing length at runtime leads to ineffective use of memory and may lead to controller reboot.
type:
Register<number>
(packing formatu16
)read and write
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
const value = await ledStrip.numPixels.read()
await ledStrip.numPixels.write(value)
- track incoming values
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
ledStrip.numPixels.subscribe(async (value) => {
...
})
write
and read
will block until a server is bound to the client.
numColumns
If the LED pixel strip is a matrix, specifies the number of columns. Otherwise, a square shape is assumed. Controllers which are sold with lights should default to the correct length and could not allow change. Increasing length at runtime leads to ineffective use of memory and may lead to controller reboot.
type:
Register<number>
(packing formatu16
)optional: this register may not be implemented
read and write
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
const value = await ledStrip.numColumns.read()
await ledStrip.numColumns.write(value)
- track incoming values
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
ledStrip.numColumns.subscribe(async (value) => {
...
})
write
and read
will block until a server is bound to the client.
maxPower
Limit the power drawn by the light-strip (and controller).
type:
Register<number>
(packing formatu16
)read and write
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
const value = await ledStrip.maxPower.read()
await ledStrip.maxPower.write(value)
- track incoming values
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
ledStrip.maxPower.subscribe(async (value) => {
...
})
write
and read
will block until a server is bound to the client.
maxPixels
The maximum supported number of pixels.
All writes to num_pixels
are clamped to max_pixels
.
type:
Register<number>
(packing formatu16
)constant: the register value will not change (until the next reset)
read only
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
const value = await ledStrip.maxPixels.read()
write
and read
will block until a server is bound to the client.
numRepeats
How many times to repeat the program passed in run
command.
Should be set before the run
command.
Setting to 0
means to repeat forever.
type:
Register<number>
(packing formatu16
)read and write
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
const value = await ledStrip.numRepeats.read()
await ledStrip.numRepeats.write(value)
- track incoming values
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
ledStrip.numRepeats.subscribe(async (value) => {
...
})
write
and read
will block until a server is bound to the client.
variant
Specifies the shape of the light strip.
type:
Register<number>
(packing formatu8
)optional: this register may not be implemented
constant: the register value will not change (until the next reset)
read only
import { LedStrip } from "@devicescript/core"
const ledStrip = new LedStrip()
// ...
const value = await ledStrip.variant.read()
write
and read
will block until a server is bound to the client.
Syntax
The input is split at spaces. The following tokens are supported:
- a command name (see below)
- a decimal number (0-16383)
- a color, in HTML syntax '#ff0000' for red, etc
- a single '#' which will take color (24-bit number) from list of arguments; the list of arguments has an array or colors it will encode all elements of the array
- a single '%' which takes a number (0-16383) from the list of arguments
Commands
setall C+
- set all pixels in current range to given color patternfade C+
- set pixels in current range to colors between colors in sequencefadehsv C+
- similar tofade()
, but colors are specified and faded in HSVrotfwd K
- rotate (shift) pixels byK
positions away from the connectorrotback K
- same, but towards the connectorshow M=50
- send buffer to strip and waitM
millisecondsrange P=0 N=length W=1 S=0
- range from pixelP
,N
pixels long (currently unsupported: everyW
pixels skipS
pixels)mode K=0
- set update modetmpmode K=0
- set update mode for next command onlysetone P C
- set one pixel atP
(in current range) to given colormult V
- macro to multiply current range by given value (float)C+
means one or more colorsV
is a floating point numberOther letters (
K
,M
,N
,P
,W
,S
) represent integers, with their default values if omitted
Examples
import { LedStrip } from "@devicescript-core"
const led = new LedStrip()
// turn off all lights
await led.runEncoded("setall #000000")
// the same
await led.runEncoded("setall #", 0)
// set first pixel to red, last to blue, and interpolate the ones in between
await led.runEncoded("fade # #", 0xff0000, 0x0000ff)
// the same; note the usage of an array []
await led.runEncoded("fade #", [0xff0000, 0x0000ff])
// set pixels 2-7 to white
await led.runEncoded("range 2 5 setall #ffffff")
// the same
await led.runEncoded("range % % setall #", 2, 5, 0xffffff)