Handling Multiple Clients with OOB Interrupts
The OOB (Out-of-Band Interrupt) GPIO allows a single GPIO to act as an interrupt responding to one or more types of network connection or data ready event. See system.oob.gpio.
This demonstration starts a server on a ZentriOS device. Another ZentriOS device connects as a client. The demonstration uses TCP protocol, but the OOB interrupt GPIO approach works equally well with UDP and TLS.
In the first part of the demonstration, Handling a Single Client, GPIOs are assigned to assert on client connect and client data ready.
In the second part of the demonstration, Handling Multiple Clients, an OOB GPIO is assigned to assert on client connect and client data ready. Because the OOB GPIO resets when its status is read, this provides a way for an MCU to track connect, disconnect and data ready for multiple clients without continuous polling.
Prerequisites
This demonstration uses two Mackerel evaluation boards.
Set Up
Open a ZentriOS Terminal to both Mackerel boards. The Mackerel board that runs the TCP server is designated Module A. The Mackerel board that connects as a client is designated Module B.
The Module A configuration demonstrates the OOB interrupt approach. The Module B configuration simulates clients.
Module A ZentriOS Commands | Description |
---|---|
|
|
A data indicator GPIO can be set for the UDP protocol. See:
On rebooting, module A displays responses similar to the following:
Rebooting
[Disassociated]
ZentriOS-2.2.0.12, Built:2015-04-08 20:12:21 for AMW004.3, Board:AMW004-E03.3
[Ready]
IPv4 address: 10.10.10.1
SoftAP 'tcp_server_ap' started
TCP server listening on port: 3000
Handling a Single Client
Connect Module B as a client to the Module A server. We use the tcp_client -g
option to specify a data GPIO to indicate when data is ready on the client stream.
This works with TLS and UDP as well: the -g
data GPIO option is also available for the tls_client and udp_client commands.
Module B ZentriOS Commands | Description |
---|---|
|
|
Note that Module A User LED 1 (tcp.server.connected_gpio) lights. It remains lit while Module B is connected. The server disconnects the client automatically after tcp.server.idle_timeout seconds, by default 60
. The client can disconnect by closing the stream.
If Module B disconnects, Module A registers that the stream is closed.
On Module B, close stream 0:
Module B ZentriOS Commands | Description |
---|---|
|
|
Module B Response | Description |
---|---|
|
|
Module A Response | Description |
---|---|
|
|
Note that Module A LED 1 (tcp.server.connected_gpio) turns off.
Reconnect Module B to the module A server:
Module B ZentriOS Commands | Description |
---|---|
|
|
Module A LED 1 (tcp.server.connected_gpio) lights.
Now on Module B write to stream 0.
Module B ZentriOS Commands | Description |
---|---|
|
|
Note that Module A User LED 2 (tcp.server.data_gpio) lights. It remains lit until Module A reads the data.
On Module A, read the data on stream 0.
Module A ZentriOS Commands | Description |
---|---|
|
|
Note that Module A User LED 2 (tcp.server.data_gpio) turns off when the data is read.
Send a message back from Module A to Module B:
Module A ZentriOS Commands | Description |
---|---|
|
|
Note that Module B User LED 1 (tcp_client stream:0
) lights. It remains lit until Module B reads the data.
On Module B, read the data on stream 0.
Module B ZentriOS Commands | Description |
---|---|
|
|
The variables tcp.server.connected_gpio and tcp.server.data_gpio handle the simple case when only one client connects and sends data to the server.
Handling Multiple Clients
To avoid continuous polling, another solution is needed when multiple clients are connecting, disconnecting and sending data.
After the first client connects to the server, tcp.server.connected_gpio remains asserted. To detect a second client while the first client remains connected, it would be necessary to poll continuously with the stream_poll all command.
The same considerations apply to detecting client data on a stream. Until the data from the first client is read, it is not possible to detect data from a second client using tcp.server.data_gpio, without polling continuously.
The system.oob.gpio (Out-Of_Band Interrupt GPIO) provides a solution. Set up the system.oob.gpio
to respond to the tcp.server.connected_gpio
event. By default the system.oob.gpio
is asserted on both the rising and falling edge of the event, so it goes high when the TCP client connects, and again when it disconnects.
On reading system.oob.status, the system.oob.gpio is de-asserted, ready to handle the next connect, disconnect or data event.
In a real-world application, the system.oob.gpio is wired to a host MCU interrupt, and the interrupt handler services the interrupt by getting system.oob.status to determine which events have taken place, and perhaps polling for stream information and taking other actions.
Provided the events do not take place faster than the MCU can handle interrupts, this approach can deal with multiple clients.
In this demonstration we simulate the interrupt handler by issuing commands manually.
Module A Setup
Module A ZentriOS Commands | Description |
---|---|
|
|
Connecting TCP Clients
Module B can simulate multiple TCP clients by connecting up to 8 times. Each time ZentriOS assigns a different stream.
Repeat the following command 9 times.
Module B ZentriOS Commands | Description |
---|---|
|
|
On the last attempt, the command fails with a response as follows:
> tcp_client 10.10.10.1 3000
Max streams exceeded
Command failed
List the open streams on Module B. Module B shows 8 TCP client streams. The response is similar to the following:
Module B ZentriOS Commands | Description |
---|---|
|
|
List the open streams on Module A. Module A has 8 TCP server streams open with ports matching those on Module B:
Module A ZentriOS Commands | Description |
---|---|
|
|
OOB Interrupt Handler
Note that Module A LED 1 turns on after the first connection from module B.
When Module A LED 1 lights we simulate the interrupt handler triggered by the system.oob.gpio. The interrupt handler gets the system.oob.status variable, then polls all streams with stream_poll all, and parses the response to determine what processing is required:
Module A ZentriOS Commands | Description |
---|---|
|
|
The response is a pipe-separated list of all open streams. Each stream entry consists of a comma-separated list of stream number and stream status:
0
: stream connected1
: data available2
: stream closed
We parse the poll response and determine there is no data to read.
Now Module B writes to two of the streams. We arbitrarily select stream 4 and 7:
Module B ZentriOS Commands | Description |
---|---|
|
|
Module A LED1 lights, and we simulate the interrupt handler.
Module A ZentriOS Commands | Description |
---|---|
|
|
Read data from stream 4:
Module A ZentriOS Commands | Description |
---|---|
|
|
Now on Module B close stream 6:
Module B ZentriOS Commands | Description |
---|---|
|
|
Module A LED1 lights, and we simulate the interrupt handler.
Module A ZentriOS Commands | Description |
---|---|
|
|
Now read stream 7:
Module A ZentriOS Commands | Description |
---|---|
|
|
This demonstrates that an MCU responding to an OOB interrupt can handle multiple clients, connecting, disconnecting and sending data asynchronously. Polling is required only in response to the OOB interrupt.
Change Log
Modified | Changes | ZentriOS Version Required |
---|---|---|
2015-Apr-10 | Created | 2.2+ |