From cfd7a63360a1f955bc9e303dfebdb0841f78f701 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 05:42:39 +0200 Subject: [PATCH 01/31] some renamings --- packages/runtime/src/colors.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/runtime/src/colors.ts b/packages/runtime/src/colors.ts index 5cc8b4cbe2..947de5ea4d 100644 --- a/packages/runtime/src/colors.ts +++ b/packages/runtime/src/colors.ts @@ -159,13 +159,14 @@ export class PixelBuffer { * @param color RGB color */ setColor(pixeloffset: number, color: number) { + pixeloffset = pixeloffset | 0 while (pixeloffset < 0) pixeloffset += this.length const i = this.start + (pixeloffset << 0) if (i < this.start || i >= this.start + this.length) return const bi = i * 3 - this.buffer.setAt(bi, "u8", (color >> 16) & 0xff) - this.buffer.setAt(bi + 1, "u8", (color >> 8) & 0xff) - this.buffer.setAt(bi + 2, "u8", color & 0xff) + this.buffer[bi] = (color >> 16) & 0xff + this.buffer[bi + 1] = (color >> 8) & 0xff + this.buffer[bi + 2] = color & 0xff } /** @@ -174,19 +175,19 @@ export class PixelBuffer { * @returns */ getColor(pixeloffset: number): number { + pixeloffset = pixeloffset | 0 while (pixeloffset < 0) pixeloffset += this.length const i = this.start + (pixeloffset << 0) if (i < this.start || i >= this.start + this.length) return undefined const bi = i * 3 - const r = this.buffer.getAt(bi, "u8") - const g = this.buffer.getAt(bi + 1, "u8") - const b = this.buffer.getAt(bi + 2, "u8") - + const r = this.buffer[bi] + const g = this.buffer[bi + 1] + const b = this.buffer[bi + 2] return rgb(r, g, b) } /** - * Renders a bar grpah on the LEDs + * Renders a bar graph on the LEDs * @param value * @param high * @returns @@ -269,7 +270,7 @@ export class PixelBuffer { * @param length length of the range * @returns a view of the color buffer */ - range(start: number, length?: number): PixelBuffer { + view(start: number, length?: number): PixelBuffer { const rangeStart = this.start + (start << 0) const rangeLength = length === undefined From f561c26a8cffcd1975d97d4d2cf6946693e1e05a Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 09:08:52 +0200 Subject: [PATCH 02/31] adding led driver --- packages/core/src/index.ts | 1 - packages/core/src/led.ts | 54 -------- packages/drivers/src/index.ts | 1 + packages/drivers/src/led.ts | 150 ++++++++++++++++++++++ packages/runtime/src/colors.ts | 189 ++-------------------------- packages/runtime/src/index.ts | 3 +- packages/runtime/src/pixelbuffer.ts | 164 ++++++++++++++++++++++++ 7 files changed, 324 insertions(+), 238 deletions(-) delete mode 100644 packages/core/src/led.ts create mode 100644 packages/drivers/src/led.ts create mode 100644 packages/runtime/src/pixelbuffer.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 5374d219a2..99a878bcdc 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -8,7 +8,6 @@ import "./array" import "./string" import "./events" import "./jacdac" -import "./led" import "./lightbulb" import "./rotaryencoder" import "./button" diff --git a/packages/core/src/led.ts b/packages/core/src/led.ts deleted file mode 100644 index c651ba39b1..0000000000 --- a/packages/core/src/led.ts +++ /dev/null @@ -1,54 +0,0 @@ -// This file contains implementation for actions/registers marked `client` in the spec, -// as well as additional functionality on different service clients (Roles) - -import * as ds from "@devicescript/core" - -declare module "@devicescript/core" { - interface Led { - /** - * Sets all the pixels to the given RGB color. - * @param rgb 24bit color number - */ - setAll(rgb: number): Promise - - /** - * Sets the brightness between 0 (off) and 1 (full). - * @param brightness - */ - setBrightness(brightness: number): Promise - - /** - * Turns off the LEDs. - */ - off(): Promise - } -} - -ds.Led.prototype.setAll = async function (rgb) { - const len = await this.numPixels.read() - const buflen = len * 3 - const buf = Buffer.alloc(buflen) - - let idx = 0 - const r = (rgb >> 16) & 0xff - const g = (rgb >> 8) & 0xff - const b = rgb & 0xff - while (idx < buflen) { - buf.setAt(idx, "u8", r) - buf.setAt(idx + 1, "u8", g) - buf.setAt(idx + 2, "u8", b) - idx = idx + 3 - } - await this.pixels.write(buf) -} - -ds.Led.prototype.setBrightness = async function (brightness) { - await this.intensity.write(brightness) -} - -ds.Led.prototype.off = async function () { - const len = await this.numPixels.read() - const buflen = len * 3 - const buf = Buffer.alloc(buflen) - await this.pixels.write(buf) -} diff --git a/packages/drivers/src/index.ts b/packages/drivers/src/index.ts index 352ee3834f..c6ab57fca4 100644 --- a/packages/drivers/src/index.ts +++ b/packages/drivers/src/index.ts @@ -14,5 +14,6 @@ export * from "./dotmatrix" export * from "./st7735" export * from "./uc8151" export * from "./trafficlight" +export * from "./led" configureHardware({ scanI2C: false }) diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/led.ts new file mode 100644 index 0000000000..d7fc60c148 --- /dev/null +++ b/packages/drivers/src/led.ts @@ -0,0 +1,150 @@ +import * as ds from "@devicescript/core" +import { PixelBuffer, pixelBuffer } from "@devicescript/runtime" +import { Server, ServerOptions, startServer } from "@devicescript/server" + +export interface LedServerOptions { + length: number + columns?: number + maxPower?: number + ledsPerPixel?: number + waveLength?: number + luminousIntensity?: number + variant?: ds.LedVariant + // TODO configure pins? +} + +class LedServer extends Server implements ds.LedServerSpec { + private _intensity: number + private _columns: number + private _maxPower: number + private _ledPerPixels: number + private _waveLength: number + private _luminousIntensity: number + private _variant: ds.LedVariant + + readonly buffer: PixelBuffer + + constructor(options: LedServerOptions & ServerOptions) { + super(ds.Led.spec, options) + this.buffer = pixelBuffer(options.length) + this._columns = options.columns + this._maxPower = options.maxPower + this._ledPerPixels = options.ledsPerPixel + this._waveLength = options.waveLength + this._luminousIntensity = options.luminousIntensity + this._variant = options.variant + } + + pixels(): ds.Buffer { + if (this.buffer.length < 64) return this.buffer.buffer + else return Buffer.alloc(0) + } + set_pixels(value: ds.Buffer): void { + this.buffer.buffer.blitAt(0, value, 0, value.length) + } + intensity(): number { + return this._intensity + } + set_intensity(value: number): void { + this._intensity = Math.clamp(0, value, 1) + } + actualBrightness(): number { + return this._intensity + } + numPixels(): number { + return this.buffer.length + } + numColumns(): number { + return this._columns + } + maxPower(): number { + return this._maxPower + } + set_maxPower(value: number): void { + this._maxPower = value + } + ledsPerPixel(): number { + return this._ledPerPixels + } + waveLength(): number { + return this._waveLength + } + luminousIntensity(): number { + return this._luminousIntensity + } + variant(): ds.LedVariant { + return this._variant + } + async show(): Promise { + // TODO send buffer to hardware + + // sim support + if (ds.isSimulator()) { + const topic = `jd/${this.serviceIndex}/leds` + ds._twinMessage(topic, this.buffer.buffer) + } + } +} + +/** + * Starts a programmable LED server. + * Simulation is supported for up to 64 LEDs; otherwise only the simulator + * will reflect the state of LEDs. + * @param options + * @returns + */ +export async function startLedServer( + options: LedServerOptions & ServerOptions +) { + const { length } = options + const server = new LedServer(options) + const buffer = server.buffer + const client = new ds.Led(startServer(server)) + + ;(client as any as LedWithBuffer)._buffer = buffer + + client.show = async function () { + // TODO send buffer to hardware + + // send to register + if (length <= 64) await client.pixels.write(buffer.buffer) + else if (ds.isSimulator()) { + const topic = `jd/${server.serviceIndex}/pixels` + ds._twinMessage(topic, buffer.buffer) + } + } + + return client +} + +interface LedWithBuffer { + _buffer: PixelBuffer +} + +declare module "@devicescript/core" { + interface Led { + /** + * Gets the pixel buffer to perform coloring operations. + * Call `show` to send the buffer to the LED strip. + */ + buffer(): Promise + /** + * Sends the pixel buffer to the LED driver + */ + show(): Promise + } +} + +ds.Led.prototype.buffer = async function () { + let b = (this as any as LedWithBuffer)._buffer + if (!b) { + const n = await this.numPixels.read() + ;(this as any as LedWithBuffer)._buffer = b = pixelBuffer(n) + } + return b +} + +ds.Led.prototype.show = async function () { + const b = (this as any as LedWithBuffer)._buffer + if (b && b.length <= 64) await this.pixels.write(b.buffer) +} diff --git a/packages/runtime/src/colors.ts b/packages/runtime/src/colors.ts index 947de5ea4d..6e34ecca17 100644 --- a/packages/runtime/src/colors.ts +++ b/packages/runtime/src/colors.ts @@ -1,5 +1,3 @@ -import * as ds from "@devicescript/core" - /** * Well known colors */ @@ -126,6 +124,13 @@ function unpackB(rgb: number): number { return (rgb >> 0) & 0xff } +/** + * Alpha blending of each color channel and returns a rgb 24bit color + * @param color + * @param alpha factor of blending, 0 for color, 1 for otherColor + * @param otherColor + * @returns + */ export function blend(color: number, alpha: number, otherColor: number) { alpha = Math.max(0, Math.min(0xff, alpha | 0)) const malpha = 0xff - alpha @@ -134,183 +139,3 @@ export function blend(color: number, alpha: number, otherColor: number) { const b = (unpackB(color) * malpha + unpackB(otherColor) * alpha) >> 8 return rgb(r, g, b) } - -/** - * A buffer of RGB colors - */ -export class PixelBuffer { - /** - * Number of pixels in the buffer - */ - readonly buffer: ds.Buffer - readonly start: number - readonly length: number - - constructor(buffer: ds.Buffer, start: number, length: number) { - ds.assert(buffer.length >= (start + length) * 3, "buffer too small") - this.buffer = buffer - this.start = start - this.length = length - } - - /** - * Set a pixel color in the buffer - * @param pixeloffset pixel offset. if negative starts from the end - * @param color RGB color - */ - setColor(pixeloffset: number, color: number) { - pixeloffset = pixeloffset | 0 - while (pixeloffset < 0) pixeloffset += this.length - const i = this.start + (pixeloffset << 0) - if (i < this.start || i >= this.start + this.length) return - const bi = i * 3 - this.buffer[bi] = (color >> 16) & 0xff - this.buffer[bi + 1] = (color >> 8) & 0xff - this.buffer[bi + 2] = color & 0xff - } - - /** - * Reads the pixel color at the given offsret - * @param pixeloffset pixel offset. if negative starts from the end - * @returns - */ - getColor(pixeloffset: number): number { - pixeloffset = pixeloffset | 0 - while (pixeloffset < 0) pixeloffset += this.length - const i = this.start + (pixeloffset << 0) - if (i < this.start || i >= this.start + this.length) return undefined - const bi = i * 3 - const r = this.buffer[bi] - const g = this.buffer[bi + 1] - const b = this.buffer[bi + 2] - return rgb(r, g, b) - } - - /** - * Renders a bar graph on the LEDs - * @param value - * @param high - * @returns - */ - setBarGraph( - value: number, - high: number, - options?: { - emptyRangeColor?: number - zeroColor?: number - } - ): void { - if (high <= 0) { - const emptyRangeColor = options?.emptyRangeColor - this.clear() - this.setColor( - 0, - isNaN(emptyRangeColor) ? 0xffff00 : emptyRangeColor - ) - return - } - - value = Math.abs(value) - const n = this.length - const n1 = n - 1 - let v = Math.idiv(value * n, high) - if (v === 0) { - const zeroColor = options?.zeroColor - this.setColor(0, isNaN(zeroColor) ? 0x666600 : zeroColor) - for (let i = 1; i < n; ++i) this.setColor(i, 0) - } else { - for (let i = 0; i < n; ++i) { - if (i <= v) { - const b = Math.idiv(i * 255, n1) - this.setColor(i, rgb(b, 0, 255 - b)) - } else this.setColor(i, 0) - } - } - } - - /** - * Writes a gradient between the two colors. - * @param startColor - * @param endColor - */ - setGradient( - startColor: number, - endColor: number, - start?: number, - end?: number - ): void { - // normalize range - if (start < 0) start += this.length - start = start || 0 - if (end < 0) end += this.length - end = end === undefined ? this.length - 1 : end - // check if any work needed - const steps = end - start - if (steps < 1) return - - this.setColor(start, startColor) - this.setColor(end, endColor) - for (let i = start + 1; i < end - 1; ++i) { - const alpha = Math.idiv(0xff * i, steps) - const c = blend(startColor, alpha, endColor) - this.setColor(i, c) - } - } - - /** - * Clears the buffer to #000000 - */ - clear() { - this.buffer.fillAt(this.start, this.length, 0) - } - - /** - * Creates a range view over the color buffer - * @param start start index - * @param length length of the range - * @returns a view of the color buffer - */ - view(start: number, length?: number): PixelBuffer { - const rangeStart = this.start + (start << 0) - const rangeLength = - length === undefined - ? this.length - start - : Math.min(length, this.length - start) - return new PixelBuffer(this.buffer, rangeStart, rangeLength) - } -} - -/** - * Create a color buffer that allows you to manipulate a range of pixels - * @param numPixels number of pixels - */ -export function pixelBuffer(numPixels: number) { - numPixels = numPixels << 0 - const buf = ds.Buffer.alloc(numPixels * 3) - return new PixelBuffer(buf, 0, numPixels) -} - -declare module "@devicescript/core" { - interface Led { - /** - * Allocates a pixel buffer for the LED string. - */ - allocateBuffer(): Promise - - /** - * Writes the pixels to the LED client - * @param led LED client - */ - write(pixels: PixelBuffer): Promise - } -} - -ds.Led.prototype.allocateBuffer = async function () { - const numPixels = await this.numPixels.read() - return pixelBuffer(numPixels) -} - -ds.Led.prototype.write = async function (pixels: PixelBuffer) { - if (!pixels) return - await this.pixels.write(pixels.buffer) -} diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 5d7064a525..4948362fb4 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -4,4 +4,5 @@ export * from "./schedule" export * from "./encodeURIComponent" export * from "./valuedashboard" export * from "./map" -export * from "./set" \ No newline at end of file +export * from "./set" +export * from "./pixelbuffer" diff --git a/packages/runtime/src/pixelbuffer.ts b/packages/runtime/src/pixelbuffer.ts new file mode 100644 index 0000000000..6412a5bca4 --- /dev/null +++ b/packages/runtime/src/pixelbuffer.ts @@ -0,0 +1,164 @@ +import * as ds from "@devicescript/core" +import { blend, rgb } from "./colors" + +/** + * A buffer of RGB colors + */ +export class PixelBuffer { + /** + * Array of RGB colors + * @internal + */ + readonly buffer: ds.Buffer + /** + * Starting pixel index in the original buffer + */ + readonly start: number + /** + * Number of pixels in the buffer + */ + readonly length: number + + constructor(buffer: ds.Buffer, start: number, length: number) { + ds.assert(buffer.length >= (start + length) * 3, "buffer too small") + this.buffer = buffer + this.start = start + this.length = length + } + + /** + * Set a pixel color in the buffer + * @param pixeloffset pixel offset. if negative starts from the end + * @param color RGB color + */ + setColor(pixeloffset: number, color: number) { + pixeloffset = pixeloffset | 0 + while (pixeloffset < 0) pixeloffset += this.length + const i = this.start + (pixeloffset << 0) + if (i < this.start || i >= this.start + this.length) return + const bi = i * 3 + this.buffer[bi] = (color >> 16) & 0xff + this.buffer[bi + 1] = (color >> 8) & 0xff + this.buffer[bi + 2] = color & 0xff + } + + /** + * Reads the pixel color at the given offsret + * @param pixeloffset pixel offset. if negative starts from the end + * @returns + */ + getColor(pixeloffset: number): number { + pixeloffset = pixeloffset | 0 + while (pixeloffset < 0) pixeloffset += this.length + const i = this.start + (pixeloffset << 0) + if (i < this.start || i >= this.start + this.length) return undefined + const bi = i * 3 + const r = this.buffer[bi] + const g = this.buffer[bi + 1] + const b = this.buffer[bi + 2] + return rgb(r, g, b) + } + + /** + * Renders a bar graph on the LEDs + * @param value + * @param high + * @returns + */ + setBarGraph( + value: number, + high: number, + options?: { + emptyRangeColor?: number + zeroColor?: number + } + ): void { + if (high <= 0) { + const emptyRangeColor = options?.emptyRangeColor + this.clear() + this.setColor( + 0, + isNaN(emptyRangeColor) ? 0xffff00 : emptyRangeColor + ) + return + } + + value = Math.abs(value) + const n = this.length + const n1 = n - 1 + let v = Math.idiv(value * n, high) + if (v === 0) { + const zeroColor = options?.zeroColor + this.setColor(0, isNaN(zeroColor) ? 0x666600 : zeroColor) + for (let i = 1; i < n; ++i) this.setColor(i, 0) + } else { + for (let i = 0; i < n; ++i) { + if (i <= v) { + const b = Math.idiv(i * 255, n1) + this.setColor(i, rgb(b, 0, 255 - b)) + } else this.setColor(i, 0) + } + } + } + + /** + * Writes a gradient between the two colors. + * @param startColor + * @param endColor + */ + setGradient( + startColor: number, + endColor: number, + start?: number, + end?: number + ): void { + // normalize range + if (start < 0) start += this.length + start = start || 0 + if (end < 0) end += this.length + end = end === undefined ? this.length - 1 : end + // check if any work needed + const steps = end - start + if (steps < 1) return + + this.setColor(start, startColor) + this.setColor(end, endColor) + for (let i = start + 1; i < end - 1; ++i) { + const alpha = Math.idiv(0xff * i, steps) + const c = blend(startColor, alpha, endColor) + this.setColor(i, c) + } + } + + /** + * Clears the buffer to #000000 + */ + clear() { + this.buffer.fillAt(this.start, this.length, 0) + } + + /** + * Creates a range view over the color buffer + * @param start start index + * @param length length of the range + * @returns a view of the color buffer + */ + view(start: number, length?: number): PixelBuffer { + const rangeStart = this.start + (start << 0) + const rangeLength = + length === undefined + ? this.length - start + : Math.min(length, this.length - start) + return new PixelBuffer(this.buffer, rangeStart, rangeLength) + } +} + +/** + * Create a color buffer that allows you to manipulate a range of pixels + * @param numPixels number of pixels + */ +export function pixelBuffer(numPixels: number) { + numPixels = numPixels | 0 + const buf = ds.Buffer.alloc(numPixels * 3) + return new PixelBuffer(buf, 0, numPixels) +} From 7c96ddb3aa4e7482c4e11a8ac4b3d2e18addfa4d Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 09:09:49 +0200 Subject: [PATCH 03/31] unused function --- packages/drivers/src/led.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/led.ts index d7fc60c148..024757e94d 100644 --- a/packages/drivers/src/led.ts +++ b/packages/drivers/src/led.ts @@ -75,15 +75,6 @@ class LedServer extends Server implements ds.LedServerSpec { variant(): ds.LedVariant { return this._variant } - async show(): Promise { - // TODO send buffer to hardware - - // sim support - if (ds.isSimulator()) { - const topic = `jd/${this.serviceIndex}/leds` - ds._twinMessage(topic, this.buffer.buffer) - } - } } /** From 08570a15b9deb8a9d82f4bd4c6fd7b36d193e2aa Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 09:33:32 +0200 Subject: [PATCH 04/31] more led functionalities --- packages/drivers/src/led.ts | 36 +++++++++++-- packages/runtime/src/colors.ts | 2 +- packages/runtime/src/main.ts | 8 +-- packages/runtime/src/pixelbuffer.ts | 79 +++++++++++++++++++++++------ 4 files changed, 101 insertions(+), 24 deletions(-) diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/led.ts index 024757e94d..627fe48e9d 100644 --- a/packages/drivers/src/led.ts +++ b/packages/drivers/src/led.ts @@ -1,9 +1,21 @@ import * as ds from "@devicescript/core" -import { PixelBuffer, pixelBuffer } from "@devicescript/runtime" +import { PixelBuffer, fade, pixelBuffer } from "@devicescript/runtime" import { Server, ServerOptions, startServer } from "@devicescript/server" export interface LedServerOptions { + /** + * Number of LEDs + */ length: number + /** + * Brightness applied to pixels before being rendered. + * This allocate twice the memory if less than 1 as an additional buffer is needed to compute the color. + * @default 1 + */ + intensity?: number + /** + * Number of columns of a LED matrix + */ columns?: number maxPower?: number ledsPerPixel?: number @@ -27,6 +39,7 @@ class LedServer extends Server implements ds.LedServerSpec { constructor(options: LedServerOptions & ServerOptions) { super(ds.Led.spec, options) this.buffer = pixelBuffer(options.length) + this._intensity = options.intensity ?? 1 this._columns = options.columns this._maxPower = options.maxPower this._ledPerPixels = options.ledsPerPixel @@ -75,6 +88,20 @@ class LedServer extends Server implements ds.LedServerSpec { variant(): ds.LedVariant { return this._variant } + + /** + * Apply brightness and gamma correction + */ + render(): ds.Buffer { + const b = this.buffer + // full brightness so we can use the buffer as is + if (this._intensity >= 1) return b.buffer + + // apply brightness + const r = b.allocClone() + r.fade(this._intensity) + return r.buffer + } } /** @@ -95,13 +122,14 @@ export async function startLedServer( ;(client as any as LedWithBuffer)._buffer = buffer client.show = async function () { + const b = server.render() // TODO send buffer to hardware - // send to register if (length <= 64) await client.pixels.write(buffer.buffer) - else if (ds.isSimulator()) { + if (ds.isSimulator()) { + // the simulator handles brightness separately const topic = `jd/${server.serviceIndex}/pixels` - ds._twinMessage(topic, buffer.buffer) + ds._twinMessage(topic, server.buffer.buffer) } } diff --git a/packages/runtime/src/colors.ts b/packages/runtime/src/colors.ts index 6e34ecca17..4b126cceb2 100644 --- a/packages/runtime/src/colors.ts +++ b/packages/runtime/src/colors.ts @@ -99,7 +99,7 @@ export function hsv(hue: number, sat: number = 255, val: number = 255): number { * @param brightness the amount of brightness to apply to the color between 0 and 1. */ export function fade(color: number, brightness: number): number { - brightness = Math.max(0, Math.min(0xff, brightness * 0xff)) + brightness = Math.max(0, Math.min(0xff, brightness << 8)) if (brightness < 0xff) { let red = (color >> 16) & 0xff let green = (color >> 8) & 0xff diff --git a/packages/runtime/src/main.ts b/packages/runtime/src/main.ts index 3d7020756f..4619ae7c0c 100644 --- a/packages/runtime/src/main.ts +++ b/packages/runtime/src/main.ts @@ -20,13 +20,13 @@ describe("rgb", () => { describe("colorbuffer", () => { test("setpixelcolor", () => { const buf = pixelBuffer(3) - buf.setColor(1, 0x123456) - expect(buf.getColor(1)).toBe(0x123456) + buf.setAt(1, 0x123456) + expect(buf.at(1)).toBe(0x123456) }) test("setpixelcolor negative", () => { const buf = pixelBuffer(3) - buf.setColor(-1, 0x123456) - expect(buf.getColor(-1)).toBe(0x123456) + buf.setAt(-1, 0x123456) + expect(buf.at(-1)).toBe(0x123456) }) test("setbargraph", () => { const buf = pixelBuffer(4) diff --git a/packages/runtime/src/pixelbuffer.ts b/packages/runtime/src/pixelbuffer.ts index 6412a5bca4..ab57e22415 100644 --- a/packages/runtime/src/pixelbuffer.ts +++ b/packages/runtime/src/pixelbuffer.ts @@ -31,10 +31,10 @@ export class PixelBuffer { * @param pixeloffset pixel offset. if negative starts from the end * @param color RGB color */ - setColor(pixeloffset: number, color: number) { + setAt(pixeloffset: number, color: number) { pixeloffset = pixeloffset | 0 while (pixeloffset < 0) pixeloffset += this.length - const i = this.start + (pixeloffset << 0) + const i = this.start + pixeloffset if (i < this.start || i >= this.start + this.length) return const bi = i * 3 this.buffer[bi] = (color >> 16) & 0xff @@ -47,10 +47,10 @@ export class PixelBuffer { * @param pixeloffset pixel offset. if negative starts from the end * @returns */ - getColor(pixeloffset: number): number { + at(pixeloffset: number): number { pixeloffset = pixeloffset | 0 while (pixeloffset < 0) pixeloffset += this.length - const i = this.start + (pixeloffset << 0) + const i = this.start + pixeloffset if (i < this.start || i >= this.start + this.length) return undefined const bi = i * 3 const r = this.buffer[bi] @@ -59,6 +59,58 @@ export class PixelBuffer { return rgb(r, g, b) } + /** + * Apply a conversion function to all pixels + * @param converter + */ + apply(converter: (c: number, index: number) => number) { + const n = this.length + const buf = this.buffer + for (let i = this.start; i < n; ++i) { + const bi = i * 3 + + const r = buf[bi] + const g = buf[bi + 1] + const b = buf[bi + 2] + const c = rgb(r, g, b) + + const cr = converter(c, i) + + buf[bi] = (cr >> 16) & 0xff + buf[bi + 1] = (cr >> 8) & 0xff + buf[bi + 2] = cr & 0xff + } + } + + /** + * Fades each color channels according to the brigthness value between 0 dark and 1 full brightness. + * @param brightness + */ + fade(brightness: number) { + brightness = Math.max(0, Math.min(0xff, brightness << 8)) + if (brightness < 0xff) { + const s = this.start * 3 + const n = this.length * 3 + const buf = this.buffer + for (let i = s; i < n; ++i) { + buf[i] = (buf[i] * brightness) >> 8 + } + } + } + + /** + * Allocates a clone of the buffer view + * @returns + */ + allocClone() { + const res = new PixelBuffer( + this.buffer.slice(this.start * 3, (this.start + this.length) * 3), + 0, + this.length + ) + return res + } + /** * Renders a bar graph on the LEDs * @param value @@ -76,10 +128,7 @@ export class PixelBuffer { if (high <= 0) { const emptyRangeColor = options?.emptyRangeColor this.clear() - this.setColor( - 0, - isNaN(emptyRangeColor) ? 0xffff00 : emptyRangeColor - ) + this.setAt(0, isNaN(emptyRangeColor) ? 0xffff00 : emptyRangeColor) return } @@ -89,14 +138,14 @@ export class PixelBuffer { let v = Math.idiv(value * n, high) if (v === 0) { const zeroColor = options?.zeroColor - this.setColor(0, isNaN(zeroColor) ? 0x666600 : zeroColor) - for (let i = 1; i < n; ++i) this.setColor(i, 0) + this.setAt(0, isNaN(zeroColor) ? 0x666600 : zeroColor) + for (let i = 1; i < n; ++i) this.setAt(i, 0) } else { for (let i = 0; i < n; ++i) { if (i <= v) { const b = Math.idiv(i * 255, n1) - this.setColor(i, rgb(b, 0, 255 - b)) - } else this.setColor(i, 0) + this.setAt(i, rgb(b, 0, 255 - b)) + } else this.setAt(i, 0) } } } @@ -121,12 +170,12 @@ export class PixelBuffer { const steps = end - start if (steps < 1) return - this.setColor(start, startColor) - this.setColor(end, endColor) + this.setAt(start, startColor) + this.setAt(end, endColor) for (let i = start + 1; i < end - 1; ++i) { const alpha = Math.idiv(0xff * i, steps) const c = blend(startColor, alpha, endColor) - this.setColor(i, c) + this.setAt(i, c) } } From 8e26d9fefcbe58badc4ddcb75b1c0b50db407fbe Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 09:44:33 +0200 Subject: [PATCH 05/31] porting some function --- packages/drivers/src/led.ts | 6 ++--- packages/runtime/src/pixelbuffer.ts | 37 +++++++++++++++++++++-------- packages/sampleprj/src/mainled.ts | 15 ++++++++---- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/led.ts index 627fe48e9d..9038e4e759 100644 --- a/packages/drivers/src/led.ts +++ b/packages/drivers/src/led.ts @@ -111,9 +111,9 @@ class LedServer extends Server implements ds.LedServerSpec { * @param options * @returns */ -export async function startLedServer( +export async function startLed( options: LedServerOptions & ServerOptions -) { +): Promise { const { length } = options const server = new LedServer(options) const buffer = server.buffer @@ -129,7 +129,7 @@ export async function startLedServer( if (ds.isSimulator()) { // the simulator handles brightness separately const topic = `jd/${server.serviceIndex}/pixels` - ds._twinMessage(topic, server.buffer.buffer) + await ds._twinMessage(topic, server.buffer.buffer) } } diff --git a/packages/runtime/src/pixelbuffer.ts b/packages/runtime/src/pixelbuffer.ts index ab57e22415..86aa9eca7c 100644 --- a/packages/runtime/src/pixelbuffer.ts +++ b/packages/runtime/src/pixelbuffer.ts @@ -59,6 +59,24 @@ export class PixelBuffer { return rgb(r, g, b) } + /** + * Sets all the color in the buffer to the given color + * @param c 24bit rgb color + */ + setAll(c: number) { + const r = (c >> 16) & 0xff + const g = (c >> 8) & 0xff + const b = c & 0xff + const n = this.length + const buf = this.buffer + for (let i = 0; i < n; ++i) { + const bi = (this.start + i) * 3 + buf[0] = r + buf[1] = g + buf[2] = b + } + } + /** * Apply a conversion function to all pixels * @param converter @@ -66,8 +84,8 @@ export class PixelBuffer { apply(converter: (c: number, index: number) => number) { const n = this.length const buf = this.buffer - for (let i = this.start; i < n; ++i) { - const bi = i * 3 + for (let i = 0; i < n; ++i) { + const bi = (this.start + i) * 3 const r = buf[bi] const g = buf[bi + 1] @@ -88,13 +106,12 @@ export class PixelBuffer { */ fade(brightness: number) { brightness = Math.max(0, Math.min(0xff, brightness << 8)) - if (brightness < 0xff) { - const s = this.start * 3 - const n = this.length * 3 - const buf = this.buffer - for (let i = s; i < n; ++i) { - buf[i] = (buf[i] * brightness) >> 8 - } + if (brightness >= 0xff) return + const s = this.start * 3 + const n = (this.start + this.length) * 3 + const buf = this.buffer + for (let i = s; i < n; ++i) { + buf[i] = (buf[i] * brightness) >> 8 } } @@ -183,7 +200,7 @@ export class PixelBuffer { * Clears the buffer to #000000 */ clear() { - this.buffer.fillAt(this.start, this.length, 0) + this.buffer.fillAt(this.start * 3, this.length * 3, 0) } /** diff --git a/packages/sampleprj/src/mainled.ts b/packages/sampleprj/src/mainled.ts index a7dc52e65a..85e3fc68c3 100644 --- a/packages/sampleprj/src/mainled.ts +++ b/packages/sampleprj/src/mainled.ts @@ -1,10 +1,17 @@ -import { delay, Led } from "@devicescript/core" +import { delay, Led, LedVariant } from "@devicescript/core" import { rgb } from "@devicescript/runtime" +import { startLed } from "@devicescript/drivers" -const led = new Led() +const led = await startLed({ + length: 12, + variant: LedVariant.Strip +}) +const pixels = await led.buffer() setTimeout(async () => { - await led.setAll(rgb(255, 0, 0)) + pixels.setAll(rgb(255, 0, 0)) + await led.show() await delay(500) - await led.setAll(0) + pixels.setAll(0) + await led.show() }, 500) From 654f73c5d1ac905a4414e47a6b1cf594f1879ad9 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 09:53:49 +0200 Subject: [PATCH 06/31] fixed setall --- packages/runtime/src/pixelbuffer.ts | 6 +++--- packages/sampleprj/src/mainled.ts | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/runtime/src/pixelbuffer.ts b/packages/runtime/src/pixelbuffer.ts index 86aa9eca7c..f009047d2d 100644 --- a/packages/runtime/src/pixelbuffer.ts +++ b/packages/runtime/src/pixelbuffer.ts @@ -71,9 +71,9 @@ export class PixelBuffer { const buf = this.buffer for (let i = 0; i < n; ++i) { const bi = (this.start + i) * 3 - buf[0] = r - buf[1] = g - buf[2] = b + buf[bi] = r + buf[bi + 1] = g + buf[bi + 2] = b } } diff --git a/packages/sampleprj/src/mainled.ts b/packages/sampleprj/src/mainled.ts index 85e3fc68c3..3ae0b32cb1 100644 --- a/packages/sampleprj/src/mainled.ts +++ b/packages/sampleprj/src/mainled.ts @@ -8,10 +8,13 @@ const led = await startLed({ }) const pixels = await led.buffer() -setTimeout(async () => { +setInterval(async () => { pixels.setAll(rgb(255, 0, 0)) await led.show() - await delay(500) + await delay(1000) + pixels.setAll(0) await led.show() + await delay(500) + }, 500) From 49369ee534552c21fe07b4dafec2187a652f1d45 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 09:55:47 +0200 Subject: [PATCH 07/31] add large number of LEDs example --- packages/sampleprj/src/mainled.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/sampleprj/src/mainled.ts b/packages/sampleprj/src/mainled.ts index 3ae0b32cb1..c8c74767c2 100644 --- a/packages/sampleprj/src/mainled.ts +++ b/packages/sampleprj/src/mainled.ts @@ -4,17 +4,30 @@ import { startLed } from "@devicescript/drivers" const led = await startLed({ length: 12, - variant: LedVariant.Strip + variant: LedVariant.Strip, }) const pixels = await led.buffer() +const led2 = await startLed({ + length: 256, + variant: LedVariant.Strip, +}) +const pixels2 = await led.buffer() + setInterval(async () => { pixels.setAll(rgb(255, 0, 0)) await led.show() + + pixels2.setAll(rgb(0, 0, 255)) + await led2.show() + await delay(1000) pixels.setAll(0) await led.show() - await delay(500) + pixels2.setAll(0) + await led2.show() + + await delay(500) }, 500) From 41ea22658ec47d5ab9619626c7e5498ffe231a5c Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 10:03:01 +0200 Subject: [PATCH 08/31] send image buffer if needed --- packages/drivers/src/led.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/led.ts index 9038e4e759..5d66936397 100644 --- a/packages/drivers/src/led.ts +++ b/packages/drivers/src/led.ts @@ -126,9 +126,9 @@ export async function startLed( // TODO send buffer to hardware if (length <= 64) await client.pixels.write(buffer.buffer) - if (ds.isSimulator()) { + else if (ds.isSimulator()) { // the simulator handles brightness separately - const topic = `jd/${server.serviceIndex}/pixels` + const topic = `jd/${server.serviceIndex}/leds` await ds._twinMessage(topic, server.buffer.buffer) } } From 0ec4ec7be30aea77ab68032150df1cb4775816cf Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 10:10:40 +0200 Subject: [PATCH 09/31] add TODO --- packages/drivers/src/led.ts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/led.ts index 5d66936397..aa84a3ae2c 100644 --- a/packages/drivers/src/led.ts +++ b/packages/drivers/src/led.ts @@ -90,17 +90,18 @@ class LedServer extends Server implements ds.LedServerSpec { } /** - * Apply brightness and gamma correction + * Display buffer on hardware */ - render(): ds.Buffer { - const b = this.buffer + async show(): Promise { + let b = this.buffer // full brightness so we can use the buffer as is - if (this._intensity >= 1) return b.buffer - - // apply brightness - const r = b.allocClone() - r.fade(this._intensity) - return r.buffer + if (this._intensity < 1) { + // apply brightness + const r = b.allocClone() + r.fade(this._intensity) + b = r + } + // TODO: render b to hardware } } @@ -122,9 +123,7 @@ export async function startLed( ;(client as any as LedWithBuffer)._buffer = buffer client.show = async function () { - const b = server.render() - // TODO send buffer to hardware - + await server.show() if (length <= 64) await client.pixels.write(buffer.buffer) else if (ds.isSimulator()) { // the simulator handles brightness separately From 56e84f1ee6909faeaf950d6fef675c9b9b923cee Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 10:36:29 +0200 Subject: [PATCH 10/31] docs on socket limitations --- website/docs/developer/net/index.mdx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/website/docs/developer/net/index.mdx b/website/docs/developer/net/index.mdx index 0a50410c59..80bdff79e6 100644 --- a/website/docs/developer/net/index.mdx +++ b/website/docs/developer/net/index.mdx @@ -69,3 +69,8 @@ const status = await socket.readLine() console.log(status) await socket.close() ``` + +### Limitations + +- Only one socket can be open at a time. On most devices, a TLS will use a large part of the usable memory. +- Sockets can be connect to server but not listen so it is not possible to implement a HTTP server currently. From 769b5ab850d049880b7be7898ac033c4216e0075 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 10:46:19 +0200 Subject: [PATCH 11/31] move display dimmer --- packages/drivers/src/led.ts | 12 ++++++++++++ .../sampleprj/src/maindisplaydimmer.ts | 1 + 2 files changed, 13 insertions(+) rename devs/samples/display-dimmer.ts => packages/sampleprj/src/maindisplaydimmer.ts (93%) diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/led.ts index aa84a3ae2c..86cb7b5725 100644 --- a/packages/drivers/src/led.ts +++ b/packages/drivers/src/led.ts @@ -150,6 +150,12 @@ declare module "@devicescript/core" { * Sends the pixel buffer to the LED driver */ show(): Promise + + /** + * Sets all pixel color to the given color + * @param c Set + */ + setAll(c: number): Promise } } @@ -166,3 +172,9 @@ ds.Led.prototype.show = async function () { const b = (this as any as LedWithBuffer)._buffer if (b && b.length <= 64) await this.pixels.write(b.buffer) } + +ds.Led.prototype.setAll = async function (c: number) { + const b = await this.buffer() + b.setAll(c) + await this.show() +} \ No newline at end of file diff --git a/devs/samples/display-dimmer.ts b/packages/sampleprj/src/maindisplaydimmer.ts similarity index 93% rename from devs/samples/display-dimmer.ts rename to packages/sampleprj/src/maindisplaydimmer.ts index ee377e74fa..6ade4ac27a 100644 --- a/devs/samples/display-dimmer.ts +++ b/packages/sampleprj/src/maindisplaydimmer.ts @@ -1,4 +1,5 @@ import * as ds from "@devicescript/core" +import "@devicescript/runtime" const pot = new ds.Potentiometer() const ledD = new ds.Led() From 8334918a41cec87ea3a4b505b0835ad2dc20897e Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 11:07:37 +0200 Subject: [PATCH 12/31] fix weird verif error --- packages/drivers/src/led.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/led.ts index 86cb7b5725..8f22be5e8a 100644 --- a/packages/drivers/src/led.ts +++ b/packages/drivers/src/led.ts @@ -1,5 +1,5 @@ import * as ds from "@devicescript/core" -import { PixelBuffer, fade, pixelBuffer } from "@devicescript/runtime" +import { PixelBuffer, pixelBuffer } from "@devicescript/runtime" import { Server, ServerOptions, startServer } from "@devicescript/server" export interface LedServerOptions { @@ -117,14 +117,12 @@ export async function startLed( ): Promise { const { length } = options const server = new LedServer(options) - const buffer = server.buffer const client = new ds.Led(startServer(server)) - ;(client as any as LedWithBuffer)._buffer = buffer - + ;(client as any as LedWithBuffer)._buffer = server.buffer client.show = async function () { await server.show() - if (length <= 64) await client.pixels.write(buffer.buffer) + if (length <= 64) await client.pixels.write(server.buffer.buffer) else if (ds.isSimulator()) { // the simulator handles brightness separately const topic = `jd/${server.serviceIndex}/leds` @@ -177,4 +175,4 @@ ds.Led.prototype.setAll = async function (c: number) { const b = await this.buffer() b.setAll(c) await this.show() -} \ No newline at end of file +} From 7d52c5f97c239b7d56af92453b77705c2401a844 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 12:11:51 +0200 Subject: [PATCH 13/31] fix samples --- jacdac-ts | 2 +- packages/drivers/src/led.ts | 8 +++---- packages/sampleprj/src/maindisplaydimmer.ts | 7 +++--- packages/sampleprj/src/mainled.ts | 24 ++++++++------------- 4 files changed, 17 insertions(+), 24 deletions(-) diff --git a/jacdac-ts b/jacdac-ts index 79cc13796b..c68cbbb232 160000 --- a/jacdac-ts +++ b/jacdac-ts @@ -1 +1 @@ -Subproject commit 79cc13796b09df362f051212a0af04d31b740eb6 +Subproject commit c68cbbb232dce1cd873599f95e6cd073cb1c550d diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/led.ts index 8f22be5e8a..7b69af9c56 100644 --- a/packages/drivers/src/led.ts +++ b/packages/drivers/src/led.ts @@ -144,16 +144,16 @@ declare module "@devicescript/core" { * Call `show` to send the buffer to the LED strip. */ buffer(): Promise + /** * Sends the pixel buffer to the LED driver */ show(): Promise /** - * Sets all pixel color to the given color - * @param c Set + * Sets all pixel color to the given color and renders the buffer */ - setAll(c: number): Promise + showAll(c: number): Promise } } @@ -171,7 +171,7 @@ ds.Led.prototype.show = async function () { if (b && b.length <= 64) await this.pixels.write(b.buffer) } -ds.Led.prototype.setAll = async function (c: number) { +ds.Led.prototype.showAll = async function (c: number) { const b = await this.buffer() b.setAll(c) await this.show() diff --git a/packages/sampleprj/src/maindisplaydimmer.ts b/packages/sampleprj/src/maindisplaydimmer.ts index 6ade4ac27a..ab38c263bc 100644 --- a/packages/sampleprj/src/maindisplaydimmer.ts +++ b/packages/sampleprj/src/maindisplaydimmer.ts @@ -4,7 +4,6 @@ import "@devicescript/runtime" const pot = new ds.Potentiometer() const ledD = new ds.Led() const btn = new ds.Button() -let p pot.reading.subscribe(async p => { console.log("tick", p) @@ -12,13 +11,13 @@ pot.reading.subscribe(async p => { }) ledD.binding().subscribe(async () => { - await ledD.setAll(0xff0000) + await ledD.showAll(0xff0000) }) btn.down.subscribe(async () => { - await ledD.setAll(0xff00ff) + await ledD.showAll(0xff00ff) }) btn.up.subscribe(async () => { - await ledD.setAll(0x0000ff) + await ledD.showAll(0x0000ff) }) diff --git a/packages/sampleprj/src/mainled.ts b/packages/sampleprj/src/mainled.ts index c8c74767c2..90cab04a7e 100644 --- a/packages/sampleprj/src/mainled.ts +++ b/packages/sampleprj/src/mainled.ts @@ -2,32 +2,26 @@ import { delay, Led, LedVariant } from "@devicescript/core" import { rgb } from "@devicescript/runtime" import { startLed } from "@devicescript/drivers" +const jdled = new Led() const led = await startLed({ length: 12, - variant: LedVariant.Strip, + columns: 3, + variant: LedVariant.Matrix, }) -const pixels = await led.buffer() - const led2 = await startLed({ length: 256, variant: LedVariant.Strip, }) -const pixels2 = await led.buffer() setInterval(async () => { - pixels.setAll(rgb(255, 0, 0)) - await led.show() - - pixels2.setAll(rgb(0, 0, 255)) - await led2.show() - + await jdled.showAll(0x00ff00) + await led.showAll(0xff0000) + await led2.showAll(0x0000ff) await delay(1000) - pixels.setAll(0) - await led.show() - - pixels2.setAll(0) - await led2.show() + await jdled.showAll(0x0f0fff) + await led.showAll(0x00ff00) + await led2.showAll(0x00ff00) await delay(500) }, 500) From ea406abbe9d6687abd327f1425c1b9d53c2ecd0e Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 12:20:51 +0200 Subject: [PATCH 14/31] test verification --- packages/drivers/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/drivers/src/index.ts b/packages/drivers/src/index.ts index c6ab57fca4..9ed17900fe 100644 --- a/packages/drivers/src/index.ts +++ b/packages/drivers/src/index.ts @@ -14,6 +14,6 @@ export * from "./dotmatrix" export * from "./st7735" export * from "./uc8151" export * from "./trafficlight" -export * from "./led" +//export * from "./led" configureHardware({ scanI2C: false }) From 7ab1ee09564780411ff0cb9e9ce357acabc6a369 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 12:21:21 +0200 Subject: [PATCH 15/31] undo --- packages/drivers/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/drivers/src/index.ts b/packages/drivers/src/index.ts index 9ed17900fe..c6ab57fca4 100644 --- a/packages/drivers/src/index.ts +++ b/packages/drivers/src/index.ts @@ -14,6 +14,6 @@ export * from "./dotmatrix" export * from "./st7735" export * from "./uc8151" export * from "./trafficlight" -//export * from "./led" +export * from "./led" configureHardware({ scanI2C: false }) From 88b42617086c084781e2d29f27e79386ff80d51e Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sat, 22 Jul 2023 12:35:50 +0200 Subject: [PATCH 16/31] updated jacdac-ts --- jacdac-ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jacdac-ts b/jacdac-ts index c68cbbb232..ad54417a22 160000 --- a/jacdac-ts +++ b/jacdac-ts @@ -1 +1 @@ -Subproject commit c68cbbb232dce1cd873599f95e6cd073cb1c550d +Subproject commit ad54417a2273bfd794cdbf3eb42f2a002b4dc29e From d82d1d43b821dd4cd57ba4079eee89f987fe3d8d Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 23 Jul 2023 07:37:35 +0200 Subject: [PATCH 17/31] added LED display --- packages/drivers/src/led.ts | 48 +++++++++++++++++++++++ packages/graphics/src/image.ts | 8 ++-- packages/graphics/src/palette.ts | 61 +++++++++++++++++++++-------- packages/runtime/src/pixelbuffer.ts | 2 +- 4 files changed, 97 insertions(+), 22 deletions(-) diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/led.ts index 7b69af9c56..fb0c2e1164 100644 --- a/packages/drivers/src/led.ts +++ b/packages/drivers/src/led.ts @@ -1,4 +1,5 @@ import * as ds from "@devicescript/core" +import { Display, Image, Palette } from "@devicescript/graphics" import { PixelBuffer, pixelBuffer } from "@devicescript/runtime" import { Server, ServerOptions, startServer } from "@devicescript/server" @@ -176,3 +177,50 @@ ds.Led.prototype.showAll = async function (c: number) { b.setAll(c) await this.show() } + +/** + * Mounts a Display interface over a LED to make it act as a screen. + * This function allocates one image. + * @param led + * @param palette + * @returns + */ +export async function startLedDisplay( + led: ds.Led, + palette?: Palette +): Promise { + if (!palette) { + const waveLength = await led.waveLength + if (waveLength !== undefined) palette = Palette.monochrome() + else palette = Palette.arcade() + } + + const buffer = await led.buffer() + + const width = (await led.numColumns.read()) || 1 + const height = (buffer.length / width) | 0 + + const bpp = palette.length === 2 ? 1 : 4 + + const image = Image.alloc(width, height, bpp) + + const init = async () => {} + + const show = async () => { + for (let x = 0; x < width; ++x) { + for (let y = 0; y < height; ++y) { + const ci = image.get(x, y) + const c = palette.getAt(ci) + buffer.setAt(y * width + x, c) + } + } + await led.show() + } + + return { + image, + palette, + init, + show, + } +} diff --git a/packages/graphics/src/image.ts b/packages/graphics/src/image.ts index 6309cc6620..0ed09108eb 100644 --- a/packages/graphics/src/image.ts +++ b/packages/graphics/src/image.ts @@ -33,14 +33,14 @@ export declare class Image { buffer: Buffer /** - * Get a pixel color + * Return a copy of the current image */ - get(x: number, y: number): number + clone(): Image /** - * Return a copy of the current image + * Get a pixel color */ - clone(): Image + get(x: number, y: number): number /** * Set pixel color diff --git a/packages/graphics/src/palette.ts b/packages/graphics/src/palette.ts index d813d97887..463bc20d68 100644 --- a/packages/graphics/src/palette.ts +++ b/packages/graphics/src/palette.ts @@ -1,7 +1,14 @@ export class Palette { readonly buffer: Buffer - readonly numColors: number + /** + * Number of colors in the palette + */ + readonly length: number + /** + * Allocates a 4bpp palette similar to MakeCode Arcade + * @returns + */ static arcade() { return new Palette(hex` 000000 ffffff ff2121 ff93c4 ff8135 fff609 249ca3 78dc52 @@ -9,6 +16,10 @@ export class Palette { `) } + /** + * Allocates a 1bpp monochrome palette + * @returns + */ static monochrome() { return new Palette(hex`000000 ffffff`) } @@ -16,28 +27,41 @@ export class Palette { constructor(init: Buffer) { this.buffer = init.slice(0) this.buffer.set(init) - this.numColors = (this.buffer.length / 3) >> 0 + this.length = (this.buffer.length / 3) >> 0 } - color(idx: number) { - if (idx < 0 || idx >= this.numColors) return 0 + /** + * Returns the 24bit RGB color at the given index + * @param index + * @returns + */ + getAt(index: number) { + if (index < 0 || index >= this.length) return 0 return ( - (this.buffer[3 * idx + 0] << 16) | - (this.buffer[3 * idx + 1] << 8) | - (this.buffer[3 * idx + 2] << 0) + (this.buffer[3 * index] << 16) | + (this.buffer[3 * index + 1] << 8) | + (this.buffer[3 * index + 2] << 0) ) } - setColor(idx: number, color: number) { - this.buffer[3 * idx + 0] = color >> 16 - this.buffer[3 * idx + 1] = color >> 8 - this.buffer[3 * idx + 2] = color >> 0 + /** + * Sets a 24bit RGB color at the given index + * @param index + * @param color 24bit RGB color + */ + setAt(index: number, color: number) { + this.buffer[3 * index] = color >> 16 + this.buffer[3 * index + 1] = color >> 8 + this.buffer[3 * index + 2] = color >> 0 } - // r,g,b,padding + /** + * Packs palette for Jacdac packet + * @returns + */ packed(): Buffer { - const res: Buffer = Buffer.alloc(this.numColors << 2) - for (let i = 0; i < this.numColors; ++i) { + const res: Buffer = Buffer.alloc(this.length << 2) + for (let i = 0; i < this.length; ++i) { res[i * 4] = this.buffer[i * 3] res[i * 4 + 1] = this.buffer[i * 3 + 1] res[i * 4 + 2] = this.buffer[i * 3 + 2] @@ -45,11 +69,14 @@ export class Palette { return res } - // r,g,b,padding + /** + * Unpacks palette from Jacdac packet + * @param buffer + */ unpack(buffer: Buffer) { - if (buffer.length >> 2 !== this.numColors) + if (buffer.length >> 2 !== this.length) throw new RangeError("incorrect number of colors") - for (let i = 0; i < this.numColors; ++i) { + for (let i = 0; i < this.length; ++i) { this.buffer[i * 3] = buffer[i * 4] this.buffer[i * 3 + 1] = buffer[i * 4 + 1] this.buffer[i * 3 + 2] = buffer[i * 4 + 2] diff --git a/packages/runtime/src/pixelbuffer.ts b/packages/runtime/src/pixelbuffer.ts index f009047d2d..e0c184a18c 100644 --- a/packages/runtime/src/pixelbuffer.ts +++ b/packages/runtime/src/pixelbuffer.ts @@ -13,7 +13,7 @@ export class PixelBuffer { /** * Starting pixel index in the original buffer */ - readonly start: number + private readonly start: number /** * Number of pixels in the buffer */ From 6acabfc413ceee7c284c1f1e680c232148bf680a Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 23 Jul 2023 08:00:18 +0200 Subject: [PATCH 18/31] allocate palette auto --- packages/drivers/src/led.ts | 17 ++++------------- packages/sampleprj/src/mainled.ts | 19 +++++++++++++++++-- website/docs/developer/graphics/display.mdx | 1 + 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/led.ts index fb0c2e1164..6e96291754 100644 --- a/packages/drivers/src/led.ts +++ b/packages/drivers/src/led.ts @@ -18,7 +18,6 @@ export interface LedServerOptions { * Number of columns of a LED matrix */ columns?: number - maxPower?: number ledsPerPixel?: number waveLength?: number luminousIntensity?: number @@ -29,7 +28,6 @@ export interface LedServerOptions { class LedServer extends Server implements ds.LedServerSpec { private _intensity: number private _columns: number - private _maxPower: number private _ledPerPixels: number private _waveLength: number private _luminousIntensity: number @@ -42,7 +40,6 @@ class LedServer extends Server implements ds.LedServerSpec { this.buffer = pixelBuffer(options.length) this._intensity = options.intensity ?? 1 this._columns = options.columns - this._maxPower = options.maxPower this._ledPerPixels = options.ledsPerPixel this._waveLength = options.waveLength this._luminousIntensity = options.luminousIntensity @@ -71,17 +68,11 @@ class LedServer extends Server implements ds.LedServerSpec { numColumns(): number { return this._columns } - maxPower(): number { - return this._maxPower - } - set_maxPower(value: number): void { - this._maxPower = value - } ledsPerPixel(): number { return this._ledPerPixels } waveLength(): number { - return this._waveLength + return this._waveLength || 0 } luminousIntensity(): number { return this._luminousIntensity @@ -179,7 +170,7 @@ ds.Led.prototype.showAll = async function (c: number) { } /** - * Mounts a Display interface over a LED to make it act as a screen. + * Mounts a Display interface over a LED matrix to make it act as a screen. * This function allocates one image. * @param led * @param palette @@ -190,8 +181,8 @@ export async function startLedDisplay( palette?: Palette ): Promise { if (!palette) { - const waveLength = await led.waveLength - if (waveLength !== undefined) palette = Palette.monochrome() + const waveLength = await led.waveLength.read() + if (waveLength) palette = Palette.monochrome() else palette = Palette.arcade() } diff --git a/packages/sampleprj/src/mainled.ts b/packages/sampleprj/src/mainled.ts index 90cab04a7e..00e4e0d987 100644 --- a/packages/sampleprj/src/mainled.ts +++ b/packages/sampleprj/src/mainled.ts @@ -1,27 +1,42 @@ import { delay, Led, LedVariant } from "@devicescript/core" import { rgb } from "@devicescript/runtime" -import { startLed } from "@devicescript/drivers" +import { startLed, startLedDisplay } from "@devicescript/drivers" const jdled = new Led() const led = await startLed({ length: 12, columns: 3, - variant: LedVariant.Matrix, + variant: LedVariant.Ring, }) const led2 = await startLed({ length: 256, variant: LedVariant.Strip, }) +const ledm = await startLed({ + columns: 16, + length: 256, + variant: LedVariant.Matrix, +}) +const display = await startLedDisplay(ledm) +let ci = 0 setInterval(async () => { + ci = (ci+1) % display.palette.length + if (!ci) ci++ await jdled.showAll(0x00ff00) await led.showAll(0xff0000) await led2.showAll(0x0000ff) + display.image.fill(0) + display.image.drawCircle(6, 6, 5, ci) + await display.show() await delay(1000) await jdled.showAll(0x0f0fff) await led.showAll(0x00ff00) await led2.showAll(0x00ff00) + display.image.fill(0) + display.image.drawRect(2, 2, 10, 8, ci) + await display.show() await delay(500) }, 500) diff --git a/website/docs/developer/graphics/display.mdx b/website/docs/developer/graphics/display.mdx index 6072602fd7..ca5462d969 100644 --- a/website/docs/developer/graphics/display.mdx +++ b/website/docs/developer/graphics/display.mdx @@ -25,6 +25,7 @@ on the simulated device (simulation does not work on hardware device as the comm import { SSD1306Driver, startIndexedScreen } from "@devicescript/drivers" const display = await startIndexedScreen( + // implements Display new SSD1306Driver({ width: 128, height: 64, devAddr: 0x3c }) ) display.image.print(`Hello world!`, 3, 10) From 0072b8de36276155d555d34f2d7b5a95194847ec Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 23 Jul 2023 09:24:20 +0200 Subject: [PATCH 19/31] move to functions for tree shacking --- packages/drivers/src/led.ts | 6 +- packages/runtime/src/main.ts | 43 +++--- packages/runtime/src/pixelbuffer.ts | 225 +++++++++++++++------------- 3 files changed, 151 insertions(+), 123 deletions(-) diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/led.ts index 6e96291754..be153e77ab 100644 --- a/packages/drivers/src/led.ts +++ b/packages/drivers/src/led.ts @@ -1,6 +1,6 @@ import * as ds from "@devicescript/core" import { Display, Image, Palette } from "@devicescript/graphics" -import { PixelBuffer, pixelBuffer } from "@devicescript/runtime" +import { PixelBuffer, fillSolid, fillFade, pixelBuffer } from "@devicescript/runtime" import { Server, ServerOptions, startServer } from "@devicescript/server" export interface LedServerOptions { @@ -90,7 +90,7 @@ class LedServer extends Server implements ds.LedServerSpec { if (this._intensity < 1) { // apply brightness const r = b.allocClone() - r.fade(this._intensity) + fillFade(r, this._intensity) b = r } // TODO: render b to hardware @@ -165,7 +165,7 @@ ds.Led.prototype.show = async function () { ds.Led.prototype.showAll = async function (c: number) { const b = await this.buffer() - b.setAll(c) + fillSolid(b, c) await this.show() } diff --git a/packages/runtime/src/main.ts b/packages/runtime/src/main.ts index 4619ae7c0c..416f9e545e 100644 --- a/packages/runtime/src/main.ts +++ b/packages/runtime/src/main.ts @@ -1,5 +1,16 @@ import { describe, expect, test } from "@devicescript/test" -import { encodeURIComponent, pixelBuffer, rgb, schedule, setStatusLight, uptime, Map, Set } from "." +import { + encodeURIComponent, + pixelBuffer, + rgb, + schedule, + setStatusLight, + uptime, + Map, + Set, + fillGradient, + fillBarGraph, +} from "." import { delay } from "@devicescript/core" describe("rgb", () => { @@ -30,12 +41,12 @@ describe("colorbuffer", () => { }) test("setbargraph", () => { const buf = pixelBuffer(4) - buf.setBarGraph(5, 10) + fillBarGraph(buf, 5, 10) console.log(buf.buffer) }) test("gradient", () => { const buf = pixelBuffer(4) - buf.setGradient(0xff0000, 0x00ff00) + fillGradient(buf, 0xff0000, 0x00ff00) console.log(buf.buffer) }) }) @@ -109,7 +120,7 @@ describe("encodeURIComponent tests", () => { }) }) -describe('Test Es Map Class', () => { +describe("Test Es Map Class", () => { function msg(m: string) { console.log(m) } @@ -144,15 +155,13 @@ describe('Test Es Map Class', () => { ]) msg("map test constructor") expect(map.size() === 3).toBe(true) - }) msg("Map tests completed") }) -describe('Test Es Set Class', () => { - +describe("Test Es Set Class", () => { test("add", () => { - let elements = new Set(); + let elements = new Set() expect(elements === elements.add(1)).toBe(true) expect(elements.size === 1).toBe(true) @@ -166,21 +175,20 @@ describe('Test Es Set Class', () => { expect(elements.size === 3).toBe(true) }) - test("clear", () => { - let elements = new Set(); - [1, 3, 1, 4, 5, 3].forEach(element => { + let elements = new Set() + ;[1, 3, 1, 4, 5, 3].forEach(element => { elements.add(element) }) expect(elements.size === 4).toBe(true) - elements.clear(); + elements.clear() expect(elements.size === 0).toBe(true) }) test("delete", () => { - let elements = new Set(); - ["a", "b", "e", "b", "d", "c", "a"].forEach(element => { + let elements = new Set() + ;["a", "b", "e", "b", "d", "c", "a"].forEach(element => { elements.add(element) }) @@ -196,10 +204,9 @@ describe('Test Es Set Class', () => { expect(elements.size === 4).toBe(true) }) - test("has", () => { - let elements = new Set(); - ["a", "d", "f", "d", "d", "a", "g"].forEach(element => { + let elements = new Set() + ;["a", "d", "f", "d", "d", "a", "g"].forEach(element => { elements.add(element) }) @@ -208,4 +215,4 @@ describe('Test Es Set Class', () => { expect(elements.has("f")).toBe(true) expect(!elements.has("e")).toBe(true) }) -}) \ No newline at end of file +}) diff --git a/packages/runtime/src/pixelbuffer.ts b/packages/runtime/src/pixelbuffer.ts index e0c184a18c..a782a103df 100644 --- a/packages/runtime/src/pixelbuffer.ts +++ b/packages/runtime/src/pixelbuffer.ts @@ -13,7 +13,7 @@ export class PixelBuffer { /** * Starting pixel index in the original buffer */ - private readonly start: number + readonly start: number /** * Number of pixels in the buffer */ @@ -42,6 +42,20 @@ export class PixelBuffer { this.buffer[bi + 2] = color & 0xff } + /** + * Setter for individual RGB colors. Does not check bounds and should be used with care. + * @param pixeloffset + * @param r + * @param g + * @param b + */ + setRgbAt(pixeloffset: number, r: number, g: number, b: number) { + const i = (this.start + (pixeloffset | 0)) * 3 + this.buffer[i] = r + this.buffer[i + 1] = g + this.buffer[i + 2] = b + } + /** * Reads the pixel color at the given offsret * @param pixeloffset pixel offset. if negative starts from the end @@ -59,24 +73,6 @@ export class PixelBuffer { return rgb(r, g, b) } - /** - * Sets all the color in the buffer to the given color - * @param c 24bit rgb color - */ - setAll(c: number) { - const r = (c >> 16) & 0xff - const g = (c >> 8) & 0xff - const b = c & 0xff - const n = this.length - const buf = this.buffer - for (let i = 0; i < n; ++i) { - const bi = (this.start + i) * 3 - buf[bi] = r - buf[bi + 1] = g - buf[bi + 2] = b - } - } - /** * Apply a conversion function to all pixels * @param converter @@ -100,21 +96,6 @@ export class PixelBuffer { } } - /** - * Fades each color channels according to the brigthness value between 0 dark and 1 full brightness. - * @param brightness - */ - fade(brightness: number) { - brightness = Math.max(0, Math.min(0xff, brightness << 8)) - if (brightness >= 0xff) return - const s = this.start * 3 - const n = (this.start + this.length) * 3 - const buf = this.buffer - for (let i = s; i < n; ++i) { - buf[i] = (buf[i] * brightness) >> 8 - } - } - /** * Allocates a clone of the buffer view * @returns @@ -128,74 +109,6 @@ export class PixelBuffer { return res } - /** - * Renders a bar graph on the LEDs - * @param value - * @param high - * @returns - */ - setBarGraph( - value: number, - high: number, - options?: { - emptyRangeColor?: number - zeroColor?: number - } - ): void { - if (high <= 0) { - const emptyRangeColor = options?.emptyRangeColor - this.clear() - this.setAt(0, isNaN(emptyRangeColor) ? 0xffff00 : emptyRangeColor) - return - } - - value = Math.abs(value) - const n = this.length - const n1 = n - 1 - let v = Math.idiv(value * n, high) - if (v === 0) { - const zeroColor = options?.zeroColor - this.setAt(0, isNaN(zeroColor) ? 0x666600 : zeroColor) - for (let i = 1; i < n; ++i) this.setAt(i, 0) - } else { - for (let i = 0; i < n; ++i) { - if (i <= v) { - const b = Math.idiv(i * 255, n1) - this.setAt(i, rgb(b, 0, 255 - b)) - } else this.setAt(i, 0) - } - } - } - - /** - * Writes a gradient between the two colors. - * @param startColor - * @param endColor - */ - setGradient( - startColor: number, - endColor: number, - start?: number, - end?: number - ): void { - // normalize range - if (start < 0) start += this.length - start = start || 0 - if (end < 0) end += this.length - end = end === undefined ? this.length - 1 : end - // check if any work needed - const steps = end - start - if (steps < 1) return - - this.setAt(start, startColor) - this.setAt(end, endColor) - for (let i = start + 1; i < end - 1; ++i) { - const alpha = Math.idiv(0xff * i, steps) - const c = blend(startColor, alpha, endColor) - this.setAt(i, c) - } - } - /** * Clears the buffer to #000000 */ @@ -228,3 +141,111 @@ export function pixelBuffer(numPixels: number) { const buf = ds.Buffer.alloc(numPixels * 3) return new PixelBuffer(buf, 0, numPixels) } + +/** + * Writes a gradient between the two colors. + * @param startColor + * @param endColor + */ +export function fillGradient( + pixels: PixelBuffer, + startColor: number, + endColor: number, + start?: number, + end?: number +): void { + // normalize range + if (start < 0) start += pixels.length + start = start || 0 + if (end < 0) end += pixels.length + end = end === undefined ? pixels.length - 1 : end + // check if any work needed + const steps = end - start + if (steps < 1) return + + pixels.setAt(start, startColor) + pixels.setAt(end, endColor) + for (let i = start + 1; i < end - 1; ++i) { + const alpha = Math.idiv(0xff * i, steps) + const c = blend(startColor, alpha, endColor) + pixels.setAt(i, c) + } +} + +/** + * Renders a bar graph of value (absolute value) on a pixel buffer + * @param pixels + * @param value + * @param high + * @param options + * @returns + */ +export function fillBarGraph( + pixels: PixelBuffer, + value: number, + high: number, + options?: { + /** + * Color used when the range is empty + */ + emptyRangeColor?: number + /** + * Color used when the value is 0 + */ + zeroColor?: number + } +) { + if (high <= 0) { + const emptyRangeColor = options?.emptyRangeColor + pixels.clear() + pixels.setAt(0, isNaN(emptyRangeColor) ? 0xffff00 : emptyRangeColor) + return + } + + value = Math.abs(value) + const n = pixels.length + const n1 = n - 1 + let v = Math.idiv(value * n, high) + if (v === 0) { + const zeroColor = options?.zeroColor + pixels.setAt(0, isNaN(zeroColor) ? 0x666600 : zeroColor) + for (let i = 1; i < n; ++i) pixels.setAt(i, 0) + } else { + for (let i = 0; i < n; ++i) { + if (i <= v) { + const b = Math.idiv(i * 255, n1) + pixels.setRgbAt(i, b, 0, 255 - b) + } else pixels.setRgbAt(i, 0, 0, 0) + } + } +} + +/** + * Sets all the color in the buffer to the given color + * @param c 24bit rgb color + */ +export function fillSolid(pixels: PixelBuffer, c: number) { + const r = (c >> 16) & 0xff + const g = (c >> 8) & 0xff + const b = c & 0xff + const n = pixels.length + for (let i = 0; i < n; ++i) { + pixels.setRgbAt(i, r, g, b) + } +} + +/** + * Fades each color channels according to the brigthness value between 0 dark and 1 full brightness. + * @param brightness + */ +export function fillFade(pixels: PixelBuffer, brightness: number) { + brightness = Math.max(0, Math.min(0xff, (brightness | 0) << 8)) + + const s = pixels.start * 3 + const n = (pixels.start + pixels.length) * 3 + const buf = pixels.buffer + + for (let i = s; i < n; ++i) { + buf[i] = (buf[i] * brightness) >> 8 + } +} From 3f8e12acec077a64ace3b40492b94cac9a3ef6f0 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 23 Jul 2023 15:03:53 +0200 Subject: [PATCH 20/31] add gamma correction --- packages/drivers/src/led.ts | 21 ++++++++++++++++----- packages/runtime/src/pixelbuffer.ts | 27 +++++++++++++++++++++++++-- packages/sampleprj/src/mainled.ts | 3 ++- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/led.ts index be153e77ab..3c67e3c79b 100644 --- a/packages/drivers/src/led.ts +++ b/packages/drivers/src/led.ts @@ -1,6 +1,12 @@ import * as ds from "@devicescript/core" import { Display, Image, Palette } from "@devicescript/graphics" -import { PixelBuffer, fillSolid, fillFade, pixelBuffer } from "@devicescript/runtime" +import { + PixelBuffer, + fillSolid, + fillFade, + pixelBuffer, + correctGamma, +} from "@devicescript/runtime" import { Server, ServerOptions, startServer } from "@devicescript/server" export interface LedServerOptions { @@ -22,7 +28,10 @@ export interface LedServerOptions { waveLength?: number luminousIntensity?: number variant?: ds.LedVariant - // TODO configure pins? + /** + * Specify the amount of gamma correction + */ + gamma?: number } class LedServer extends Server implements ds.LedServerSpec { @@ -32,6 +41,7 @@ class LedServer extends Server implements ds.LedServerSpec { private _waveLength: number private _luminousIntensity: number private _variant: ds.LedVariant + private _gamma: number readonly buffer: PixelBuffer @@ -44,6 +54,7 @@ class LedServer extends Server implements ds.LedServerSpec { this._waveLength = options.waveLength this._luminousIntensity = options.luminousIntensity this._variant = options.variant + this._gamma = options.gamma } pixels(): ds.Buffer { @@ -87,10 +98,10 @@ class LedServer extends Server implements ds.LedServerSpec { async show(): Promise { let b = this.buffer // full brightness so we can use the buffer as is - if (this._intensity < 1) { - // apply brightness + if (this._intensity < 1 || this._gamma) { const r = b.allocClone() - fillFade(r, this._intensity) + if (this._intensity < 1) fillFade(r, this._intensity) + if (this._gamma) correctGamma(r, this._gamma) b = r } // TODO: render b to hardware diff --git a/packages/runtime/src/pixelbuffer.ts b/packages/runtime/src/pixelbuffer.ts index a782a103df..4fad0416ae 100644 --- a/packages/runtime/src/pixelbuffer.ts +++ b/packages/runtime/src/pixelbuffer.ts @@ -242,10 +242,33 @@ export function fillFade(pixels: PixelBuffer, brightness: number) { brightness = Math.max(0, Math.min(0xff, (brightness | 0) << 8)) const s = pixels.start * 3 - const n = (pixels.start + pixels.length) * 3 + const e = (pixels.start + pixels.length) * 3 const buf = pixels.buffer - for (let i = s; i < n; ++i) { + for (let i = s; i < e; ++i) { buf[i] = (buf[i] * brightness) >> 8 } } + +/** + * Applies a inplace gamma correction to the pixel colors + * @param pixels + * @param gamma + */ +export function correctGamma(pixels: PixelBuffer, gamma: number = 2.7) { + const s = pixels.start * 3 + const e = (pixels.start + pixels.length) * 3 + const buf = pixels.buffer + + for (let i = s; i < e; ++i) { + const c = buf[i] + let o = + c === 0 + ? 0 + : c === 0xff + ? 0xff + : Math.round(Math.pow(c / 255.0, gamma) * 255.0) + if (c > 0 && o === 0) o = 1 + buf[i] = o + } +} diff --git a/packages/sampleprj/src/mainled.ts b/packages/sampleprj/src/mainled.ts index 00e4e0d987..c623ba3f3c 100644 --- a/packages/sampleprj/src/mainled.ts +++ b/packages/sampleprj/src/mainled.ts @@ -16,12 +16,13 @@ const ledm = await startLed({ columns: 16, length: 256, variant: LedVariant.Matrix, + gamma: 2.7, }) const display = await startLedDisplay(ledm) let ci = 0 setInterval(async () => { - ci = (ci+1) % display.palette.length + ci = (ci + 1) % display.palette.length if (!ci) ci++ await jdled.showAll(0x00ff00) await led.showAll(0xff0000) From 3b1635efe214b916de594ea7705fd83327d3d2ba Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 23 Jul 2023 16:44:57 +0200 Subject: [PATCH 21/31] split led display --- packages/drivers/src/index.ts | 1 + packages/drivers/src/led.ts | 48 ----------------------------- packages/drivers/src/leddisplay.ts | 49 ++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 48 deletions(-) create mode 100644 packages/drivers/src/leddisplay.ts diff --git a/packages/drivers/src/index.ts b/packages/drivers/src/index.ts index c6ab57fca4..1848ae2659 100644 --- a/packages/drivers/src/index.ts +++ b/packages/drivers/src/index.ts @@ -15,5 +15,6 @@ export * from "./st7735" export * from "./uc8151" export * from "./trafficlight" export * from "./led" +export * from "./leddisplay" configureHardware({ scanI2C: false }) diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/led.ts index 3c67e3c79b..a9e8f916f7 100644 --- a/packages/drivers/src/led.ts +++ b/packages/drivers/src/led.ts @@ -1,5 +1,4 @@ import * as ds from "@devicescript/core" -import { Display, Image, Palette } from "@devicescript/graphics" import { PixelBuffer, fillSolid, @@ -179,50 +178,3 @@ ds.Led.prototype.showAll = async function (c: number) { fillSolid(b, c) await this.show() } - -/** - * Mounts a Display interface over a LED matrix to make it act as a screen. - * This function allocates one image. - * @param led - * @param palette - * @returns - */ -export async function startLedDisplay( - led: ds.Led, - palette?: Palette -): Promise { - if (!palette) { - const waveLength = await led.waveLength.read() - if (waveLength) palette = Palette.monochrome() - else palette = Palette.arcade() - } - - const buffer = await led.buffer() - - const width = (await led.numColumns.read()) || 1 - const height = (buffer.length / width) | 0 - - const bpp = palette.length === 2 ? 1 : 4 - - const image = Image.alloc(width, height, bpp) - - const init = async () => {} - - const show = async () => { - for (let x = 0; x < width; ++x) { - for (let y = 0; y < height; ++y) { - const ci = image.get(x, y) - const c = palette.getAt(ci) - buffer.setAt(y * width + x, c) - } - } - await led.show() - } - - return { - image, - palette, - init, - show, - } -} diff --git a/packages/drivers/src/leddisplay.ts b/packages/drivers/src/leddisplay.ts new file mode 100644 index 0000000000..77f12992fe --- /dev/null +++ b/packages/drivers/src/leddisplay.ts @@ -0,0 +1,49 @@ +import * as ds from "@devicescript/core" +import { Display, Image, Palette } from "@devicescript/graphics" + +/** + * Mounts a Display interface over a LED matrix to make it act as a screen. + * This function allocates one image. + * @param led + * @param palette + * @returns + */ +export async function startLedDisplay( + led: ds.Led, + palette?: Palette +): Promise { + if (!palette) { + const waveLength = await led.waveLength.read() + if (waveLength) palette = Palette.monochrome() + else palette = Palette.arcade() + } + + const buffer = await led.buffer() + + const width = (await led.numColumns.read()) || 1 + const height = (buffer.length / width) | 0 + + const bpp = palette.length === 2 ? 1 : 4 + + const image = Image.alloc(width, height, bpp) + + const init = async () => {} + + const show = async () => { + for (let x = 0; x < width; ++x) { + for (let y = 0; y < height; ++y) { + const ci = image.get(x, y) + const c = palette.getAt(ci) + buffer.setAt(y * width + x, c) + } + } + await led.show() + } + + return { + image, + palette, + init, + show, + } +} From 90ce385e302e63d02bcce355ead96181c2085e5b Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 23 Jul 2023 16:54:31 +0200 Subject: [PATCH 22/31] reduce import sizes --- cli/src/snippets.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/snippets.ts b/cli/src/snippets.ts index 7dd05145d6..14eacbb2d6 100644 --- a/cli/src/snippets.ts +++ b/cli/src/snippets.ts @@ -55,7 +55,7 @@ export async function snippets(options: SnippetsOptions) { await writeFile(fullname, snip) imports += `import "./${mod}"\n` numsnip++ - if (numsnip % 150 == 0) { + if (numsnip % 100 == 0) { allImports.push(imports) imports = "" } From 611650147d8518d38732eb6c911fbc4cb0cceb6f Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 23 Jul 2023 17:08:43 +0200 Subject: [PATCH 23/31] move led to runtime --- packages/drivers/src/led.ts | 47 +---------------------------------- packages/runtime/src/index.ts | 1 + packages/runtime/src/led.ts | 46 ++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 46 deletions(-) create mode 100644 packages/runtime/src/led.ts diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/led.ts index a9e8f916f7..2e26e04623 100644 --- a/packages/drivers/src/led.ts +++ b/packages/drivers/src/led.ts @@ -1,7 +1,6 @@ import * as ds from "@devicescript/core" import { PixelBuffer, - fillSolid, fillFade, pixelBuffer, correctGamma, @@ -121,7 +120,7 @@ export async function startLed( const server = new LedServer(options) const client = new ds.Led(startServer(server)) - ;(client as any as LedWithBuffer)._buffer = server.buffer + ;(client as any)._buffer = server.buffer client.show = async function () { await server.show() if (length <= 64) await client.pixels.write(server.buffer.buffer) @@ -134,47 +133,3 @@ export async function startLed( return client } - -interface LedWithBuffer { - _buffer: PixelBuffer -} - -declare module "@devicescript/core" { - interface Led { - /** - * Gets the pixel buffer to perform coloring operations. - * Call `show` to send the buffer to the LED strip. - */ - buffer(): Promise - - /** - * Sends the pixel buffer to the LED driver - */ - show(): Promise - - /** - * Sets all pixel color to the given color and renders the buffer - */ - showAll(c: number): Promise - } -} - -ds.Led.prototype.buffer = async function () { - let b = (this as any as LedWithBuffer)._buffer - if (!b) { - const n = await this.numPixels.read() - ;(this as any as LedWithBuffer)._buffer = b = pixelBuffer(n) - } - return b -} - -ds.Led.prototype.show = async function () { - const b = (this as any as LedWithBuffer)._buffer - if (b && b.length <= 64) await this.pixels.write(b.buffer) -} - -ds.Led.prototype.showAll = async function (c: number) { - const b = await this.buffer() - fillSolid(b, c) - await this.show() -} diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 4948362fb4..31fba8233b 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -6,3 +6,4 @@ export * from "./valuedashboard" export * from "./map" export * from "./set" export * from "./pixelbuffer" +export * from "./led" \ No newline at end of file diff --git a/packages/runtime/src/led.ts b/packages/runtime/src/led.ts new file mode 100644 index 0000000000..adfc7cbd32 --- /dev/null +++ b/packages/runtime/src/led.ts @@ -0,0 +1,46 @@ +import * as ds from "@devicescript/core" +import { PixelBuffer, fillSolid, pixelBuffer } from "./pixelbuffer" + +interface LedWithBuffer { + _buffer: PixelBuffer +} + +declare module "@devicescript/core" { + interface Led { + /** + * Gets the pixel buffer to perform coloring operations. + * Call `show` to send the buffer to the LED strip. + */ + buffer(): Promise + + /** + * Sends the pixel buffer to the LED driver + */ + show(): Promise + + /** + * Sets all pixel color to the given color and renders the buffer + */ + showAll(c: number): Promise + } +} + +ds.Led.prototype.buffer = async function () { + let b = (this as any as LedWithBuffer)._buffer + if (!b) { + const n = await this.numPixels.read() + ;(this as any as LedWithBuffer)._buffer = b = pixelBuffer(n) + } + return b +} + +ds.Led.prototype.show = async function () { + const b = (this as any as LedWithBuffer)._buffer + if (b && b.length <= 64) await this.pixels.write(b.buffer) +} + +ds.Led.prototype.showAll = async function (c: number) { + const b = await this.buffer() + fillSolid(b, c) + await this.show() +} From 8fc0c9cd12935d1996e14833f337da220a994380 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 23 Jul 2023 17:39:58 +0200 Subject: [PATCH 24/31] getting started on docs --- compiler/src/specgen.ts | 3 +-- website/docs/api/clients-custom/led-about.mdp | 15 +++++++++++++++ website/docs/developer/leds/_category_.json | 4 ++++ website/docs/developer/leds/index.mdx | 15 +++++++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 website/docs/api/clients-custom/led-about.mdp create mode 100644 website/docs/developer/leds/_category_.json create mode 100644 website/docs/developer/leds/index.mdx diff --git a/compiler/src/specgen.ts b/compiler/src/specgen.ts index d24a7c4c30..13ea6ad663 100644 --- a/compiler/src/specgen.ts +++ b/compiler/src/specgen.ts @@ -460,8 +460,7 @@ This service is ${status} and may change in the future. ` : undefined, patchLinks(info.notes["short"]), - `- client for [${info.name} service](https://microsoft.github.io/jacdac-docs/services/${info.shortId}/)`, - baseclass ? `- inherits ${baseclass}` : undefined, + `\n{@import optional ../clients-custom/${info.shortId}-short.mdp}\n`, info.notes["long"] ? `## About diff --git a/website/docs/api/clients-custom/led-about.mdp b/website/docs/api/clients-custom/led-about.mdp new file mode 100644 index 0000000000..db9f3a156e --- /dev/null +++ b/website/docs/api/clients-custom/led-about.mdp @@ -0,0 +1,15 @@ +:::note + +Additional runtime support is provided for `Led` +by importing the `@devicescript/runtime` package. + +Please refer to the [LEDs developer documentation](/developer/leds). + +```ts +import { Led } from "@devicescript/core" +import "@devicescript/runtime" + +const led = new Led() +``` + +::: diff --git a/website/docs/developer/leds/_category_.json b/website/docs/developer/leds/_category_.json new file mode 100644 index 0000000000..247b3e8875 --- /dev/null +++ b/website/docs/developer/leds/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "LEDs", + "position": 10.5 +} diff --git a/website/docs/developer/leds/index.mdx b/website/docs/developer/leds/index.mdx new file mode 100644 index 0000000000..ae4035ab1b --- /dev/null +++ b/website/docs/developer/leds/index.mdx @@ -0,0 +1,15 @@ +--- +title: LEDs +--- + +# LEDs + +Controlling strips of programmable LEDs can be done through the `Led` client. +This client requires to import the `@devicescript/runtime` to get all the functionalities. + +```ts +import { Led } from "@devicescript/core" +import "@devicescript/runtime" + +const led = new Led() +``` \ No newline at end of file From 2c57c90ecee1cce45f307fcc954e94756c6a432f Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 23 Jul 2023 18:05:11 +0200 Subject: [PATCH 25/31] more docs --- packages/runtime/src/pixelbuffer.ts | 11 +- website/docs/api/runtime/index.mdx | 8 ++ website/docs/api/runtime/pixelbuffer.mdx | 109 ++++++++++++++++++ .../api/runtime/{index.md => schedule.mdx} | 6 +- website/docs/developer/leds/index.mdx | 39 ++++++- website/docs/samples/schedule-blinky.mdx | 2 +- 6 files changed, 159 insertions(+), 16 deletions(-) create mode 100644 website/docs/api/runtime/index.mdx create mode 100644 website/docs/api/runtime/pixelbuffer.mdx rename website/docs/api/runtime/{index.md => schedule.mdx} (82%) diff --git a/packages/runtime/src/pixelbuffer.ts b/packages/runtime/src/pixelbuffer.ts index 4fad0416ae..822bd09d20 100644 --- a/packages/runtime/src/pixelbuffer.ts +++ b/packages/runtime/src/pixelbuffer.ts @@ -150,15 +150,10 @@ export function pixelBuffer(numPixels: number) { export function fillGradient( pixels: PixelBuffer, startColor: number, - endColor: number, - start?: number, - end?: number + endColor: number ): void { - // normalize range - if (start < 0) start += pixels.length - start = start || 0 - if (end < 0) end += pixels.length - end = end === undefined ? pixels.length - 1 : end + const start = pixels.start + const end = pixels.start + pixels.length // check if any work needed const steps = end - start if (steps < 1) return diff --git a/website/docs/api/runtime/index.mdx b/website/docs/api/runtime/index.mdx new file mode 100644 index 0000000000..4073c6cfbf --- /dev/null +++ b/website/docs/api/runtime/index.mdx @@ -0,0 +1,8 @@ +# Runtime + +The `@devicescript/runtime` [builtin](/developer/packages) module +provides additional runtime helpers. + +```ts +import "@devicescript/runtime" +``` diff --git a/website/docs/api/runtime/pixelbuffer.mdx b/website/docs/api/runtime/pixelbuffer.mdx new file mode 100644 index 0000000000..e11cc07be0 --- /dev/null +++ b/website/docs/api/runtime/pixelbuffer.mdx @@ -0,0 +1,109 @@ +# PixelBuffer + +A 1D color vector to support color manipulation of LED strips. All colors are 24bit RGB colors. + +The pixel buffer is typically accessed through a [Led](/api/clients/led) client. + +```ts +import { Led } from "@devicescript/core" +import "@devicescript/runtime" + +const led = new Led() +// highlight-next-line +const pixels = await led.buffer() +``` + +It can also be allocated using `pixelBuffer`. + +```ts +import { pixelBuffer } from "@devicescript/runtime" + +// highlight-next-line +const pixels = pixelBuffer(32) +``` + +## Usage + +### `at`, `setAt` + +Indexing functions similar to `Array.at`, they allow to set color of individual LEDs and support negative indices. + +```ts skip +const c0 = pixels.at(0) +pixels.setAt(1, c0) +``` + +### `clear` + +Clears all colors to `#000000`. + +```ts skip +pixels.clear() +``` + +### `view` + +Creates a aliased range view of the buffer so that you can apply operations on a subset of the colors. + +```ts skip +const view = pixels.view(5, 10) +view.clear() +``` + +## Helpers + +Here are a few helpers built for `PixelBuffer`, but many other could be added! + +### `fillSolid` + +This helper function asigns the given color to the entire range. + +```ts +import { Led } from "@devicescript/core" +import { fillSolid } from "@devicescript/runtime" + +const led = new Led() +const pixels = await led.buffer() + +// highlight-next-line +fillSolid(pixels, 0x00_ff_00) + +await led.show() +``` + +### `fillGradient` + +Helper function that does a linear interpolation of two colors accross the RGB space. + +```ts +import { Led } from "@devicescript/core" +import { fillGradient } from "@devicescript/runtime" + +const led = new Led() +const pixels = await led.buffer() + +// highlight-next-line +fillGradient(pixels, 0x00_ff_00, 0x00_00_ff) + +await led.show() +``` + +### `fillBarGraph` + +A tiny bar chart engine to render on a value on a LED strip + +```ts +import { Led } from "@devicescript/core" +import { fillBarChart } from "@devicescript/runtime" + +const led = new Led() +const pixels = await led.buffer() + +const current = 25 +const max = 100 + +// highlight-next-line +fillBarChart(pixels, current, max) + +await led.show() +``` diff --git a/website/docs/api/runtime/index.md b/website/docs/api/runtime/schedule.mdx similarity index 82% rename from website/docs/api/runtime/index.md rename to website/docs/api/runtime/schedule.mdx index c9c7b5bf76..44c5416a0a 100644 --- a/website/docs/api/runtime/index.md +++ b/website/docs/api/runtime/schedule.mdx @@ -1,8 +1,4 @@ -# Runtime - -The `@devicescript/runtime` [builtin](/developer/packages) module provides additional runtime helpers. - -### `schedule` {#schedule} +# `schedule` The schedule function combines `setTimeout` and `setInterval` to provide a single function that can be used to schedule a function to run once or repeatedly. The function also tracks the number of invocation, total elapsed time and time since last invocation. diff --git a/website/docs/developer/leds/index.mdx b/website/docs/developer/leds/index.mdx index ae4035ab1b..b7e1212fe6 100644 --- a/website/docs/developer/leds/index.mdx +++ b/website/docs/developer/leds/index.mdx @@ -5,11 +5,46 @@ title: LEDs # LEDs Controlling strips of programmable LEDs can be done through the `Led` client. -This client requires to import the `@devicescript/runtime` to get all the functionalities. + +**This client requires to import the `@devicescript/runtime` to get all the functionalities.** + +```ts +import { Led } from "@devicescript/core" +// highlight-next-line +import "@devicescript/runtime" + +const led = new Led() +``` + +## PixelBuffer and show + +The `Led` client has a [pixel buffer](/api/runtime/pixelbuffer), a 1D vector of colors, +that can be used to perform color operations, and a `show` function to render the buffer to the hardware. + +A typical LED program would then look like this: + +```ts +import { Led } from "@devicescript/core" +import { fillSolid } from "@devicescript/runtime" + +const led = new Led() +// retreive pixel buffer from led +const pixels = await led.buffer() +// do operations on pixels, like setting LEDs to green +fillSolid(pixels, 0x00ee00) +// send colors to hardware +await led.show() +``` + +## showAll + +A convenience function `showAll` is provided to set the color of all LEDs. ```ts import { Led } from "@devicescript/core" import "@devicescript/runtime" const led = new Led() -``` \ No newline at end of file +// highlight-next-line +await led.showAll(0x00ee00) +``` diff --git a/website/docs/samples/schedule-blinky.mdx b/website/docs/samples/schedule-blinky.mdx index 823e2e02fe..ed9067aea8 100644 --- a/website/docs/samples/schedule-blinky.mdx +++ b/website/docs/samples/schedule-blinky.mdx @@ -5,7 +5,7 @@ title: Schedule Blinky # Schedule Blinky -This sample blinks the onboard LED using the [schedule](/api/runtime#schedule) +This sample blinks the onboard LED using the [schedule](/api/runtime/schedule) ```ts import { schedule, setStatusLight } from "@devicescript/runtime" From 3954a1c78b84abff8fa6ac5b5f2964636a2afee4 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 23 Jul 2023 18:11:42 +0200 Subject: [PATCH 26/31] more docs --- packages/drivers/src/index.ts | 3 +-- packages/drivers/src/{led.ts => ledserver.ts} | 0 packages/runtime/src/index.ts | 3 ++- .../{drivers => runtime}/src/leddisplay.ts | 0 website/docs/developer/leds/display.mdx | 27 +++++++++++++++++++ website/docs/developer/leds/index.mdx | 13 +++++++++ 6 files changed, 43 insertions(+), 3 deletions(-) rename packages/drivers/src/{led.ts => ledserver.ts} (100%) rename packages/{drivers => runtime}/src/leddisplay.ts (100%) create mode 100644 website/docs/developer/leds/display.mdx diff --git a/packages/drivers/src/index.ts b/packages/drivers/src/index.ts index 1848ae2659..468b4d6c13 100644 --- a/packages/drivers/src/index.ts +++ b/packages/drivers/src/index.ts @@ -14,7 +14,6 @@ export * from "./dotmatrix" export * from "./st7735" export * from "./uc8151" export * from "./trafficlight" -export * from "./led" -export * from "./leddisplay" +export * from "./ledserver" configureHardware({ scanI2C: false }) diff --git a/packages/drivers/src/led.ts b/packages/drivers/src/ledserver.ts similarity index 100% rename from packages/drivers/src/led.ts rename to packages/drivers/src/ledserver.ts diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 31fba8233b..25cea63c04 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -6,4 +6,5 @@ export * from "./valuedashboard" export * from "./map" export * from "./set" export * from "./pixelbuffer" -export * from "./led" \ No newline at end of file +export * from "./led" +export * from "./leddisplay" \ No newline at end of file diff --git a/packages/drivers/src/leddisplay.ts b/packages/runtime/src/leddisplay.ts similarity index 100% rename from packages/drivers/src/leddisplay.ts rename to packages/runtime/src/leddisplay.ts diff --git a/website/docs/developer/leds/display.mdx b/website/docs/developer/leds/display.mdx new file mode 100644 index 0000000000..23b118ea35 --- /dev/null +++ b/website/docs/developer/leds/display.mdx @@ -0,0 +1,27 @@ +--- +title: Display +--- + +# Led Display + +You can wrap an LED matrix as a [Display](/developer/graphics/display) +and render graphics to it as if it was just another screen. + +```ts +import { Led } from "@devicescript/core" +import { startLedDisplay } from "@devicescript/runtime" + +const led = new Led() +// highlight-next-line +const display = await startLedDisplay(led) +// render +display.image.print(`Hello world!`, 3, 10) +// and show +await display.show() +``` + +The palette is automatically infered from the `waveLength` register; otherwise you can provide one. + +```ts skip +const display = await startLedDisplay(led, palette) +``` diff --git a/website/docs/developer/leds/index.mdx b/website/docs/developer/leds/index.mdx index b7e1212fe6..283819edcc 100644 --- a/website/docs/developer/leds/index.mdx +++ b/website/docs/developer/leds/index.mdx @@ -48,3 +48,16 @@ const led = new Led() // highlight-next-line await led.showAll(0x00ee00) ``` + +## startLedDisplay + +You can mount a LED matrix as a [display](./leds/display). + +```ts +import { Led } from "@devicescript/core" +import { startLedDisplay } from "@devicescript/runtime" + +const led = new Led() +// highlight-next-line +const display = await startLedDisplay(led) +``` From 50f90697045345b1c49a2c3c3027daeea389e051 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 23 Jul 2023 18:21:07 +0200 Subject: [PATCH 27/31] more docs --- packages/drivers/src/ledserver.ts | 9 +++++++++ packages/sampleprj/src/mainled.ts | 1 - website/docs/developer/leds/display.mdx | 2 +- website/docs/developer/leds/driver.mdx | 27 +++++++++++++++++++++++++ website/docs/developer/leds/index.mdx | 18 ++++++++++++++++- 5 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 website/docs/developer/leds/driver.mdx diff --git a/packages/drivers/src/ledserver.ts b/packages/drivers/src/ledserver.ts index 2e26e04623..6a597912d5 100644 --- a/packages/drivers/src/ledserver.ts +++ b/packages/drivers/src/ledserver.ts @@ -23,8 +23,17 @@ export interface LedServerOptions { */ columns?: number ledsPerPixel?: number + /** + * For monochrome LEDs, the LED wavelength + */ waveLength?: number + /** + * The luminous power of the LEDs, is it very bright? + */ luminousIntensity?: number + /** + * The shape and topology of the LEDs + */ variant?: ds.LedVariant /** * Specify the amount of gamma correction diff --git a/packages/sampleprj/src/mainled.ts b/packages/sampleprj/src/mainled.ts index c623ba3f3c..d1385c26e1 100644 --- a/packages/sampleprj/src/mainled.ts +++ b/packages/sampleprj/src/mainled.ts @@ -1,5 +1,4 @@ import { delay, Led, LedVariant } from "@devicescript/core" -import { rgb } from "@devicescript/runtime" import { startLed, startLedDisplay } from "@devicescript/drivers" const jdled = new Led() diff --git a/website/docs/developer/leds/display.mdx b/website/docs/developer/leds/display.mdx index 23b118ea35..2be0aa3c8f 100644 --- a/website/docs/developer/leds/display.mdx +++ b/website/docs/developer/leds/display.mdx @@ -2,7 +2,7 @@ title: Display --- -# Led Display +# LED Display You can wrap an LED matrix as a [Display](/developer/graphics/display) and render graphics to it as if it was just another screen. diff --git a/website/docs/developer/leds/driver.mdx b/website/docs/developer/leds/driver.mdx new file mode 100644 index 0000000000..b5e94a9f26 --- /dev/null +++ b/website/docs/developer/leds/driver.mdx @@ -0,0 +1,27 @@ +--- +title: LED Driver +--- + +# LED Driver + +You can start a driver for WS2812 or AP102 using `startLed`. + +```ts +import { LedVariant } from "@devicescript/core" +import { startLed } from "@devicescript/drivers" + +// highlight-start +const led = await startLed({ + length: 32, + variant: LedVariant.Ring, +}) +// highlight-end +``` + +## Simulation and long strips (> 64 LEDs) + +For short LED strips, 64 LEDs or less, DeviceScript provides full simulation and device twin +for hardware and simulated devices. + +For strips longer than 64 LEDs, the simulator device will work but the hardware device twin will +not work anymore. This is a simple limitation that the data overflows the packets used in Jacdac. diff --git a/website/docs/developer/leds/index.mdx b/website/docs/developer/leds/index.mdx index 283819edcc..e9b2847238 100644 --- a/website/docs/developer/leds/index.mdx +++ b/website/docs/developer/leds/index.mdx @@ -16,6 +16,22 @@ import "@devicescript/runtime" const led = new Led() ``` +## Driver + +You can start a driver for WS2812 or AP102 using [startLed](./leds/driver). + +```ts +import { LedVariant } from "@devicescript/core" +import { startLed } from "@devicescript/drivers" + +// highlight-start +const led = await startLed({ + length: 32, + variant: LedVariant.Ring, +}) +// highlight-end +``` + ## PixelBuffer and show The `Led` client has a [pixel buffer](/api/runtime/pixelbuffer), a 1D vector of colors, @@ -49,7 +65,7 @@ const led = new Led() await led.showAll(0x00ee00) ``` -## startLedDisplay +## LED Display You can mount a LED matrix as a [display](./leds/display). From 2f8c04a02b8192c15669102614b767e6c6227fed Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 23 Jul 2023 18:21:58 +0200 Subject: [PATCH 28/31] reorg docs --- website/docs/developer/status-light.mdx | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/website/docs/developer/status-light.mdx b/website/docs/developer/status-light.mdx index fb871f3938..3f5bd88e4c 100644 --- a/website/docs/developer/status-light.mdx +++ b/website/docs/developer/status-light.mdx @@ -1,14 +1,30 @@ --- sidebar_position: 1.1 description: Learn how to use the status light on DeviceScript to message - application states. Control the status LED until a system LED pattern is - scheduled by the runtime. + application states. Control the status LED until a system LED pattern is + scheduled by the runtime. --- # Status Light DeviceScript uses various blinking patterns on the status LED to message application states. +## Controlling the status led + +You can set the status LED (until a system LED pattern is scheduled by the runtime) +using the `setStatusLight` function. + +```ts +import { delay } from "@devicescript/core" +import { setStatusLight } from "@devicescript/runtime" + +setInterval(async () => { + await setStatusLight(0x00ff00) // green + await delay(500) + await setStatusLight(0x000000) // off +}, 500) +``` + ## Network connectivity - connecting to network: slow yellow glow @@ -28,19 +44,3 @@ DeviceScript uses various blinking patterns on the status LED to message applica - generic error: short red blink The blinking patterns are defined at https://github.com/microsoft/jacdac-c/blob/main/inc/jd_io.h#L77 - -## Controlling the status led - -You can also set the status LED (until a system LED pattern is scheduled by the runtime) -using the `setStatusLight` function. - -```ts -import { delay } from "@devicescript/core" -import { setStatusLight } from "@devicescript/runtime" - -setInterval(async () => { - await setStatusLight(0x00ff00) // green - await delay(500) - await setStatusLight(0x000000) // off -}, 500) -``` From d0956c35fcd84ed43457263d4276c1a2e7774089 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 23 Jul 2023 18:24:17 +0200 Subject: [PATCH 29/31] fix sample --- website/docs/api/runtime/pixelbuffer.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/api/runtime/pixelbuffer.mdx b/website/docs/api/runtime/pixelbuffer.mdx index e11cc07be0..921f816b80 100644 --- a/website/docs/api/runtime/pixelbuffer.mdx +++ b/website/docs/api/runtime/pixelbuffer.mdx @@ -94,7 +94,7 @@ A tiny bar chart engine to render on a value on a LED strip ```ts import { Led } from "@devicescript/core" -import { fillBarChart } from "@devicescript/runtime" +import { fillBarGraph } from "@devicescript/runtime" const led = new Led() const pixels = await led.buffer() @@ -103,7 +103,7 @@ const current = 25 const max = 100 // highlight-next-line -fillBarChart(pixels, current, max) +fillBarGraph(pixels, current, max) await led.show() ``` From a0571e26747e77c36a74b86b971d63bcdcf92278 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 23 Jul 2023 18:36:22 +0200 Subject: [PATCH 30/31] little disclaimer --- website/docs/developer/leds/index.mdx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/website/docs/developer/leds/index.mdx b/website/docs/developer/leds/index.mdx index e9b2847238..cb4d7b5733 100644 --- a/website/docs/developer/leds/index.mdx +++ b/website/docs/developer/leds/index.mdx @@ -32,6 +32,13 @@ const led = await startLed({ // highlight-end ``` +:::tip + +Support for hardware strips is still under constructions. Currently, simulation is supported +but hardware in progress. + +::: + ## PixelBuffer and show The `Led` client has a [pixel buffer](/api/runtime/pixelbuffer), a 1D vector of colors, From c0e212d75cbbcae8368279ef3cdc4f576c289ba3 Mon Sep 17 00:00:00 2001 From: pelikhan Date: Sun, 23 Jul 2023 18:43:22 +0200 Subject: [PATCH 31/31] fix sample --- packages/sampleprj/src/mainled.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/sampleprj/src/mainled.ts b/packages/sampleprj/src/mainled.ts index d1385c26e1..41caf3a541 100644 --- a/packages/sampleprj/src/mainled.ts +++ b/packages/sampleprj/src/mainled.ts @@ -1,5 +1,6 @@ import { delay, Led, LedVariant } from "@devicescript/core" -import { startLed, startLedDisplay } from "@devicescript/drivers" +import { startLed } from "@devicescript/drivers" +import { startLedDisplay } from "@devicescript/runtime" const jdled = new Led() const led = await startLed({