Communication
This module provides the SerialCommunication and MQTTCommunication classes that enable communication between the PC, Arduino / Teensy microcontrollers running Ataraxis software and the other infrastructure with MQTT bindings.
SerialCommunication supports the PC-MicroController communication over USB / UART interface, while MQTTCommunication supports the communication over the MQTT protocol (virtual / real TCP sockets). MQTTCommunication can be used to transmit data locally (within the same PC) or remotely via a network connection.
Additionally, this module exposes message and helper structures used to serialize and deserialize the transmitted data.
- class ataraxis_communication_interface.communication.ControllerIdentification(transport_layer)
Bases:
object
Identifies the connected microcontroller by communicating its unique byte id-code.
For the ID codes to be unique, they have to be manually assigned to the Kernel class of each concurrently used microcontroller.
This class initializes to nonsensical defaults and expects the SerialCommunication class that manages its lifetime to call update_message_data() method when necessary to parse valid incoming message data.
- Parameters:
transport_layer (
TransportLayer
) – The reference to the TransportLayer class that is initialized and managed by the SerialCommunication class. This reference is used to read and parse the message data.
- protocol_code
Stores the protocol code used by this type of messages.
- message
Stores the serialized message payload.
- controller_id
The unique ID of the microcontroller. This ID is hardcoded in the microcontroller firmware and helps track which AXMC firmware is running on the given controller.
- __repr__()
Returns a string representation of the ControllerIdentification object.
- Return type:
str
- update_message_data()
Reads and parses the data stored in the reception buffer of the TransportLayer class, overwriting class attributes.
This method should be called by the SerialCommunication class whenever KernelData message is received and needs to be parsed (as indicated by the incoming message protocol). This method will then access the reception buffer and attempt to parse the data.
- Return type:
None
- class ataraxis_communication_interface.communication.DequeueModuleCommand(module_type, module_id, return_code=np.uint8(0))
Bases:
object
Instructs the addressed Module to clear (empty) its command queue.
Note, clearing the command queue does not terminate already executing commands, but it prevents recurrent commands from running again.
- __post_init__()
Packs the data into the numpy array to optimize future transmission speed.
- Return type:
None
- __repr__()
Returns a string representation of the RepeatedModuleCommand object.
- Return type:
str
-
module_id:
uint8
The ID of the specific module within the broader module-family.
-
module_type:
uint8
The type (family) code of the module to which the command is addressed.
-
packed_data:
ndarray
[Any
,dtype
[uint8
]] |None
= None Stores serialized message data.
-
protocol_code:
uint8
= np.uint8(3) Stores the protocol code used by this type of messages.
-
return_code:
uint8
= np.uint8(0) When this attribute is set to a value other than 0, the microcontroller will send this code back to the PC upon successfully receiving and decoding the command.
- class ataraxis_communication_interface.communication.KernelCommand(command, return_code=np.uint8(0))
Bases:
object
Instructs the Kernel to run the specified command exactly once.
Currently, the Kernel only supports blocking one-off commands.
- __post_init__()
Packs the data into the numpy array to optimize future transmission speed.
- Return type:
None
- __repr__()
Returns a string representation of the KernelCommand object.
- Return type:
str
-
command:
uint8
The code of the command to execute. Valid command codes are in the range between 1 and 255.
-
packed_data:
ndarray
[Any
,dtype
[uint8
]] |None
= None Stores serialized message data.
-
protocol_code:
uint8
= np.uint8(4) Stores the protocol code used by this type of messages.
-
return_code:
uint8
= np.uint8(0) When this attribute is set to a value other than 0, the microcontroller will send this code back to the PC upon successfully receiving and decoding the command.
- class ataraxis_communication_interface.communication.KernelData(transport_layer)
Bases:
object
Communicates the event state-code of the Kernel and includes an additional data object.
This class initializes to nonsensical defaults and expects the SerialCommunication class that manages its lifetime to call update_message_data() method when necessary to parse valid incoming message data.
- Parameters:
transport_layer (
TransportLayer
) – The reference to the TransportLayer class that is initialized and managed by the SerialCommunication class. This reference is used to read and parse the message data.
- protocol_code
Stores the protocol code used by this type of messages.
- message
Stores the serialized message payload.
- command
The code of the command the Kernel was executing when it sent the message.
- event
The code of the event that prompted sending the message.
- data_object
The data object decoded from the received message. Note, data messages only support the objects whose prototypes are defined in the SerialPrototypes enumeration.
- _transport_layer
Stores the reference to the TransportLayer class.
- __repr__()
Returns a string representation of the KernelData object.
- Return type:
str
- update_message_data()
Reads and parses the data stored in the reception buffer of the TransportLayer class, overwriting class attributes.
This method should be called by the SerialCommunication class whenever KernelData message is received and needs to be parsed (as indicated by the incoming message protocol). This method will then access the reception buffer and attempt to parse the data.
- Raises:
ValueError – If the prototype code transmitted with the message is not valid.
- Return type:
None
- class ataraxis_communication_interface.communication.KernelParameters(action_lock, ttl_lock, return_code=np.uint8(0))
Bases:
object
Instructs the Kernel to update the microcontroller-wide parameters with the values included in the message.
These parameters are shared by the Kernel and all custom Modules, and the exact parameter layout is hardcoded. This is in contrast to Module parameters, that differ between module types.
- __post_init__()
Packs the data into the numpy array to optimize future transmission speed.
- Return type:
None
- __repr__()
Returns a string representation of the KernelParameters object.
- Return type:
str
-
action_lock:
bool
Determines whether the controller allows non-ttl modules to change output pin states. When True, all hardware-connected pins are blocked from changing states. This has no effect on sensor and TTL pins.
-
packed_data:
ndarray
[Any
,dtype
[uint8
]] |None
= None Stores serialized message data.
-
parameters_size:
ndarray
[Any
,dtype
[uint8
]] |None
= None Stores the total size of serialized parameters in bytes.
-
protocol_code:
uint8
= np.uint8(6) Stores the protocol code used by this type of messages.
-
return_code:
uint8
= np.uint8(0) When this attribute is set to a value other than 0, the microcontroller will send this code back to the PC upon successfully receiving and decoding the command.
-
ttl_lock:
bool
Same as action_lock, but specifically controls output TTL (Transistor-to-Transistor Logic) pin activity. This has no effect on sensor and non-ttl hardware-connected pins.
- class ataraxis_communication_interface.communication.KernelState(transport_layer)
Bases:
object
Communicates the event state-code of the Kernel.
This class initializes to nonsensical defaults and expects the SerialCommunication class that manages its lifetime to call update_message_data() method when necessary to parse valid incoming message data.
- Parameters:
transport_layer (
TransportLayer
) – The reference to the TransportLayer class that is initialized and managed by the SerialCommunication class. This reference is used to read and parse the message data.
- protocol_code
Stores the protocol code used by this type of messages.
- message
Stores the serialized message payload.
- command
The code of the command the Kernel was executing when it sent the message.
- event
The code of the event that prompted sending the message.
- __repr__()
Returns a string representation of the KernelState object.
- Return type:
str
- update_message_data()
Reads and parses the data stored in the reception buffer of the TransportLayer class, overwriting class attributes.
This method should be called by the SerialCommunication class whenever KernelData message is received and needs to be parsed (as indicated by the incoming message protocol). This method will then access the reception buffer and attempt to parse the data.
- Return type:
None
- class ataraxis_communication_interface.communication.MQTTCommunication(ip='127.0.0.1', port=1883, monitored_topics=None)
Bases:
object
Wraps an MQTT client and exposes methods for bidirectionally communicating with other clients connected to the same MQTT broker.
This class leverages MQTT protocol on Python side and to establish bidirectional communication between the Python process running this class and other MQTT clients. Primarily, the class is intended to be used together with SerialCommunication class to transfer data between microcontrollers and the rest of the infrastructure used during runtime. Usually, both communication classes will be managed by the same process (core) that handles the necessary transformations to bridge MQTT and Serial communication protocols used by this library. This class is not designed to be instantiated directly and should instead be used through the MicroControllerInterface class available through this library!
Notes
MQTT protocol requires a broker that facilitates communication, which this class does NOT provide. Make sure your infrastructure includes a working MQTT broker before using this class. See https://mqtt.org/ for more details.
- Parameters:
ip (
str
, default:'127.0.0.1'
) – The IP address of the MQTT broker that facilitates the communication.port (
int
, default:1883
) – The socket port used by the MQTT broker that facilitates the communication.monitored_topics (
None
|tuple
[str
,...
], default:None
) – The list of MQTT topics which the class instance should subscribe to and monitor for incoming messages.
- _ip
Stores the IP address of the MQTT broker.
- _port
Stores the port used by the broker’s TCP socket.
- _connected
Tracks whether the class instance is currently connected to the MQTT broker.
- _monitored_topics
Stores the topics the class should monitor for incoming messages sent by other MQTT clients.
- _output_queue
A multithreading queue used to buffer incoming messages received from other MQTT clients before their data is requested via class methods.
- _client
Stores the initialized mqtt client instance that carries out the communication.
- __del__()
Ensures proper resource release when the class instance is garbage-collected.
- Return type:
None
- __repr__()
Returns a string representation of the MQTTCommunication object.
- Return type:
str
- _on_message(_client, _userdata, message)
The callback function used to receive data from MQTT broker.
When passed to the client, this function will be called each time a new message is received. This function will then record the message topic and payload and put them into the output_queue for the data to be consumed by external processes.
- Parameters:
_client (
Client
) – The MQTT client that received the message. Currently not used._userdata (
Any
) – Custom user-defined data. Currently not used.message (
MQTTMessage
) – The received MQTT message.
- Return type:
None
- connect()
Connects to the MQTT broker and subscribes to the requested input topics.
This method has to be called to initialize communication, both for incoming and outgoing messages. Any message sent to the MQTT broker from other clients before this method is called may not reach this class.
- Return type:
None
Notes
If this class instance subscribes (listens) to any topics, it will start a perpetually active thread with a listener callback to monitor incoming traffic.
- Raises:
RuntimeError – If the MQTT broker cannot be connected to using the provided IP and Port.
- disconnect()
Disconnects the client from the MQTT broker.
- Return type:
None
- get_data()
Extracts and returns the first available message stored inside the instance buffer queue.
- Return type:
tuple
[str
,bytes
|bytearray
] |None
- Returns:
A two-element tuple. The first element is a string that communicates the MQTT topic of the received message. The second element is the payload of the message, which is a bytes or bytearray object. If no buffered objects are stored in the queue (queue is empty), returns None.
- Raises:
RuntimeError – If the instance is not connected to the MQTT broker.
- property has_data: bool
Returns True if the instance received messages from other MQTT clients and can output received data via the get_dataq() method.
- send_data(topic, payload=None)
Publishes the input payload to the specified MQTT topic.
This method should be used for sending data to MQTT via one of the input topics. This method does not verify the validity of the input topic or payload data.
- Parameters:
topic (
str
) – The MQTT topic to publish the data to.payload (
str
|bytes
|bytearray
|float
|None
, default:None
) – The data to be published. When set to None, an empty message will be sent, which is often used as a boolean trigger.
- Raises:
RuntimeError – If the instance is not connected to the MQTT broker.
- Return type:
None
- class ataraxis_communication_interface.communication.ModuleData(transport_layer)
Bases:
object
Communicates the event state-code of the sender Module and includes an additional data object.
This class initializes to nonsensical defaults and expects the SerialCommunication class that manages its lifetime to call update_message_data() method when necessary to parse valid incoming message data.
- Parameters:
transport_layer (
TransportLayer
) – The reference to the TransportLayer class that is initialized and managed by the SerialCommunication class. This reference is used to read and parse the message data.
- protocol_code
Stores the protocol code used by this type of messages.
- message
Stores the serialized message payload.
- module_type
The type (family) code of the module that sent the message.
- module_id
The ID of the specific module within the broader module-family.
- command
The code of the command the module was executing when it sent the message.
- event
The code of the event that prompted sending the message.
- data_object
The data object decoded from the received message. Note, data messages only support the objects whose prototypes are defined in the SerialPrototypes enumeration.
- _transport_layer
Stores the reference to the TransportLayer class.
- __repr__()
Returns a string representation of the ModuleData object.
- Return type:
str
- update_message_data()
Reads and parses the data stored in the reception buffer of the TransportLayer class, overwriting class attributes.
This method should be called by the SerialCommunication class whenever ModuleData message is received and needs to be parsed (as indicated by the incoming message protocol). This method will then access the reception buffer and attempt to parse the data.
- Raises:
ValueError – If the prototype code transmitted with the message is not valid.
- Return type:
None
- class ataraxis_communication_interface.communication.ModuleIdentification(transport_layer)
Bases:
object
Identifies a hardware module instance by communicating its combined type + id 16-bit code.
It is expected that each hardware module instance will have a unique combination of type (family) code and instance (ID) code. The user assigns both type and ID codes at their discretion when writing the main .cpp file for each microcontroller.
This class initializes to nonsensical defaults and expects the SerialCommunication class that manages its lifetime to call update_message_data() method when necessary to parse valid incoming message data.
- Parameters:
transport_layer (
TransportLayer
) – The reference to the TransportLayer class that is initialized and managed by the SerialCommunication class. This reference is used to read and parse the message data.
- protocol_code
Stores the protocol code used by this type of messages.
- message
Stores the serialized message payload.
- module_type_id
The unique uint16 code that results from combining the type and ID codes of the module instance.
- __repr__()
Returns a string representation of the ModuleIdentification object.
- Return type:
str
- update_message_data()
Reads and parses the data stored in the reception buffer of the TransportLayer class, overwriting class attributes.
This method should be called by the SerialCommunication class whenever KernelData message is received and needs to be parsed (as indicated by the incoming message protocol). This method will then access the reception buffer and attempt to parse the data.
- Return type:
None
- class ataraxis_communication_interface.communication.ModuleParameters(module_type, module_id, parameter_data, return_code=np.uint8(0))
Bases:
object
Instructs the addressed Module to overwrite its custom parameters object with the included object data.
- __post_init__()
Packs the data into the numpy array to optimize future transmission speed.
- Return type:
None
- __repr__()
Returns a string representation of the ModuleParameters object.
- Return type:
str
-
module_id:
uint8
The ID of the specific module within the broader module-family.
-
module_type:
uint8
The type (family) code of the module to which the parameters are addressed.
-
packed_data:
ndarray
[Any
,dtype
[uint8
]] |None
= None Stores serialized message data.
-
parameter_data:
tuple
[signedinteger
[Any
] |unsignedinteger
[Any
] |floating
[Any
] |bool
,...
] A tuple of parameter values to send. Each value will be serialized into bytes and sequentially packed into the data object included with the message. Each parameter value has to use a scalar numpy type.
-
parameters_size:
ndarray
[Any
,dtype
[uint8
]] |None
= None Stores the total size of serialized parameters in bytes.
-
protocol_code:
uint8
= np.uint8(5) Stores the protocol code used by this type of messages.
-
return_code:
uint8
= np.uint8(0) When this attribute is set to a value other than 0, the microcontroller will send this code back to the PC upon successfully receiving and decoding the command.
- class ataraxis_communication_interface.communication.ModuleState(transport_layer)
Bases:
object
Communicates the event state-code of the sender Module.
This class initializes to nonsensical defaults and expects the SerialCommunication class that manages its lifetime to call update_message_data() method when necessary to parse valid incoming message data.
- Parameters:
transport_layer (
TransportLayer
) – The reference to the TransportLayer class that is initialized and managed by the SerialCommunication class. This reference is used to read and parse the message data.
- protocol_code
Stores the protocol code used by this type of messages.
- message
Stores the serialized message payload.
- module_type
The type (family) code of the module that sent the message.
- module_id
The ID of the specific module within the broader module-family.
- command
The code of the command the module was executing when it sent the message.
- event
The code of the event that prompted sending the message.
- __repr__()
Returns a string representation of the ModuleState object.
- Return type:
str
- update_message_data()
Reads and parses the data stored in the reception buffer of the TransportLayer class, overwriting class attributes.
This method should be called by the SerialCommunication class whenever ModuleData message is received and needs to be parsed (as indicated by the incoming message protocol). This method will then access the reception buffer and attempt to parse the data.
- Return type:
None
- class ataraxis_communication_interface.communication.OneOffModuleCommand(module_type, module_id, command, return_code=np.uint8(0), noblock=np.True_)
Bases:
object
Instructs the addressed Module to run the specified command exactly once (non-recurrently).
- __post_init__()
Packs the data into the numpy array to optimize future transmission speed.
- Return type:
None
- __repr__()
Returns a string representation of the OneOffModuleCommand object.
- Return type:
str
-
command:
uint8
The code of the command to execute. Valid command codes are in the range between 1 and 255.
-
module_id:
uint8
The ID of the specific module within the broader module-family.
-
module_type:
uint8
The type (family) code of the module to which the command is addressed.
-
noblock:
bool
= np.True_ Determines whether the command runs in blocking or non-blocking mode. If set to False, the controller will block in-place for any sensor- or time-waiting loops during command execution. Otherwise, the controller will run other commands while waiting for the block to complete.
-
packed_data:
ndarray
[Any
,dtype
[uint8
]] |None
= None Stores serialized message data.
-
protocol_code:
uint8
= np.uint8(2) Stores the protocol code used by this type of messages.
-
return_code:
uint8
= np.uint8(0) When this attribute is set to a value other than 0, the microcontroller will send this code back to the PC upon successfully receiving and decoding the command.
- class ataraxis_communication_interface.communication.ReceptionCode(transport_layer)
Bases:
object
Returns the reception_code originally received from the PC to indicate that the message with that code was received and parsed.
This class initializes to nonsensical defaults and expects the SerialCommunication class that manages its lifetime to call update_message_data() method when necessary to parse valid incoming message data.
- Parameters:
transport_layer (
TransportLayer
) – The reference to the TransportLayer class that is initialized and managed by the SerialCommunication class. This reference is used to read and parse the message data.
- protocol_code
Stores the protocol code used by this type of messages.
- message
Stores the serialized message payload.
- reception_code
The reception code originally sent as part of the outgoing Command or Parameters messages.
- __repr__()
Returns a string representation of the ReceptionCode object.
- Return type:
str
- update_message_data()
Reads and parses the data stored in the reception buffer of the TransportLayer class, overwriting class attributes.
This method should be called by the SerialCommunication class whenever KernelData message is received and needs to be parsed (as indicated by the incoming message protocol). This method will then access the reception buffer and attempt to parse the data.
- Return type:
None
- class ataraxis_communication_interface.communication.RepeatedModuleCommand(module_type, module_id, command, return_code=np.uint8(0), noblock=np.True_, cycle_delay=np.uint32(0))
Bases:
object
Instructs the addressed Module to repeatedly (recurrently) run the specified command.
- __post_init__()
Packs the data into the numpy array to optimize future transmission speed.
- Return type:
None
- __repr__()
Returns a string representation of the RepeatedModuleCommand object.
- Return type:
str
-
command:
uint8
The code of the command to execute. Valid command codes are in the range between 1 and 255.
-
cycle_delay:
uint32
= np.uint32(0) The period of time, in microseconds, to delay before repeating (cycling) the command.
-
module_id:
uint8
The ID of the specific module within the broader module-family.
-
module_type:
uint8
The type (family) code of the module to which the command is addressed.
-
noblock:
bool
= np.True_ Determines whether the command runs in blocking or non-blocking mode. If set to False, the controller will block in-place for any sensor- or time-waiting loops during command execution. Otherwise, the controller will run other commands while waiting for the block to complete.
-
packed_data:
ndarray
[Any
,dtype
[uint8
]] |None
= None Stores serialized message data.
-
protocol_code:
uint8
= np.uint8(1) Stores the protocol code used by this type of messages.
-
return_code:
uint8
= np.uint8(0) When this attribute is set to a value other than 0, the microcontroller will send this code back to the PC upon successfully receiving and decoding the command.
- class ataraxis_communication_interface.communication.SerialCommunication(source_id, microcontroller_serial_buffer_size, usb_port, logger_queue, baudrate=115200, *, test_mode=False)
Bases:
object
Wraps a TransportLayer class instance and exposes methods that allow communicating with a microcontroller running ataraxis-micro-controller library using the USB or UART protocol.
This class is built on top of the TransportLayer, designed to provide the microcontroller communication interface (API) for other Ataraxis libraries. This class is not designed to be instantiated directly and should instead be used through the MicroControllerInterface class available through this library!
Notes
This class is explicitly designed to use the same parameters as the Communication class used by the microcontroller. Do not modify this class unless you know what you are doing.
Due to the use of many non-pickleable classes, this class cannot be piped to a remote process and has to be initialized by the remote process directly.
This class is designed to integrate with DataLogger class available from the ataraxis_data_structures library. The DataLogger is used to write all incoming and outgoing messages to disk as serialized message payloads.
- Parameters:
source_id (
uint8
) – The ID code to identify the source of the logged messages. This is used by the DataLogger to distinguish between log sources (classes that sent data to be logged) and, therefore, has to be unique for all Ataraxis classes that use DataLogger and are active at the same time.microcontroller_serial_buffer_size (
int
) – The size, in bytes, of the buffer used by the target microcontroller’s Serial buffer. Usually, this information is available from the microcontroller’s manufacturer (UART / USB controller specification).usb_port (
str
) – The name of the USB port to use for communication to, e.g.: ‘COM3’ or ‘/dev/ttyUSB0’. This has to be the port to which the target microcontroller is connected. Use the list_available_ports() function available from this library to get the list of discoverable serial port names.logger_queue (
Queue
) – The multiprocessing Queue object exposed by the DataLogger class (via ‘input_queue’ property). This queue is used to buffer and pipe data to be logged to the logger cores.baudrate (
int
, default:115200
) – The baudrate to use for the communication over the UART protocol. Should match the value used by the microcontrollers that only support UART protocol. This is ignored for microcontrollers that use the USB protocol.test_mode (
bool
, default:False
) – This parameter is only used during testing. When True, it initializes the underlying TransportLayer class in the test configuration. Make sure this is set to False during production runtime.
- _transport_layer
The TransportLayer instance that handles the communication.
- _module_data
Received ModuleData messages are unpacked into this structure.
- _kernel_data
Received KernelData messages are unpacked into this structure.
- _module_state
Received ModuleState messages are unpacked into this structure.
- _kernel_state
Received KernelState messages are unpacked into this structure.
- _controller_identification
Received ControllerIdentification messages are unpacked into this structure.
- _module_identification
Received ModuleIdentification messages are unpacked into this structure.
- _reception_code
Received ReceptionCode messages are unpacked into this structure.
- _timestamp_timer
The PrecisionTimer instance used to stamp incoming and outgoing data as it is logged.
- _source_id
Stores the unique integer-code that identifies the class instance in data logs.
- _logger_queue
Stores the multiprocessing Queue that buffers and pipes the data to the Logger process(es).
- _usb_port
Stores the ID of the USB port used for communication.
- __repr__()
Returns a string representation of the SerialCommunication object.
- Return type:
str
- _log_data(timestamp, data)
Packages and sends the input data to teh DataLogger instance that writes it to disk.
- Parameters:
timestamp (
int
) – The value of the timestamp timer ‘elapsed’ property that communicates the number of elapsed microseconds relative to the ‘onset’ timestamp.data (
ndarray
[Any
,dtype
[uint8
]]) – The byte-serialized message payload that was sent or received.
- Return type:
None
- receive_message()
Receives the incoming message from the connected microcontroller and parses into the appropriate structure.
This method uses the protocol code, assumed to be stored in the first variable of each received payload, to determine how to parse the data. It then parses into a precreated message structure stored in class attributes.
Notes
To optimize overall runtime speed, this class creates message structures for all supported messages at initialization and overwrites the appropriate message attribute with the data extracted from each received message payload. This method than returns the reference to the overwritten class attribute. Therefore, it is advised to copy or finish working with the structure returned by this method before receiving another message. Otherwise, it is possible that the received message will be used to overwrite the data of the previously referenced structure, leading to the loss of unprocessed / unsaved data.
- Return type:
ModuleData
|ModuleState
|KernelData
|KernelState
|ControllerIdentification
|ModuleIdentification
|ReceptionCode
|None
- Returns:
A reference the parsed message structure instance stored in class attributes, or None, if no message was received. Note, None return does not indicate an error, but rather indicates that the microcontroller did not send any data.
- Raises:
ValueError – If the received message uses an invalid (unrecognized) message protocol code.
- send_message(message)
Serializes the input command or parameters message and sends it to the connected microcontroller.
This method relies on every valid outgoing message structure exposing a packed_data attribute, that contains the serialized payload data to be sent. Functionally, this method is a wrapper around the TransportLayer’s write_data() and send_data() methods.
- Parameters:
message (
RepeatedModuleCommand
|OneOffModuleCommand
|DequeueModuleCommand
|KernelCommand
|KernelParameters
|ModuleParameters
) – The command or parameters message to send to the microcontroller.- Return type:
None
- class ataraxis_communication_interface.communication.SerialProtocols(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)
Bases:
IntEnum
Stores the protocol codes used in data transmission between the PC and the microcontroller over the serial port.
Each sent and received message starts with the specific protocol code from this enumeration that instructs the receiver on how to process the rest of the data payload. The codes available through this class have to match the contents of the kProtocols Enumeration available from the ataraxis-micro-controller library (axmc_communication_assets namespace).
Notes
The values available through this enumeration should be read through their ‘as_uint8’ property to enforce the type expected by other classes from ths library.
- CONTROLLER_IDENTIFICATION = 12
Protocol used to identify the controller connected to a particular USB port. This service protocol is used by the controller that receives the ‘IdentifyController’ Kernel-addressed command and replies with it’s ID code. This protocol is automatically used by the MicrocontrollerInterface class during initialization and should not be used manually.
- DEQUEUE_MODULE_COMMAND = 3
Protocol for sending Module-addressed commands that remove all queued commands (including recurrent commands).
- KERNEL_COMMAND = 4
Protocol for sending Kernel-addressed commands. All Kernel commands are always non-repeatable (one-shot).
- KERNEL_DATA = 8
Protocol for receiving Kernel-sent data or error messages that include an arbitrary data object in addition to event state-code.
- KERNEL_PARAMETERS = 6
Protocol for sending Kernel-addressed parameters. The parameters transmitted via these messages will be used to overwrite the global parameters shared by the Kernel and all Modules of the microcontroller (global runtime parameters).
- KERNEL_STATE = 10
Protocol for receiving Kernel-sent data or error messages that do not include additional data objects.
- MODULE_DATA = 7
Protocol for receiving Module-sent data or error messages that include an arbitrary data object in addition to event state-code.
- MODULE_IDENTIFICATION = 13
Protocol used to identify all hardware module instances managed by the connected microcontroller. This service protocol is used by the controller that receives the ‘IdentifyModules’ Kernel-addressed command and sequentially transmits the combined type_id uint16 code for each managed module instance. This protocol is automatically used by the MicrocontrollerInterface class during initialization and should not be used manually.
- MODULE_PARAMETERS = 5
Protocol for sending Module-addressed parameters. This relies on transmitting arbitrary sized parameter objects likely to be unique for each module type (family).
- MODULE_STATE = 9
Protocol for receiving Module-sent data or error messages that do not include additional data objects.
- ONE_OFF_MODULE_COMMAND = 2
Protocol for sending Module-addressed commands that should not be repeated (executed only once).
- RECEPTION_CODE = 11
Protocol used to ensure that the microcontroller has received a previously sent command or parameter message. Specifically, when an outgoing message includes a reception_code, this code is transmitted back to the PC using this service protocol to acknowledge message reception. Currently, this protocol is only intended for testing purposes, as at this time the Communication class does not explicitly ensure message delivery.
- REPEATED_MODULE_COMMAND = 1
Protocol for sending Module-addressed commands that should be repeated (executed recurrently).
- UNDEFINED = 0
Not a valid protocol code. This is used to initialize the Communication class of the microcontroller.
- as_uint8()
Convert the enum value to numpy.uint8 type.
- Returns:
The enum value as a numpy unsigned 8-bit integer.
- Return type:
np.uint8
- class ataraxis_communication_interface.communication.SerialPrototypes(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)
Bases:
IntEnum
Stores the prototype codes used in data transmission between the PC and the microcontroller over the serial port.
Prototype codes are used by Data messages (Kernel and Module) to inform the receiver about the structure (prototype) that can be used to deserialize the included data object. Transmitting these codes with the message ensures that the receiver has the necessary information to decode the data without doing any additional processing. In turn, this allows optimizing the reception procedure to efficiently decode the data objects.
Notes
While the use of 8-bit (byte) value limits the number of mapped prototypes to 255 (256 if 0 is made a valid value), this number should be enough to support many unique runtime configurations.
- EIGHT_BOOLS = 31
An array of 8 8-bit booleans
- EIGHT_FLOAT32S = 108
An array of 8 single-precision 32-bit floating-point numbers
- EIGHT_FLOAT64S = 144
An array of 8 double-precision 64-bit floating-point numbers
- EIGHT_INT16S = 73
An array of 8 signed 16-bit integers
- EIGHT_INT32S = 107
An array of 8 signed 32-bit integers
- EIGHT_INT64S = 143
An array of 8 signed 64-bit integers
- EIGHT_INT8S = 33
An array of 8 signed 8-bit integers
- EIGHT_UINT16S = 72
An array of 8 unsigned 16-bit integers
- EIGHT_UINT32S = 106
An array of 8 unsigned 32-bit integers
- EIGHT_UINT64S = 142
An array of 8 unsigned 64-bit integers
- EIGHT_UINT8S = 32
An array of 8 unsigned 8-bit integers
- ELEVEN_BOOLS = 50
An array of 11 8-bit booleans
- ELEVEN_FLOAT32S = 123
An array of 11 single-precision 32-bit floating-point numbers
- ELEVEN_FLOAT64S = 153
An array of 11 double-precision 64-bit floating-point numbers
- ELEVEN_INT16S = 88
An array of 11 signed 16-bit integers
- ELEVEN_INT32S = 122
An array of 11 signed 32-bit integers
- ELEVEN_INT64S = 152
An array of 11 signed 64-bit integers
- ELEVEN_INT8S = 52
An array of 11 signed 8-bit integers
- ELEVEN_UINT16S = 87
An array of 11 unsigned 16-bit integers
- ELEVEN_UINT32S = 121
An array of 11 unsigned 32-bit integers
- ELEVEN_UINT64S = 151
An array of 11 unsigned 64-bit integers
- ELEVEN_UINT8S = 51
An array of 11 unsigned 8-bit integers
- FIFTEEN_BOOLS = 69
An array of 15 8-bit booleans
- FIFTEEN_FLOAT32S = 141
An array of 15 single-precision 32-bit floating-point numbers
- FIFTEEN_FLOAT64S = 165
An array of 15 double-precision 64-bit floating-point numbers
- FIFTEEN_INT16S = 105
An array of 15 signed 16-bit integers
- FIFTEEN_INT32S = 140
An array of 15 signed 32-bit integers
- FIFTEEN_INT64S = 164
An array of 15 signed 64-bit integers
- FIFTEEN_INT8S = 71
An array of 15 signed 8-bit integers
- FIFTEEN_UINT16S = 104
An array of 15 unsigned 16-bit integers
- FIFTEEN_UINT32S = 139
An array of 15 unsigned 32-bit integers
- FIFTEEN_UINT64S = 163
An array of 15 unsigned 64-bit integers
- FIFTEEN_UINT8S = 70
An array of 15 unsigned 8-bit integers
- FIVE_BOOLS = 20
An array of 5 8-bit booleans
- FIVE_FLOAT32S = 86
An array of 5 single-precision 32-bit floating-point numbers
- FIVE_FLOAT64S = 120
An array of 5 double-precision 64-bit floating-point numbers
- FIVE_INT16S = 49
An array of 5 signed 16-bit integers
- FIVE_INT32S = 85
An array of 5 signed 32-bit integers
- FIVE_INT64S = 119
An array of 5 signed 64-bit integers
- FIVE_INT8S = 22
An array of 5 signed 8-bit integers
- FIVE_UINT16S = 48
An array of 5 unsigned 16-bit integers
- FIVE_UINT32S = 84
An array of 5 unsigned 32-bit integers
- FIVE_UINT64S = 118
An array of 5 unsigned 64-bit integers
- FIVE_UINT8S = 21
An array of 5 unsigned 8-bit integers
- FOURTEEN_BOOLS = 64
An array of 14 8-bit booleans
- FOURTEEN_FLOAT32S = 135
An array of 14 single-precision 32-bit floating-point numbers
- FOURTEEN_FLOAT64S = 162
An array of 14 double-precision 64-bit floating-point numbers
- FOURTEEN_INT16S = 100
An array of 14 signed 16-bit integers
- FOURTEEN_INT32S = 134
An array of 14 signed 32-bit integers
- FOURTEEN_INT64S = 161
An array of 14 signed 64-bit integers
- FOURTEEN_INT8S = 66
An array of 14 signed 8-bit integers
- FOURTEEN_UINT16S = 99
An array of 14 unsigned 16-bit integers
- FOURTEEN_UINT32S = 133
An array of 14 unsigned 32-bit integers
- FOURTEEN_UINT64S = 160
An array of 14 unsigned 64-bit integers
- FOURTEEN_UINT8S = 65
An array of 14 unsigned 8-bit integers
- FOUR_BOOLS = 12
An array of 4 8-bit booleans
- FOUR_FLOAT32S = 76
An array of 4 single-precision 32-bit floating-point numbers
- FOUR_FLOAT64S = 111
An array of 4 double-precision 64-bit floating-point numbers
- FOUR_INT16S = 35
An array of 4 signed 16-bit integers
- FOUR_INT32S = 75
An array of 4 signed 32-bit integers
- FOUR_INT64S = 110
An array of 4 signed 64-bit integers
- FOUR_INT8S = 14
An array of 4 signed 8-bit integers
- FOUR_UINT16S = 34
An array of 4 unsigned 16-bit integers
- FOUR_UINT32S = 74
An array of 4 unsigned 32-bit integers
- FOUR_UINT64S = 109
An array of 4 unsigned 64-bit integers
- FOUR_UINT8S = 13
An array of 4 unsigned 8-bit integers
- NINE_BOOLS = 42
An array of 9 8-bit booleans
- NINE_FLOAT32S = 114
An array of 9 single-precision 32-bit floating-point numbers
- NINE_FLOAT64S = 147
An array of 9 double-precision 64-bit floating-point numbers
- NINE_INT16S = 81
An array of 9 signed 16-bit integers
- NINE_INT32S = 113
An array of 9 signed 32-bit integers
- NINE_INT64S = 146
An array of 9 signed 64-bit integers
- NINE_INT8S = 44
An array of 9 signed 8-bit integers
- NINE_UINT16S = 80
An array of 9 unsigned 16-bit integers
- NINE_UINT32S = 112
An array of 9 unsigned 32-bit integers
- NINE_UINT64S = 145
An array of 9 unsigned 64-bit integers
- NINE_UINT8S = 43
An array of 9 unsigned 8-bit integers
- ONE_BOOL = 1
1 8-bit boolean
- ONE_FLOAT32 = 19
1 single-precision 32-bit floating-point number
- ONE_FLOAT64 = 41
1 double-precision 64-bit floating-point number
- ONE_INT16 = 8
1 signed 16-bit integer
- ONE_INT32 = 18
1 signed 32-bit integer
- ONE_INT64 = 40
1 signed 64-bit integer
- ONE_INT8 = 3
1 signed 8-bit integer
- ONE_UINT16 = 7
1 unsigned 16-bit integer
- ONE_UINT32 = 17
1 unsigned 32-bit integer
- ONE_UINT64 = 39
1 unsigned 64-bit integer
- ONE_UINT8 = 2
1 unsigned 8-bit integer
- SEVEN_BOOLS = 28
An array of 7 8-bit booleans
- SEVEN_FLOAT32S = 103
An array of 7 single-precision 32-bit floating-point numbers
- SEVEN_FLOAT64S = 138
An array of 7 double-precision 64-bit floating-point numbers
- SEVEN_INT16S = 68
An array of 7 signed 16-bit integers
- SEVEN_INT32S = 102
An array of 7 signed 32-bit integers
- SEVEN_INT64S = 137
An array of 7 signed 64-bit integers
- SEVEN_INT8S = 30
An array of 7 signed 8-bit integers
- SEVEN_UINT16S = 67
An array of 7 unsigned 16-bit integers
- SEVEN_UINT32S = 101
An array of 7 unsigned 32-bit integers
- SEVEN_UINT64S = 136
An array of 7 unsigned 64-bit integers
- SEVEN_UINT8S = 29
An array of 7 unsigned 8-bit integers
- SIX_BOOLS = 23
An array of 6 8-bit booleans
- SIX_FLOAT32S = 93
An array of 6 single-precision 32-bit floating-point numbers
- SIX_FLOAT64S = 129
An array of 6 double-precision 64-bit floating-point numbers
- SIX_INT16S = 57
An array of 6 signed 16-bit integers
- SIX_INT32S = 92
An array of 6 signed 32-bit integers
- SIX_INT64S = 128
An array of 6 signed 64-bit integers
- SIX_INT8S = 25
An array of 6 signed 8-bit integers
- SIX_UINT16S = 56
An array of 6 unsigned 16-bit integers
- SIX_UINT32S = 91
An array of 6 unsigned 32-bit integers
- SIX_UINT64S = 127
An array of 6 unsigned 64-bit integers
- SIX_UINT8S = 24
An array of 6 unsigned 8-bit integers
- TEN_BOOLS = 45
An array of 10 8-bit booleans
- TEN_FLOAT32S = 117
An array of 10 single-precision 32-bit floating-point numbers
- TEN_FLOAT64S = 150
An array of 10 double-precision 64-bit floating-point numbers
- TEN_INT16S = 83
An array of 10 signed 16-bit integers
- TEN_INT32S = 116
An array of 10 signed 32-bit integers
- TEN_INT64S = 149
An array of 10 signed 64-bit integers
- TEN_INT8S = 47
An array of 10 signed 8-bit integers
- TEN_UINT16S = 82
An array of 10 unsigned 16-bit integers
- TEN_UINT32S = 115
An array of 10 unsigned 32-bit integers
- TEN_UINT64S = 148
An array of 10 unsigned 64-bit integers
- TEN_UINT8S = 46
An array of 10 unsigned 8-bit integers
- THIRTEEN_BOOLS = 61
An array of 13 8-bit booleans
- THIRTEEN_FLOAT32S = 132
An array of 13 single-precision 32-bit floating-point numbers
- THIRTEEN_FLOAT64S = 159
An array of 13 double-precision 64-bit floating-point numbers
- THIRTEEN_INT16S = 98
An array of 13 signed 16-bit integers
- THIRTEEN_INT32S = 131
An array of 13 signed 32-bit integers
- THIRTEEN_INT64S = 158
An array of 13 signed 64-bit integers
- THIRTEEN_INT8S = 63
An array of 13 signed 8-bit integers
- THIRTEEN_UINT16S = 97
An array of 13 unsigned 16-bit integers
- THIRTEEN_UINT32S = 130
An array of 13 unsigned 32-bit integers
- THIRTEEN_UINT64S = 157
An array of 13 unsigned 64-bit integers
- THIRTEEN_UINT8S = 62
An array of 13 unsigned 8-bit integers
- THREE_BOOLS = 9
An array of 3 8-bit booleans
- THREE_FLOAT32S = 60
An array of 3 single-precision 32-bit floating-point numbers
- THREE_FLOAT64S = 96
An array of 3 double-precision 64-bit floating-point numbers
- THREE_INT16S = 27
An array of 3 signed 16-bit integers
- THREE_INT32S = 59
An array of 3 signed 32-bit integers
- THREE_INT64S = 95
An array of 3 signed 64-bit integers
- THREE_INT8S = 11
An array of 3 signed 8-bit integers
- THREE_UINT16S = 26
An array of 3 unsigned 16-bit integers
- THREE_UINT32S = 58
An array of 3 unsigned 32-bit integers
- THREE_UINT64S = 94
An array of 3 unsigned 64-bit integers
- THREE_UINT8S = 10
An array of 3 unsigned 8-bit integers
- TWELVE_BOOLS = 53
An array of 12 8-bit booleans
- TWELVE_FLOAT32S = 126
An array of 12 single-precision 32-bit floating-point numbers
- TWELVE_FLOAT64S = 156
An array of 12 double-precision 64-bit floating-point numbers
- TWELVE_INT16S = 90
An array of 12 signed 16-bit integers
- TWELVE_INT32S = 125
An array of 12 signed 32-bit integers
- TWELVE_INT64S = 155
An array of 12 signed 64-bit integers
- TWELVE_INT8S = 55
An array of 12 signed 8-bit integers
- TWELVE_UINT16S = 89
An array of 12 unsigned 16-bit integers
- TWELVE_UINT32S = 124
An array of 12 unsigned 32-bit integers
- TWELVE_UINT64S = 154
An array of 12 unsigned 64-bit integers
- TWELVE_UINT8S = 54
An array of 12 unsigned 8-bit integers
- TWO_BOOLS = 4
An array of 2 8-bit booleans
- TWO_FLOAT32S = 38
An array of 2 single-precision 32-bit floating-point numbers
- TWO_FLOAT64S = 79
An array of 2 double-precision 64-bit floating-point numbers
- TWO_INT16S = 16
An array of 2 signed 16-bit integers
- TWO_INT32S = 37
An array of 2 signed 32-bit integers
- TWO_INT64S = 78
An array of 2 signed 64-bit integers
- TWO_INT8S = 6
An array of 2 signed 8-bit integers
- TWO_UINT16S = 15
An array of 2 unsigned 16-bit integers
- TWO_UINT32S = 36
An array of 2 unsigned 32-bit integers
- TWO_UINT64S = 77
An array of 2 unsigned 64-bit integers
- TWO_UINT8S = 5
An array of 2 unsigned 8-bit integers
- as_uint8()
Converts the enum value to numpy.uint8 type.
- Return type:
uint8
- Returns:
The enum value as a numpy unsigned 8-bit integer.
- get_prototype()
Returns the prototype object associated with this prototype enum value.
The prototype object returned by this method can be passed to the reading method of the TransportLayer class to deserialize the received data object. This should be automatically done by the SerialCommunication class that uses this enum class.
- Return type:
Union
[bool
,uint8
,int8
,uint16
,int16
,uint32
,int32
,uint64
,int64
,float32
,float64
,ndarray
[Any
,dtype
[bool
]],ndarray
[Any
,dtype
[uint8
]],ndarray
[Any
,dtype
[int8
]],ndarray
[Any
,dtype
[uint16
]],ndarray
[Any
,dtype
[int16
]],ndarray
[Any
,dtype
[uint32
]],ndarray
[Any
,dtype
[int32
]],ndarray
[Any
,dtype
[uint64
]],ndarray
[Any
,dtype
[int64
]],ndarray
[Any
,dtype
[float32
]],ndarray
[Any
,dtype
[float64
]]]- Returns:
The prototype object that is either a numpy scalar or shallow array type.
- classmethod get_prototype_for_code(code)
Returns the prototype object associated with the input prototype code.
The prototype object returned by this method can be passed to the reading method of the TransportLayer class to deserialize the received data object. This should be automatically done by the SerialCommunication class that uses this enum.
- Parameters:
code (
uint8
) – The prototype byte-code to retrieve the prototype for.- Return type:
Union
[bool
,uint8
,int8
,uint16
,int16
,uint32
,int32
,uint64
,int64
,float32
,float64
,ndarray
[Any
,dtype
[bool
]],ndarray
[Any
,dtype
[uint8
]],ndarray
[Any
,dtype
[int8
]],ndarray
[Any
,dtype
[uint16
]],ndarray
[Any
,dtype
[int16
]],ndarray
[Any
,dtype
[uint32
]],ndarray
[Any
,dtype
[int32
]],ndarray
[Any
,dtype
[uint64
]],ndarray
[Any
,dtype
[int64
]],ndarray
[Any
,dtype
[float32
]],ndarray
[Any
,dtype
[float64
]],None
]- Returns:
The prototype object that is either a numpy scalar or shallow array type. If the input code is not one of the supported codes, returns None to indicate a matching error.
MicroController Interface
This module provides the ModuleInterface and MicroControllerInterface classes. They aggregate the methods to enable Python PC clients to bidirectionally interface with custom hardware modules managed by an Arduino or Teensy microcontroller. Additionally, these classes contain the bindings to connect to the microcontrollers via the MQTT protocol, facilitating indirect communication with local and remote processes over MQTT broker.
Each microcontroller hardware module that manages physical hardware should be matched to a specialized interface derived from the base ModuleInterface class. Similarly, for each concurrently active microcontroller, there has to be a specific MicroControllerInterface instance that manages the ModuleInterface instances for the modules of that controller.
- class ataraxis_communication_interface.microcontroller_interface.MicroControllerInterface(controller_id, microcontroller_serial_buffer_size, microcontroller_usb_port, data_logger, module_interfaces, baudrate=115200, mqtt_broker_ip='127.0.0.1', mqtt_broker_port=1883)
Bases:
object
Interfaces with an Arduino or Teensy microcontroller running ataraxis-micro-controller library.
This class contains the logic that sets up a remote daemon process with SerialCommunication, MQTTCommunication, and DataLogger bindings to facilitate bidirectional communication and data logging between the microcontroller and concurrently active local (same PC) and remote (network) processes. Additionally, it exposes methods that send runtime parameters and commands to the Kernel and Module classes running on the connected microcontroller.
Notes
An instance of this class has to be instantiated for each microcontroller active at the same time. The communication will not be started until the start() method of the class instance is called.
This class uses SharedMemoryArray to control the runtime of the remote process, which makes it impossible to have more than one instance of this class with the same controller_id at a time.
Initializing MicroControllerInterface also completes the configuration of all ModuleInterface instances passed to the class constructor. It is essential to initialize both the interfaces and the MicroControllerInterface to have access to the full range of functionality provided by each ModuleInterface class.
- Parameters:
controller_id (
uint8
) – The unique identifier code of the managed microcontroller. This information is hardcoded via the ataraxis-micro-controller (AXMC) library running on the microcontroller, and this class ensures that the code used by the connected microcontroller matches this argument when the connection is established. Critically, this code is also used as the source_id for the data sent from this class to the DataLogger. Therefore, it is important for this code to be unique across ALL concurrently active Ataraxis data producers, such as: microcontrollers and video systems. Valid codes are values between 1 and 255.microcontroller_serial_buffer_size (
int
) – The size, in bytes, of the microcontroller’s serial interface (UART or USB) buffer. This size is used to calculate the maximum size of transmitted and received message payloads. This information is usually available from the microcontroller’s vendor.microcontroller_usb_port (
str
) – The serial USB port to which the microcontroller is connected. This information is used to set up the bidirectional serial communication with the controller. You can use list_available_ports() function from ataraxis-transport-layer-pc library to discover addressable USB ports to pass to this argument. The function is also accessible through the CLI command: ‘axtl-ports’.data_logger (
DataLogger
) – An initialized DataLogger instance used to log the data produced by this Interface instance. The DataLogger itself is NOT managed by this instance and will need to be activated separately. This instance only extracts the necessary information to pipe the data to the logger.module_interfaces (
tuple
[ModuleInterface
,...
]) – A tuple of classes that inherit from the ModuleInterface class that interface with specific hardware module instances managed by the connected microcontroller.baudrate (
int
, default:115200
) – The baudrate at which the serial communication should be established. This argument is ignored for microcontrollers that use the USB communication protocol, such as most Teensy boards. The correct baudrate for microcontrollers using the UART communication protocol depends on the clock speed of the microcontroller’s CPU and the supported UART revision. Setting this to an unsupported value for microcontrollers that use UART will result in communication errors.mqtt_broker_ip (
str
, default:'127.0.0.1'
) – The ip address of the MQTT broker used for MQTT communication. Typically, this would be a ‘virtual’ ip-address of the locally running MQTT broker, but the class can carry out cross-machine communication if necessary. MQTT communication will only be initialized if any of the input modules requires this functionality.mqtt_broker_port (
int
, default:1883
) – The TCP port of the MQTT broker used for MQTT communication. This is used in conjunction with the mqtt_broker_ip argument to connect to the MQTT broker.
- Raises:
TypeError – If any of the input arguments are not of the expected type.
- _controller_id
Stores the id byte-code of the managed microcontroller.
- _usb_port
Stores the USB port to which the controller is connected.
- _baudrate
Stores the baudrate to use for serial communication with the controller.
- _microcontroller_serial_buffer_size
Stores the microcontroller’s serial buffer size, in bytes.
- _mqtt_ip
Stores the IP address of the MQTT broker used for MQTT communication.
- _mqtt_port
Stores the port number of the MQTT broker used for MQTT communication.
- _mp_manager
Stores the multiprocessing Manager used to initialize and manage input and output Queue objects.
- _input_queue
Stores the multiprocessing Queue used to pipe the data to be sent to the microcontroller to the remote communication process.
- _terminator_array
Stores the SharedMemoryArray instance used to control the runtime of the remote communication process.
- _communication_process
Stores the (remote) Process instance that runs the communication cycle.
- _watchdog_thread
A thread used to monitor the runtime status of the remote communication process.
- _reset_command
Stores the pre-packaged Kernel-addressed command that resets the microcontroller’s hardware and software.
- _disable_locks
Stores the pre-packaged Kernel parameters configuration that disables all pin locks. This allows writing to all microcontroller pins.
- _enable_locks
Stores the pre-packaged Kernel parameters configuration that enables all pin locks. This prevents every Module managed by the Kernel from writing to any of the microcontroller pins.
- _started
Tracks whether the communication process has been started. This is used to prevent calling the start() and stop() methods multiple times.
- _start_mqtt_client
Determines whether to connect to MQTT broker during the main runtime cycle.
- __del__()
Ensures that all class resources are properly released when the class instance is garbage-collected.
- Return type:
None
- __repr__()
Returns a string representation of the class instance.
- Return type:
str
- static _runtime_cycle(controller_id, module_interfaces, input_queue, logger_queue, terminator_array, usb_port, baudrate, microcontroller_buffer_size, mqtt_ip, mqtt_port, start_mqtt_client)
This method aggregates the communication runtime logic and is used as the target for the communication process.
This method is designed to run in a remote Process. It encapsulates the steps for sending and receiving the data from the connected microcontroller. Primarily, the method routes the data between the microcontroller, the multiprocessing queues (inpout and output) managed by the Interface instance, and the MQTT broker. Additionally, it manages data logging by interfacing with the DataLogger class via the logger_queue.
Notes
Each managed ModuleInterface may contain custom logic for processing and routing the data. This method calls the custom logic bindings for each interface on a need-based method.
- Parameters:
controller_id (
uint8
) – The byte-code identifier of the target microcontroller. This is used to ensure that the instance interfaces with the correct controller and to source-stamp logged data.module_interfaces (
tuple
[ModuleInterface
,...
]) – A tuple that stores ModuleInterface classes managed by this MicroControllerInterface instance.input_queue (
Queue
) – The multiprocessing queue used to issue commands to the microcontroller.logger_queue (
Queue
) – The queue exposed by the DataLogger class that is used to buffer and pipe received and outgoing messages to be logged (saved) to disk.terminator_array (
SharedMemoryArray
) – The shared memory array used to control the communication process runtime.usb_port (
str
) – The serial port to which the target microcontroller is connected.baudrate (
int
) – The communication baudrate to use. This option is ignored for controllers that use USB interface, but is essential for controllers that use the UART interface.microcontroller_buffer_size (
int
) – The size of the microcontroller’s serial buffer. This is used to determine the maximum size of the incoming and outgoing message payloads.mqtt_ip (
str
) – The IP-address of the MQTT broker to use for communication with other MQTT processes.mqtt_port (
int
) – The port number of the MQTT broker to use for communication with other MQTT processes.start_mqtt_client (
bool
) – Determines whether to start the MQTT client used by MQTTCommunication instance.
- Return type:
None
- _watchdog()
This method is used by the watchdog thread to ensure the communication process is alive during runtime.
This method will raise a RuntimeError if it detects that a process has prematurely shut down. It will verify process states every ~20 ms and will release the GIL between checking the states.
- Return type:
None
Notes
If the method detects that the communication process is not alive, it will carry out the necessary resource cleanup before raising the error and terminating the class runtime.
- lock_controller()
Configures connected MicroController parameters to prevent all modules from writing to any output pin.
- Return type:
None
- reset_controller()
Resets the connected MicroController to use default hardware and software parameters.
- Return type:
None
- send_message(message)
Sends the input message to the microcontroller managed by this interface instance.
This is the primary interface for communicating with the Microcontroller. It allows sending all valid outgoing message structures to the Microcontroller for further processing. This is the only interface explicitly designed to communicate both with hardware modules and the Kernel class that manages the runtime of the microcontroller.
- Return type:
None
Notes
During initialization, the MicroControllerInterface provides each managed ModuleInterface with the reference to the input_queue object. Each ModuleInterface can use its own _input_queue attribute to send the data to the communication process, eliminating the need for the data to go through this method. If you are developing a custom interface, you have the option for using either queue interface for submitting data to be sent to the microcontroller.
- Raises:
TypeError – If the input message is not a valid outgoing message structure.
- start()
Initializes the communication with the target microcontroller and the MQTT broker.
The MicroControllerInterface class will not be able to carry out any communications until this method is called. After this method finishes its runtime, a watchdog thread is used to monitor the status of the process until stop() method is called, notifying the user if the process terminates prematurely.
- Return type:
None
Notes
If send_message() was called before calling start(), all queued messages will be transmitted in one step. Multiple commands addressed to the same module sent in this fashion will likely interfere with each-other.
As part of this method runtime, the interface will verify the target microcontroller’s configuration to ensure compatibility.
- Raises:
RuntimeError – If the instance fails to initialize the communication runtime.
- stop()
Shuts down the communication process and frees all reserved resources.
- Return type:
None
- unlock_controller()
Configures connected MicroController parameters to allow all modules to write to any output pin.
- Return type:
None
- class ataraxis_communication_interface.microcontroller_interface.ModuleInterface(module_type, module_id, mqtt_communication, error_codes=None, data_codes=None, mqtt_command_topics=None)
Bases:
object
The base class from which all custom ModuleInterface classes should inherit.
Inheriting from this class grants all subclasses the static API that the MicroControllerInterface class uses to interface with specific module interfaces. It is essential that all abstract methods defined in this class are implemented for each custom module interface implementation that subclasses this class.
Notes
Similar to the ataraxis-micro-controller (AXMC) library, the interface class has to be implemented separately for each custom module. The (base) class exposes the static API used by the MicroControllerInterface class to integrate each custom interface implementation with the general communication runtime cycle. To make this integration possible, this class defines some abstract (pure virtual) methods that developers have to implement for their interfaces. Follow the implementation guidelines in the docstrings of each abstract method and check the examples for further guidelines on how to implement each abstract method.
When inheriting from this class, remember to call the parent’s init method in the child class init method by using ‘super().__init__()’! If this is not done, the MicroControllerInterface class will likely not be able to properly interact with your custom interface class!
All data received from or sent to the microcontroller is automatically logged as byte-serialized numpy arrays. If you do not need any additional processing steps, such as sending or receiving data over MQTT, do not enable any custom processing flags when initializing this superclass!
In addition to interfacing with the module, the class also contains methods to parse logged module data. It is expected that these modules will be used immediately after runtime to parse raw logged data and transform it into the desired format for further processing and analysis.
Some attributes of this class are assigned by the managing MicroControllerInterface class at its initialization. Therefore, to be fully functional, each ModuleInterface class has to be bound to an initialized MicroControllerInterface instance.
- Parameters:
module_type (
uint8
) – The id-code that describes the broad type (family) of custom hardware modules managed by this interface class. This value has to match the code used by the custom module implementation on the microcontroller. Valid byte-codes range from 1 to 255.module_id (
uint8
) – The code that identifies the specific custom hardware module instance managed by the interface class instance. This is used to identify unique modules in a broader module family, such as different rotary encoders if more than one is used at the same time. Valid byte-codes range from 1 to 255.mqtt_communication (
bool
) – Determines whether this interface needs to communicate with MQTT. If your implementation of the process_received_data() method requires sending data to MQTT via MQTTCommunication, set this flag to True when implementing the class. Similarly, if your interface is configured to receive commands from MQTT, set this flag to True.error_codes (
set
[uint8
] |None
, default:None
) – A set that stores the numpy uint8 (byte) codes used by the interface module to communicate runtime errors. This set will be used during runtime to identify and raise error messages in response to managed module sending error State and Data messages to the PC. Note, status codes 0 through 50 are reserved for internal library use and should NOT be used as part of this set or custom hardware module class design. If the class does not produce runtime errors, set to None.data_codes (
set
[uint8
] |None
, default:None
) – A set that stores the numpy uint8 (byte) codes used by the interface module to communicate states and data that needs additional processing. All incoming messages from the module are automatically logged to disk during communication runtime. Messages with event-codes from this set would also be passed to the process_received_data() method for additional processing. If the class does not require additional processing for any incoming data, set to None.mqtt_command_topics (
set
[str
] |None
, default:None
) – A set of MQTT topics used by other MQTT clients to send commands to the module accessible through this interface instance. If the interface does not receive commands from mqtt, set this to None. The MicroControllerInterface set will use the set to initialize the MQTTCommunication class instance to monitor the requested topics and will use the use parse_mqtt_command() method to convert MQTT messages to module-addressed command structures.
- _module_type
Stores the type (family) of the interfaced module.
- _module_id
Stores the specific module instance ID within the broader type (family).
- _type_id
Stores the type and id combined into a single uint16 value. This value should be unique for all possible type-id pairs and is used to ensure that each used module instance has a unique ID-type combination.
- _data_codes
Stores all event-codes that require additional processing.
- _mqtt_command_topics
Stores MQTT topics to monitor for incoming commands.
- _error_codes
Stores all expected error-codes as a set.
- _mqtt_communication
Determines whether this interface needs to communicate with MQTT.
- _log_directory
Stores the path to the directory where the MicroControllerInterface that manages this class logs all received and transmitted messages related to this interface. The value for this attribute is assigned automatically by the managing MicroControllerInterface class during its initialization.
- _microcontroller_id
Stores the unique ID byte-code of the microcontroller that controls the hardware module interfaced by this class instance. The value for this attribute is assigned automatically by the managing MicroControllerInterface class during its initialization.
- _input_queue
Stores the multiprocessing queue that enables sending the data to the microcontroller via the managing MicroControllerInterface class. Putting messages into this queue is equivalent to submitting them to the send_data() method exposed by the managing MicroControllerInterface class. The value for this attribute is assigned automatically by the managing MicroControllerInterface class during its initialization.
- Raises:
TypeError – If input arguments are not of the expected type.
- __repr__()
Returns the string representation of the ModuleInterface instance.
- Return type:
str
- property data_codes: set[uint8]
Returns the set of message event-codes that are processed during runtime, in addition to logging them to disk.
- property error_codes: set[uint8]
Returns the set of error event-codes used by the module instance.
- extract_logged_data()
Extracts the data sent by the hardware module instance running on the microcontroller from the .npz log file generated during ModuleInterface runtime.
This method reads the compressed ‘.npz’ archives generated by the MicroControllerInterface class that works with this ModuleInterface during runtime and extracts all custom event-codes and data objects transmitted by the interfaced module instance from the microcontroller.
Notes
The extracted data will NOT contain library-reserved events and messages. This includes all Kernel messages and module messages with event codes 0 through 50. The only exception to this rule is messages with event code 2, which report completion of commands. These messages are parsed in addition to custom messages sent by each hardware module.
This method should be used as a convenience abstraction for the inner workings of the DataLogger class. For each ModuleInterface, it will decode and return the logged runtime data sent to the PC by the specific hardware module instance controlled by the interface. You need to manually implement further data processing steps as necessary for your specific use case and module implementation.
- Return type:
dict
[Any
,list
[dict
[str
,uint64
|Any
]]]- Returns:
A dictionary that uses numpy uint8 event codes as keys and stores lists of dictionaries under each key. Each inner dictionary contains three elements. First, an uint64 timestamp, representing the number of microseconds since the UTC epoch onset. Second, the data object, transmitted with the message (or None, for state-only events). Third, the uint8 code of the command that the module was executing when it sent the message to the PC.
- Raises:
RuntimeError – If this method is called before the ModuleInterface is used to initialize a MicroControllerInterface class.
ValueError – If the input path is not valid or does not point to an existing .npz archive.
- abstract initialize_remote_assets()
Initializes custom interface assets to be used in the remote process.
This method is called at the beginning of the communication runtime by the managing MicroControllerInterface. Use this method to create and initialize any assets that cannot be pickled (to be transferred into the remote process).
- Return type:
None
Notes
This method is called early in the preparation phase of the communication runtime, before any communication is actually carried out. Use this method to initialize unpickable assets, such as PrecisionTimer instances or connect to shared resources, such as SharedMemory buffers.
All assets managed or created by this method should be stored in the ModuleInterface instance’s attributes.
- property module_id: uint8
Returns the code that identifies the specific Module instance managed by the Interface class instance.
- property module_type: uint8
Returns the id-code that describes the broad type (family) of Modules managed by this interface class.
- property mqtt_command_topics: set[str]
Returns the set of MQTT topics this instance monitors for incoming MQTT commands.
- property mqtt_communication: bool
Returns True if the class instance is configured to communicate with MQTT during runtime.
- abstract parse_mqtt_command(topic, payload)
Packages and returns a ModuleCommand message to send to the microcontroller, based on the input MQTT command message topic and payload.
This method is called by the MicroControllerInterface when other MQTT clients send command messages to one of the topics monitored by this ModuleInterface instance. This method resolves, packages, and returns the appropriate ModuleCommand message structure, based on the input message topic and payload.
Notes
This method is called only if ‘mqtt_command_topics’ class argument was used to set the monitored topics during class initialization. This method will never receive a message with a topic that is not inside the ‘mqtt_command_topics’ set.
Use this method to translate incoming MQTT messages into the appropriate command messages for the hardware module. While we currently do not explicitly support translating MQTT messages into parameter messages, this can be added in the future if enough interest is shown.
See the /examples folder included with the library for examples on how to implement this method.
- Parameters:
topic (
str
) – The MQTT topic to which the other MQTT client sent the module-addressed command.payload (
bytes
|bytearray
) – The payload of the message.
- Return type:
OneOffModuleCommand
|RepeatedModuleCommand
|DequeueModuleCommand
|None
- Returns:
A OneOffModuleCommand, RepeatedModuleCommand, or DequeueModuleCommand instance that stores the message to be sent to the microcontroller. None, if the class instance is not configured to receive commands from MQTT.
- abstract process_received_data(message)
Processes the incoming message and executes user-defined logic.
This method is called by the MicroControllerInterface when the ModuleInterface instance receives a message from the microcontroller that uses an event code provided at class initialization as ‘data_codes’ argument. This method should be used to implement custom processing logic for the incoming data.
Notes
Primarily, this method is intended to execute custom data transmission logic. For example, it can be used to send a message over MQTT (via a custom implementation or our MQTTCommunication class), put the data into a multithreading or multiprocessing queue, or use it to set a SharedMemory object. Use this method as a gateway to inject custom data handling into the communication runtime.
Keep the logic inside this method as minimal as possible. All data from the microcontroller goes through the same communication process, so it helps to minimize real time processing of the data, as it allows for better communication throughput. Treat this method like you would treat a microcontroller hardware interrupt function.
If your communication / processing assets cannot be pickled (to be transferred into the remote process used for communication), implement their initialization via the initialize_remote_assets() method.
See the /examples folder included with the library for examples on how to implement this method.
- Parameters:
message (
ModuleData
|ModuleState
) – The ModuleState or ModuleData object that stores the message received from the module instance running on the microcontroller.- Return type:
None
- reset_command_queue()
Instructs the microcontroller to clear all queued commands for the specific module instance managed by this ModuleInterface.
If the ModuleInterface has not been used to initialize the MicroControllerInterface, raises a RuntimeError.
- Return type:
None
- property type_id: uint16
Returns the unique 16-bit unsigned integer value that results from combining the type-code and the id-code of the instance.