{"id":24044446,"url":"https://github.com/sun-lab-nbb/ataraxis-communication-interface","last_synced_at":"2026-01-16T07:41:42.965Z","repository":{"id":270523237,"uuid":"905286576","full_name":"Sun-Lab-NBB/ataraxis-communication-interface","owner":"Sun-Lab-NBB","description":"A Python library that enables interfacing with custom hardware modules running on Arduino or Teensy microcontrollers through Python interface clients.","archived":false,"fork":false,"pushed_at":"2025-04-07T16:17:04.000Z","size":608,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-04-20T00:54:26.709Z","etag":null,"topics":["ataraxis","communication","interface","microcontroller","mqtt","serial","unity"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Sun-Lab-NBB.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-18T14:21:24.000Z","updated_at":"2025-04-07T16:15:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"a4477748-3dab-4bb7-9750-6194704ddafc","html_url":"https://github.com/Sun-Lab-NBB/ataraxis-communication-interface","commit_stats":null,"previous_names":["sun-lab-nbb/ataraxis-communication-interface"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sun-Lab-NBB%2Fataraxis-communication-interface","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sun-Lab-NBB%2Fataraxis-communication-interface/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sun-Lab-NBB%2Fataraxis-communication-interface/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sun-Lab-NBB%2Fataraxis-communication-interface/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Sun-Lab-NBB","download_url":"https://codeload.github.com/Sun-Lab-NBB/ataraxis-communication-interface/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249834786,"owners_count":21331988,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ataraxis","communication","interface","microcontroller","mqtt","serial","unity"],"created_at":"2025-01-08T23:31:09.007Z","updated_at":"2026-01-16T07:41:42.950Z","avatar_url":"https://github.com/Sun-Lab-NBB.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ataraxis-communication-interface\n\nA Python library that provides the centralized interface for exchanging commands and data between Arduino and Teensy\nmicrocontrollers and host-computers.\n\n![PyPI - Version](https://img.shields.io/pypi/v/ataraxis-communication-interface)\n![PyPI - Python Version](https://img.shields.io/pypi/pyversions/ataraxis-communication-interface)\n[![uv](https://tinyurl.com/uvbadge)](https://github.com/astral-sh/uv)\n[![Ruff](https://tinyurl.com/ruffbadge)](https://github.com/astral-sh/ruff)\n![type-checked: mypy](https://img.shields.io/badge/type--checked-mypy-blue?style=flat-square\u0026logo=python)\n![PyPI - License](https://img.shields.io/pypi/l/ataraxis-communication-interface)\n![PyPI - Status](https://img.shields.io/pypi/status/ataraxis-communication-interface)\n![PyPI - Wheel](https://img.shields.io/pypi/wheel/ataraxis-communication-interface)\n\n___\n\n## Detailed Description\n\nThe library allows interfacing with custom hardware modules controlled by Arduino or Teensy microcontrollers\nrunning the companion [microcontroller library](https://github.com/Sun-Lab-NBB/ataraxis-micro-controller). To do so,\nthe library defines a shared API that can be integrated into user-defined interfaces by subclassing the (base)\nModuleInterface class. It also provides the MicroControllerInterface class that manages the microcontroller-PC\ncommunication and the MQTTCommunication class that allows exchanging data between local and remote clients over the\nMQTT (TCP) protocol.\n\n___\n\n## Features\n\n- Supports Windows, Linux, and macOS.\n- Provides the framework for writing and deploying custom interfaces for the hardware module instances managed\n  by the companion [microcontroller library](https://github.com/Sun-Lab-NBB/ataraxis-micro-controller).\n- Abstracts communication and microcontroller runtime management via the centralized microcontroller interface class.\n- Leverages MQTT protocol to support exchanging data between multiple local and remote clients.\n- Uses JIT compilation and LRU caching to optimize the runtime efficiency of all library assets.\n- Contains many sanity checks performed at initialization time to minimize the potential for unexpected\n  behavior and data corruption.\n- Includes an MCP server for AI agent integration (compatible with Claude Desktop and other MCP clients).\n- GPL 3 License.\n\n___\n\n## Table of Contents\n\n- [Dependencies](#dependencies)\n- [Installation](#installation)\n- [Usage](#usage)\n- [CLI Commands](#cli-commands)\n- [MCP Server](#mcp-server-agentic-integration)\n- [API Documentation](#api-documentation)\n- [Developers](#developers)\n- [Versioning](#versioning)\n- [Authors](#authors)\n- [License](#license)\n- [Acknowledgements](#acknowledgments)\n\n___\n\n## Dependencies\n\n- **MQTT broker**, if the library is intended to be used for sending and receiving data over the MQTT protocol. The\n  library was tested with a locally running [mosquitto MQTT broker](https://mosquitto.org/) version **2.0.22**.\n\nFor users, all other library dependencies are installed automatically by all supported installation methods\n(see [Installation](#installation) section).\n\n***Note!*** Developers should see the [Developers](#developers) section for information on installing additional\ndevelopment dependencies.\n\n___\n\n## Installation\n\n### Source\n\nNote, installation from source is ***highly discouraged*** for anyone who is not an active project developer.\n\n1. Download this repository to the local machine using the preferred method, such as git-cloning. Use one of the\n   [stable releases](https://github.com/Sun-Lab-NBB/ataraxis-communication-interface/releases).\n2. If the downloaded distribution is stored as a compressed archive, unpack it using the appropriate decompression tool.\n3. ```cd``` to the root directory of the prepared project distribution.\n4. Run ```python -m pip install .``` to install the project. Alternatively, if using a distribution with precompiled\n   binaries, use ```python -m pip install WHEEL_PATH```, replacing 'WHEEL_PATH' with the path to the wheel file.\n\n### pip\nUse the following command to install the library using pip:\n```\npip install ataraxis-communication-interface\n```\n\n___\n\n## Usage\n\n### Quickstart\nThis section demonstrates how to use custom hardware module interfaces compatible with this library. See\n[this section](#implementing-custom-module-interfaces) for instructions on how to implement module interface classes.\nNote, the example below should be run together with the companion\n[microcontroller module](https://github.com/Sun-Lab-NBB/ataraxis-micro-controller#quickstart) example.\nSee the [example_runtime.py](./examples/example_runtime.py) for the .py implementation of this example.\n```python\nfrom pathlib import Path\nimport tempfile\n\nimport numpy as np\nfrom ataraxis_time import PrecisionTimer, TimerPrecisions\nfrom ataraxis_data_structures import DataLogger, assemble_log_archives\nfrom ataraxis_base_utilities import console, LogLevel\nfrom ataraxis_communication_interface import MicroControllerInterface, extract_logged_hardware_module_data\n\n# Imports the TestModuleInterface class from the companion example file (examples/example_interface.py).\n# Run this script from the 'examples' directory or adjust the import path accordingly.\nfrom examples.example_interface import TestModuleInterface\n\n# Since MicroControllerInterface uses multiple processes, it has to be called with the '__main__' guard\nif __name__ == \"__main__\":\n    # Enables the console module to communicate the example's runtime progress via the terminal.\n    console.enable()\n\n    # Specifies the directory where to save all incoming and outgoing messages processed by the MicroControllerInterface\n    # instance for each hardware module.\n    tempdir = tempfile.TemporaryDirectory()  # Creates a temporary directory for illustration purposes\n    output_directory = Path(tempdir.name)\n\n    # Instantiates the DataLogger, which is used to save all incoming and outgoing MicroControllerInterface messages\n    # to disk. See https://github.com/Sun-Lab-NBB/ataraxis-data-structures for more details on DataLogger class.\n    data_logger = DataLogger(output_directory=output_directory, instance_name=\"AMC\")\n    data_logger.start()  # The DataLogger has to be started before it can save any log entries.\n\n    # Defines two interface instances, one for each TestModule used at the same time. Note that each instance uses\n    # different module_id codes, but the same type (family) id code.\n    interface_1 = TestModuleInterface(module_type=np.uint8(1), module_id=np.uint8(1))\n    interface_2 = TestModuleInterface(module_type=np.uint8(1), module_id=np.uint8(2))\n    interfaces = (interface_1, interface_2)\n\n    # Instantiates the MicroControllerInterface. This class functions similar to the Kernel class from the\n    # ataraxis-micro-controller library and abstracts most inner-workings of the library. Note; example expects a\n    # Teensy 4.1 microcontroller, and the parameters defined below may not be optimal for all supported\n    # microcontrollers!\n    mc_interface = MicroControllerInterface(\n        controller_id=np.uint8(222),\n        buffer_size=8192,\n        port=\"/dev/ttyACM1\",\n        data_logger=data_logger,\n        module_interfaces=interfaces,\n        baudrate=115200,\n        keepalive_interval=5000,\n    )\n    console.echo(\"Initializing the communication process...\")\n\n    # Starts the serial communication with the microcontroller by initializing a separate process that handles the\n    # communication. This method may take up to 15 seconds to execute, as it verifies that the microcontroller is\n    # configured correctly, given the MicroControllerInterface configuration.\n    mc_interface.start()\n\n    console.echo(\"Communication process: Initialized.\", level=LogLevel.SUCCESS)\n    console.echo(\"Updating hardware module runtime parameters...\")\n\n    # Due to the current SharedMemoryArray implementation, the SHM instances require additional setup after the\n    # communication process is started.\n    interface_1.start_shared_memory_array()\n    interface_2.start_shared_memory_array()\n\n    # Generates and sends new runtime parameters to both hardware module instances running on the microcontroller.\n    # On and Off durations are in microseconds.\n    interface_1.set_parameters(\n        on_duration=np.uint32(1000000), off_duration=np.uint32(1000000), echo_value=np.uint16(121)\n    )\n    interface_2.set_parameters(\n        on_duration=np.uint32(5000000), off_duration=np.uint32(5000000), echo_value=np.uint16(333)\n    )\n\n    console.echo(\"Hardware module runtime parameters: Updated.\", level=LogLevel.SUCCESS)\n\n    console.echo(\"Sending the 'echo' command to the TestModule 1...\")\n\n    # Requests instance 1 to return its echo value. By default, the echo command only runs once.\n    interface_1.echo()\n\n    # Waits until the microcontroller responds to the echo command. The interface is configured to update shared\n    # memory array index 2 with the received echo value when it receives the response from the microcontroller.\n    while interface_1.shared_memory[2] == 0:\n        continue\n\n    # Retrieves and prints the microcontroller's response. The returned value should match the parameter set above: 121.\n    console.echo(message=f\"TestModule 1 echo value: {interface_1.shared_memory[2]}.\", level=LogLevel.SUCCESS)\n\n    # Demonstrates the use of non-blocking recurrent commands.\n    console.echo(\"Executing the example non-blocking runtime, standby for ~5 seconds...\")\n\n    # Instructs the first TestModule instance to start pulsing the managed pin (Pin 5 by default). With the parameters\n    # sent earlier, it keeps the pin ON for 1 second and keeps it off for ~ 2 seconds (1 from off_duration,\n    # 1 from waiting before repeating the command). The microcontroller repeats this command at regular intervals\n    # until it is given a new command or receives a 'dequeue' command (see below).\n    interface_1.pulse(repetition_delay=np.uint32(1000000), noblock=True)\n\n    # Instructs the second TestModule instance to start sending its echo value to the PC every 500 milliseconds.\n    interface_2.echo(repetition_delay=np.uint32(500000))\n\n    # Delays for 5 seconds, accumulating echo values from TestModule 2 and pin On / Off notifications from TestModule\n    # 1. Uses the PrecisionTimer instance to delay the main process for 5 seconds.\n    delay_timer = PrecisionTimer(precision=TimerPrecisions.SECOND)\n    delay_timer.delay(delay=5, block=False)\n\n    # Cancels both recurrent commands by issuing a dequeue command. Note, the dequeue command does not interrupt already\n    # running commands, it only prevents further command repetitions.\n    interface_1.reset_command_queue()\n    interface_2.reset_command_queue()\n\n    # The result seen here depends on the communication speed between the PC and the microcontroller and the precision\n    # of the microcontroller's clock. For Teensy 4.1, which was used to write this example, the pin is expected to\n    # pulse ~2 times and the echo value is expected to be transmitted ~10 times during the test period.\n    console.echo(message=\"Non-blocking runtime: Complete.\", level=LogLevel.SUCCESS)\n    console.echo(f\"TestModule 1 Pin pulses: {interface_1.shared_memory[0]}\")\n    console.echo(f\"TestModule 2 Echo values: {interface_2.shared_memory[1]}\")\n\n    # Resets the pulse and echo counters before executing the demonstration below.\n    interface_1.shared_memory[0] = 0\n    interface_2.shared_memory[1] = 0\n\n    # Repeats the example above, but now uses blocking commands instead of non-blocking.\n    console.echo(\"Executing the example blocking runtime, standby for ~5 seconds...\")\n    interface_1.pulse(repetition_delay=np.uint32(1000000), noblock=False)\n    interface_2.echo(repetition_delay=np.uint32(500000))\n    delay_timer.delay(delay=5, block=False)  # Reuses the same delay timer\n    interface_1.reset_command_queue()\n    interface_2.reset_command_queue()\n\n    # This time, since the pin pulsing performed by module 1 interferes with the echo command performed by module 2,\n    # both pulse and echo counters are expected to be ~5.\n    console.echo(message=\"Blocking runtime: Complete.\", level=LogLevel.SUCCESS)\n    console.echo(f\"TestModule 1 Pin pulses: {interface_1.shared_memory[0]}\")\n    console.echo(f\"TestModule 2 Echo values: {interface_2.shared_memory[1]}\")\n\n    # Stops the communication process and releases all resources used during runtime.\n    mc_interface.stop()\n    console.echo(\"Communication process: Stopped.\", level=LogLevel.SUCCESS)\n\n    # Stops the DataLogger and assembles all logged data into a single .npz archive file. This step is required to be\n    # able to extract the logged message data for further analysis.\n    data_logger.stop()\n    console.echo(\"Assembling the message log archive...\")\n    assemble_log_archives(log_directory=data_logger.output_directory, remove_sources=True, verbose=True)\n\n    # To process the data logged during runtime, it must be extracted from the archive created above. This can be\n    # done with the help of the `extract_logged_hardware_module_data` function:\n    console.echo(\"Extracting the logged message data...\")\n    log_data = extract_logged_hardware_module_data(\n        log_path=data_logger.output_directory.joinpath(f\"222_log.npz\"),\n        module_type_id=(\n            (int(interface_1.module_type), int(interface_1.module_id)),\n            (int(interface_2.module_type), int(interface_2.module_id)),\n        ),\n    )\n    # Uses pulse off and echo event codes to determine the total number of TestModule 1 pulses and TestModule 2 echo\n    # values encountered during runtime according to the processed log data.\n    module_1_pulses = len(log_data[0].event_data[np.uint8(53)])\n    module_2_echo_values = len(log_data[1].event_data[np.uint8(54)])\n    console.echo(\n        message=(\n            f\"According to the extracted data, during runtime the TestModule 1 emitted a total of {module_1_pulses} \"\n            f\"pulses and the TestModule 2 sent {module_2_echo_values} echo values.\"\n        ),\n        level=LogLevel.SUCCESS,\n    )\n```\n\n### User-Defined Variables\nThis library is designed to flexibly support many different use patterns. To do so, it intentionally avoids hardcoding\ncertain metadata variables that allow the PC interface to individuate and address the managed microcontroller and\nspecific hardware module instances. **Each end user has to manually define these values both for the microcontroller\nand the PC.**\n\nTwo of these variables, the `module_type` and the `module_id` are used by the (base) **ModuleInterface** class. The\nremaining `controller_id` variable is used by the **MicroControllerInterface** class. See the\n[companion library's](https://github.com/Sun-Lab-NBB/ataraxis-micro-controller#user-defined-variables) ReadMe for more\ndetails about each user-defined metadata variable. Typically, these variables are set in the microcontroller code and\nthe PC code is adjusted to match the microcontroller code’s state.\n\n### Keepalive\nA major runtime safety feature of this library is the support for keepalive messaging. To work as intended, **both the\nPC (MicroControllerInterface instance) and the microcontroller (Kernel instance) must be configured to use the same\nkeepalive interval.**\n\nWhen enabled, the MicroControllerInterface instance sends a 'keepalive' command at regular intervals, specified by the\n`keepalive_interval` initialization argument. If the microcontroller does not receive the command for\n**two consecutive interval windows**, it aborts the runtime by resetting the microcontroller’s hardware and software to\nthe default state and sends an error message to the PC. If the PC does not receive the microcontroller’s acknowledgement\nthat it has received the keepalive command within **one interval windows from sending the previous command**, it aborts\nthe communication runtime with an error.\n\nThe keepalive functionality is **disabled** (set to 0) by default, but it is recommended to enable it for most use\ncases. See the [API documentation for the MicroControllerInterface class](#api-documentation) for more details on\nconfiguring the keepalive messaging.\n\n***Note!*** The appropriate keepalive interval depends on the communication speed and the CPU frequency of the\nmicrocontroller. For a fast microcontroller (teensy4.1) that uses the USB communication interface, an appropriate\nkeepalive interval is typically measured in milliseconds (100 to 500). For a slower microcontroller (arduino mega) with\na UART communication interface using the baudrate of 115200, the appropriate keepalive interval is typically measured\nin seconds (2 to 5).\n\n### Communication\nDuring runtime, all communication with the microcontroller is routed via the MicroControllerInterface instance that\nimplements the centralized communication and control interface for each microcontroller. To optimize runtime\nperformance, the communication is managed by a daemonic process running in a separate CPU thread (core).\n\nWhen the data is sent to the microcontroller, it is first transferred to the communication process, which then transmits\nit to the microcontroller. When the data is received from the microcontroller, it is mostly handled by the communication\nprocess, unless the end user implements the logic for routing it to other runtime processes.\n\n### Data Logging\nThis library relies on the [DataLogger](https://github.com/Sun-Lab-NBB/ataraxis-data-structures#datalogger) class to\nsave all incoming and outgoing messages to disk during PC-microcontroller communication. Each message sent or received\nby the PC is serialized and saved as an uncompressed **.npy** file.\n\nThe same DataLogger instance as used by the MicroControllerInterface instances may be shared by multiple other Ataraxis\nassets that generate log entries, such as [VideoSystem](https://github.com/Sun-Lab-NBB/ataraxis-video-system) classes.\nTo support using the same logger instance for multiple concurrently active sources,\n**each source has to use a unique identifier value (controller id) when sending data to the logger instance**.\n\n**Note!** Currently, only the MicroControllerInterface supports logging the data to disk.\n\n#### Log Format\nEach message is logged as a one-dimensional numpy uint8 array, saved as an .npy file. Inside the array, the data is\norganized in the following order:\n1. The uint8 id of the data source (microcontroller). The ID occupies the first byte of each log entry.\n2. The uint64 timestamp that specifies the number of microseconds elapsed since the acquisition of the **onset**\n   timestamp (see below). The timestamp occupies **8** bytes following the ID byte. This value communicates when each\n   message was sent or received by the PC.\n3. The serialized message payload sent to the microcontroller or received from the microcontroller. The payload can\n   be deserialized using the appropriate message structure. The payload occupies all remaining bytes, following the\n   source ID and the timestamp.\n\n#### Onset timestamp:\nEach MicroControllerInterface generates an `onset` timestamp as part of its `start()` method runtime. This log entry\nuses a modified data order and stores the current UTC time, accurate to microseconds, as the total number of\nmicroseconds elapsed since the UTC epoch onset. All further log entries for the same source use the timestamp section\nof their payloads to communicate the number of microseconds elapsed since the onset timestamp acquisition.\n\nThe onset log entry uses the following data organization order:\n1. The uint8 id of the data source (microcontroller).\n2. The uint64 value **0** that occupies 8 bytes following the source id. A 'timestamp' value of 0 universally indicates\n   that the log entry stores the onset timestamp.\n3. The uint64 value that stores the number of microseconds elapsed since the UTC epoch onset. This value specifies the\n   current time when the onset timestamp was generated.\n\n#### Working with MicroControllerInterface Logs\n\nSee the [quickstart](#quickstart) example above for a demonstration on how to assemble and parse the message\nlog archives generated by the MicroControllerInterface instance at runtime.\n\n**Note!** Currently, the log parsing function only works with messages that use event-codes greater than 50 and only\nwith messages sent by custom hardware module instances. The only exception to this rule is the **Command Completion**\nevents (event code 2), which are also parsed for each hardware module.\n\nThe logged data is packaged into a hierarchical structure the segments messages by each custom hardware module instance,\npacking each in the **ExtractedModuleData** dataclass instances. Each instance further segments the data into 'events'\nby storing extracted data in a dictionary that uses event-codes as keys and tuples of **ExtractedMessageData** instances\nas values. Each **ExtractedMessageData** stores the data of a single message received from the respective hardware\nmodule during runtime.\n\n### Custom Module Interfaces\nFor this library, an interface is a class that contains the logic for sending the command and parameter data to the\nhardware module and receiving and processing the data sent by the module to the PC. The microcontroller and PC libraries\nensure that the data is efficiently moved between the module and the interface and saved (logged) to disk. The rest of\nthe module-interface interaction is up to the end user (module / interface developer).\n\n### Implementing Custom Module Interfaces\nAll module interfaces intended to be accessible through this library have to follow the implementation guidelines\ndescribed in the [example module interface implementation file](./examples/example_interface.py). Specifically,\n**all custom module interfaces have to subclass the ModuleInterface class from this library and implement all abstract\nmethods**.\n\n#### Abstract Methods\nThese methods provide the inherited API used by the centralized microcontroller interface to connect hardware module\ninterfaces to their hardware modules managed by the companion microcontroller. Specifically, the\nMicroControllerInterface calls these methods as part of the remote communication process’s runtime cycle to work with\nthe data sent by the custom hardware module.\n\n#### initialize_remote_assets\nThis method is called by the MicroControllerInterface once for each ModuleInterface at the beginning of the\ncommunication cycle. The method should be used to initialize or configure custom assets (queues, shared memory buffers,\ntimers, etc.) that need to be processed from the (remote) communication process.\n```python\ndef initialize_remote_assets(self) -\u003e None:\n    # Connects to the shared memory array from the remote process.\n    self._shared_memory.connect()\n```\n\n#### terminate_remote_assets\nThis method is the inverse of the initialize_remote_assets() method. It is called by the MicroControllerInterface for\neach ModuleInterface at the end of the communication cycle. This method should be used to clean up (terminate) any\nassets initialized at the beginning of the communication runtime to ensure all resources are released before the process\nis terminated.\n```python\ndef terminate_remote_assets(self) -\u003e None:\n    # The shared memory array must be manually disconnected from each process that uses it to prevent runtime\n    # errors.\n    self._shared_memory.disconnect()\n```\n\n#### process_received_data\nThis method allows processing incoming module messages as they are received by the PC. The MicroControllerInterface\ninstance calls this method for any ModuleState or ModuleData message received from the hardware module, if the\nevent code of the message matches one of the codes in the data_codes attribute of the module’s interface instance.\n\n**Note!** The MicroControllerInterface class ***automatically*** saves (logs) each received and sent message to disk.\nTherefore, this method should ***not*** be used to save the data for post-runtime processing. Instead, this method\nshould be used to process the data in real time or route it to other processes / machines for real time processing.\n\nSince all ModuleInterfaces used by the same MicroControllerInterface share the communication process,\n**process_received_data() should not use complex logic or processing**. Treat this method as a hardware interrupt\nfunction: its main goal is to handle the incoming data as quickly as possible and allow the communication loop to run\nfor other modules.\n\nThis example demonstrates the implementation of the processing method to send the data back to the main process:\n```python\nfrom ataraxis_communication_interface import ModuleData, ModuleState\n\ndef process_received_data(self, message: ModuleData | ModuleState) -\u003e None:\n    # Event codes 52 and 53 are used to communicate the current state of the output pin managed by the example\n    # module. State messages transmit these event-codes, so there is no additional data to parse other than\n    # event codes.\n    if message.event == 52 or message.event == 53:\n        # Code 52 indicates that the pin outputs a HIGH signal, code 53 indicates the pin outputs a LOW signal.\n        # If the pin state has changed from HIGH (52) to LOW (53), increments the pulse count stored in the shared\n        # memory array.\n        if message.event == 53 and self._previous_pin_state:\n            self._shared_memory[0] += 1\n\n        # Sets the previous pin state value to match the recorded pin state.\n        self._previous_pin_state = True if message.event == 52 else False\n\n    # The module uses code 54 messages to return its echo value to the PC.\n    elif isinstance(message, ModuleData) and message.event == 54:\n        # The echo value is transmitted by a Data message. In addition to the event code, Data messages include a\n        # data_object. Upon reception, the data object is automatically deserialized into the appropriate\n        # Python object, so it can be accessed directly.\n        self._shared_memory[2] = message.data_object  # Records the received data value to the shared memory.\n        self._shared_memory[1] += 1  # Increments the received echo value count.\n```\n\n#### Sending Data to the Microcontroller\nIn addition to abstract methods, each interface may need to send data to the microcontroller. Broadly, the outgoing\nmessages are divided into two categories: **commands** and **parameter updates**. Command messages instruct the module\nto perform a specified action. Parameter updates are used to overwrite the module’s runtime parameters to broadly adjust\nhow the module behaves while executing commands.\n\nEach interface should use the `send_parameters()` method inherited from the (base) ModuleInterface class to send\nparameter update messages to the managed module and the `send_command()` method to send command messages to the managed\nmodule. These utility method abstracts the necessary steps for packaging and transmitting the input data to the module.\n\n**Note!** These methods use LRU caching and JIT compilation to optimize their runtime speed and minimize the delay\nbetween submitting the message for transmission and it being sent to the microcontroller. Therefore, most command and\nparameter update functions / methods should be simple wrappers around these inherited methods. See the API documentation\nfor the ModuleInterface class for the details about these methods inherited by each child interface class.\n\n___\n\n## CLI Commands\n\nThis library provides several CLI commands for system diagnostics and MCP server management. All commands are available\nfrom any environment that has the library installed.\n\n### axci-id\nDiscovers connected microcontrollers by evaluating each available serial port for whether it is connected to a valid\nAtaraxis microcontroller and, if so, queries the unique identifier of that microcontroller. Internally calls the\n`print_microcontroller_ids()` function.\n\n```bash\naxci-id\n```\n\n### axci-mqtt\nChecks whether an MQTT broker is reachable at the specified host and port. Useful for verifying broker availability\nbefore running code that depends on MQTT communication. Internally calls the `check_mqtt_connectivity()` function.\n\n```bash\naxci-mqtt\n```\n\n### axci-mcp\nStarts the MCP server for AI agent integration. See the [MCP Server](#mcp-server-agentic-integration) section for\ndetails.\n\n```bash\naxci-mcp\n```\n\n___\n\n## MCP Server (Agentic Integration)\n\nThis library includes a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that enables AI agents\nto programmatically interact with microcontroller discovery and MQTT broker connectivity checking functionality.\n\n### Starting the Server\n\nStart the MCP server using the CLI:\n\n```bash\naxci-mcp\n```\n\n### Available Tools\n\n| Tool                    | Description                                                                      |\n|-------------------------|----------------------------------------------------------------------------------|\n| `list_microcontrollers` | Discovers serial ports connected to Ataraxis microcontrollers and returns IDs    |\n| `check_mqtt_broker`     | Checks whether an MQTT broker is reachable at the specified host and port        |\n\n### Claude Desktop Configuration\n\nFor integration with Claude Desktop, add the following to the Claude Desktop configuration file\n(`~/.config/claude/claude_desktop_config.json` on Linux,\n`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, or\n`%APPDATA%\\Claude\\claude_desktop_config.json` on Windows):\n\n```json\n{\n  \"mcpServers\": {\n    \"ataraxis-communication-interface\": {\n      \"command\": \"axci-mcp\"\n    }\n  }\n}\n```\n\n___\n\n## API Documentation\n\nSee the [API documentation](https://ataraxis-communication-interface-api.netlify.app/) for the\ndetailed description of the methods and classes exposed by components of this library.\n\n___\n\n## Developers\n\nThis section provides installation, dependency, and build-system instructions for project developers.\n\n### Installing the Project\n\n***Note!*** This installation method requires **mamba version 2.3.2 or above**. Currently, all Sun lab automation\npipelines require that mamba is installed through the [miniforge3](https://github.com/conda-forge/miniforge) installer.\n\n1. Download this repository to the local machine using the preferred method, such as git-cloning.\n2. If the downloaded distribution is stored as a compressed archive, unpack it using the appropriate decompression tool.\n3. ```cd``` to the root directory of the prepared project distribution.\n4. Install the core Sun lab development dependencies into the ***base*** mamba environment via the\n   ```mamba install tox uv tox-uv``` command.\n5. Use the ```tox -e create``` command to create the project-specific development environment followed by\n   ```tox -e install``` command to install the project into that environment as a library.\n\n### Additional Dependencies\n\nIn addition to installing the project and all user dependencies, install the following dependencies:\n\n1. [Python](https://www.python.org/downloads/) distributions, one for each version supported by the developed project.\n   Currently, this library supports the three latest stable versions. It is recommended to use a tool like\n   [pyenv](https://github.com/pyenv/pyenv) to install and manage the required versions.\n\n### Development Automation\n\nThis project comes with a fully configured set of automation pipelines implemented using\n[tox](https://tox.wiki/en/latest/user_guide.html). Check the [tox.ini file](tox.ini) for details about the\navailable pipelines and their implementation. Alternatively, call ```tox list``` from the root directory of the project\nto see the list of available tasks.\n\n**Note!** All pull requests for this project have to successfully complete the ```tox``` task before being merged.\nTo expedite the task’s runtime, use the ```tox --parallel``` command to run some tasks in-parallel.\n\n### Automation Troubleshooting\n\nMany packages used in 'tox' automation pipelines (uv, mypy, ruff) and 'tox' itself may experience runtime failures. In\nmost cases, this is related to their caching behavior. If an unintelligible error is encountered with\nany of the automation components, deleting the corresponding .cache (.tox, .ruff_cache, .mypy_cache, etc.) manually\nor via a CLI command typically solves the issue.\n\n___\n\n## Versioning\n\nThis project uses [semantic versioning](https://semver.org/). See the\n[tags on this repository](https://github.com/Sun-Lab-NBB/ataraxis-communication-interface/tags) for the available\nproject releases.\n\n___\n\n## Authors\n\n- Ivan Kondratyev ([Inkaros](https://github.com/Inkaros))\n- Jacob Groner ([Jgroner11](https://github.com/Jgroner11))\n\n___\n\n## License\n\nThis project is licensed under the GPL3 License: see the [LICENSE](LICENSE) file for details.\n\n___\n\n## Acknowledgments\n\n- All Sun lab [members](https://neuroai.github.io/sunlab/people) for providing the inspiration and comments during the\n  development of this library.\n- The creators of all other dependencies and projects listed in the [pyproject.toml](pyproject.toml) file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsun-lab-nbb%2Fataraxis-communication-interface","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsun-lab-nbb%2Fataraxis-communication-interface","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsun-lab-nbb%2Fataraxis-communication-interface/lists"}