Skip to content

Feature/light#355

Merged
meganetaaan merged 12 commits intostack-chan:dev/v1.0from
stc1988:feature/light
Feb 21, 2026
Merged

Feature/light#355
meganetaaan merged 12 commits intostack-chan:dev/v1.0from
stc1988:feature/light

Conversation

@stc1988
Copy link
Copy Markdown
Contributor

@stc1988 stc1988 commented Dec 30, 2025

概要

M5GO bottom付属LEDやNekomimi LEDといったNeoPixel互換LEDに対応します

ModdableSDK#1549 での型定義が必要となるので、一旦ドラフトになります。

使用方法

manifestファイルののconfigセクションに以下のようにLEDの定義を追加することで、robot.ledLedクラスのインスタンスにアクセスできます。また、robot自身にlightOnなどの関数を追加しています。

   "led": {
      "a": {
        "pin": 15,
        "length": 10
      }
    }

使用例は、mods/lighttests/ledが参考になります。

Summary by CodeRabbit

  • New Features
    • LED strip control: solid colors, blink, and rainbow animations; Robot API extended with LED control methods; interactive button-based light module.
  • Tests
    • Added a firmware test that demonstrates LED sequences and effects.
  • Chores
    • Added manifests and TypeScript path mappings to enable the new LED modules.
  • Dependencies
    • Bumped @moddable/typings to ^7.0.0.

✏️ Tip: You can customize this high-level summary in your review settings.

stc1988 and others added 10 commits December 25, 2025 23:22
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 30, 2025

📝 Walkthrough

Walkthrough

Adds LED strip support: new Led class and NeoPixel stub, integrates LEDs into Robot with control methods, a light mod wiring buttons to LED actions, manifests/tsconfig updates, and a test demonstrating LED effects.

Changes

Cohort / File(s) Summary
LED Core
firmware/stackchan/led/led.ts, firmware/stackchan/led/neopixel_stub.ts, firmware/stackchan/led/manifest_led.json
New exported Led class with on/off/blink/rainbow APIs, a no-op NeoPixel stub for non-hardware hosts, and a manifest configuring platform-specific mappings and preload.
Robot Integration
firmware/stackchan/robot.ts, firmware/stackchan/main.ts, firmware/stackchan/manifest.json
Robot constructor now accepts a led map; adds internal led storage, a led getter, and high-level methods: lightOn, lightOff, lightBlink, lightRainbow. main.ts instantiates Led objects from config; root manifest includes LED manifest.
Light Mod
firmware/mods/light/mod.js, firmware/mods/light/manifest.json
New onRobotCreated(robot) hook validating target LED and wiring button handlers to cycle color, turn off, or start rainbow on named LED.
Type/config & deps
firmware/tsconfig.json, firmware/package.json, firmware/stackchan/utilities/manifest_utility.json
Adds TS path for stackchan/led/*, bumps @moddable/typings devDependency ^6.0.0 → ^7.0.0, and adds an include entry to utilities manifest.
Tests
firmware/tests/led/main.ts, firmware/tests/led/manifest.json
New test choreography exercising Led APIs (color cycles, off, brightness, blink, rainbow) and a test manifest to build it.

Sequence Diagram

sequenceDiagram
    participant Button as Button Input
    participant Mod as Light Mod
    participant Robot as Robot
    participant Led as Led Instance
    participant NeoPixel as NeoPixel Driver

    Button->>Mod: press button (a/b/c)
    Mod->>Robot: call lightOn/Off/Blink/Rainbow(ledName, ...)
    Robot->>Led: delegate method (on/off/blink/rainbow,...)
    Led->>Led: stop existing effects (if any)
    Led->>NeoPixel: fill/set pixels & update()
    loop effect ticks
        Led->>NeoPixel: update frame
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested Reviewers

  • meganetaaan

Poem

🐰 In burrows bright, the pixels gleam,

Buttons tap, and lights I dream.
Red, green, blue — a dancing stream,
Blink and rainbow, LED team! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feature/light' is vague and generic, using a branch naming convention rather than clearly describing the actual changes. Consider a more descriptive title such as 'Add NeoPixel LED support with light module' or 'Implement LED control system for robot' that clearly conveys the primary feature being added.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@stc1988
Copy link
Copy Markdown
Contributor Author

stc1988 commented Jan 17, 2026

Moddable 7.0.0 で型定義が入り、ビルドが通るようになったので、ドラフトを外します

@stc1988 stc1988 marked this pull request as ready for review January 17, 2026 01:04
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@firmware/stackchan/led/led.ts`:
- Around line 110-121: The blink method is passing an incorrect count to the
Blink effect (it sets count: _index + _count which expands the range); update
the Blink instantiation in blink(r,g,b,duration,index?,count?) to pass count:
_count (keeping index: _index) so Blink receives the intended size, and ensure
the default calculation const _count = count ?? this.length - _index remains
unchanged.
- Around line 88-99: The auto-off Timer created in on() can leave a stale handle
that later calls off() unexpectedly; modify on() to save the Timer.set() return
value into a field (e.g., this._autoOffTimer) and ensure _stopEffect() clears
that timer (Timer.clear(this._autoOffTimer) or equivalent) before scheduling a
new Timer; when scheduling in on() assign the handle to this._autoOffTimer so
subsequent calls cancel the previous callback before creating a new one and
avoid unexpected shutdowns.

In `@firmware/stackchan/robot.ts`:
- Line 8: The code uses InstanceType<typeof Led> with a type-only import, which
breaks TS because typeof needs a runtime value; replace all occurrences of
InstanceType<typeof Led> in robot.ts with a plain Led type or Record<string,
Led> (e.g., use Record<string, Led> for maps of LEDs and Led for single
instances) and keep the import as a type-only import (import type Led) so the
type resolves correctly; ensure every place referencing InstanceType<typeof Led>
(the import area and the other two usages) is updated consistently.
🧹 Nitpick comments (3)
firmware/stackchan/led/manifest_led.json (1)

11-25: Consider consolidating duplicate platform configurations.

The mac, lin, and win platform sections are identical. If the Moddable manifest format supports platform grouping or wildcards, this could reduce duplication.

That said, the current explicit approach is clear and works correctly.

firmware/tests/led/main.ts (1)

18-19: Minor timing observation: LED auto-off before wait completes.

The led.on(255, 255, 255, 1000) will automatically turn off after 1 second, but asyncWait(2000) waits 2 seconds. This means the LED will be off for the last second of the wait period. If this is intentional for visual demonstration purposes, consider adding a brief comment for clarity.

firmware/mods/light/mod.js (1)

12-30: Consider adding null checks for buttons.

Looking at firmware/stackchan/default-mods/on-robot-created.ts, button handlers are guarded with null checks like if (robot.button.a != null). This prevents errors on devices without all three buttons.

♻️ Suggested defensive pattern
-  robot.button.a.onChanged = function () {
+  if (robot.button.a != null) {
+    robot.button.a.onChanged = function () {
       if (this.read()) {
         const firstColor = colors.shift()
         colors.push(firstColor)
         robot.lightOn('a', firstColor.r, firstColor.g, firstColor.b)
       }
     }
+  }

Apply similarly to buttons b and c.

Comment on lines +88 to +99
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)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, find and read the LED file
find . -path "*/firmware/stackchan/led/led.ts" -type f

Repository: stack-chan/stack-chan

Length of output: 96


🏁 Script executed:

# Also try to locate it with fd
fd -t f "led.ts" firmware/

Repository: stack-chan/stack-chan

Length of output: 94


🏁 Script executed:

# Let's check the repository structure
git ls-files | grep -E "firmware.*led" | head -20

Repository: stack-chan/stack-chan

Length of output: 235


🏁 Script executed:

# Read the LED file to see the full context
cat -n ./firmware/stackchan/led/led.ts

Repository: stack-chan/stack-chan

Length of output: 4661


🏁 Script executed:

# Check for Timer.clear usage in the codebase
rg "Timer\.clear" --type ts --type js

Repository: stack-chan/stack-chan

Length of output: 878


🏁 Script executed:

# Check for Timer imports and usage patterns
rg "Timer\." --type ts --type js -A 2 | head -50

Repository: stack-chan/stack-chan

Length of output: 2446


🏁 Script executed:

# Look for type definitions or d.ts files related to Timer
find . -name "*.d.ts" -o -name "*timer*" -type f | grep -v node_modules | head -20

Repository: stack-chan/stack-chan

Length of output: 387


🏁 Script executed:

# Check one of the examples of Timer.clear usage to understand the pattern
cat -n firmware/stackchan/drivers/dynamixel-driver.ts | grep -A 3 -B 3 "Timer.clear"

Repository: stack-chan/stack-chan

Length of output: 269


🏁 Script executed:

# Check the preference-server example
cat -n firmware/stackchan/services/preference-server.ts | grep -A 3 -B 3 "Timer.clear"

Repository: stack-chan/stack-chan

Length of output: 273


🏁 Script executed:

# Check if Timer type definitions exist
find . -path "*/typings*" -name "*.d.ts" -exec grep -l "Timer" {} \;

Repository: stack-chan/stack-chan

Length of output: 47


🏁 Script executed:

# Check how _stopEffect is called across the class
rg "_stopEffect" firmware/stackchan/led/led.ts -B 2 -A 2

Repository: stack-chan/stack-chan

Length of output: 813


🏁 Script executed:

# Verify the pattern - check if there are other similar timer clearing patterns
rg "#.*Timer.*=" firmware/stackchan/drivers/dynamixel-driver.ts -B 1 -A 1

Repository: stack-chan/stack-chan

Length of output: 47


🏁 Script executed:

# Look at how other files store timer handles
rg "private.*Timer" firmware/stackchan/drivers/ -A 1 | head -30

Repository: stack-chan/stack-chan

Length of output: 47


🏁 Script executed:

# Check a complete example of timer handle management
cat -n firmware/stackchan/drivers/sg90-driver.ts | grep -A 5 -B 5 "_driveHandler"

Repository: stack-chan/stack-chan

Length of output: 1412


Cancel stale auto-off timers to prevent unexpected shutdowns.

When on() is called with a duration before a previous timer fires, the old callback still executes and turns off the LEDs unexpectedly. Store the timer handle and clear it in _stopEffect() before scheduling a new one.

🛠️ Proposed fix
 export default class Led extends NeoStrand {
   private _effect?: NeoStrandEffect
+  private _offTimer?: ReturnType<typeof Timer.set>
@@
   private _stopEffect() {
     if (this._effect) {
       this.stop()
       this._effect = undefined
     }
+    if (this._offTimer) {
+      Timer.clear(this._offTimer)
+      this._offTimer = undefined
+    }
   }
@@
-    if (duration) {
-      Timer.set(() => {
-        this.off(_index, _count)
-      }, duration)
-    }
+    if (duration) {
+      this._offTimer = Timer.set(() => {
+        this.off(_index, _count)
+      }, duration)
+    }
🤖 Prompt for AI Agents
In `@firmware/stackchan/led/led.ts` around lines 88 - 99, The auto-off Timer
created in on() can leave a stale handle that later calls off() unexpectedly;
modify on() to save the Timer.set() return value into a field (e.g.,
this._autoOffTimer) and ensure _stopEffect() clears that timer
(Timer.clear(this._autoOffTimer) or equivalent) before scheduling a new Timer;
when scheduling in on() assign the handle to this._autoOffTimer so subsequent
calls cancel the previous callback before creating a new one and avoid
unexpected shutdowns.

Comment thread firmware/stackchan/led/led.ts
Comment thread firmware/stackchan/robot.ts
Copy link
Copy Markdown
Collaborator

@meganetaaan meganetaaan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nekomimi LEDを使って動作確認しました。LGTMです 🙆

@meganetaaan meganetaaan merged commit aa055c3 into stack-chan:dev/v1.0 Feb 21, 2026
3 checks passed
meganetaaan added a commit to sskw-ugo/stack-chan that referenced this pull request Mar 11, 2026
meganetaaan added a commit that referenced this pull request Mar 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants