Skip to content

feat(examples): add ethercat-real-bus (EK1100 + EL1008)#8

Merged
patdhlk merged 5 commits into
mainfrom
feat/ethercat-real-bus-example
May 21, 2026
Merged

feat(examples): add ethercat-real-bus (EK1100 + EL1008)#8
patdhlk merged 5 commits into
mainfrom
feat/ethercat-real-bus-example

Conversation

@patdhlk
Copy link
Copy Markdown
Owner

@patdhlk patdhlk commented May 21, 2026

Summary

Adds examples/ethercat-real-bus/ — a standalone Cargo crate that drives a real Beckhoff EK1100 + EL1008 from a Linux host (Raspberry Pi the canonical target) via EthercrabBusDriver. Reads the EL1008's 8 digital input bits at 10 ms cadence and prints the byte every time it changes.

Fourth integration example in examples/; CI compiles it but does NOT run it (no NIC, no hardware in CI). Mirrors the established examples/* standalone-crate conventions (empty [workspace], [patch.crates-io] marker block, /target .gitignore, publish = false, no .runnable marker).

What this shows

  • EthercrabBusDriver::new(&PDU_STORAGE, opts) — real-bus driver construction (bus-integration feature on taktora-connector-ethercat).
  • Connecting → Up health handshake via Connector::subscribe_health.
  • ChannelReader<u8, N> over an EthercatRouting PDO slice — same surface every other connector in this workspace exposes.
  • A minimal RawByteCodec defined inline (JsonCodec can't decode raw PDI bytes). Purpose-built for this example; intentionally not promoted to taktora-connector-codec.

Files

New crate under examples/ethercat-real-bus/:

  • Cargo.toml — pins taktora-* = "0.1" (and taktora-connector-core = "0.2"), enables bus-integration on taktora-connector-ethercat.
  • Cargo.lock — committed (application crate).
  • .gitignore/target.
  • README.md — hardware checklist, host setup, setcap recipe, run instructions, troubleshooting.
  • src/main.rs — ~250 LoC: CLI (--nic, --ticks), RawByteCodec impl + 4 unit tests, bring-up wiring, health-polling item (250 ms), scan-and-print item (10 ms).

Workspace plumbing:

  • examples/README.md — fourth row in the "What's here" table.
  • README.md (root) — fourth row in the Integration examples table.
  • .github/workflows/ci.ymlexamples/ethercat-real-bus added to the examples job's rust-cache workspaces: list.

Test plan

  • cargo build --release in the example crate.
  • cargo test --release — 4 RawByteCodec round-trip + error tests pass.
  • cargo clippy --release --all-targets -- -D warnings — clean.
  • ./scripts/check-examples.sh — green; new example reports skipping run (no .runnable marker).
  • cargo test --workspace --all-features -- --test-threads=1 — 394 passing, 0 failing.
  • Manual smoke test on a Raspberry Pi + EK1100 + EL1008 (out-of-band; the example was designed for exactly this hardware).

Spec / design

  • Design doc: docs/superpowers/specs/2026-05-21-ethercat-real-bus-design.md (local; docs/superpowers/ is gitignored).
  • Plan: docs/superpowers/plans/2026-05-21-ethercat-real-bus.md.
  • No new sphinx-needs requirements — this example is a downstream demo of the existing FEAT_0041 work.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the ethercat-real-bus example, demonstrating real-world EtherCAT integration with Beckhoff hardware using the taktora framework. It includes a custom RawByteCodec for 8-bit digital inputs and comprehensive setup documentation for Linux hosts. Review feedback suggests optimizing state management in main.rs by removing an unnecessary Arc<Mutex> wrapper for a variable used only within a single closure, which avoids redundant synchronization and allocation overhead.

Comment on lines +180 to +181
let last_value: Arc<Mutex<Option<u8>>> = Arc::new(Mutex::new(None));
let last_value_for_item = Arc::clone(&last_value);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The last_value state does not need to be wrapped in an Arc<Mutex> because it is only used within a single ExecutableItem closure. Since the closure is move and the executor treats items as FnMut, you can simply capture a local Option<u8> variable. This avoids unnecessary synchronization and allocation overhead.

Suggested change
let last_value: Arc<Mutex<Option<u8>>> = Arc::new(Mutex::new(None));
let last_value_for_item = Arc::clone(&last_value);
let mut last_value: Option<u8> = None;

Comment on lines +194 to +199
let mut last = last_value_for_item.lock().expect("poisoned");
if last.as_ref().copied() != Some(v) {
let elapsed_ms = started_at.elapsed().as_millis();
println!("t=+{elapsed_ms:>6}ms bits=0b{v:08b} decimal={v}");
*last = Some(v);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Simplified usage of last_value after removing the redundant Arc<Mutex> synchronization.

                if last_value != Some(v) {
                    let elapsed_ms = started_at.elapsed().as_millis();
                    println!("t=+{elapsed_ms:>6}ms  bits=0b{v:08b}  decimal={v}");
                    last_value = Some(v);
                }

@patdhlk patdhlk merged commit c9fa803 into main May 21, 2026
27 checks passed
@patdhlk patdhlk deleted the feat/ethercat-real-bus-example branch May 21, 2026 18:13
@github-actions github-actions Bot mentioned this pull request May 21, 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.

1 participant