RepoPilotOpen in app →

scottbez1/smartknob

Haptic input knob with software-defined endstops and virtual detents

Mixed

Stale — last commit 2y ago

weakest axis
Use as dependencyConcerns

non-standard license (Other); last commit was 2y ago…

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • 8 active contributors
  • Other licensed
  • CI configured
Show all 7 evidence items →
  • 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 ConcernsMixed 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.

Variant:
RepoPilot: Forkable
[![RepoPilot: Forkable](https://repopilot.app/api/badge/scottbez1/smartknob?axis=fork)](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:

  1. 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.
  2. 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.
  3. 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 |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>scottbez1/smartknob</code></summary>
#!/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).

</details>

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 ecosystem
  • README.md — Project overview explaining the SmartKnob hardware concept, motor availability, and design philosophy that all contributors must understand
  • electronics/LevelShifterBreakout/LevelShifterBreakout.sch — Critical schematic for voltage level conversion between microcontroller and motor driver, essential for electronics contributors
  • electronics/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

  1. Create new workspace directory under packages/ following naming convention (e.g., packages/smartknobjs-myplatform) (package.json)
  2. Define package.json with smartknobjs-core as dependency and appropriate platform entry points (packages/smartknobjs-core)
  3. Implement communication transport using SmartKnobProto message types from smartknobjs-proto (packages/smartknobjs-proto)
  4. Export device interface matching core API (e.g., writeSetting, readSensor) (packages/smartknobjs-core)
  5. Add workspace reference to root package.json workspaces array (package.json)

Add a new sensor or peripheral to the hardware

  1. Create custom component symbol in electronics/lib/ directory (e.g., NewSensor.lib, NewSensor.dcm) (electronics/lib/VEML7700.lib)
  2. Design footprint in KiCAD with 3D model reference (NewSensor.pretty directory) (electronics/lib/VEML7700.pretty/VEML7700-TOP.kicad_mod)
  3. Integrate into main PCB schematic with appropriate decoupling and protection circuits (electronics/LevelShifterBreakout/LevelShifterBreakout.sch)
  4. Add sensor interface/driver package to JavaScript packages if firmware communication is needed (packages/smartknobjs-core)

Add a new example application

  1. Create new directory under packages/example-* with package.json depending on smartknobjs-core or platform-specific package (packages/example-cli)
  2. Implement main entry point demonstrating device communication and setting/reading state (packages/example-webserial-basic)
  3. Add workspace entry to root package.json workspaces array (package.json)
  4. 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
  • 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 used
  • espressif/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 patterns
  • kevinmehall/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

📝Recent commits

Click to expand
  • 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.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Mixed signals · scottbez1/smartknob — RepoPilot