> For the complete documentation index, see [llms.txt](https://docs.sky-pulse.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.sky-pulse.org/api-reference/readme.md).

# Communication API

Integrate the Helios Ecosystem into your C2 stack easily using our API.

The SkyPulse communication API is based on MAVlink 2, using custom-structured messages built on the `MAVLINK_MSG_ID_DATA32` message (170).

{% hint style="info" %}
The SkyPulse communication stack is not provided by default, and is only relevant on units that ship with  the **C2 Integration license**&#x20;
{% endhint %}

### Messages schema:

The messages are parsed according to the following schema:

**General Message structure:**

<table data-header-hidden><thead><tr><th width="82"></th><th width="100"></th><th width="79.333251953125"></th><th width="92.6666259765625"></th><th width="86.666748046875"></th><th width="90.666748046875"></th><th width="90.6666259765625"></th><th width="89.3333740234375"></th><th width="91.3333740234375"></th><th width="88.666748046875"></th><th width="79.3333740234375"></th><th></th><th data-hidden></th></tr></thead><tbody><tr><td>Version</td><td>source ID</td><td>dest ID</td><td>Signal ID</td><td>param 1</td><td>param 2</td><td>param 3</td><td>param 4</td><td>param 5</td><td>unused</td><td>Auth 1</td><td>Auth 2</td><td><strong>Title</strong></td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4 - 7</td><td>8 -11</td><td>12 - 15</td><td>16 - 19</td><td>20 -23</td><td>28 - 29</td><td>30</td><td>31</td><td><strong>Byte #</strong></td></tr></tbody></table>

**Sanity:**

| **Title**  | Version | source ID | dest ID       | Signal ID | unused | unused | unused  | unused  | unused | unused  | unused  | Auth 1 | Auth 2 |
| ---------- | ------- | --------- | ------------- | --------- | ------ | ------ | ------- | ------- | ------ | ------- | ------- | ------ | ------ |
| **Byte #** | 0       | 1         | 2             | 3         | 4 - 7  | 8 -11  | 12 - 15 | 16 - 19 | 20 -23 | 24 - 27 | 28 - 29 | 30     | 31     |
| **Value**  | 1       |           | 0 (broadcast) | 1         |        |        |         |         |        |         |         | 0xCD   | 0xAB   |

**Takeoff:**

| **Title**  | Version | source ID | dest ID       | Signal ID | height | unused | unused  | unused  | unused | unused  | unused  | Auth 1 | Auth 2 |
| ---------- | ------- | --------- | ------------- | --------- | ------ | ------ | ------- | ------- | ------ | ------- | ------- | ------ | ------ |
| **Byte #** | 0       | 1         | 2             | 3         | 4 - 7  | 8 -11  | 12 - 15 | 16 - 19 | 20 -23 | 24 - 27 | 28 - 29 | 30     | 31     |
| **Value**  | 1       |           | 0 (broadcast) | 4         | meters |        |         |         |        |         |         | 0xCD   | 0xAB   |

**Idle:**

| **Title**  | Version | source ID | dest ID       | Signal ID | unused | unused | unused  | unused  | unused | unused  | unused  | Auth 1 | Auth 2 |
| ---------- | ------- | --------- | ------------- | --------- | ------ | ------ | ------- | ------- | ------ | ------- | ------- | ------ | ------ |
| **Byte #** | 0       | 1         | 2             | 3         | 4 - 7  | 8 -11  | 12 - 15 | 16 - 19 | 20 -23 | 24 - 27 | 28 - 29 | 30     | 31     |
| **Value**  | 1       |           | 0 (broadcast) | 6         |        |        |         |         |        |         |         | 0xCD   | 0xAB   |

**streaming on:**

| **Title**  | Version | source ID | dest ID       | Signal ID | unused | unused | unused  | unused  | unused | unused  | unused  | Auth 1 | Auth 2 |
| ---------- | ------- | --------- | ------------- | --------- | ------ | ------ | ------- | ------- | ------ | ------- | ------- | ------ | ------ |
| **Byte #** | 0       | 1         | 2             | 3         | 4 - 7  | 8 -11  | 12 - 15 | 16 - 19 | 20 -23 | 24 - 27 | 28 - 29 | 30     | 31     |
| **Value**  | 1       |           | 0 (broadcast) | 9         |        |        |         |         |        |         |         | 0xCD   | 0xAB   |

**Streaming off:**

| **Title**  | Version | source ID | dest ID       | Signal ID | unused | unused | unused  | unused  | unused | unused  | unused  | Auth 1 | Auth 2 |
| ---------- | ------- | --------- | ------------- | --------- | ------ | ------ | ------- | ------- | ------ | ------- | ------- | ------ | ------ |
| **Byte #** | 0       | 1         | 2             | 3         | 4 - 7  | 8 -11  | 12 - 15 | 16 - 19 | 20 -23 | 24 - 27 | 28 - 29 | 30     | 31     |
| **Value**  | 1       |           | 0 (broadcast) | 10        |        |        |         |         |        |         |         | 0xCD   | 0xAB   |

**Set target:**

| **Title**  | Version | source ID | dest ID       | Signal ID | frame ID | x center | y center | width   | height | unused  | unused  | Auth 1 | Auth 2 |
| ---------- | ------- | --------- | ------------- | --------- | -------- | -------- | -------- | ------- | ------ | ------- | ------- | ------ | ------ |
| **Byte #** | 0       | 1         | 2             | 3         | 4 - 7    | 8 -11    | 12 - 15  | 16 - 19 | 20 -23 | 24 - 27 | 28 - 29 | 30     | 31     |
| **Value**  | 1       |           | 0 (broadcast) | 13        | #        | pixel    | pixel    | pixels  | pixels |         |         | 0xCD   | 0xAB   |

**Intercept:**

| **Title**  | Version | source ID | dest ID       | Signal ID | frame ID | x center | y center | width   | height | unused  | unused  | Auth 1 | Auth 2 |
| ---------- | ------- | --------- | ------------- | --------- | -------- | -------- | -------- | ------- | ------ | ------- | ------- | ------ | ------ |
| **Byte #** | 0       | 1         | 2             | 3         | 4 - 7    | 8 -11    | 12 - 15  | 16 - 19 | 20 -23 | 24 - 27 | 28 - 29 | 30     | 31     |
| **Value**  | 1       |           | 0 (broadcast) | 15        |          |          |          |         |        |         |         | 0xCD   | 0xAB   |

### Example message:

**Set target:**

| **Title**  | Version | source ID | dest ID       | Signal ID | frame ID | x center | y center | width   | height | unused  | unused  | Auth 1 | Auth 2 |
| ---------- | ------- | --------- | ------------- | --------- | -------- | -------- | -------- | ------- | ------ | ------- | ------- | ------ | ------ |
| **Byte #** | 0       | 1         | 2             | 3         | 4 - 7    | 8 -11    | 12 - 15  | 16 - 19 | 20 -23 | 24 - 27 | 28 - 29 | 30     | 31     |
| **Value**  | 1       | 2         | 0 (broadcast) | 13        | 10       | 100      | 200      | 300     | 400    |         |         | 0xCD   | 0xAB   |

**Bytes sent:**

```
0x0000:  4500 004a c17f 0000 7f11 fb48 c0a8 014d
0x0010:  c0a8 fc3c f358 1775 0036 19ad fd22 0000
0x0020:  01ff 00aa 0000 0020 0100 000d 0a00 0000
0x0030:  6400 0000 c800 0000 2c01 0000 9001 0000
0x0040:  0000 0000 0000 cdab 9b83
```

### ***Drone states (metadata.state)***

The state field in the overlay metadata is one of these values:

| **State**   | **Description**                                                                                                                                                                 |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `IDLE`      | Default state. No active mission. Drone is waiting for commands.                                                                                                                |
| `HALT`      | Drone is commanded to stop. Controller calls halt() to stop movement.                                                                                                           |
| `SEEK`      | Drone is searching for a target. Runs detection on each frame; when a target is found, it is set and a snapshot is saved.                                                       |
| `INTERCEPT` | Drone is tracking and approaching a target. Runs tracking on each frame and commands the controller to approach the target pixel. If the target is lost, it falls back to SEEK. |

### **Stream overlay metadata (metadata\_json)** <a href="#id-3-stream-overlay-metadata-metadata_json" id="id-3-stream-overlay-metadata-metadata_json"></a>

JSON object sent with each video frame for client-side overlay rendering.

#### **Always present** <a href="#always-present" id="always-present"></a>

| **Field**     | **Type** | **Description**                                             |
| ------------- | -------- | ----------------------------------------------------------- |
| `iteration`   | int      | DroneControl update loop iteration                          |
| `state`       | string   | Current state name (e.g. `"IDLE"`, `"SEEK"`, `"INTERCEPT"`) |
| `target_lost` | boolean  | Whether the tracker has lost the target                     |

#### **Optional (may be omitted on controller/position errors)** <a href="#optional-may-be-omitted-on-controller-position-errors" id="optional-may-be-omitted-on-controller-position-errors"></a>

| **Field**                 | **Type** | **Description**                                                       |
| ------------------------- | -------- | --------------------------------------------------------------------- |
| `location`                | object   | Drone position in local NED: `{ "x": float, "y": float, "z": float }` |
| `rotation`                | object   | Drone attitude: `{ "pitch": float, "roll": float, "yaw": float }`     |
| `desired_direction_pixel` | object   | Desired direction in frame coords: `{ "x": int, "y": int }`           |
| `control_direction_pixel` | object   | Control direction in frame coords: `{ "x": int, "y": int }`           |

#### **Present when frame is available** <a href="#present-when-frame-is-available" id="present-when-frame-is-available"></a>

| **Field**            | **Type** | **Description**                                                                                                   |
| -------------------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `frame_size`         | object   | Frame dimensions: `{ "width": int, "height": int }`                                                               |
| `stream_res`         | object   | Stream resolution (if set): `{ "width": int, "height": int }`                                                     |
| `flow`               | object   | Optical flow arrow: `{ "start": { "x": int, "y": int }, "end": { "x": int, "y": int } }` (center → center + flow) |
| `tracker_estimation` | object   | Current tracker location: `{ "x": int, "y": int }`                                                                |
| `tracker_prediction` | object   | Predicted location: `{ "x": int, "y": int }`                                                                      |

#### **Present when target is tracked (not lost)** <a href="#present-when-target-is-tracked-not-lost" id="present-when-target-is-tracked-not-lost"></a>

| **Field** | **Type** | **Description**                                                                                                              |                                 |
| --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
| `target`  | object   | Target ROI and metadata: \`{ "roi": { "x": int, "y": int, "width": int, "height": int }, "detection\_type": "pixel\_lock" \\ | "yolo", "confidence": float }\` |

#### **Frame identity and SET\_TARGET\_BY\_PIXEL result** <a href="#frame-identity-and-set_target_by_pixel-result" id="frame-identity-and-set_target_by_pixel-result"></a>

| **Field**         | **Type**  | **Description** |                                                                             |
| ----------------- | --------- | --------------- | --------------------------------------------------------------------------- |
| `frame_id`        | int \\    | null            | Frame ID (16-bit, wraps). Used for SET\_TARGET\_BY\_PIXEL click correlation |
| `mono_ts_usec`    | int \\    | null            | Timestamp in microseconds                                                   |
| `last_set_target` | object \\ | null            | Result of last SET\_TARGET\_BY\_PIXEL attempt                               |

`last_set_target` on success:

{

&#x20; "`ok`": true,

&#x20; "`frame_id`": int,

&#x20; "`original_x`": int,

&#x20; "`original_y`": int,

&#x20; "`current_x`": int,

&#x20; "`current_y`": int,

&#x20; "`roi`": \[`x, y, width, height`]

}

last\_set\_target on failure:

{

&#x20; "`ok`": false,

&#x20; "`reason`": string,

&#x20; "`frame_id`": int,

&#x20; "`x`": int,

&#x20; "`y`": int

}

&#x20;

`reason` values: `"no_flow_history"`, `"frame_id_not_found"`, `"out_of_bounds"`, `"no_frame_available"`, `"segmentation_failed"`.

***

Note: The payload is a JSON string (`json.dumps(metadata)`). Coordinates are in capture resolution unless `stream_res` is present, in which case the client can map to stream resolution.
