Skip to content
This repository was archived by the owner on Jun 11, 2026. It is now read-only.
Draft
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
15 changes: 15 additions & 0 deletions libs/core/rgb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,19 @@ namespace rgb {
pixels[0].b = b;
ledShow(LED_BUILTIN_RGB, pixels, 1);
}

/**
* Sets an RGB sticker led to a specific red, green, blue color.
* @param name the pin name
* @param index the lef index
* @param red the red color
* @param green the green color
* @param blue the blue color
*/
//% parts="rgbled"
void setRGBStickerLed(int name, int index, int r, int g, int b) {
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
return;
}
}
}
22 changes: 21 additions & 1 deletion libs/core/rgb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace rgb {

/**
* Make the on-board RGB LED show an RGB color (range 0-255 for r, g, b).
* @param rgb RGB color of the LED, eg: Colors.Red
* @param rgb RGB color of the LED, eg: 0xff0000
*/
//% blockId="rgb_set_color" block="set rgb to %rgb=colorNumberPicker"
//% weight=90 help="rgb/set-color"
Expand Down Expand Up @@ -116,6 +116,26 @@ namespace rgb {
return rgb(255 - wheelPos * 3, wheelPos * 3, 255);
}


/**
* 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 on %pin| with index %index| to %rgb=colorNumberPicker"
//% weight=90 help="rgb/set-color"
export function setStickerColor(pin: DigitalPin, 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(pin, index, red, green, blue);
}

/**
* Get the RGB value of a known color
*/
Expand Down
11 changes: 11 additions & 0 deletions libs/core/shims.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,17 @@ 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 name the pin name
* @param index the lef index
* @param red the red color
* @param green the green color
* @param blue the blue color
*/
//% parts="rgbled" shim=rgb::setRGBStickerLed
function setRGBStickerLed(name: int32, index: int32, 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
70 changes: 70 additions & 0 deletions sim/state/rgb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,74 @@ namespace pxsim.rgb {
led.updateBuffer(newColor, 0);
runtime.queueDisplayUpdate();
}

export function setRGBStickerLed(name: number, index: number, r: number, g: number, b: number): void {
const state = board().rgbStickerState;
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]
}
}
}
}
55 changes: 39 additions & 16 deletions sim/visuals/ltc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,31 +243,15 @@ namespace pxsim.visuals {
private style: SVGStyleElement;
private defs: SVGDefsElement;
private g: SVGGElement;

private logos: SVGElement[];
private head: SVGGElement; private headInitialized = false;
private headText: SVGTextElement;
private display: SVGElement;
private buttons: SVGElement[];
private buttonsOuter: SVGElement[];
private buttonABText: SVGTextElement;
private pins: SVGElement[];
private pinLabels: SVGElement[];
private pinGradients: SVGLinearGradientElement[];
private pinTexts: SVGTextElement[];
private ledsOuter: SVGElement[];
private leds: SVGRectElement[];
private rgbLed: SVGCircleElement;
private systemLed: SVGCircleElement;
private antenna: SVGPolylineElement;
private lightLevelButton: SVGCircleElement;
private lightLevelGradient: SVGLinearGradientElement;
private lightLevelText: SVGTextElement;
private thermometerGradient: SVGLinearGradientElement;
private thermometer: SVGRectElement;
private thermometerText: SVGTextElement;
private shakeButton: SVGCircleElement;
private shakeText: SVGTextElement;
public board: pxsim.LtcBoard;
private pinNmToCoord: Map<Coord> = {};

Expand All @@ -277,6 +261,8 @@ namespace pxsim.visuals {
private scopeTextNode2: SVGTextElement;
private scopeTextNode3: SVGTextElement;

private ledStickers: RGBStickerStrip[];

constructor(public props: IBoardProps) {
this.recordPinCoords();
this.buildDom();
Expand Down Expand Up @@ -345,6 +331,7 @@ namespace pxsim.visuals {
this.updateTemperature();
this.updateRgbLed();
this.updateSerial();
this.updateRGBStickers();

if (!runtime || runtime.dead) svg.addClass(this.element, "grayscale");
else svg.removeClass(this.element, "grayscale");
Expand Down Expand Up @@ -494,6 +481,36 @@ namespace pxsim.visuals {
}
}

private updateRGBStickers() {
const state = this.board.rgbStickerState;
state.usedPins().forEach(pin => {
let rgbStrip = this.ledStickers[pin];
if (!rgbStrip) {
rgbStrip = (this.ledStickers[pin] = new RGBStickerStrip());
this.g.appendChild(rgbStrip.getSVG());
svg.hydrate(this.element, {
"version": "1.0",
"viewBox": `0 0 230 250`,
"class": "sim",
"x": "112.5px",
"y": "0px",
"width": "100%",
"height": "100%"
});

rgbStrip.moveTo(45, 140);
rgbStrip.setDataPinLocation([20 + 24 * (pin + 1), 110])
rgbStrip.setGroundPinLocation([20, 110])
rgbStrip.setPowerPinLocation([208, 110])
}
const colors = state.getColors(pin);
for (let i = 0; i < colors.length; i++) {
const color = colors[i];
rgbStrip.setLED(i, color)
}
});
}

private updateSerial() {
let state = this.board;
if (!state || !state.serialState) return;
Expand Down Expand Up @@ -663,6 +680,10 @@ namespace pxsim.visuals {
svg.child(neopixelmerge, "feMergeNode", { in: "coloredBlur" })
svg.child(neopixelmerge, "feMergeNode", { in: "SourceGraphic" })

let smallerNeopixelGlow = svg.child(this.defs, "filter", { id: "smallneopixelglow", x: "-200%", y: "-200%", width: "400%", height: "400%" });
svg.child(smallerNeopixelGlow, "feGaussianBlur", { stdDeviation: "2", result: "coloredBlur" });
smallerNeopixelGlow.appendChild(neopixelmerge.cloneNode(true));

let ledglow = svg.child(this.defs, "filter", { id: "ledglow", x: "-200%", y: "-200%", width: "400%", height: "400%" });
svg.child(ledglow, "feGaussianBlur", { stdDeviation: "3", result: "coloredBlur" });
let ledglowmerge = svg.child(ledglow, "feMerge", {});
Expand Down Expand Up @@ -707,6 +728,8 @@ namespace pxsim.visuals {
133,
157
].map(x => <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin no-drag", x: x + 7, y: 125, textAnchor: "middle" }));

this.ledStickers = [];
}

private attachEvents() {
Expand Down
1 change: 1 addition & 0 deletions sim/visuals/rgb_single.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading