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
SUBSCRIBEfor a serial instance owns it. Other connections cannot configure, write to or reset it until the owner unsubscribes or disconnects. - Port mode.
SerialProxySetModeRequestswitches a port betweenRAW(bytes pass through) andPROTOCOL(the device runs a protocol handler on the port), acknowledged withSET_MODE.
What espex implements
Ownership
espex records one owner per instance, across connections. A
client must SUBSCRIBE before anything else:
| Request from a non-owner | Result |
|---|---|
| CONFIGURE, SET_MODEM_PINS, FLUSH, SET_MODE | PORT_IN_USE, adapter not called |
| WRITE | dropped (WRITE has no acknowledgement) |
| GET_MODEM_PINS | answered: it is read-only |
| SUBSCRIBE while another live connection owns it | PORT_IN_USE |
| UNSUBSCRIBE | OK |
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:
| Mode | Adapter exports set_mode/2 | Adapter does not |
|---|---|---|
RAW | its answer | OK |
PROTOCOL | its answer | NOT_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
endUpgrade 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_PINSmay open a second handle on an instance. Whether that is allowed stays the adapter's decision. - A handle must not outlive the
subscriberpid it was opened for: a connection killed outright cannot close it, though its ownership is still released.