Skip to content
This repository was archived by the owner on Jun 11, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions libs/core/_locales/core-jsdoc-strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@
"rgb.setColor": "Make the on-board RGB LED show an RGB color (range 0-255 for r, g, b).",
"rgb.setColor|param|rgb": "RGB color of the LED, eg: 0xff0000",
"rgb.setRGBLed": "Set the rgb led to a specific red, green, blue color.",
"rgb.setRGBStickerLed": "Sets an RGB sticker led to a specific red, green, blue color.",
"rgb.setRGBStickerLed|param|index": "the led index",
"rgb.setStickerColor": "Make an RGB sticker LED show an RGB color (range 0-255 for r, g, b).",
"rgb.setStickerColor|param|rgb": "RGB color of the LED, eg: 0xff0000",
"rgb.setStickerUniformColor": "Set all RGB LEDs to a specified color (range 0-255 for r, g, b).",
"rgb.setStickerUniformColor|param|rgb": "RGB color of the LEDs, eg: 0xff0000",
"rgb.wheel": "Converts wheel position into an RGB color",
"rgb.wheel|param|wheelPos": "value between 0 to 255 to get a color value, eg: 99",
"sensing": "Events and data from sensors",
Expand Down
2 changes: 2 additions & 0 deletions libs/core/_locales/core-strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
"rgb.rgb|block": "red %red|green %green|blue %blue",
"rgb.setBrightness|block": "set brightness %brightness",
"rgb.setColor|block": "set rgb to %rgb=colorNumberPicker",
"rgb.setStickerColor|block": "set rgb sticker with index %index| to %rgb=colorNumberPicker",
"rgb.setStickerUniformColor|block": "set %count| rgb stickers to to %rgb=colorNumberPicker",
"rgb.wheel|block": "color slider %wheelPos=colorWheelPicker",
"rgb|block": "rgb",
"sensing.onPinEvent|block": "when %name=digital_pin|is %event",
Expand Down
90 changes: 88 additions & 2 deletions libs/core/rgb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,63 @@ struct pixels {

extern "C" void ledShow(uint32_t pin, void *pixels, uint32_t num_leds);

// We're limited by memory, and also by current. Limit us to 20 LEDs for now.
#define EXTERNAL_LED_LIMIT 20

/**
* Provides access to basic RGB LED functionality.
*/
//% color=#CF63CF weight=80 icon="\uf00a"
namespace rgb {

/**
* Get a persistent memory buffer big enough to hold
* at least @count LEDs.
* @param count the minimum number of LEDs
* Note that you can call this function with a larger @count
* and the resulting buffer will still contain previous values,
* but new values will be 0-filled.
*/
static struct pixels *getRgbBuffer(unsigned char count) {
static unsigned char rgb_led_count;
static struct pixels *rgb_buffer;

// Don't allow us to exceed this hard limit.
if (count > EXTERNAL_LED_LIMIT)
return NULL;

// Add more LEDs to our back buffer, if necessary
if ((count+1) > rgb_led_count) {

// [Re-]allocate the pixel buffer
rgb_buffer = (struct pixels *)realloc((void *)rgb_buffer, (count + 1) * sizeof(*rgb_buffer));
if (!rgb_buffer) {
return NULL;
}

// Ensure any gaps in LEDs are turned off, and not filled
// with random uninitialized memory.
unsigned int clear_idx;
for (clear_idx = rgb_led_count; clear_idx <= count; clear_idx++) {
rgb_buffer[clear_idx].r = 0;
rgb_buffer[clear_idx].g = 0;
rgb_buffer[clear_idx].b = 0;
}

// Update the total LED count.
rgb_led_count = count + 1;
}

return rgb_buffer;
}

static bool colorIsValid(int r, int g, int b) {
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
return false;
}
return true;
}

/**
* Set the rgb led to a specific red, green, blue color.
* @param red the red color
Expand All @@ -24,14 +75,49 @@ namespace rgb {
*/
//% parts="rgbled"
void setRGBLed(int r, int g, int b) {
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {

if (!colorIsValid(r, g, b)) {
return;
}

struct pixels pixels[1];
// The onboard RGB LED is the first LED of the offboard
// RGB buffer. Treat it like a chain of 1.
struct pixels *pixels = getRgbBuffer(1);
if (!pixels)
return;

pixels[0].r = r;
pixels[0].g = g;
pixels[0].b = b;
ledShow(LED_BUILTIN_RGB, pixels, 1);
}

/**
* Sets an RGB sticker led to a specific red, green, blue color.
* @param index the led index
* @param red the red color
* @param green the green color
* @param blue the blue color
*/
//% parts="rgbled"
void setRGBStickerLed(uint32_t index, int r, int g, int b) {

if (!colorIsValid(r, g, b)) {
return;
}

// Note that RGB Sticker LEDs are 1-indexed, because the
// onboard LED is index 0. For example, if you call
// setRGBStickerLed(0, 255, 255, 255), then this will
// set the first offboard LED to full-white.
struct pixels *pixels = getRgbBuffer(index + 1);
if (!pixels)
return;

pixels[index + 1].r = r;
pixels[index + 1].g = g;
pixels[index + 1].b = b;

ledShow(LED_BUILTIN_RGB, pixels, index + 1);
}
}
41 changes: 41 additions & 0 deletions libs/core/rgb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,47 @@ namespace rgb {
return rgb(255 - wheelPos * 3, wheelPos * 3, 255);
}

/**
* Set all RGB LEDs to a specified color (range 0-255 for r, g, b).
* @param rgb RGB color of the LEDs, eg: 0xff0000
*/
//% blockId="rgb_sticker_set_uniform_colors" block="set %count| rgb stickers to to %rgb=colorNumberPicker"
//% weight=90 help="rgb/set-color"
export function setStickerUniformColor(count: number, rgb: number) {
if (_brightness == undefined) {
_brightness = 20;
}

rgb = fade(rgb, _brightness);
let red = unpackR(rgb);
let green = unpackG(rgb);
let blue = unpackB(rgb);

let index = 0;
for (index = 0; index < count; index++) {
setRGBStickerLed(index, red, green, blue);
}
}

/**
* Make an RGB sticker LED show an RGB color (range 0-255 for r, g, b).
* @param rgb RGB color of the LED, eg: 0xff0000
*/
//% blockId="rgb_sticker_set_color" block="set rgb sticker with index %index| to %rgb=colorNumberPicker"
//% weight=90 help="rgb/set-color"
export function setStickerColor(index: number, rgb: number) {
if (_brightness == undefined) {
_brightness = 20;
}

rgb = fade(rgb, _brightness);
let red = unpackR(rgb);
let green = unpackG(rgb);
let blue = unpackB(rgb);

setRGBStickerLed(index, red, green, blue);
}

/**
* Get the RGB value of a known color
*/
Expand Down
10 changes: 10 additions & 0 deletions libs/core/shims.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ declare namespace rgb {
*/
//% parts="rgbled" shim=rgb::setRGBLed
function setRGBLed(r: int32, g: int32, b: int32): void;

/**
* Sets an RGB sticker led to a specific red, green, blue color.
* @param index the led index
* @param red the red color
* @param green the green color
* @param blue the blue color
*/
//% parts="rgbled" shim=rgb::setRGBStickerLed
function setRGBStickerLed(index: uint32, r: int32, g: int32, b: int32): void;
}

// Auto-generated. Do not edit. Really.
3 changes: 3 additions & 0 deletions sim/ltcboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ namespace pxsim {
serialState: LtcSerialState;
// TODO: not singletons
neopixelState: NeoPixelState;
rgbStickerState: RGBStickerState;

constructor() {
super()
Expand Down Expand Up @@ -94,6 +95,8 @@ namespace pxsim {

this.builtinPartVisuals["buttonpair"] = (xy: visuals.Coord) => visuals.mkBtnSvg(xy);
this.builtinPartVisuals["neopixel"] = (xy: visuals.Coord) => visuals.mkNeoPixelPart(xy);

this.rgbStickerState = new RGBStickerState();
}

receiveMessage(msg: SimulatorMessage) {
Expand Down
71 changes: 71 additions & 0 deletions sim/state/rgb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,75 @@ namespace pxsim.rgb {
led.updateBuffer(newColor, 0);
runtime.queueDisplayUpdate();
}

export function setRGBStickerLed(index: number, r: number, g: number, b: number): void {
const state = board().rgbStickerState;
const name = 1;
state.setLED(name, index, r, g, b);
runtime.queueDisplayUpdate();
}
}

namespace pxsim {
export class RGBStickerState {
protected buffers: Uint8Array[] = [];
protected lengths: number[] = [];
protected colors: Map<RGBW[]> = {};
protected dirty: Map<boolean> = {};

public setLED(pin: number, index: number, r: number, g: number, b: number) {
let buf = this.buffers[pin];

if (!buf) buf = new Uint8Array(60);

const start = index * 3;

buf[start] = g;
buf[start + 1] = r;
buf[start + 2] = b;

this.lengths[pin] = Math.max(this.lengths[pin] || 0, index + 1);

this.updateBuffer(buf, pin);
}

public getColors(pin: number): RGBW[] {
let outColors = this.colors[pin] || (this.colors[pin] = []);
if (this.dirty[pin]) {
let buf = this.buffers[pin] || (this.buffers[pin] = new Uint8Array([]));
this.readNeoPixelBuffer(buf, outColors, NeoPixelMode.RGB, this.lengths[pin]);
this.dirty[pin] = false;
}
return outColors;
}

usedPins(): number[] {
const names: number[] = [];
this.buffers.filter((buf, index) => {
if (buf) names.push(index);
})
return names;
}

protected updateBuffer(buffer: Uint8Array, pin: number) {
this.buffers[pin] = buffer;
this.dirty[pin] = true;
}

private readNeoPixelBuffer(inBuffer: Uint8Array, outColors: RGBW[], mode: NeoPixelMode, pixelCount = 0) {
let buf = inBuffer;
let stride = mode === NeoPixelMode.RGBW ? 4 : 3;
pixelCount = pixelCount || Math.floor(buf.length / stride);
for (let i = 0; i < pixelCount; i++) {
// NOTE: for whatever reason, NeoPixels pack GRB not RGB
let r = buf[i * stride + 1]
let g = buf[i * stride + 0]
let b = buf[i * stride + 2]
let w = 0;
if (stride === 4)
w = buf[i * stride + 3]
outColors[i] = [r, g, b, w]
}
}
}
}
Loading