VoidX-Protocol

The VoidX protocol was developed to allow the control of the parameters of a remote device. The goal of this communication protocol is to provide a standardized way to exchange data between a controlled device (e.g. an audio effect) and a controller (e.g. a smartphone, a PC,…). The controlled device acts as a server and the controller is a client. The latter connects to one or more devices and sends requests. The main advantage of this protocol is the possibility to develop control applications that are independent of the particular application loaded into the controlled device. Moreover, its simplicity allows this protocol to be compatible with almost every communication channel (e.g. serial, tcp/ip, Bluetooth,…).

Communication model

This communication model is based on a client-server architecture. In this model, the server is called device and the client is the application that controls the device. The application sends a request to the device and the device will process it sending back the response. The application must wait the response before sending a new request and it can send a new request only if the response does not arrive within a certain timeout.

The request and the response are byte encoded frames of variable length. To determine the length (and therefore the end) of a frame the NULL byte is used. Hence, both the application and the device must wait the NULL byte before processing the frame. This solution is particularly effective with communication channels that work with small data chunks such as BLE and UART.

Data-model

Data are stored inside a VoidX device through a tree structure. The nodes of this tree represent the parameters and every VoidX compatible device must have a minimum set of nodes in order to be correctly identified. This tree-structure has a special node, called root, from which each child node must descend. There are no limits to the number of children a node can have.

The use of this model allows the identification of nodes using their path; therefore, each node has a unique string identifier. The previous figure represents an example of data structure of an application. In this example the path for the “gain” node is the following:

root\app\gain

There are only a few rules for node names: they can not contain white-spaces and slashes. Furthermore, nodes whose name begins with an underscore should be considered read-only nodes. These nodes are particularly useful for communicating descriptive information to the application such as title, author and version. The minimum set of nodes that an application must contain is summarized in the following table.

NodeDescription
rootRoot node. Each application node descends from it.
root\appApplication node. Folder containing business logic data.
root\sysFolder containing system nodes.
root\sys\_nameApplication name, read-only.
root\sys\aliasApplication alias, user can edit this field.
root\sys\_verApplication version, read-only (format major.minor).
root\sys\_authorApplication author, read-only.
root\sys\_logoApplication logo, read-only
root\sys\_idDevice identifier, read-only (silicon hardcoded for VoidX devices).
root\sys\_licenseDevice license identifier, read-only.
root\sys\keyDevice license key for software protection.

Left empty (string with length equal to 0) to disable the security layer.

Commands

Nodes described using VoidX data model can be discovered, read and written through the commands contained in a frame. The application sends a request frame to the device, which consists of a set of commands. As it can be seen in the code chunk reported below, each command is separated by a ‘\r\n’ newline tag. The last command is followed by the NULL byte which marks the end of the frame.

<command1> nodeA\nodeB:<JSON>\r\n
<command2> nodeA\nodeC\nodeD\r\n
<command3> nodeA\nodeC\nodeD:<JSON>\0

The structure of a command is quite simple. It is composed of three elements: the type (<command>), the path (the recipient of the command) and, if necessary, JSON encoded arguments (<JSON>) of the specific command preceded by the character ‘:’.

TypeDescriptionJSON
browseReads the value and the meta-information of a node and, recursively, those of its descendants.no
readReads the value of a node and, recursively, those of its descendants.no
writeWrites the value of a specific node.{“value”:##}

Once received the request, the device will execute each command and, if necessary, it will produce the relative response. The response follows the same principle of the request: each response related to a command is separated by ‘\r\n’ newline tag. Only the last response is followed by the NULL byte and does not have the newline separator.

Browse command

The browse command is a request to the device that shows all the properties of a node and, recursively, of all its descendants. It does not need any JSON argument. This command is commonly used to discover the nodes and hence the structure of an application automatically. The response generic structure is the following:

nodeA\nodeB:<JSON>\r\n
nodeA\nodeB\nodeC:<JSON>\r\n
nodeA\nodeB\nodeD:<JSON>\0

The response of a browse request is a series of node paths followed by their JSON complete description (desc, value, type, min, max,…). By browsing the root node, the application will receive from the device its complete parameter-tree structure.

Request:

browse root\0

Response:

root:{"desc":"","value":"","type":"item","item_type":"system"}\r\n
root\sys:{"desc":"System","value":"","type":"item","item_type":"system"}\r\n
root\sys\_name:{"desc":"Name","value":"Eq 7","type":"item","item_type":"system"}\r\n
root\sys\_author:{"desc":"Author","value":"VoidX","type":"item","item_type":"system"}\r\n
root\sys\_ver:{"desc":"Version","value":"1.0","type":"item","item_type":"system"}\r\n
…
…
\0

The browse command can also work with a limited set of nodes. In the following example it is possible to see the response of a browse request related to a node that has no children. The response consists of a message with only one line.

Request:

browse root\app\on_off\0

Response:

root\app\on_off:{"desc":"Enable","value":"OFF","type":"enum","options":["ON","OFF"]}\0

Read command

The structure of this command is similar to that of the previous one. The difference is that, in this case, the device does not respond with the complete description of the nodes, but it only transmits their values. The response generic structure is the same of the browse command. Only the JSON arguments will be different and will contain only the value key:

nodeA\nodeB:{"value":"12345"}\r\n
nodeA\nodeB\nodeC:{"value":"hello"}\r\n
nodeA\nodeB\nodeD:{"value":""}\0

The following example is a partial read request related to the tree structure represented in the data-model paragraph.

Request:

read root\app\0

Response:

root\app:{"value":""}\r\n
root\app\on_off:{"value":"OFF"}\r\n
root\app\band100:{"value":0.000000}\r\n
root\app\band200:{"value":0.000000}\r\n
root\app\band400:{"value":0.000000}\r\n
root\app\band800:{"value":0.000000}\r\n
root\app\band1600:{"value":0.000000}\r\n
root\app\band3200:{"value":0.000000}\r\n
root\app\band6400:{"value":0.000000}\r\n
root\app\gain:{"value":0.000000}\0

Write command

The write command is used to change a node value. It follows the same structure of the other commands. Once received this command, the device checks if the value satisfies the node’s constraints (e.g. minimum and maximum) and updates it. To confirm this operation, the response will contain the path of the updated node and its new value.

Request (single):

write nodeA\nodeB:{"value":"65.02"}\0

Response:

nodeA\nodeB:{"value":"65.02"}\0

A request frame can be also used to set more than one node just combining single write requests and separating them by the newline tag ‘\r\n’. The following is an example of a multiple write frame.

Request (multiple):

write nodeA\nodeB:{"value":"65.02"}\r\n
write nodeA:{"value":"15"}\r\n
write nodeC:{"value":"0.1"}\0

Response:

nodeA\nodeB:{"value":"65.02"}\r\n
nodeA:{"value":"15"}\r\n
nodeC:{"value":"0.1"}\0

Node types

VoidX protocol is designed as a data-structured communication model. Data exchange between the application and the device occurs across nodes, but they must know what kind of data must be handled. Hence, from the device side, the data-model definition needs a type for each node. In order to keep the protocol as simple as possible, only a few type of nodes are defined. Node types are summarized (with the relative set of parameters) in the following table.

Node typeDescriptionParameters
NodeItemGeneric data, variable length string– Name (const string)

– Description (const string)

– Type (const string)

– Value (string)

NodeFloatNumeric data, float (IEEE754)– Name (const string)

– Description (const string)

– Min (const float)

– Max (const float)

– Default (const float)

– Value (float)

– Unit (const string)

– Shape (const 0-1 float)

– Inv (const 0-1)

– Decimals (const int)

NodeEnumFixed number of choices– Name (const string)

– Description (const string)

– Value (string that belong to options)

– Options (array of const string)

Note that only one parameter for node type is not constant. The bold parameter in the table is the effective node data unit (value) that may change over time and that will be stored in the device memory. Parameters are sent using the JSON format. An example of NodeItem parameter encoding is the following:

root\sys:{"desc":"System","value":"","type":"item","item_type":"system"}\r\n

As it can be seen from this example, each parameter consists of a key and a value. This is fully compliant with the JSON standard and can be handled with almost every coding language library.