pxt-jacdac/led/client.ts

471 строка
16 KiB
TypeScript

namespace modules {
//% fixedInstances
//% blockGap=8
export class LedClient extends jacdac.Client {
private _dirty = false
private _localPixels: Buffer
private _autoShow = true
private _autoShowUnsub: () => void
private readonly _pixels: jacdac.RegisterClient<[Buffer]>
private readonly _brightness: jacdac.RegisterClient<[number]>
private readonly _actualBrightness: jacdac.RegisterClient<[number]>
private readonly _numPixels: jacdac.RegisterClient<[number]>
private readonly _numColumns: jacdac.RegisterClient<[number]>
private readonly _maxPower: jacdac.RegisterClient<[number]>
private readonly _ledsPerPixel: jacdac.RegisterClient<[number]>
private readonly _waveLength: jacdac.RegisterClient<[number]>
private readonly _luminousIntensity: jacdac.RegisterClient<[number]>
private readonly _variant: jacdac.RegisterClient<[jacdac.LedVariant]>
constructor(role: string) {
super(jacdac.SRV_LED, role)
this._pixels = this.addRegister<[Buffer]>(
jacdac.LedReg.Pixels,
jacdac.LedRegPack.Pixels
)
this._brightness = this.addRegister<[number]>(
jacdac.LedReg.Brightness,
jacdac.LedRegPack.Brightness
)
this._actualBrightness = this.addRegister<[number]>(
jacdac.LedReg.ActualBrightness,
jacdac.LedRegPack.ActualBrightness
)
this._numPixels = this.addRegister<[number]>(
jacdac.LedReg.NumPixels,
jacdac.LedRegPack.NumPixels,
jacdac.RegisterClientFlags.Const
)
this._numColumns = this.addRegister<[number]>(
jacdac.LedReg.NumColumns,
jacdac.LedRegPack.NumColumns,
jacdac.RegisterClientFlags.Optional |
jacdac.RegisterClientFlags.Const
)
this._maxPower = this.addRegister<[number]>(
jacdac.LedReg.MaxPower,
jacdac.LedRegPack.MaxPower,
jacdac.RegisterClientFlags.Optional
)
this._ledsPerPixel = this.addRegister<[number]>(
jacdac.LedReg.LedsPerPixel,
jacdac.LedRegPack.LedsPerPixel,
jacdac.RegisterClientFlags.Optional |
jacdac.RegisterClientFlags.Const
)
this._waveLength = this.addRegister<[number]>(
jacdac.LedReg.WaveLength,
jacdac.LedRegPack.WaveLength,
jacdac.RegisterClientFlags.Optional |
jacdac.RegisterClientFlags.Const
)
this._luminousIntensity = this.addRegister<[number]>(
jacdac.LedReg.LuminousIntensity,
jacdac.LedRegPack.LuminousIntensity,
jacdac.RegisterClientFlags.Optional |
jacdac.RegisterClientFlags.Const
)
this._variant = this.addRegister<[jacdac.LedVariant]>(
jacdac.LedReg.Variant,
jacdac.LedRegPack.Variant,
jacdac.RegisterClientFlags.Optional |
jacdac.RegisterClientFlags.Const
)
// render again when number of pixels known
this._numPixels.on(jacdac.REPORT_UPDATE, () => this.show())
// maximum size (may be reduced on call to show)
this._localPixels = Buffer.create(
jacdac.CONST_LED_MAX_PIXELS_LENGTH * 3
)
}
/**
* Set the luminosity of the strip.
* At `0` the power to the strip is completely shut down.
*/
//% callInDebugger
//% group="LED"
//% block="%led brightness"
//% blockId=jacdac_led_brightness___get
//% weight=98
brightness(): number {
this.start()
const values = this._brightness.pauseUntilValues() as any[]
return values[0] * 100
}
/**
* Set the luminosity of the strip.
* At `0` the power to the strip is completely shut down.
*/
//% group="LED"
//% blockId=jacdac_led_brightness___set
//% block="set %led brightness to %value"
//% weight=97
//% value.min=0
//% value.max=100
//% value.defl=0.05
setBrightness(value: number) {
this.start()
const values = this._brightness.values as any[]
values[0] = value / 100
this._brightness.values = values as [number]
}
/**
* This is the luminosity actually applied to the strip.
* May be lower than `brightness` if power-limited by the `max_power` register.
* It will rise slowly (few seconds) back to `brightness` is limits are no longer required.
*/
//% callInDebugger
//% group="LED"
//% weight=96
actualBrightness(): number {
this.start()
const values = this._actualBrightness.pauseUntilValues() as any[]
return values[0] * 100
}
/**
* Specifies the number of pixels in the strip.
*/
//% callInDebugger
//% group="LED"
//% weight=95
//% blockId=jacdac_led_num_pixels
//% block="%led number of pixels"
numPixels(): number {
this.start()
const values = this._numPixels.pauseUntilValues() as any[]
return values[0]
}
/**
* If the LED pixel strip is a matrix, specifies the number of columns.
*/
//% callInDebugger
//% group="LED"
//% weight=94
numColumns(): number {
this.start()
const values = this._numColumns.pauseUntilValues() as any[]
return values[0]
}
/**
* Limit the power drawn by the light-strip (and controller).
*/
//% callInDebugger
//% group="LED"
//% weight=93
maxPower(): number {
this.start()
const values = this._maxPower.pauseUntilValues() as any[]
return values[0]
}
/**
* Limit the power drawn by the light-strip (and controller).
*/
//% group="LED"
//% weight=92
//% value.min=0
//% value.max=65535
//% value.defl=200
setMaxPower(value: number) {
this.start()
const values = this._maxPower.values as any[]
values[0] = value
this._maxPower.values = values as [number]
}
/**
* If known, specifies the number of LEDs in parallel on this device.
* The actual number of LEDs is `num_pixels * leds_per_pixel`.
*/
//% callInDebugger
//% group="LED"
//% weight=91
ledsPerPixel(): number {
this.start()
const values = this._ledsPerPixel.pauseUntilValues() as any[]
return values[0]
}
/**
* If monochrome LED, specifies the wave length of the LED.
* Register is missing for RGB LEDs.
*/
//% callInDebugger
//% group="LED"
//% weight=90
waveLength(): number {
this.start()
const values = this._waveLength.pauseUntilValues() as any[]
return values[0]
}
/**
* The luminous intensity of all the LEDs, at full brightness, in micro candella.
*/
//% callInDebugger
//% group="LED"
//% weight=89
luminousIntensity(): number {
this.start()
const values = this._luminousIntensity.pauseUntilValues() as any[]
return values[0]
}
/**
* Specifies the shape of the light strip.
*/
//% callInDebugger
//% group="LED"
//% weight=88
variant(): jacdac.LedVariant {
this.start()
const values = this._variant.pauseUntilValues() as any[]
return values[0]
}
/**
* Gets the pixel color buffer, where every pixel color is encoded as a 24 bit RGB color.
*/
//% callInDebugger
//% group="LED"
//% weight=98
pixels(): Buffer {
this.start()
const values = this._pixels.pauseUntilValues() as any[]
return values[0]
}
/**
* Sets the local pixel color buffer, where every pixel color is encoded as a 24 bit RGB color.
*/
//% callInDebugger
//% group="LED"
//% weight=98
setPixels(pixels: Buffer) {
if (!pixels) return
this._localPixels = pixels
this.setDirty()
}
/**
* Turn on/off the ability to automatically show changes. If false, the user must call 'show'.
* @param value
*/
setAutoShow(value: boolean) {
this._autoShow = !!value
if (this._autoShow) this.show()
else this.stopAutoShow()
}
private startAutoShow() {
if (this._autoShowUnsub) return
this._autoShowUnsub = jacdac.bus.subscribeRefresh(() =>
this.refresh()
)
this.on(jacdac.DISCONNECT, () => this.stopAutoShow())
this.show()
}
private stopAutoShow() {
const unsub = this._autoShowUnsub
this._autoShowUnsub = undefined
if (unsub) unsub()
}
private setDirty() {
if (!this._dirty) {
this._dirty = true
if (!this._autoShowUnsub) this.startAutoShow()
}
}
private refresh() {
if (this._dirty) {
this.show()
}
}
/**
* Sends the local pixel buffer to device immediately, instead of waiting for the rendering loop
*/
//% callInDebugger
//% group="LED"
//% weight=98
show() {
this.start()
const numPixels = this.numPixels()
if (
!isNaN(numPixels) &&
numPixels > 0 &&
numPixels * 3 !== this._localPixels.length
) {
// create a new buffer of the correct length and copy over
const newBuf = Buffer.create(numPixels * 3)
newBuf.write(0, this._localPixels)
this._localPixels = newBuf
}
this._pixels.values = [this._localPixels] as [Buffer]
this._dirty = isNaN(numPixels)
}
/**
* Set a single of the pixels on the strip to one RGB color.
* You need to call ``show`` to make the changes visible.
* @param rgb RGB color of the LED
*/
//% blockId="jacdac_leddisplay_set_pixel_color" block="set %display color at pixel %index to %rgb=colorNumberPicker"
//% weight=81 blockGap=8
//% group="LED"
setPixelColor(index: number, rgb: number) {
index = index | 0
const pixels = this._localPixels
if (!pixels) return
if (index >= 0 && (index + 1) * 3 <= pixels.length) {
const r = (rgb >> 16) & 0xff
const g = (rgb >> 8) & 0xff
const b = rgb & 0xff
const k = index * 3
if (
pixels[k] != r ||
pixels[k + 1] != g ||
pixels[k + 2] != b
) {
pixels[k] = r
pixels[k + 1] = g
pixels[k + 2] = b
this.setDirty()
}
}
}
/**
* Set all of the pixels on the strip to one RGB color.
* @param rgb RGB color of the LED
*/
//% blockId="jacdac_leddisplay_set_strip_color" block="set %display all to %rgb=colorNumberPicker"
//% weight=80 blockGap=8
//% group="LED"
setAll(rgb: number) {
const pixels = this._localPixels
if (!pixels) return
const r = (rgb >> 16) & 0xff
const g = (rgb >> 8) & 0xff
const b = (rgb >> 0) & 0xff
let dirty = this._dirty
for (let i = 0; i + 2 < pixels.length; i += 3) {
dirty =
dirty ||
pixels[i] != r ||
pixels[i + 1] != g ||
pixels[i + 2] != b
pixels[i] = r
pixels[i + 1] = g
pixels[i + 2] = b
}
if (dirty) this.setDirty()
}
private _barGraphHigh = 0
private _barGraphHighLast = 0
/**
* Displays a vertical bar graph based on the `value` and `high` value.
* If `high` is 0, the chart gets adjusted automatically.
* @param value current value to plot
* @param high maximum value, eg: 255
*/
//% weight=84
//% blockId=jacdac_led_show_bar_graph block="plot %strip bar graph of $value||up to $high"
plotBarGraph(value: number, high?: number): void {
if (isNaN(value)) {
this.clear()
this.setDirty()
return
}
const n = this.numPixels()
const pixels = this._localPixels
if (!pixels || n <= 0) return
value = Math.abs(value)
const now = control.millis()
// auto-scale "high" is not provided
if (high > 0) {
this._barGraphHigh = high
} else if (
value > this._barGraphHigh ||
now - this._barGraphHighLast > 10000
) {
this._barGraphHigh = value
this._barGraphHighLast = now
}
// normalize lack of data to 0..1
if (this._barGraphHigh < 16 * Number.EPSILON) this._barGraphHigh = 1
// normalize value to 0..1
const v = value / this._barGraphHigh
const dv = 1 / n
const n1 = n - 1
this.clear()
for (let cv = 0, i = 0; cv < v && i < n; ++i) {
const b = Math.idiv(i * 0xff, n - 1)
pixels[i * 3] = b
pixels[i * 3 + 2] = 0xff - b
cv += dv
}
this.setDirty()
}
/**
* Shift LEDs forward and clear with zeros.
* You need to call ``show`` to make the changes visible.
* @param offset number of pixels to shift forward, eg: 1
*/
//% blockId="jacdac_leddisplay_shift" block="shift %display pixels by %offset" blockGap=8
//% weight=40
//% group="LED"
shift(offset = 1): void {
offset = offset >> 0
if (!offset) return
const stride = 3
const pixels = this._localPixels
if (!pixels) return
pixels.shift(-offset * stride)
this.setDirty()
}
/**
* Rotate LEDs forward.
* You need to call ``show`` to make the changes visible.
* @param offset number of pixels to rotate forward, eg: 1
*/
//% blockId="jacdac_leddisplay_rotate" block="rotate %display pixels by %offset" blockGap=8
//% weight=39
rotate(offset = 1): void {
offset = offset >> 0
if (!offset) return
const stride = 3
const pixels = this._localPixels
if (!pixels) return
pixels.rotate(-offset * stride)
this.setDirty()
}
private clear() {
const pixels = this._localPixels
if (!pixels) return
pixels.fill(0, 0, pixels.length)
}
}
//% fixedInstance whenUsed
export const led1 = new LedClient("led 1")
}