-
Notifications
You must be signed in to change notification settings - Fork 0
add led #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add led #2
Changes from all commits
c2ad3e5
416eb59
91021af
da2f061
3ad21a5
f680f5c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| import { NeoStrand, NeoStrandEffect, type NeoStrandEffectDictionary } from 'neostrand' | ||
| import Timer from 'timer' | ||
|
|
||
| const Timing_WS2812B = { | ||
| mark: { level0: 1, duration0: 900, level1: 0, duration1: 350 }, | ||
| space: { level0: 1, duration0: 350, level1: 0, duration1: 900 }, | ||
| reset: { level0: 0, duration0: 100, level1: 0, duration1: 100 }, | ||
| } as const | ||
|
|
||
| class Blink extends NeoStrandEffect { | ||
| private rgbOn: number | ||
| private rgbOff: number | ||
| constructor( | ||
| dictionary: NeoStrandEffectDictionary & { | ||
| rgb: { r: number; g: number; b: number } | ||
| index?: number | ||
| count?: number | ||
| duration?: number | ||
| }, | ||
| ) { | ||
| super(dictionary) | ||
| this.name = 'Blink' | ||
| this.loop = 1 | ||
|
|
||
| if (dictionary.index) { | ||
| this.start = dictionary.index | ||
| } | ||
| if (dictionary.count) { | ||
| this.size = dictionary.count | ||
| this.end = this.start + this.size | ||
| if (this.end > this.strand.length) this.end = this.strand.length | ||
| } | ||
| this.dur = dictionary.duration ?? 1000 | ||
| this.rgbOn = this.strand.makeRGB(dictionary.rgb.r, dictionary.rgb.g, dictionary.rgb.b) | ||
| this.rgbOff = this.strand.makeRGB(0, 0, 0) | ||
| } | ||
|
|
||
| activate(effect: NeoStrandEffect): void { | ||
| effect.timeline.on(effect, { effectValue: [0, effect.dur] }, effect.dur, null, 0) | ||
| effect.reset(effect) | ||
| } | ||
|
|
||
| set effectValue(value) { | ||
| const half = this.dur / 2 | ||
| const newColor = value < half ? this.rgbOn : this.rgbOff | ||
| const currentColor = this.strand.getPixel(this.start) | ||
| if (newColor !== currentColor) { | ||
| for (let i = this.start; i < this.end; i++) this.strand.set(i, newColor, this.start, this.end) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| export default class Led extends NeoStrand { | ||
| private _effect?: NeoStrandEffect | ||
|
|
||
| constructor(parameters: { | ||
| pin: number | ||
| length?: number | ||
| order?: string | ||
| }) { | ||
| super({ | ||
| pin: parameters.pin, | ||
| length: parameters.length ?? 1, | ||
| order: parameters.order ?? 'GRB', | ||
| timing: Timing_WS2812B, | ||
| }) | ||
| } | ||
| private _fill(color: number, index: number, count: number) { | ||
| for (let i = index; i < index + count; i++) { | ||
| this.set(i, color) | ||
| } | ||
| } | ||
|
|
||
| private _stopEffect() { | ||
| if (this._effect) { | ||
| this.stop() | ||
| this._effect = undefined | ||
| } | ||
| } | ||
|
|
||
| on(r: number, g: number, b: number, duration?: number, index?: number, count?: number) { | ||
| const _index = index ?? 0 | ||
| const _count = count ?? this.length - _index | ||
| this._stopEffect() | ||
| this._fill(this.makeRGB(r, g, b), _index, _count) | ||
|
|
||
| this.update() | ||
| if (duration) { | ||
| Timer.set(() => { | ||
| this.off(_index, _count) | ||
| }, duration) | ||
| } | ||
| } | ||
|
|
||
| off(index?: number, count?: number) { | ||
| const _index = index ?? 0 | ||
| const _count = count ?? this.length - _index | ||
| this._stopEffect() | ||
| this._fill(this.makeRGB(0, 0, 0), _index, _count) | ||
| this.update() | ||
| } | ||
|
|
||
| blink(r: number, g: number, b: number, duration: number, index?: number, count?: number) { | ||
| const _index = index ?? 0 | ||
| const _count = count ?? this.length - _index | ||
| this._stopEffect() | ||
|
|
||
| this._effect = new Blink({ strand: this, rgb: { r, g, b }, index: _index, count: _count, duration: duration }) | ||
| this.setScheme([this._effect]) | ||
| this.start(50) | ||
| } | ||
|
|
||
| rainbow(index?: number, count?: number) { | ||
| const _index = index ?? 0 | ||
| const _count = count ?? this.length - _index | ||
| this._stopEffect() | ||
|
|
||
| this._effect = new NeoStrand.HueSpan({ strand: this, start: _index, end: _index + _count }) | ||
| this.setScheme([this._effect]) | ||
| this.start(50) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| { | ||
| "modules": { | ||
| "*": ["$(MODULES)/drivers/neostrand/*", "./led"], | ||
| "piu/Timeline": "$(MODULES)/piu/All/piuTimeline" | ||
| }, | ||
| "preload": ["neostrand", "piu/Timeline", "led"], | ||
| "platforms": { | ||
| "esp32": { | ||
| "include": ["$(MODULES)/drivers/neopixel/manifest.json"] | ||
| }, | ||
| "mac": { | ||
| "modules": { | ||
| "neopixel": "./neopixel_stub" | ||
| } | ||
| }, | ||
| "lin": { | ||
| "modules": { | ||
| "neopixel": "./neopixel_stub" | ||
| } | ||
| }, | ||
| "win": { | ||
| "modules": { | ||
| "neopixel": "./neopixel_stub" | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| class NeoPixel { | ||
| close() {} | ||
| update() {} | ||
|
|
||
| makeRGB(_r: number, _g: number, _b: number) { | ||
| return 0 | ||
| } | ||
| makeHSB(_h: number, _s: number, _b: number) { | ||
| return 0 | ||
| } | ||
|
|
||
| setPixel(_index: number, _color: number) {} | ||
| fill(_color: number, _index: number, _count: number) {} | ||
| getPixel(_index: number) { | ||
| return 0 | ||
| } | ||
| set brightness(_value: number) {} | ||
| get brightness() { | ||
| return 128 | ||
| } | ||
|
|
||
| get length() { | ||
| return 1 | ||
| } | ||
| get byteLength() { | ||
| return 1 | ||
| } | ||
| } | ||
| Object.freeze(NeoPixel.prototype) | ||
|
|
||
| export default NeoPixel |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ import Microphone from 'microphone' | |
| import Tone from 'tone' | ||
| import { asyncWait } from 'stackchan-util' | ||
| import loadPreferences from 'loadPreference' | ||
| import Led from 'led' | ||
|
|
||
| // wrapper button class for simulator | ||
| class SimButton { | ||
|
|
@@ -112,6 +113,15 @@ function createRobot() { | |
| const touch = TouchConstructor ? new Touch(TouchConstructor) : undefined | ||
| const microphone = Modules.has('embedded:io/audio/in') ? new Microphone() : undefined | ||
| const tone = new Tone({ volume: ttsPrefs.volume }) | ||
|
|
||
| const configLed = config.led || {} | ||
| const led = Object.fromEntries( | ||
| Object.entries(configLed).map(([key, config]) => [ | ||
| key, | ||
| new Led(config as { pin: number; length?: number; order?: string }), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using a type assertion with
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gemini-code-assist 例えばどんな対策をしたらいい? |
||
| ]), | ||
| ) | ||
|
|
||
| return new Robot({ | ||
| driver, | ||
| renderer, | ||
|
|
@@ -120,6 +130,7 @@ function createRobot() { | |
| touch, | ||
| tone, | ||
| microphone, | ||
| led, | ||
| }) | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import Led from 'led' | ||
| import { asyncWait } from 'stackchan-util' | ||
|
|
||
| // M5Stack + M5Go bottom | ||
| const ledConfig = { pin: 15, length: 10 } | ||
| const led = new Led(ledConfig) | ||
|
|
||
| led.on(255, 0, 0) | ||
| await asyncWait(500) | ||
| led.on(0, 255, 0) | ||
| await asyncWait(500) | ||
| led.on(0, 0, 255) | ||
| await asyncWait(500) | ||
| led.off() | ||
| await asyncWait(1000) | ||
|
|
||
| led.on(255, 255, 255, 1000) | ||
| await asyncWait(1000) | ||
|
|
||
| led.blink(255, 255, 0, 500) | ||
| await asyncWait(5000) | ||
| led.off() | ||
| await asyncWait(1000) | ||
|
|
||
| led.rainbow() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| { | ||
| "include": [ | ||
| "$(MODDABLE)/examples/manifest_base.json", | ||
| "$(MODDABLE)/examples/manifest_typings.json", | ||
| "../../stackchan/utilities/manifest_utility.json", | ||
| "../../stackchan/led/manifest_led.json" | ||
| ], | ||
| "modules": { | ||
| "*": ["./main"] | ||
| }, | ||
| "defines": { | ||
| "main": { | ||
| "async": 1 | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation for setting pixel colors within the
Blinkeffect is inefficient and contains a bug. Theforloop callsthis.strand.set()on each iteration, which inneostrandtriggers an update of the LED strand. This leads to multiple, unnecessary updates within a single animation frame. Furthermore, the last argument toset()isthis.end(an index), where a count is expected.A more efficient and correct approach is to use the
fill()method to update the pixel buffer for the entire segment at once. The animation timeline will then handle callingupdate()once per frame.