Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,23 @@ An EtherCAT master written in Rust.

## [Unreleased] - ReleaseDate

### Changed

- **(breaking)** [#1] `SlaveGroup::slaves` now returns an iterator over each slave with IO in the
group, instead of a plain slave.

### Added

- [#1] Added `SlaveGroup::len` and `SlaveGroup::is_empty` methods.

## [0.1.0] - 2023-01-02

### Added

- Initial release

<!-- next-url -->
[unreleased]: https://github.com/ethercrab-rs/ethercrab/compare/v0.1.0...HEAD

[#1]: https://github.com/ethercrab-rs/ethercrab/pull/1
[unreleased]: https://github.com/ethercrab-rs/ethercrab/compare/v0.1.0...HEAD
[0.1.0]: https://github.com/ethercrab-rs/ethercrab/compare/fb37346...v0.1.0
143 changes: 143 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,149 @@ Unfortunately, nightly Rust is currently required.

The MSRV for EtherCrab can be found in `rust-toolchain.toml`.

## Example

This example increments the output bytes of all detected slaves every tick. It is tested on an
EK1100 with output modules but may work on other basic slave devices.

Run with e.g.

Linux

```bash
RUST_LOG=debug cargo run --example ek1100 --release -- eth0
```

Windows

```ps
$env:RUST_LOG="debug" ; cargo run --example ek1100 --release -- '\Device\NPF_{FF0ACEE6-E8CD-48D5-A399-619CD2340465}'
```

```rust
use async_ctrlc::CtrlC;
use async_io::Timer;
use ethercrab::{
error::Error, std::tx_rx_task, Client, PduLoop, PduStorage, SlaveGroup, SubIndex, Timeouts,
};
use futures_lite::{FutureExt, StreamExt};
use smol::LocalExecutor;
use std::{sync::Arc, time::Duration};

/// Maximum number of slaves that can be stored.
const MAX_SLAVES: usize = 16;
/// Maximum PDU data payload size - set this to the max PDI size or higher.
const MAX_PDU_DATA: usize = 1100;
/// Maximum number of EtherCAT frames that can be in flight at any one time.
const MAX_FRAMES: usize = 16;
/// Maximum total PDI length.
const PDI_LEN: usize = 64;

static PDU_STORAGE: PduStorage<MAX_FRAMES, MAX_PDU_DATA> = PduStorage::new();
static PDU_LOOP: PduLoop = PduLoop::new(PDU_STORAGE.as_ref());

async fn main_inner(ex: &LocalExecutor<'static>) -> Result<(), Error> {
let interface = std::env::args()
.nth(1)
.expect("Provide interface as first argument");

log::info!("Starting EK1100 demo...");
log::info!("Ensure an EK1100 is the first slave, with any number of modules connected after");

let client = Arc::new(Client::<smol::Timer>::new(
&PDU_LOOP,
Timeouts {
wait_loop_delay: Duration::from_millis(2),
mailbox_response: Duration::from_millis(1000),
..Default::default()
},
));

ex.spawn(tx_rx_task(&interface, &client).unwrap()).detach();

let group = SlaveGroup::<MAX_SLAVES, PDI_LEN, _>::new(|slave| {
Box::pin(async {
// EL3004 needs some specific config during init to function properly
if slave.name() == "EL3004" {
log::info!("Found EL3004. Configuring...");

slave.write_sdo(0x1c12, SubIndex::Index(0), 0u8).await?;
slave.write_sdo(0x1c13, SubIndex::Index(0), 0u8).await?;

slave
.write_sdo(0x1c13, SubIndex::Index(1), 0x1a00u16)
.await?;
slave
.write_sdo(0x1c13, SubIndex::Index(2), 0x1a02u16)
.await?;
slave
.write_sdo(0x1c13, SubIndex::Index(3), 0x1a04u16)
.await?;
slave
.write_sdo(0x1c13, SubIndex::Index(4), 0x1a06u16)
.await?;
slave.write_sdo(0x1c13, SubIndex::Index(0), 4u8).await?;
}

Ok(())
})
});

let group = client
// Initialise up to 16 slave devices
.init::<MAX_SLAVES, _>(group, |groups, slave| groups.push(slave))
.await
.expect("Init");

log::info!("Group has {} slaves", group.len());

for slave in group.slaves() {
let (i, o) = slave.io();

log::info!(
"-> Slave {} {} has {} input bytes, {} output bytes",
slave.configured_address,
slave.name,
i.len(),
o.len(),
);
}

let mut tick_interval = Timer::interval(Duration::from_millis(5));

let group = Arc::new(group);
let group2 = group.clone();

while let Some(_) = tick_interval.next().await {
group.tx_rx(&client).await.expect("TX/RX");

// Increment every output byte for every slave device by one
for slave in group2.slaves() {
let (_i, o) = slave.io();

for byte in o.iter_mut() {
*byte = byte.wrapping_add(1);
}
}
}

Ok(())
}

fn main() -> Result<(), Error> {
env_logger::init();
let local_ex = LocalExecutor::new();

let ctrlc = CtrlC::new().expect("cannot create Ctrl+C handler?");

futures_lite::future::block_on(
local_ex.run(ctrlc.race(async { main_inner(&local_ex).await.unwrap() })),
);

Ok(())
}
```

## Current goals

- [x] Become a member of the
Expand Down
2 changes: 1 addition & 1 deletion examples/akd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ async fn main_inner(ex: &LocalExecutor<'static>) -> Result<(), Error> {

log::info!("Slaves moved to OP state");

log::info!("Group has {} slaves", group.slaves().len());
log::info!("Group has {} slaves", group.len());

let slave = group.slave(0).unwrap();

Expand Down
60 changes: 8 additions & 52 deletions examples/dc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,15 @@ async fn main_inner(ex: &LocalExecutor<'static>) -> Result<(), Error> {
.await
.expect("Init");

log::info!("Group has {} slaves", group.slaves().len());
log::info!("Group has {} slaves", group.len());

for (slave, slave_stuff) in group.slaves().iter().enumerate() {
let sl = group.slave(slave).unwrap();

let (i, o) = sl.io();
for slave in group.slaves() {
let (i, o) = slave.io();

log::info!(
"-> Slave {slave} {} has inputs: {}, outputs: {}",
slave_stuff.name,
"-> Slave {} {} has inputs: {}, outputs: {}",
slave.configured_address,
slave.name,
i.len(),
o.len(),
);
Expand All @@ -120,64 +119,21 @@ async fn main_inner(ex: &LocalExecutor<'static>) -> Result<(), Error> {
let group = Arc::new(group);
let group2 = group.clone();

// smol::spawn(async move {
// let mut cyclic_interval = Timer::interval(Duration::from_millis(2));

// while let Some(_) = cyclic_interval.next().await {
// group.tx_rx(&client).await.expect("TX/RX");
// }
// })
// .detach();

while let Some(_) = tick_interval.next().await {
group.tx_rx(&client).await.expect("TX/RX");

// let (_i, o) = group2.io(4).unwrap();

// o.map(|o| {
// for byte in o.iter_mut() {
// *byte += 1;
// }
// });

// Dynamic drift compensation
let (_reference_time, _wkc) = client
.frmw::<u64>(0x1000, RegisterAddress::DcSystemTime)
.await?;

for slave in 0..group2.slaves().len() {
let sl = group2.slave(slave).expect("Slave");

let (_i, o) = sl.io();

// let diff = group2
// .slave(slave, &client)
// .unwrap()
// .raw_read::<u32>(RegisterAddress::DcSystemTimeDifference)
// .await
// .map(|n| {
// let smaller = n & (1 << 31) > 0;

// // Chop off smaller/larger bit
// let number = (n & (u32::MAX >> 1)) as i32;

// if smaller {
// -number
// } else {
// number
// }
// })
// .unwrap_or(0);

// print!("{diff:+#05} ");
for slave in group2.slaves() {
let (_i, o) = slave.io();

for byte in o.iter_mut() {
// *byte = !*byte;
*byte = byte.wrapping_add(1);
}
}

// println!("");
}

Ok(())
Expand Down
12 changes: 6 additions & 6 deletions examples/ec400.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,15 @@ async fn main_inner(ex: &LocalExecutor<'static>) -> Result<(), Error> {

log::info!("Slaves moved to OP state");

log::info!("Group has {} slaves", group.slaves().len());
log::info!("Group has {} slaves", group.len());

for (slave, slave_stuff) in group.slaves().iter().enumerate() {
let sl = group.slave(slave).unwrap();
let (i, o) = sl.io();
for slave in group.slaves() {
let (i, o) = slave.io();

log::info!(
"-> Slave {slave} {} inputs: {} bytes, outputs: {} bytes",
slave_stuff.name,
"-> Slave {} {} inputs: {} bytes, outputs: {} bytes",
slave.configured_address,
slave.name,
i.len(),
o.len(),
);
Expand Down
Loading