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 CommandsDescription

set softap.auto_start           true
set softap.dhcp_server.enabled  true
set softap.ssid        tcp_server_ap
set tcp.server.auto_start       true
set tcp.server.auto_interface softap
set tcp.server.idle_timeout      300

set gpio.init                     22 none
set tcp.server.connected_gpio     22
set gpio.init                     21 none
set tcp.server.data_gpio          21

save
reboot

Start softap on boot
Provide clients with IP
TCP clients join this network
TCP server starts automatically
and listens on softap interface
Disconnect clients if idle for 5 minutes

Free User LED 1 GPIO
User LED 1 GPIO lights when client connects
Free User LED 2 GPIO
User LED 2 GPIO lights when client data ready

Save
Reboot to start softap and TCP server

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 CommandsDescription

set wlan.ssid   tcp_server_ap
set gpio.init   22 none
save
tcp_client      -g 22 10.10.10.1 3000


Specify the Module A softap SSID
Free Module B LED 1 GPIO
Save
Connect, with tcp_client data GPIO set to LED 1
Network starts automatically

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 CommandsDescription

close 0

Close stream 0
Module B ResponseDescription
> [Closed: 0]
Success

Stream 0 successfully closed

Module A ResponseDescription
> [Closed: 0]

Module A reports stream 0 closed

Note that Module A LED 1 (tcp.server.connected_gpio) turns off.

Reconnect Module B to the module A server:

Module B ZentriOS CommandsDescription
tcp_client -g 22 10.10.10.1 3000

Connect as client to Module A server

Module A LED 1 (tcp.server.connected_gpio) lights.

Now on Module B write to stream 0.

Module B ZentriOS CommandsDescription
write 0 12
hello from B
Write 12 characters to stream 0
  

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 CommandsDescription
read 0 100
hello from B

read up to 100 characters
The response is the message sent by Module B

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 CommandsDescription
write 0 12
hello from A
Write 12 characters to stream 0
  

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 CommandsDescription
read 0 100
hello from A

read up to 100 characters
The response is the message sent by Module A

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 CommandsDescription

set system.oob.event_mask       0x0C

set tcp.server.connected_gpio   -1
set system.oob.gpio             22
set system.oob.gpio_level       1
save
reboot

Set event mask for tcp.server.connected_gpio and
 tcp.server.data_gpio (0x04 AND 0x08)
Free up GPIO for Mackerel User LED 1
Set GPIO for Mackerel User LED 1 to OOB function
GPIO level to trigger is high
Save
Reboot

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 CommandsDescription
tcp_client 10.10.10.1 3000


Connect as TCP client - network starts automatically

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 CommandsDescription
> stream_list
! # Type  Info
# 0 TCPC  10.10.10.1:3000 (52313)
# 1 TCPC  10.10.10.1:3000 (57314)
# 2 TCPC  10.10.10.1:3000 (62315)
# 3 TCPC  10.10.10.1:3000 (9824)
# 4 TCPC  10.10.10.1:3000 (14825)
# 5 TCPC  10.10.10.1:3000 (19826)
# 6 TCPC  10.10.10.1:3000 (24827)
# 7 TCPC  10.10.10.1:3000 (29828)

List streams
A maximum of 8 streams can be opened








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 CommandsDescription
> stream_list
! # Type  Info
# 0 TCPS  10.10.10.1:3000 10.10.10.2:52313
# 1 TCPS  10.10.10.1:3000 10.10.10.2:57314
# 2 TCPS  10.10.10.1:3000 10.10.10.2:62315
# 3 TCPS  10.10.10.1:3000 10.10.10.2:9824
# 4 TCPS  10.10.10.1:3000 10.10.10.2:14825
# 5 TCPS  10.10.10.1:3000 10.10.10.2:19826
# 6 TCPS  10.10.10.1:3000 10.10.10.2:24827
# 7 TCPS  10.10.10.1:3000 10.10.10.2:29828

List streams
A maximum of 8 streams can be opened








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 CommandsDescription
> get system.oob.status
0x4
> stream_poll all
0,0|1,0|2,0|3,0|4,0|5,0|6,0|7,0

Get OOB status
0x04 indicates tcp.server.connected_gpio triggered
Poll all streams
8 streams are open but no data is available

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:

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 CommandsDescription
write 4 12
hello from 4
write 7 12
hello from 7
Write 12 characters to stream 3

Write 12 characters to stream 4

Module A LED1 lights, and we simulate the interrupt handler.

Module A ZentriOS CommandsDescription
> get system.oob.status
0x8
> stream_poll all
0,0|1,0|2,0|3,0|4,1|5,0|6,0|7,1

Get OOB status
0x08 indicates tcp.server.data_gpio triggered
Poll all streams
Data is available on stream 4 and stream 7

Read data from stream 4:

Module A ZentriOS CommandsDescription
> stream_read 4 100
hello from 4

Read from stream 4

Now on Module B close stream 6:

Module B ZentriOS CommandsDescription
stream_close 6
Close stream 6

Module A LED1 lights, and we simulate the interrupt handler.

Module A ZentriOS CommandsDescription
> get system.oob.status
0xC

> stream_poll all
0,0|1,0|2,0|3,0|4,0|5,0|7,1

Get OOB status
0x0C indicates both tcp.server.connected_gpio
 and tcp.server.data_gpio triggered
Poll all streams
Data still available on stream 7, stream 6 closed

Now read stream 7:

Module A ZentriOS CommandsDescription
> stream_read 7 100
hello from 7

Read from stream 7

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.


Supporting ZentriOS Editions and Versions

Change Log

ModifiedChanges
2015-Apr-10Created