EtherCAT (ethercat v0.5.0)

Copy Markdown View Source

Public runtime API for the EtherCAT protocol boundary.

start/1 returns an opaque %EtherCAT.Session{}. Every runtime operation requires that session, so a caller can never cross a stop/start boundary by resolving through the current singleton runtime:

{:ok, session} = EtherCAT.start(backend: {:raw, %{interface: "eth0"}})
:ok = EtherCAT.await_ready(session)
{:ok, :preop_ready} = EtherCAT.state(session)
{:ok, slaves} = EtherCAT.Diagnostics.slaves(session)
:ok = EtherCAT.stop(session)

With no explicit slave configuration, discovery holds the ring in PREOP; it does not start cyclic I/O. For cyclic operation, supply ordered EtherCAT.Slave.Config entries and EtherCAT.Domain.Config entries, then use await_operational/2. See the getting-started guide or the no-hardware walkthrough in EtherCAT.Simulator.

A stopped or replaced session returns {:error, :stale_session}.

Normal applications interact with EtherCAT through lifecycle operations, coherent process-data samples, static slave descriptions, protocol status, notifications, and explicit protocol writes.

A %EtherCAT.Sample{} is a coherent decoded observation from one domain cycle. It is protocol truth, not projected machine state. Different domains do not share a consistency boundary.

Semantic commands, machine state, and machine events belong above this library in a separate integration such as an Entity.Provider.

Specialist APIs live under:

Host applications must supervise EtherCAT.Runtime before calling this API. start/1 and stop/1 control one session inside that host-owned runtime; they do not boot the supervision tree themselves.

Summary

Functions

Block until session reaches operational cyclic runtime.

Wait for PREOP readiness, completed deactivation, or operational cyclic runtime.

Return the static protocol description for one slave.

Return static protocol descriptions for the session.

Read a decoded input and its refresh timestamp in host monotonic microseconds.

Return the latest retained sample for one slave and domain.

Return the latest retained domain samples for one slave.

Return configured slave names for the session.

Start a master session and return its opaque identity.

Return the session lifecycle state.

Return current protocol/runtime status for one slave.

Stop exactly session.

Subscribe a process to protocol observations from one slave.

Cancel a protocol subscription. Repeated cancellation is harmless.

Stage one decoded output signal for the session's next domain cycle.

Types

description()

@type description() :: EtherCAT.SlaveDescription.t()

domain_id()

@type domain_id() :: atom()

inventory()

@type inventory() :: %{optional(slave_name()) => description()}

sample_map()

@type sample_map() :: %{optional(domain_id()) => EtherCAT.Sample.t()}

session()

@type session() :: EtherCAT.Session.t()

session_query_error()

@type session_query_error() ::
  {:error,
   :not_started
   | :stale_session
   | :timeout
   | {:server_exit, term()}
   | {:diagnostic_unavailable, term(), term()}}

session_query_result(value)

@type session_query_result(value) :: {:ok, value} | session_query_error()

session_state()

@type session_state() ::
  :discovering
  | :awaiting_preop
  | :preop_ready
  | :deactivated
  | :operational
  | :activation_blocked
  | :recovering

slave_name()

@type slave_name() :: atom()

slave_status()

@type slave_status() :: EtherCAT.Slave.Status.t()

Functions

await_operational(session, timeout_ms \\ 10000)

@spec await_operational(EtherCAT.Session.t(), pos_integer()) :: :ok | {:error, term()}

Block until session reaches operational cyclic runtime.

await_ready(session, timeout_ms \\ 10000)

@spec await_ready(EtherCAT.Session.t(), pos_integer()) :: :ok | {:error, term()}

Wait for PREOP readiness, completed deactivation, or operational cyclic runtime.

Use await_operational/2 when your application requires cyclic I/O.

describe(session, slave_name)

@spec describe(EtherCAT.Session.t(), slave_name()) ::
  {:ok, description()} | {:error, term()}

Return the static protocol description for one slave.

inventory(session)

Return static protocol descriptions for the session.

read(session, slave_name, signal_name)

@spec read(EtherCAT.Session.t(), slave_name(), atom()) ::
  {:ok, {term(), integer()}} | {:error, term()}

Read a decoded input and its refresh timestamp in host monotonic microseconds.

Returns {:error, :not_ready} before the first valid domain refresh and {:error, {:stale, details}} after the domain freshness window expires. Decode failures remain errors, not substitute values. Unlike sample/3, this reads the current domain image with freshness checking rather than a retained change-driven observation.

sample(session, slave_name, domain_id)

@spec sample(EtherCAT.Session.t(), slave_name(), domain_id()) ::
  {:ok, EtherCAT.Sample.t()} | {:error, term()}

Return the latest retained sample for one slave and domain.

Returns {:error, :not_ready} when no sample is retained for that domain, including before its first input observation. await_operational/2 alone does not guarantee a sample is already available. Inspect Sample.errors for per-signal decode failures and status/2 for current runtime health; a retained sample is not a liveness guarantee.

samples(session, slave_name)

@spec samples(EtherCAT.Session.t(), slave_name()) ::
  {:ok, sample_map()} | {:error, term()}

Return the latest retained domain samples for one slave.

slaves(session)

Return configured slave names for the session.

start(opts \\ [])

@spec start(keyword()) :: {:ok, EtherCAT.Session.t()} | {:error, term()}

Start a master session and return its opaque identity.

The host must already supervise EtherCAT.Runtime. Startup is asynchronous: use await_ready/2 for PREOP provisioning or await_operational/2 for cyclic I/O.

Options:

  • :backend (required) — raw, redundant raw, or UDP EtherCAT.Backend description
  • :slaves — EtherCAT.Slave.Config entries in physical bus order. Omit or pass [] to assign discovered devices the names :coupler, :slave_1, … and hold them in PREOP. These are positional names, not inferred device types.
  • :domains — EtherCAT.Domain.Config entries, default []. Domain IDs must exist before slave process-data assignments refer to them.
  • :dc — EtherCAT.DC.Config or keyword options; defaults to %EtherCAT.DC.Config{}. Set nil to disable Distributed Clocks.
  • :base_station — first assigned station address, default 0x1000
  • :scan_stable_ms / :scan_poll_ms — stable-count window and discovery poll interval in milliseconds, default 1000 / 100
  • :frame_timeout_ms — optional bus response timeout override in milliseconds; otherwise the runtime derives it from the configured cycle budget

Only one session can be active per BEAM node. This operation assigns station addresses and changes slave state; do not share its interface with another master.

state(session)

Return the session lifecycle state.

status(session, slave_name)

@spec status(EtherCAT.Session.t(), slave_name()) ::
  {:ok, slave_status()} | {:error, term()}

Return current protocol/runtime status for one slave.

stop(session)

@spec stop(EtherCAT.Session.t()) :: :ok | {:error, term()}

Stop exactly session.

subscribe(session, slave_name, pid \\ self())

@spec subscribe(EtherCAT.Session.t(), slave_name(), pid()) ::
  {:ok, reference(), slave_status(), sample_map()} | {:error, term()}

Subscribe a process to protocol observations from one slave.

Registration and the returned status/sample values share the slave process's serialization boundary. Subsequent observations arrive as {:ethercat, ref, payload} messages, where payload is an EtherCAT.Sample or EtherCAT.Notification. Each registration has a unique reference.

Delivery is push-based with no backpressure or dropping. Subscribers must keep up; use samples/2 to poll the latest values when every cycle is unnecessary. Registrations end when the subscriber or slave exits.

unsubscribe(session, slave_name, ref)

@spec unsubscribe(EtherCAT.Session.t(), slave_name(), reference()) ::
  :ok | {:error, term()}

Cancel a protocol subscription. Repeated cancellation is harmless.

Already queued messages retain their reference and may still be received.

write(session, slave_name, signal_name, value)

@spec write(EtherCAT.Session.t(), slave_name(), atom(), term()) ::
  :ok | {:error, term()}

Stage one decoded output signal for the session's next domain cycle.

:ok means the driver encoded the value and the runtime staged it. It is not an acknowledgement from the device or proof of physical actuation. Subsequent writes can replace the staged value before a cycle transmits it.