# 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
)
```
