With the communication protocol clearly defined in \cref{sec:tinyframe,sec:units_overview}, and \cref{sec:wireless} for the wireless gateway, the implementation of client libraries is relatively straightforward. Two have been developed as a proof-of-concept, in languages C and Python.
This is the only platform-dependent part of the library. Unix-based systems provide a standardized POSIX API to configure the serial port. A raw access to \gls{USB} endpoints is possible using the libUSB C library. Access to the serial port or \gls{USB} from C on MS Windows has not been investigated, but should be possible using proprietary APIs.
Accessing the serial port or \gls{USB} endpoints from Python is more straightforward thanks to the cross platform libraries \textit{PySerial} and \textit{PyUSB}.
The host side of the communication protocol described in \cref{sec:tinyframe} should be implemented as a part of the library. This includes the reading and writing of configuration files, unit list read-out, command payload building, and asynchronous event parsing.
Additional utilities may be defined on top of this basic protocol support for the command API of different GEX units, as described in \cref{sec:units_overview}. Those unit-specific ``drivers'' are available in the provided Python library.
The Python GEX library implements both a serial port access and raw access to \gls{USB} endpoints. Its development has been prioritized over the C library because of its potential to integrate with MATLAB, and because it promises to be the most convenient method to interact with GEX thanks to the ease-of-use that comes with the Python syntax. This library provides a high level \gls{API} above the individual unit types, removing the burden of building and parsing of the binary command payloads from the user.
To use the wireless gateway, wrap either low-level transport in \mono{gex.DongleAdapter}. The adapter configures the gateway and converts the regular protocol to the right format.
\begin{pythoncode}
# Gateway connection example
transport = gex.DongleAdapter(gex.TrxRawUSB(remote=True), slave=42)
An example Python program displaying a test pattern on a \gls{LED} matrix using the \gls{I2C}-connected driver chip IS31FL3730 is presented in \cref{lst:py_api} as an illustration of the library usage. A photo of the produced \gls{LED} pattern can be seen in \cref{fig:pydemo}.
\caption[GEX Zero with the Micro Dot pHAT add-on board]{\label{fig:pydemo}GEX Zero with the Micro Dot pHAT add-on board, showing a test pattern defined in a Python script}
First, a client instance is created, receiving the transport as an argument. We use a \mono{with} block in the example to ensure the transport is safely closed before the program ends, even if that happens due to an exception; this is similar to the \mono{try-finally} pattern, but the de-initialization is done automatically. The client (and subsequently the transport) can be closed manually by calling its \mono{.close()} method. Inside the \mono{with} block, the script proceeds to create unit handles and use them to perform the desired task, in our case a communication with the \gls{LED} matrix driver over the \gls{I2C} bus.
The Python library can be accessed from MATLAB scripts thanks to MATLAB's two-way Python integration~\cite{matlabpy}. Controlling GEX from MATLAB may be useful when additional processing is required, e.g., with data from the \gls{ADC}; however, in many cases, an open source alternative native to Python exists that could be used for the same purpose, such as the NumPy and SciPy libraries~\cite{numpyscipy}.
The example in \cref{lst:matlab_api} (and \cref{fig:matlabpic}) demonstrates the use of MATLAB to calculate the frequency spectrum of an analog signal captured with GEX. The syntax needed to use the serial port transport (instead of a raw access to USB endpoints) is shown in a comment.
The C library is more simplistic than the Python one; it supports only the serial port transport (\gls{UART} or \gls{CDCACM}) and does not implement asynchronous polling or the unit support drivers. The implemented features---the transport, a basic protocol handler, and payload building and parsing utilities---are sufficient for most applications, though less convenient than the Python library.
This low-level library is intended for applications where the performance of the Python implementation is insufficient, or where an integration with existing C code is required. The full \gls{API} can be found in the library header files. A C version of the example Python script shown above, controlling an \gls{LED} matrix driver, is presented in \cref{lst:c_api_full}. The payloads in this example are represented as binary strings for simplicity. Two better methods of payload construction are available: using C structs, or taking advantage of the Payload Builder utility (bundled with TinyFrame).
\caption{\label{lst:c_api_full} An example C program (GNU C99) controlling GEX using the low-level GEX library; this code has the same effect as the Python script shown in \cref{lst:py_api}, with payloads built following the command tables from \cref{sec:units_overview}.}
\caption{\label{lst:c_api_struct} The variable-length struct approach to payload building}
\end{listing}
The structure-based method utilizes C structs to access individual fields in the payload. Simple payloads can be represented by a struct without problems, but payloads of a dynamic length pose a challenge; we can either define a new struct for each required length, or, when the variable-length array is located at the end of the payload, a struct with the largest needed payload size is defined and the real length is then specified when sending the message. The latter approach is illustrated in \cref{lst:c_api_struct}.
\subsection{Using the Payload Builder Utility}
\begin{listing}
\begin{ccode}
uint8_t buff[20];
PayloadBuilder pb;
pb = pb_init(&buff, 20, NULL);
pb_u16(&pb, 0x61);
pb_u8(&pb, 0x00);
pb_u8(&pb, 0x18);
GEX_Send(bus, 2, buff, pb_length(&pb));
pb_rewind(&pb); // reset the builder for a new frame
The Payload Builder utility offers a flexible solution to the construction of arbitrary binary payloads. It is used in the GEX firmware to construct messages and events, along with the binary settings storage content.
An example of Payload Builder's usage is shown in \cref{lst:c_api_pb}. We give it a byte buffer and it then fills it with the payload values, taking care of buffer overflow, and advancing the write pointer by the right number of bytes. The third parameter of \mono{pb\_init()} is optional, a pointer to a function called when the buffer overflows; this callback can flush the buffer and rewind it, or report an error.
Payload Builder is accompanied by Payload Parser, a tool doing the exact opposite. While it is not needed in our example, we will find this utility useful when processing command responses or events payloads. The full \gls{API} of those utilities can be found in their header files.