Skip to content

feat(mcp266): set/zero encoder counts and read back the position PID record - #842

Open
ewang360 wants to merge 2 commits into
esp-cpp:mainfrom
ewang360:feat/mcp266-encoder-set-pid-readback
Open

ewang360 wants to merge 2 commits into
esp-cpp:mainfrom
ewang360:feat/mcp266-encoder-set-pid-readback

Conversation

@ewang360

@ewang360 ewang360 commented Oct 5, 2026

Copy link
Copy Markdown

Description

espp::Mcp266 wraps the MCP266's manufacturer command mirror for the position PID setter but leaves two neighbours unwrapped: the encoder setter (commands 22/23) and the position PID read-back (63/64). A caller that wants to home a quadrature encoder, or detect that a controller power-cycled back to its factory [0, 0] clamp, currently has to reach past Mcp266 to the CanopenClient with hand-written object indices. This adds:

  • set_encoder(axis, count, ec) — command 22/23. Per-axis; set_encoder(axis, 0, ec) resets one encoder.
  • reset_encoders(ec) — command 20, zeros both, matching espp::Basicmicro::reset_encoders().
  • read_position_pid(...) / set_position_pid(...) — the seven-field record, same float-gain signature as espp::Basicmicro, with the setter's D, P, I field order handled. Gains are scaled x1024 through a new scale_position_gain() in the host-testable core that rounds to nearest and clamps non-negative, mirroring Basicmicro's scale_pid_gain().
  • read_position_limits(axis, min, max, ec) — the MinPos/MaxPos clamp alone (two SDOs), as a cheap "did the controller reset" check.

configure_position_loop() is refactored onto the same private read/write helpers; no behaviour change. AxisObjects gains encoder_set, and the core gains kResetEncodersObject and kPositionGainScale. README, RST docs and the example are updated (the example now logs the PID record after configuring the loop).

Motivation and Context

Needed by a wheelchair-base controller (RAMMP MIB) whose carriage joints use incremental encoders on MCP266s and must be homed against limit switches every boot, and which needs to notice when a controller has power-cycled while the host kept running. Discussed with @finger563, who said a PR adding these would be welcome.

How has this been tested?

  • Host tests pass (components/mcp266/test/mcp266_host_test.cpp), including new checks for the 0x2014/0x2016/0x2017 objects, encoder_set on both axes, the gain scale, and scale_position_gain() rounding/clamping.
  • pre-commit run (clang-format) passes on all changed files.
  • The modified mcp266.hpp compiles cleanly for esp32p4 on ESP-IDF v6.1 in a downstream project via override_path.
  • Not yet hardware-verified. set_encoder and reset_encoders follow the same mirror rule as the objects already verified on an MCP266, but I do not currently have access to the hardware. The read-back path (commands 63/64) was exercised on an MCP266 through raw SDOs in the downstream project before this wrapper existed. I will bench-test set_encoder as soon as I have a controller again and report here; happy to hold the merge until then if preferred.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation Update
  • Hardware (schematic, board, system design) change
  • Software change

Checklist:

  • My change requires a change to the documentation.
  • I have added / updated the documentation related to this change via either README or WIKI

Software

  • I have added tests to cover my changes.
  • I have updated the .github/workflows/build.yml file to add my new test to the automated cloud build github action. (Not needed: the mcp266 examples are already in the matrix.)
  • All new and existing tests passed.
  • My code follows the code style of this project.

🤖 Generated with Claude Code

…record

Adds to espp::Mcp266, through the same manufacturer command mirror
(0x2000 + packet-serial command) the component already uses:

- set_encoder(axis, count): command 22/23, so a quadrature encoder can be
  homed against a limit switch or restored to a remembered count after
  power-up.
- reset_encoders(): command 20, matching espp::Basicmicro::reset_encoders.
- read_position_pid(...) / set_position_pid(...): the seven-field record
  (commands 63/64 and 61/62) with the same float-gain signature as
  espp::Basicmicro, handling the setter's D, P, I field order.
- read_position_limits(axis, min, max): the MinPos/MaxPos clamp alone,
  two SDOs, as a cheap check that the controller still holds its
  configuration (it reverts to the factory [0, 0] clamp on a power cycle).

configure_position_loop() now uses the shared read/write helpers. The
core gains the encoder-set objects, the reset-encoders object and the
x1024 gain scale, with host tests; README, docs and the example updated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@finger563
finger563 requested review from finger563 and a balanced review from Copilot October 5, 2026 21:15
@finger563 finger563 added enhancement New feature or request mcp266 labels Oct 5, 2026

Copilot AI left a comment

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.

Copilot review overview

🟡 Changes recommended

Position gains are narrowed to signed 32-bit values, incorrectly clamping writes and producing negative readbacks for valid unsigned records.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
What changed in this PR

Adds MCP266 encoder controls and position-PID read/write support.

Changes:

  • Adds encoder set/reset APIs.
  • Adds PID readback, limit reading, gain scaling, and setter support.
  • Updates tests, documentation, and example logging.
File Description
components/​mcp266/​include/​mcp266.hpp Adds public encoder and PID APIs.
components/​mcp266/​include/​detail/​mcp266_core.hpp Adds object mappings and gain scaling.
components/​mcp266/​test/​mcp266_host_test.cpp Tests mappings and gain conversion.
components/​mcp266/​example/​main/​mcp266_example.cpp Logs configured PID values.
components/​mcp266/​example/​README.md Documents example readback.
components/​mcp266/​README.md Documents the new APIs.
doc/​en/​motor_control/​mcp266.rst Updates component documentation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread components/mcp266/include/detail/mcp266_core.hpp Outdated
Comment thread components/mcp266/include/mcp266.hpp Outdated
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown

✅Static analysis result - no issues found! ✅

The record's P, I, D, MaxI and Deadzone fields are unsigned on the
device; only MinPos / MaxPos are signed. scale_position_gain() now
clamps to the full [0, UINT32_MAX] range like Basicmicro's
scale_pid_gain(), and read_position_pid() decodes the gain fields from
their bit pattern instead of dividing the signed slot, so a gain with
bit 31 set no longer reads back negative. The i32 SDO helpers are
unchanged: they move a 4-byte pattern either way.

Addresses the Copilot review on esp-cpp#842.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Copilot AI left a comment

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.

Copilot review overview

🔵 Needs a closer look

The new hardware-writing encoder commands remain unverified on an MCP266 and require final human judgment before merging.

Review effort: Balanced
Findings: None

Resolved since last review (2)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request mcp266

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants