1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
//! A performant, `async`-first EtherCAT MainDevice written in pure Rust.
//!
//! # Crate features
//!
//! - `std` (enabled by default) - exposes the [`std`] module, containing helpers to run the TX/RX
//! loop on desktop operating systems.
//! - `defmt` - enable logging with the [`defmt`](https://docs.rs/defmt) crate.
//! - `log` - enable logging with the [`log`](https://docs.rs/log) crate. This is enabled by default
//! when the `std` feature is enabled.
//! - `serde` - enable `serde` impls for some public items.
//!
//! For `no_std` targets, it is recommended to add this crate with
//!
//! ```bash
//! cargo add --no-default-features --features defmt
//! ```
//!
//! # Examples
//!
//! This example increments the output bytes of all detected SubDevices every tick. It is tested on an
//! EK1100 with output modules but may work on other basic SubDevices.
//!
//! 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,no_run
//! use env_logger::Env;
//! use ethercrab::{
//! error::Error, std::{ethercat_now, tx_rx_task}, MainDevice, MainDeviceConfig, PduStorage, Timeouts
//! };
//! use std::{sync::Arc, time::Duration};
//! use tokio::time::MissedTickBehavior;
//!
//! /// Maximum number of SubDevices that can be stored. This must be a power of 2 greater than 1.
//! const MAX_SUBDEVICES: 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();
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Error> {
//! env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
//!
//! let interface = std::env::args()
//! .nth(1)
//! .expect("Provide network interface as first argument.");
//!
//! log::info!("Starting EK1100 demo...");
//! log::info!("Ensure an EK1100 is the first SubDevice, with any number of modules connected after");
//! log::info!("Run with RUST_LOG=ethercrab=debug or =trace for debug information");
//!
//! let (tx, rx, pdu_loop) = PDU_STORAGE.try_split().expect("can only split once");
//!
//! let maindevice = Arc::new(MainDevice::new(
//! pdu_loop,
//! Timeouts {
//! wait_loop_delay: Duration::from_millis(2),
//! mailbox_response: Duration::from_millis(1000),
//! ..Default::default()
//! },
//! MainDeviceConfig::default(),
//! ));
//!
//! tokio::spawn(tx_rx_task(&interface, tx, rx).expect("spawn TX/RX task"));
//!
//! let mut group = maindevice
//! .init_single_group::<MAX_SUBDEVICES, PDI_LEN>(ethercat_now)
//! .await
//! .expect("Init");
//!
//! log::info!("Discovered {} SubDevices", group.len());
//!
//! for subdevice in group.iter(&maindevice) {
//! // Special case: if an EL3004 module is discovered, it needs some specific config during
//! // init to function properly
//! if subdevice.name() == "EL3004" {
//! log::info!("Found EL3004. Configuring...");
//!
//! subdevice.sdo_write(0x1c12, 0, 0u8).await?;
//! subdevice.sdo_write(0x1c13, 0, 0u8).await?;
//!
//! subdevice.sdo_write(0x1c13, 1, 0x1a00u16).await?;
//! subdevice.sdo_write(0x1c13, 2, 0x1a02u16).await?;
//! subdevice.sdo_write(0x1c13, 3, 0x1a04u16).await?;
//! subdevice.sdo_write(0x1c13, 4, 0x1a06u16).await?;
//! subdevice.sdo_write(0x1c13, 0, 4u8).await?;
//! }
//! }
//!
//! let mut group = group.into_op(&maindevice).await.expect("PRE-OP -> OP");
//!
//! for subdevice in group.iter(&maindevice) {
//! let io = subdevice.io_raw();
//!
//! log::info!(
//! "-> SubDevice {:#06x} {} inputs: {} bytes, outputs: {} bytes",
//! subdevice.configured_address(),
//! subdevice.name(),
//! io.inputs().len(),
//! io.outputs().len()
//! );
//! }
//!
//! let mut tick_interval = tokio::time::interval(Duration::from_millis(5));
//! tick_interval.set_missed_tick_behavior(MissedTickBehavior::Skip);
//!
//! loop {
//! group.tx_rx(&maindevice).await.expect("TX/RX");
//!
//! // Increment every output byte for every SubDevice by one
//! for mut subdevice in group.iter(&maindevice) {
//! let mut io = subdevice.io_raw_mut();
//!
//! for byte in io.outputs().iter_mut() {
//! *byte = byte.wrapping_add(1);
//! }
//! }
//!
//! tick_interval.tick().await;
//! }
//! }
//! ```
// MUST go first so everything else can see the macros inside
pub
pub use AlStatusCode;
pub use SubIndex;
pub use ;
pub use ;
use EthernetAddress;
pub use MainDevice;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use SubDeviceState;
pub use Timeouts;
const LEN_MASK: u16 = 0b0000_0111_1111_1111;
const ETHERCAT_ETHERTYPE: u16 = 0x88a4;
const MASTER_ADDR: EthernetAddress = EthernetAddress;
/// Starting address for discovered subdevices.
const BASE_SUBDEVICE_ADDRESS: u16 = 0x1000;
type SpinStrategy = Yield;
type SpinStrategy = Spin;