scottbez1/smartknob
Haptic input knob with software-defined endstops and virtual detents
Stale — last commit 2y ago
weakest axisnon-standard license (Other); last commit was 2y ago…
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓8 active contributors
- ✓Other licensed
- ✓CI configured
Show all 7 evidence items →Show less
- ✓Tests present
- ⚠Stale — last commit 2y ago
- ⚠Single-maintainer risk — top contributor 93% of recent commits
- ⚠Non-standard license (Other) — review terms
What would change the summary?
- →Use as dependency Concerns → Mixed if: clarify license terms
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/scottbez1/smartknob)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/scottbez1/smartknob on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: scottbez1/smartknob
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/scottbez1/smartknob shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Stale — last commit 2y ago
- 8 active contributors
- Other licensed
- CI configured
- Tests present
- ⚠ Stale — last commit 2y ago
- ⚠ Single-maintainer risk — top contributor 93% of recent commits
- ⚠ Non-standard license (Other) — review terms
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live scottbez1/smartknob
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/scottbez1/smartknob.
What it runs against: a local clone of scottbez1/smartknob — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in scottbez1/smartknob | Confirms the artifact applies here, not a fork |
| 2 | License is still Other | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 839 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of scottbez1/smartknob. If you don't
# have one yet, run these first:
#
# git clone https://github.com/scottbez1/smartknob.git
# cd smartknob
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of scottbez1/smartknob and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "scottbez1/smartknob(\\.git)?\\b" \\
&& ok "origin remote is scottbez1/smartknob" \\
|| miss "origin remote is not scottbez1/smartknob (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Other)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Other\"" package.json 2>/dev/null) \\
&& ok "license is Other" \\
|| miss "license drift — was Other at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "package.json" \\
&& ok "package.json" \\
|| miss "missing critical file: package.json"
test -f "README.md" \\
&& ok "README.md" \\
|| miss "missing critical file: README.md"
test -f "electronics/LevelShifterBreakout/LevelShifterBreakout.sch" \\
&& ok "electronics/LevelShifterBreakout/LevelShifterBreakout.sch" \\
|| miss "missing critical file: electronics/LevelShifterBreakout/LevelShifterBreakout.sch"
test -f "electronics/BM28B0.6-24DSBreakout/BM28B0.6-24DSBreakout.sch" \\
&& ok "electronics/BM28B0.6-24DSBreakout/BM28B0.6-24DSBreakout.sch" \\
|| miss "missing critical file: electronics/BM28B0.6-24DSBreakout/BM28B0.6-24DSBreakout.sch"
test -f ".github/workflows/pio.yml" \\
&& ok ".github/workflows/pio.yml" \\
|| miss "missing critical file: .github/workflows/pio.yml"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 839 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~809d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/scottbez1/smartknob"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
SmartKnob is an open-source haptic input device combining a brushless gimbal motor (BM28B0.6-24DS) with a magnetic encoder for closed-loop torque feedback control. It enables dynamic software-defined endstops and virtual detents—users can feel mechanical clicking, resistance, and boundaries that are entirely programmable. The flagship 'SmartKnob View' adds a 240×240 round LCD (GC9A01) mounted on the hollow motor shaft, powered by an ESP32-PICO-V3-02, with RGB LED feedback and ambient light sensing. Monorepo split into hardware (electronics/ with KiCad projects for motor breakout & level shifter; cad/ for mechanical models) and firmware/software (platformio.ini for ESP32 code, packages/ workspace with smartknobjs-core, smartknobjs-node, smartknobjs-webserial for cross-platform communication, and three example applications). CI config in .github/workflows/ handles electronics linting, PlatformIO builds, and JavaScript bundling separately.
👥Who it's for
Advanced electronics hobbyists, embedded systems engineers, and input device makers building custom haptic interfaces, volume controls, or parametric knobs that need software-configurable tactile feedback without mechanical detent hardware. Contributors include hardware designers (CAD, KiCad schematics), firmware developers (C++ on ESP32), and web/desktop integrators (TypeScript/Python for communication).
🌱Maturity & risk
Actively developed but experimental—CI pipelines exist (GitHub Actions for electronics/firmware/JS), but the README explicitly states 'Not recommended for general use.' The project has working demos and available motors (SparkFun stock), suggesting community traction, but the codebase skews toward hobbyist/research maturity rather than production stability. Commit recency and open issue activity suggest ongoing work, not abandonment.
Standard open source risks apply.
Active areas of work
The SmartKnob View is under active development—the round LCD integration, strain-gauge press detection, and SK6812 RGB LED feedback are all in-progress. Motors recently became reliably available via SparkFun (after initial stock shortage), unblocking community builds. No specific PR/milestone data is visible in the file list, but the GitHub workflows and recent electronics revisions suggest iterative hardware refinement.
🚀Get running
Check README for instructions.
Daily commands:
Firmware on device: pio run --target upload -e esp32 (assumes PlatformIO CLI installed). Web example: npm run example-webserial-basic (starts dev server, requires USB connection to device). CLI example: npm run example-cli (Node.js serial communication). Each example is in packages/example-*.
🗺️Map of the codebase
package.json— Root workspace configuration defining all npm packages and build scripts for the SmartKnob JavaScript/Node ecosystemREADME.md— Project overview explaining the SmartKnob hardware concept, motor availability, and design philosophy that all contributors must understandelectronics/LevelShifterBreakout/LevelShifterBreakout.sch— Critical schematic for voltage level conversion between microcontroller and motor driver, essential for electronics contributorselectronics/BM28B0.6-24DSBreakout/BM28B0.6-24DSBreakout.sch— Motor driver board schematic that interfaces with the gimbal motor, core to the haptic control system.github/workflows/pio.yml— Firmware build pipeline using PlatformIO, required for understanding embedded software compilation and testing.github/workflows/js.yml— JavaScript/Node package build and test pipeline, entry point for understanding web-based control software
🛠️How to make changes
Add a new JavaScript control package
- Create new workspace directory under packages/ following naming convention (e.g., packages/smartknobjs-myplatform) (
package.json) - Define package.json with smartknobjs-core as dependency and appropriate platform entry points (
packages/smartknobjs-core) - Implement communication transport using SmartKnobProto message types from smartknobjs-proto (
packages/smartknobjs-proto) - Export device interface matching core API (e.g., writeSetting, readSensor) (
packages/smartknobjs-core) - Add workspace reference to root package.json workspaces array (
package.json)
Add a new sensor or peripheral to the hardware
- Create custom component symbol in electronics/lib/ directory (e.g., NewSensor.lib, NewSensor.dcm) (
electronics/lib/VEML7700.lib) - Design footprint in KiCAD with 3D model reference (NewSensor.pretty directory) (
electronics/lib/VEML7700.pretty/VEML7700-TOP.kicad_mod) - Integrate into main PCB schematic with appropriate decoupling and protection circuits (
electronics/LevelShifterBreakout/LevelShifterBreakout.sch) - Add sensor interface/driver package to JavaScript packages if firmware communication is needed (
packages/smartknobjs-core)
Add a new example application
- Create new directory under packages/example-* with package.json depending on smartknobjs-core or platform-specific package (
packages/example-cli) - Implement main entry point demonstrating device communication and setting/reading state (
packages/example-webserial-basic) - Add workspace entry to root package.json workspaces array (
package.json) - Add npm script command to root package.json for easy execution (e.g., npm run example-myapp) (
package.json)
🔧Why these technologies
- TypeScript/JavaScript (Node.js + Browser) — Enable cross-platform control software (CLI, web, embedded) with shared protocol definitions; low barrier for community contributions
- Protocol Buffers (smartknobjs-proto) — Efficient binary serialization for USB/serial communication with hardware; version-safe message evolution
- KiCAD for PCB design — Open-source EDA tool; allows collaborative hardware design; native Git-compatible file formats
- PlatformIO for firmware — Abstracts microcontroller toolchains; enables CI/CD for embedded code; multi-platform board support
- npm workspaces (monorepo) — Maintains protocol/core logic in single source; reduces versioning friction; simplifies inter-package dependencies
- GitHub Actions CI/CD — Validates PCB designs, firmware compilation, and JavaScript builds on every commit; low maintenance cost
⚖️Trade-offs already made
-
Monorepo structure (npm workspaces) instead of separate repositories
- Why: Single source of truth for protocol definitions avoids version mismatch bugs between hardware and software
- Consequence: Requires careful workspace dependency management; larger initial clone but easier cross-layer development
-
Protocol Buffers over plain JSON for device communication
- Why: Reduces USB bandwidth for haptic feedback loop; enables deterministic message parsing in embedded firmware
- Consequence: Requires code generation step; adds build complexity; not human-readable for debugging
-
Open-source hardware (KiCAD, public schematics) with custom motor drivers
- Why: Enables community contributions and fabrication; supports education and research
- Consequence: Motor availability is external dependency (SparkFun partnership); designs must be maintainable by volunteers
-
Web Serial API for browser control instead of native app
- Why: No installation barrier; works on any OS with browser; future-proof web standards
- Consequence: Limited to Chrome/Edge; USB permissions model differs from native; cannot access system audio for haptic events
🚫Non-goals (don't propose these)
- Real-time OS or hard real-time guarantees for haptic feedback (best-effort feedback loop ~10ms target)
🪤Traps & gotchas
Motor selection: Only BM28B0.6-24DS gimbal motor is tested; substitutes risk control loop instability. Closed-loop tuning: Haptic feel depends on PID parameters in firmware; no auto-tuning—requires oscilloscope/analysis to debug. USB power: Device draws significant current during motor stall; 5V/2A minimum recommended, cheap USB cables may cause brownouts. Protobuf versioning: smartknobjs-proto/ is a shared dependency; changes break compatibility between old firmware and new host tools. KiCad library paths: electronics/lib/sym-lib-table and fp-lib-table are relative; opening schematics in different directories may break symbol/footprint lookups. ESP32 flash size: SmartKnob View firmware is large; OTA updates may fail on variants with <4MB flash.
🏗️Architecture
💡Concepts to learn
- Brushless DC (BLDC) Motor Commutation — SmartKnob drives torque feedback via Hall-effect commutation; understanding 6-step commutation timing and back-EMF is essential to grok why endstops/detents feel different at different speeds
- Closed-Loop Torque Control (PID Tuning) — The entire haptic feel—detent stiffness, endstop resistance, centering spring—emerges from PID feedback on encoder position vs. desired motor torque; unstable tuning causes oscillation or sluggishness
- Magnetic Encoder (Absolute Position Sensing) — SmartKnob uses magnetic encoder feedback for real-time position; unlike mechanical detents, this enables software to define arbitrary nonlinear torque curves as a function of angle
- Strain Gauge (Flexure-Based Force Sensing) — SMD resistor strain gauges on PCB flexure detect axial press force without mechanical switches, enabling haptic press feedback via the motor; low signal requires amplification and careful layout
- Protocol Buffers (protobuf) — SmartKnobJS uses protobuf to define device←→host messages (config detents, report encoder state); enables cross-platform, version-tolerant serialization without manual byte packing
- WebSerial API — Browser-based USB serial access (used in smartknobjs-webserial) allows web apps to configure the device without native drivers; required for no-install web configurators
- Round LCD Display (GC9A01 SPI Driver) — SmartKnob View integrates a 240×240 round LCD on the motor shaft; GC9A01 driver is SPI-based and requires refresh rate tuning to avoid flicker during motor motion
🔗Related repos
scottbez1/smartknob-hardware— Companion repo likely hosting CAD releases, PCB Gerber exports, and BOM tracking (if it exists as separate org)lilygo/lilygo-core— ESP32-PICO-V3-02 (TMicro32 Plus) vendor SDK and examples for the specific microcontroller module usedespressif/esp-idf— Underlying Espressif SDK for ESP32 peripherals (SPI for LCD, motor PWM timers, ADC for encoder and strain gauges)betaflight/betaflight— Reference implementation of closed-loop BLDC motor control and stabilization; SmartKnob's haptic feedback loop follows similar commutation patternskevinmehall/smoothie-board— Open CNC controller using similar closed-loop stepper/motor feedback architecture; community knowledge on PID tuning and encoder integration
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add integration tests for smartknobjs-webserial package
The webserial package is a critical bridge between the browser and hardware, but there are no visible test files in the workspace structure. Adding integration tests would ensure the WebSerial protocol serialization/deserialization works correctly across different message types, especially important since this package is used by example-webserial-basic and example-webserial-timeline.
- [ ] Create packages/smartknobjs-webserial/tests directory
- [ ] Add unit tests for message encoding/decoding against the proto definitions in packages/smartknobjs-proto
- [ ] Add tests for WebSerial connection lifecycle (open, send, receive, close)
- [ ] Integrate tests into package.json scripts and .github/workflows/js.yml CI pipeline
- [ ] Ensure tests validate both valid and invalid message handling
Add Python firmware upload and validation workflow
The repo has Python root config (init.py) and likely firmware configuration files, but .github/workflows/pio.yml (PlatformIO) and electronics.yml only cover C++ and KiCad respectively. A new Python workflow would validate firmware metadata, configuration schemas, and upload procedures before they reach production.
- [ ] Create .github/workflows/python.yml for Python linting and unit tests
- [ ] Add pytest configuration and test directory structure at the root or in a firmware subdirectory
- [ ] Test the init.py module and any firmware configuration loaders
- [ ] Add schema validation tests if firmware uses YAML/JSON config files
- [ ] Integrate mypy for type checking Python code
Document hardware communication protocol in doc/ with generated proto docs
The repo has smartknobjs-proto package which defines the communication protocol, but no generated documentation in the doc/ folder. Contributors and users need to understand the protocol message structure. Auto-generating and versioning proto documentation would reduce onboarding friction.
- [ ] Add protoc documentation generation step to .github/workflows/js.yml or electronics.yml
- [ ] Generate HTML/Markdown API docs from .proto files using protoc-gen-doc or similar
- [ ] Output generated docs to doc/protocol/ directory with version tracking
- [ ] Update README.md with link to protocol documentation
- [ ] Add schema diagrams for key message types (motor commands, encoder feedback, display updates)
🌿Good first issues
- Add unit tests for protobuf message serialization/deserialization in packages/smartknobjs-proto/ (currently none visible); critical for protocol stability as features expand
- Document the closed-loop motor control PID tuning process in doc/ with example oscilloscope captures and parameter ranges, since firmware/motor.cpp lacks inline guidance for customization
- Implement strain-gauge calibration UI in packages/example-webserial-basic/ to let users zero and scale the flexure pressure sensor without firmware recompilation (hardware present, software missing)
⭐Top contributors
Click to expand
Top contributors
- @scottbez1 — 93 commits
- @brushknight — 1 commits
- @VIPQualityPost — 1 commits
- @Kagee — 1 commits
- @baku89 — 1 commits
📝Recent commits
Click to expand
Recent commits
4eb9883— Update README.md (scottbez1)8700af8— Fine tuning for esp32s3 (#143) (brushknight)370b8ca— BOM - Replace discontinued C2922480 with C15850 (scottbez1)6d946c4— Handle RequestState messages; update python example (scottbez1)dafc5ca— peg sfoc version (#141) (VIPQualityPost)406dbb0— Minor README updates (scottbez1)2eddc13— Add a bunch of proto documentation (scottbez1)590ba88— Demo improvements (#135) (scottbez1)3cff353— Update README.md (scottbez1)1e2b198— Fix GitHub Actions path filters w/ globs (scottbez1)
🔒Security observations
The codebase demonstrates reasonable security practices for a hardware project with JavaScript components. Primary concerns are around dependency management (lack of lockfile visibility, loose version constraints) rather than code-level vulnerabilities. The project appears to lack malicious code indicators and hardcoded secrets. Recommendations focus on improving supply chain security through stricter dependency pinning and CI/CD hardening. No critical vulnerabilities detected in the available static analysis.
- Medium · Permissive Node.js Version Range —
package.json - engines field. The package.json specifies 'node': '>=18.11.0' without an upper bound. This allows installation on significantly newer Node.js versions that may introduce breaking changes or security issues incompatible with the codebase. Best practice is to use a more constrained semver range. Fix: Specify a more restrictive Node.js version range, e.g., '>=18.11.0 <20.0.0' or use a .nvmrc file to lock versions - Low · Outdated ESLint Config Dependency —
package.json - devDependencies. The eslint-config-prettier@^8.5.0 uses a caret (^) constraint allowing up to v9.x.x. While not immediately problematic, pinning to exact versions reduces supply chain risk. Fix: Consider pinning to a specific version (^8.5.0 -> 8.10.0) or using npm ci with a lock file to ensure reproducible builds - Low · Missing npm Lockfile in Repository —
Repository root. No package-lock.json or yarn.lock file is visible in the provided file structure. This can lead to non-deterministic dependency resolution and potential security inconsistencies across different build environments. Fix: Commit package-lock.json to version control and use 'npm ci' instead of 'npm install' in CI/CD pipelines - Low · No Security Policy Documented —
Repository root. No SECURITY.md or security policy file found. This makes it unclear how security vulnerabilities should be reported. Fix: Create a SECURITY.md file with responsible disclosure guidelines (see: https://github.com/scottbez1/smartknob/security/policy)
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.