Skip to content

Multi-Protocol Communication

The make87 platform enables direct communication between applications running on different nodes through a protocol-agnostic messaging system. Applications can communicate using any protocol or middleware by declaring their communication patterns in the MAKE87.yml manifest.

Core Communication Concepts

make87 organizes all communication into six fundamental patterns that map to common messaging paradigms across different protocols:

Pattern Direction Use Case Protocol Examples
Publisher One-to-many Broadcasting data streams Zenoh pub, MQTT publish, DDS writer
Subscriber Many-to-one1 Receiving data streams Zenoh sub, MQTT subscribe, DDS reader
Requester Request-reply Making service calls, querying data Zenoh query, HTTP client, gRPC client
Provider Reply-request Responding to queries, offering services Zenoh queryable, HTTP endpoint, gRPC service
Server Persistent Long-running services (HTTP, streaming) HTTP server, RTSP server, WebSocket server
Client Connection Connecting to persistent services HTTP client, RTSP client, WebSocket client

Message Specifications

The spec field in each communication pattern is a string that serves as a soft contract to check compatibility between applications. This allows make87 to verify that two applications can communicate before deployment.

Message Spec Examples

make87 Message Pack (Protocol Buffers):

spec: "make87_messages.image.compressed.ImagePNG"
spec: "make87_messages.spatial.pose.Pose3D"

Custom Message Types:

spec: "my_company.robot.JointStates"
spec: "custom_protocol.sensor.IMUData"

API Specifications (for Server/Client):

spec: "frigate_api:15"           # API name and version
spec: "openapi:my_service:v2"    # OpenAPI spec reference
spec: "rtsp"                     # Protocol placeholder

The spec is flexible - anything is possible as long as both sides agree on it. For server/client APIs, it's recommended to reference an API specification like OpenAPI schemas or include the API name and version.

Message Encoding

The optional encoding field specifies how messages are serialized over the wire. This is particularly useful when the same message type can be encoded in multiple formats:

Protocol Buffer Messages:

publishers:
  - name: sensor_data
    spec: "make87_messages.sensor.Temperature"
    encoding: "proto"    # Binary Protocol Buffers (default)
  - name: sensor_json
    spec: "make87_messages.sensor.Temperature"
    encoding: "json"     # JSON representation of the same message

If not specified, the encoding is assumed to default to the protocol's standard format.

Protocol Flexibility

Unlike traditional platforms that lock you into a specific middleware, make87 supports any protocol by abstracting the communication patterns. You specify:

  1. Interface definition - What communication patterns your application uses
  2. Protocol selection - Which protocol to use (Zenoh, HTTP, MQTT, etc.)
  3. Message types - What data structures are exchanged
  4. Configuration - Protocol-specific settings

The platform handles connection establishment, service discovery, and routing while your application code remains protocol-agnostic.

Protocol Field as Soft Contract

The protocol field in each interface is a string that serves as a soft contract for matching applications that can communicate with each other. Like the spec field, this is completely flexible and up to the developer.

Standard Protocol Names:

interfaces:
  - name: sensor_data
    protocol: zenoh        # Standard Zenoh protocol
  - name: web_api
    protocol: http         # Standard HTTP protocol
  - name: iot_telemetry
    protocol: mqtt         # Standard MQTT protocol

Custom Protocol Names:

interfaces:
  - name: robot_control
    protocol: my-custom-protocol    # Your own protocol implementation
  - name: legacy_system
    protocol: company-internal-v2   # Internal protocol version
  - name: experimental
    protocol: research-protocol-alpha  # Experimental implementation

Protocol Matching: make87 uses the protocol field to determine which applications can communicate. Two applications can only exchange data if their interfaces use the same protocol string. This allows you to:

  • Use standard protocols like zenoh, http, mqtt, dds
  • Implement custom protocols with any name you choose
  • Version your protocols (e.g., my-protocol-v1, my-protocol-v2)
  • Isolate different communication channels by protocol name

The protocol string is purely for matching - make87 doesn't validate or enforce any specific protocol implementation. Your application code is responsible for implementing the actual protocol logic.

Supported Protocols

make87 works with any protocol through its abstraction layer. Common protocols include:

  • DDS / ROS2: Real-time pub/sub for robotics and industrial automation
  • MQTT: Lightweight messaging for IoT and telemetry
  • HTTP / REST: Standard web APIs and control interfaces
  • WebSocket (WS): Low-latency bidirectional streaming
  • Zenoh: High-performance pub/sub for edge computing
  • MCP (Model Context Protocol): For AI model communication
  • RTSP: Video streaming protocol
  • gRPC: High-performance RPC framework
  • Custom protocols: Use any transport stack compatible with your codebase

Publisher / Subscriber Pattern

Publisher/Subscriber is ideal for streaming data and event-driven communication where data flows from one or more sources to multiple consumers.

Publisher/Subscriber patterns typically work on topics plus IP/port combinations, where the topic name identifies the data stream and the underlying protocol handles the networking.

When to Use

  • Streaming sensor data (camera feeds, telemetry, logs)
  • Broadcasting events or notifications
  • Real-time data distribution
  • One-to-many communication

Example: Image Stream

A camera application publishes image data, while multiple AI applications subscribe to process different aspects:

Camera Publisher:

interfaces:
  - name: camera_feed
    protocol: zenoh
    publishers:
      - name: raw_images
        spec: "make87_messages.image.compressed.ImagePNG"
        config:
          type: object
          properties:
            congestion_control:
              type: string
              enum: [DROP, BLOCK]
              default: DROP
            priority:
              type: string
              enum: [REAL_TIME, INTERACTIVE_HIGH, DATA]
              default: REAL_TIME

AI Subscriber:

interfaces:
  - name: vision_processing
    protocol: zenoh
    subscribers:
      - name: image_input
        spec: "make87_messages.image.compressed.ImagePNG"
        config:
          type: object
          properties:
            handler:
              type: object
              properties:
                handler_type:
                  type: string
                  enum: [ FIFO, RING ]
                  default: RING
                capacity:
                  type: integer
                  minimum: 0
                  description: "Capacity of the handler. For FIFO, this is the maximum number of messages it can hold. For RING, this is the size of the ring buffer."
                  default: 10

Requester / Provider Pattern

Requester/Provider enables request-response communication where applications query services and receive responses.

Like Publisher/Subscriber, Requester/Provider patterns work on endpoints plus IP/port combinations, where the endpoint identifies the service interface.

When to Use

  • Service calls and RPC-style interactions
  • Database queries or data retrieval
  • Configuration requests
  • Synchronous operations requiring acknowledgment

Example: Object Detection Service

An AI application provides object detection as a service:

Detection Provider:

interfaces:
  - name: ai_services
    protocol: zenoh
    providers:
      - name: detect_objects
        request_spec: "make87_messages.image.compressed.ImagePNG"
        response_spec: "make87_messages.detection.box.Box2DAxisAligned"

Client Requester:

interfaces:
  - name: vision_client
    protocol: zenoh
    requesters:
      - name: object_detection
        request_spec: "make87_messages.image.compressed.ImagePNG"
        response_spec: "make87_messages.detection.box.Box2DAxisAligned"

Server / Client Pattern

Server/Client is designed for persistent connection-based services like web servers, streaming protocols, or long-running TCP connections.

Server/Client patterns mainly work on IP/port combinations, where endpoints are defined by the message specification (such as OpenAPI schemas with multiple HTTP endpoints).

When to Use

  • HTTP web services and REST APIs
  • Streaming protocols (RTSP, WebSocket)
  • Persistent TCP/UDP connections
  • File servers or database connections

Example: RTSP Streaming

An RTSP server streams video data, while clients connect to consume the feed:

RTSP Server:

interfaces:
    - name: video_stream
      protocol: rtsp
      servers:
          - name: camera_stream
            spec: "rtsp"
            port:
                name: stream
                target_port: 554
                protocol: rtsp

RTSP Client:

interfaces:
    - name: video_client
      protocol: rtsp
      clients:
          - name: camera_viewer
            spec: "rtsp"

Example: Mixed Protocol System

A complete example showing multiple protocols in one system:

apiVersion: v1
name: fusion-node
interfaces:
  - name: lidar_feed
    protocol: dds
    publishers:
      - name: point_cloud
        spec: "sensor_msgs.PointCloud2"
        config:
          type: object
          properties:
            reliability:
              type: string
              enum: [reliable, best_effort]
              default: best_effort
  - name: telemetry_out
    protocol: mqtt
    publishers:
      - name: system_status
        spec: "make87_messages.system.Status"
        config:
          type: object
          properties:
            qos:
              type: integer
              enum: [0, 1, 2]
              default: 1
  - name: config_api
    protocol: http
    servers:
      - name: configuration
        spec: "fusion_config_api:v2"
        port:
          name: config
          target_port: 8080
          protocol: http
  - name: dashboard_feed
    protocol: ws
    servers:
      - name: live_stream
        spec: "dashboard_stream:v1"
        port:
          name: stream
          target_port: 9001
          protocol: ws

Choosing the Right Pattern

Need Pattern Reason
Stream sensor data to multiple consumers Publisher/Subscriber Efficient one-to-many distribution via topics
Request data on-demand Requester/Provider Synchronous query-response via endpoints
Serve a web interface Server/Client Persistent HTTP connections with multiple API endpoints
Send commands to actuators Publisher/Subscriber Fire-and-forget messaging via topics
Validate configurations Requester/Provider Need confirmation/validation via endpoints
Stream video feeds Publisher/Subscriber or Server/Client Depends on protocol (Zenoh pub/sub topics or RTSP servers)

Configuration Schema

The optional config field under each communication pattern contains a YAML-encoded JSON schema that defines protocol-specific configuration parameters. This schema is completely flexible and allows you to configure any aspect of the communication behavior.

Example: Zenoh Publisher Configuration

publishers:
  - name: camera_feed
    spec: "make87_messages.image.ImageRGB"
    config:
      type: object
      properties:
        congestion_control:
          type: string
          enum: [ DROP, BLOCK ]
          default: DROP
        priority:
          type: string
          enum:
            - REAL_TIME
            - INTERACTIVE_HIGH
            - INTERACTIVE_LOW
            - DATA_HIGH
            - DATA
            - DATA_LOW
            - BACKGROUND
          default: DATA
        express:
          type: boolean
          default: true
        reliability:
          type: string
          enum: [ BEST_EFFORT, RELIABLE ]
          default: RELIABLE

Example: MQTT Publisher Configuration

publishers:
  - name: device_status
    spec: "make87_messages.device.Status"
    config:
      type: object
      properties:
        qos:
          type: integer
          enum: [0, 1, 2]
          default: 1
          description: "Quality of Service level"
        retain:
          type: boolean
          default: false
          description: "Whether messages should be retained by broker"

Example: HTTP Server Configuration

servers:
  - name: api_endpoint
    spec: "my_api:v1.0"
    config:
      type: object
      properties:
        enable_cors:
          type: boolean
          default: true
        timeout_ms:
          type: integer
          default: 30000
          minimum: 1000
        rate_limit:
          type: object
          properties:
            requests_per_minute:
              type: integer
              default: 100
            burst_size:
              type: integer
              default: 10

The configuration schema supports all standard JSON Schema features including: - Type validation (string, integer, boolean, object, array) - Enums for restricted value sets - Default values for automatic parameter initialization - Nested objects for complex configuration structures - Descriptions for documentation - Validation constraints (minimum, maximum, pattern, etc.)

Best Practices

Pattern Selection

  • Publisher/Subscriber: Streaming data, events, one-to-many distribution
  • Requester/Provider: Service calls, queries, request-response interactions
  • Server/Client: Persistent connections, web services, streaming protocols

Message Design

  • Use structured message types for type safety and compatibility
  • Include timestamps for synchronization across distributed systems
  • Design message hierarchies for organized data management
  • Consider using make87's message pack for standardized Protocol Buffer messages

Performance Optimization

  • Co-locate high-frequency communicating applications on the same node
  • Use appropriate QoS settings for your reliability requirements
  • Consider message size and frequency for bandwidth optimization
  • Choose encoding formats that balance performance and compatibility

Runtime Integration

  • Access interface configurations through the MAKE87_CONFIG environment variable
  • For complete details on runtime configuration structure and usage, see Runtime Configuration

By leveraging make87's protocol-agnostic approach, you can build applications that communicate efficiently across any infrastructure while maintaining the flexibility to change protocols as requirements evolve.


  1. Note: make87's System designer only allows to draw one-to-one subscribers for now, but many-to-one can be configured by manually setting the topic names to the same value in the UI. This is a limitation of the current System designer, not the platform itself.