Packages

Livebook tools for EtherCAT discovery, runtime inspection, control, and diagnostics.

Current section

Files

Jump to
kino_ethercat examples 01_ethercat_introduction.livemd
Raw

examples/01_ethercat_introduction.livemd

# EtherCAT Introduction
```elixir
Mix.install([
{:kino_ethercat, "~> 0.3.3"}
])
```
## The Ring
One master. Three slaves.
```mermaid
%%{init: {'deterministicIds': true, 'deterministicIDSeed': 'ethercat-intro-ring'}}%%
flowchart LR
M[Master] --> C
subgraph S[Slaves]
C[Coupler]
I[Inputs]
O[Outputs]
C --> I
I --> O
end
```
## One Cycle
The master uses cyclic EtherCAT frames.
```mermaid
%%{init: {'deterministicIds': true, 'deterministicIDSeed': 'ethercat-intro-cycle'}}%%
sequenceDiagram
participant M as Master
participant R as EtherCAT ring
M->>R: cyclic EtherCAT frame
R-->>M: inputs + working counter
M->>M: check the cycle
```
## Start The Simulator
Evaluate the simulator cell, then follow the short instructions in the `Introduction` tab.
<!-- livebook:{"attrs":"eyJjb25uZWN0aW9ucyI6W3sic291cmNlX2lkIjoiMyIsInNvdXJjZV9zaWduYWwiOiJjaDEiLCJ0YXJnZXRfaWQiOiIyIiwidGFyZ2V0X3NpZ25hbCI6ImNoMSJ9XSwiZXhwZXJ0X21vZGUiOmZhbHNlLCJzZWxlY3RlZCI6W3siZHJpdmVyIjoiS2lub0V0aGVyQ0FULkRyaXZlci5FSzExMDAiLCJpZCI6IjEiLCJuYW1lIjoiY291cGxlciJ9LHsiZHJpdmVyIjoiS2lub0V0aGVyQ0FULkRyaXZlci5FTDE4MDkiLCJpZCI6IjIiLCJuYW1lIjoiaW5wdXRzIn0seyJkcml2ZXIiOiJLaW5vRXRoZXJDQVQuRHJpdmVyLkVMMjgwOSIsImlkIjoiMyIsIm5hbWUiOiJvdXRwdXRzIn1dfQ","chunks":null,"kind":"Elixir.KinoEtherCAT.SmartCells.Simulator","livebook_object":"smart_cell"} -->
```elixir
alias EtherCAT.Simulator
alias EtherCAT.Simulator.Slave
simulator_ip = {127, 0, 0, 2}
_ = Simulator.stop()
devices = [
Slave.from_driver(KinoEtherCAT.Driver.EK1100, name: :coupler),
Slave.from_driver(KinoEtherCAT.Driver.EL1809, name: :inputs),
Slave.from_driver(KinoEtherCAT.Driver.EL2809, name: :outputs)
]
{:ok, _supervisor} = Simulator.start(devices: devices, udp: [ip: simulator_ip, port: 34980])
:ok = Slave.connect({:outputs, :ch1}, {:inputs, :ch1})
Kino.Layout.tabs(
Introduction: KinoEtherCAT.introduction(),
Simulator: KinoEtherCAT.simulator(),
Faults: KinoEtherCAT.simulator_faults()
)
```
```mermaid
%%{init: {'deterministicIds': true, 'deterministicIDSeed': 'ethercat-intro-loopback'}}%%
flowchart LR
O[outputs.ch1] --> I[inputs.ch1]
```
This is the small path we will use later:
* `outputs.ch1 -> inputs.ch1`
<!-- livebook:{"attrs":"eyJhd2FpdF9sb2NrIjpmYWxzZSwiZGNfY3ljbGVfbnMiOjEwMDAwMDAwLCJkY19lbmFibGVkIjpmYWxzZSwiZG9tYWlucyI6W3siY3ljbGVfdGltZV9tcyI6MTAsImN5Y2xlX3RpbWVfdXMiOjEwMDAwLCJpZCI6Im1haW4iLCJtaXNzX3RocmVzaG9sZCI6MTAwMH1dLCJob3N0IjoiMTI3LjAuMC4yIiwiaW50ZXJmYWNlIjoiZXRoMCIsImxvY2tfdGhyZXNob2xkX25zIjoxMDAsImxvY2tfdGltZW91dF9tcyI6NTAwMCwicG9ydCI6MzQ5ODAsInNsYXZlcyI6W3siZGlzY292ZXJlZF9uYW1lIjoiY291cGxlciIsImRvbWFpbl9pZCI6IiIsImRyaXZlciI6IiIsIm5hbWUiOiJjb3VwbGVyIiwicHJvZHVjdF9jb2RlIjo3MjEwMDk0Niwic3RhdGlvbiI6NDA5NiwidmVuZG9yX2lkIjoyfSx7ImRpc2NvdmVyZWRfbmFtZSI6ImlucHV0cyIsImRvbWFpbl9pZCI6Im1haW4iLCJkcml2ZXIiOiJLaW5vRXRoZXJDQVQuRHJpdmVyLkVMMTgwOSIsIm5hbWUiOiJpbnB1dHMiLCJwcm9kdWN0X2NvZGUiOjExODU2Njk5NCwic3RhdGlvbiI6NDA5NywidmVuZG9yX2lkIjoyfSx7ImRpc2NvdmVyZWRfbmFtZSI6Im91dHB1dHMiLCJkb21haW5faWQiOiJtYWluIiwiZHJpdmVyIjoiS2lub0V0aGVyQ0FULkRyaXZlci5FTDI4MDkiLCJuYW1lIjoib3V0cHV0cyIsInByb2R1Y3RfY29kZSI6MTg0MTAyOTk0LCJzdGF0aW9uIjo0MDk4LCJ2ZW5kb3JfaWQiOjJ9XSwidHJhbnNwb3J0IjoidWRwIiwidHJhbnNwb3J0X21vZGUiOiJhdXRvIiwid2FybXVwX2N5Y2xlcyI6MH0","chunks":null,"kind":"Elixir.KinoEtherCAT.SmartCells.Setup","livebook_object":"smart_cell"} -->
```elixir
start_opts = [
transport: :udp,
host: {127, 0, 0, 2},
port: 34980,
bind_ip: {127, 0, 0, 1},
frame_timeout_ms: 10,
domains: [%EtherCAT.Domain.Config{id: :main, cycle_time_us: 10000, miss_threshold: 1000}],
dc: nil,
slaves: [
%EtherCAT.Slave.Config{name: :coupler, target_state: :op},
%EtherCAT.Slave.Config{
name: :inputs,
driver: KinoEtherCAT.Driver.EL1809,
process_data: {:all, :main},
target_state: :op
},
%EtherCAT.Slave.Config{
name: :outputs,
driver: KinoEtherCAT.Driver.EL2809,
process_data: {:all, :main},
target_state: :op
}
]
]
_ = EtherCAT.stop()
setup_result =
with :ok <- EtherCAT.start(start_opts),
:ok <- EtherCAT.await_running(),
:ok <- EtherCAT.await_operational() do
:ok
end
case setup_result do
:ok ->
Kino.Layout.tabs(
Master: KinoEtherCAT.master(),
"Task Manager": KinoEtherCAT.diagnostics()
)
{:error, reason} ->
_ = EtherCAT.stop()
Kino.Markdown.new("""
## EtherCAT setup failed
`#{inspect(reason)}`
The generated setup cell kept the notebook running instead of crashing.
Re-run the cell once the bus is stable. Configuration timeouts usually mean one or more slaves did not reach the requested state in time.
""")
end
```
## Start The Master
Now let the setup flow create the real master session.
<!-- livebook:{"attrs":"eyJzZWxlY3RlZCI6W3siYml0X3NpemUiOjEsImRlZmF1bHRfd2lkZ2V0Ijoic3dpdGNoIiwiZGlyZWN0aW9uIjoib3V0cHV0IiwiZG9tYWluIjoibWFpbiIsImtleSI6InNsYXZlXzIuY2gxIiwibGFiZWwiOm51bGwsInNpZ25hbCI6ImNoMSIsInNpZ25hbF9pbmRleCI6MCwic2xhdmUiOiJzbGF2ZV8yIiwic2xhdmVfaW5kZXgiOjIsIndpZGdldCI6ImF1dG8ifSx7ImJpdF9zaXplIjoxLCJkZWZhdWx0X3dpZGdldCI6ImxlZCIsImRpcmVjdGlvbiI6ImlucHV0IiwiZG9tYWluIjoibWFpbiIsImtleSI6InNsYXZlXzEuY2gxIiwibGFiZWwiOm51bGwsInNpZ25hbCI6ImNoMSIsInNpZ25hbF9pbmRleCI6MCwic2xhdmUiOiJzbGF2ZV8xIiwic2xhdmVfaW5kZXgiOjEsIndpZGdldCI6ImF1dG8ifV19","chunks":null,"kind":"Elixir.KinoEtherCAT.SmartCells.Visualizer","livebook_object":"smart_cell"} -->
```elixir
widgets = [
KinoEtherCAT.Widgets.switch(:slave_2, :ch1),
KinoEtherCAT.Widgets.led(:slave_1, :ch1)
]
case widgets do
[] -> Kino.nothing()
[widget] -> widget
_ -> Kino.Layout.grid(widgets, columns: 2)
end
|> Kino.render()
Kino.nothing()
```
## Try ch1
Now change `outputs.ch1` through the normal EtherCAT path.
When you toggle the output, `inputs.ch1` should change too.
```mermaid
%%{init: {'deterministicIds': true, 'deterministicIDSeed': 'ethercat-intro-ch1-sequence'}}%%
sequenceDiagram
participant M as Master
participant O as outputs.ch1
participant I as inputs.ch1
M->>O: write output bit
O->>I: loopback wiring
I-->>M: input bit in next cycle
```
```elixir
Kino.Layout.grid(
[
KinoEtherCAT.Widgets.switch(:slave_2, :ch1, label: "outputs.ch1"),
KinoEtherCAT.Widgets.led(:slave_1, :ch1, label: "inputs.ch1")
],
columns: 2
)
```