Skip to content

Serial Schema

The configuration schema for SerialCodec is a crucial aspect of setting up and using the codec effectively. It helps define all the Command/Query interfaces that you can use for testing and development. A schema is defined to enforce the structure and types of the configuration options that the SerialCodec expects.

Overview

The configuration schema is defined in a YAML file and includes several key components, such as encoding settings, signal definitions, and message configurations. Each component has specific types and default values.

Command/Query Signals

Commands are instructions that indicate a desired change in the state or behavior of an something. These commands execute operations such as 'led set', and are not expected to return/capture data. Each command signal is an object containing a name and the command to perform the operation.

Queries are instructions to retrieve information from something. These commands execute operations such as 'led get', and are expected to make no modifications to something. They should be used solely for data retrieval. Each query signal is an object containing a name and the query to perform the operation.

CommandQueries are a combination of the two above. They encapsulate instructions that indicate a desired change in the state or behavior of an something, along with instructions to retrieve information from something. For example, you may want to encapsulate led set and led get into a single led object. Each command query signal is an object containing a name and both the command and query to perform the operations.

Note

Queries also contain an optional field for pattern matching. This is useful for when a query may return a large output stream, while you may only be interested in some unique part of it. Patterns use regex to extract the desired output!

Query Messages

Query Messages are very similar to signals in that they contain instructions to retrieve information from something. The main difference is that a single query may return multiple values of interest. These values of interest are represented as individual signals within the larger payload of the message query.

For example, a query such as temp info maybe retrieve a message that looks like:

> temp info
  Temperature Info
      Ambient: 25.5
      CPU: 47.3
      GPU: 50.1
>
In this example, we want all three data points: Ambient, CPU, GPU. We can define multiple signals under the message, along with the pattern required for extracting the value for each given signals. Each message is an object containing a name, signals, and the query to perform the operation.

Schema Components

Basic Settings

These settings define the fundamental behavior and properties of the SerialCodec.

Field Type Default Description
encoding string "utf-8" Character encoding for communication. Specifies how text data is encoded during transmission. Common values are "utf-8", "ascii", etc.
prompt_pattern string None A regular expression pattern that the codec uses to recognize the readiness of the device to accept commands. This is crucial for syncing communication. Example: "^>", which matches a line starting with '>'. Optional but recommended for reliable communication.
prompt_timeout_s integer 1 The maximum time in seconds the codec will wait for the prompt pattern to appear in the device's response. This timeout helps in handling cases where the device is unresponsive or slow.
cmd_termination string "\r\n" Characters that are appended to the end of every command sent to the device. It signifies the end of a command. Common values are "\r\n" (carriage return and newline) and "\n" (newline).
query_termination string "\r\n" Similar to cmd_termination, but for query commands. This tells the device that the end of the query has been reached.
Field Type Required Description
signals dict Yes Dictionary of signal names and their configurations.

Signal Configuration

Field Type Description
cmd string Command to be sent.
query string or dict Query command, or dictionary with command and pattern.
data_type string Type of data expected in response (e.g., "float", "int", "string").
timeout_s integer Timeout in seconds for the signal.

Messages

Messages are collections of signals sent via a single query.

Field Type Description
messages dict Dictionary of message names and configurations.

Message Configuration

Field Type Description
query string or dict Query command or dictionary containing command and pattern.
data_type string Type of data expected in response.
timeout_s integer Timeout in seconds for the message.
sub_signals dict Additional signals within the message, with patterns and data types.

Example Configuration

cli.yml

# NOTE: This is meant to serve as an example configuration for a serial codec.
# More information about the attributes and schema in this file can be found in the docs:
#   https://docs.zeloscloud.io/py/codecs/serial/serial-schema/
encoding: "utf-8"
prompt_pattern: '\x1b\[[0-9;]*muart:~\$ \x1b\[[0-9;]*m$' # Example zephyr-shell prompt pattern -> 'uart:~$'
prompt_timeout_s: 2
echo: True
cmd_termination: "\r\n"
query_termination: "\r\n"

signals:
  # This is an example CmdSignal. It contains only a command, no query
  device_restart:
    cmd: "restart now"

  # This is an example CmdSignal that takes a value. It uses format specifiers to determine
  # where the command you want to place the value. Additionally, you send multiple values -> "led {} {}"
  led_enable:
    cmd: "led enable {}"

  # This is an example QuerySignal. It contains only a query, no command
  wifi_status:
    query: "status get wifi"
    # This will convert the decoded response to a int. You can do this for any signal
    data_type: int

  # This is an example QuerySignal with pattern matching. It contains only a query and a regex pattern.
  device_uptime:
    query:
      cmd: "uptime get"
      pattern: (\\d+\\.\\d+\\.\\d+)"

  # This is an example CmdQuerySignal. It contains both a command and a query.
  wifi_psk:
    cmd: "settings set wifi/psk {}"
    query: "settings get wifi/psk"

  # This is an example CmdQuerySignal. It contains both a command and a query, with pattern matching.
  hardware_revision:
    cmd: "hwrev set {}"
    query:
      cmd: "hwrev get"
      pattern: "(?<=Current hardware revision: ).*"

# These define messages that can contain multiple points of interest
messages:
  # Message example: Contains multiple signals
  device_info:
    query: "info get all"
    signals:
      firmware_version:
        pattern: "Firmware: (\\d+\\.\\d+\\.\\d+)"
      battery_level:
        pattern: "Battery: (\\d+\\.\\d+)%"

        # This will convert the decoded response to a float. You can do this for each signal
        data_type: float

Format Strings

You may set multiple values for a command by adding multiple {} in to the cmd. When setting this data, you would send the data ordered as a tuple like such: codec.signal.set((1,2,3)).


Message: Device Information

  • Query Sent: "info get all\r\n"
  • Response: "Device Info: Firmware: 1.2.3, Battery: 80.0%"
  • Signal Data Parsed:
  • firmware_version: "1.2.3"
  • battery_level: 80.0

Signal Data Types

The SerialCodec schema supports the use of custom data types for parsing the data returned by signals.

The data_type field in a signal's configuration can reference any data type that can be resolved by Python's pydoc.locate. This function dynamically locates a Python object by its name and returns the object itself, allowing you to use built-in types (like int, float, str) as well as custom or third-party types.

Utilizing Built-In types

For example, if a device returns a float string in response to a query, you can use float to parse this response:

signals:
  device_temperature:
    query: "device temperature"
    data_type: float

If a device returns a JSON string in response to a query, you can use json.loads to parse this response and covert to a json dict object:

signals:
  device_status:
    query: "status get"
    data_type: json.loads

Utilizing Custom types

Example:

Suppose you have a custom data type defined in your project:

# In my_custom_types.py
class CustomDataType:
    def __init__(self, value):
        # Custom initialization logic
        self.value = value

    # Custom methods or properties

You can use CustomDataType as a data_type in your schema:

signals:
  custom_signal:
    query: "get custom data"
    data_type: "my_custom_types.CustomDataType"

In this example, when custom_signal receives data, the SerialCodec will use CustomDataType to parse and represent this data.