espex 0.11.0 advertises API 1.17 and later. This page covers what 1.17 added.

What's new

API 1.17 changes the serial proxy in two ways:

  • One owner per port. The connection that sent SUBSCRIBE for a serial instance owns it. Other connections cannot configure, write to or reset it until the owner unsubscribes or disconnects.
  • Port mode. SerialProxySetModeRequest switches a port between RAW (bytes pass through) and PROTOCOL (the device runs a protocol handler on the port), acknowledged with SET_MODE.

What espex implements

Ownership

espex records one owner per instance, across connections. A client must SUBSCRIBE before anything else:

Request from a non-ownerResult
CONFIGURE, SET_MODEM_PINS, FLUSH, SET_MODEPORT_IN_USE, adapter not called
WRITEdropped (WRITE has no acknowledgement)
GET_MODEM_PINSanswered: it is read-only
SUBSCRIBE while another live connection owns itPORT_IN_USE
UNSUBSCRIBEOK

UNSUBSCRIBE and a disconnect both close the owner's handle (Espex.SerialProxy.close/1) before releasing the instance, so a free instance really is free. Only the owner receives SerialProxyDataReceived. See "Ownership" in Espex.SerialProxy.

Port mode

The optional Espex.SerialProxy.set_mode/2 is the whole surface:

ModeAdapter exports set_mode/2Adapter does not
RAWits answerOK
PROTOCOLits answerNOT_SUPPORTED

A mode outside the enum is INVALID_ARGUMENT. espex reapplies PROTOCOL after a CONFIGURE reopens the port, and the mode ends with the handle: Espex.SerialProxy.close/1 is where the adapter tears its protocol handler down. See "Port mode" in Espex.SerialProxy.

Usage

defmodule MyApp.SerialPorts do
  @behaviour Espex.SerialProxy

  # ... list_instances/0, open/3, write/2, close/1 as before ...

  @impl true
  def set_mode(handle, :protocol), do: MyApp.Tap.start(handle)
  def set_mode(handle, :raw), do: MyApp.Tap.stop(handle)

  @impl true
  def close(handle) do
    MyApp.Tap.stop(handle)
    MyApp.UART.close(handle)
  end
end

Upgrade notes

  • A client that writes without subscribing first now gets PORT_IN_USE, or its bytes are dropped. Home Assistant subscribes first.
  • espex still opens a handle per connection, so a non-owner's GET_MODEM_PINS may open a second handle on an instance. Whether that is allowed stays the adapter's decision.
  • A handle must not outlive the subscriber pid it was opened for: a connection killed outright cannot close it, though its ownership is still released.