> ## Documentation Index
> Fetch the complete documentation index at: https://dragonwingdocs.qualcomm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 消息

Qualcomm® 传感中枢（QSH）框架使用基于 protocol buffer 编码消息、数据流和传感器唯一标识符（SUID）的统一通信模型。此模型是应用处理器上的客户端应用程序与低功耗处理器上的传感器驱动程序和算法双方共用的契约。

<Note>
  `.proto` 文件定义了双方使用的消息模式。客户端应用程序使用它们对通过 `sendRequest()` 发送的请求进行编码。驱动程序和算法开发者使用它们来定义其传感器接受和发出的内容。
</Note>

## 传感器之间的通信

在 QSH 框架内，每个传感器驱动程序和算法都被视为传感器。这意味着传感器间通信（例如计步算法消费加速度计数据，或倾斜唤醒算法消费陀螺仪数据）与应用处理器上的客户端应用程序使用相同的请求和事件消息模型。

所有传入、传出以及传感器之间的通信都通过数据流上的请求和事件消息进行。消息负载使用 nanopb 生成器、编码器和解码器以 protocol buffer 格式编码。QSH 框架管理每条消息的负载长度、消息 ID 和时间戳元数据。

下图显示了数据客户端与数据源之间通过数据流进行的通信：

<div className="flex flex-col items-center gap-2">
  <img src="https://mintcdn.com/qualcomm-prod/h7j64QujhJHkLxyj/Technologies/Sensors/media/qcs6490/sensors-comunication2.svg?fit=max&auto=format&n=h7j64QujhJHkLxyj&q=85&s=cde7256f2c7c44d71364fc8d49523ed4" alt="图：数据客户端与数据源之间的传感器通信" width="812" height="256" data-path="Technologies/Sensors/media/qcs6490/sensors-comunication2.svg" />

  <p className="text-sm text-gray-700">图：数据客户端与数据源之间的传感器通信</p>
</div>

如图所示：

* 客户端到传感器（请求）：客户端发送请求消息以启用、禁用或重新配置传感器。每个请求都以特定的 SUID 为目标。客户端管理器验证请求并将其路由到目标传感器，目标传感器再将其传递给相应的传感器实例进行处理。

* 传感器到客户端（事件）：传感器实例以异步方式将事件消息发送回其已注册的客户端。客户端可以是应用处理器上的应用，也可以是在低功耗处理器上运行的其他传感器和算法。事件由新的传感器数据、配置更改、刷新（flush）完成或错误触发。

以下时序图显示了客户端应用程序、客户端管理器与传感器或驱动程序之间的完整消息生命周期：

```mermaid theme={null}
%%{init: {'theme': 'default'}}%%
sequenceDiagram
    participant App as Client Application
    participant CM as Client Manager
    participant S as Sensor / Driver
    App->>CM: sns_client_request_msg(SUID, msg_id, payload)
    CM-->>App: sns_client_resp_msg (ack)
    CM->>S: Client request
    S-->>CM: sensor event data
    CM-->>App: sns_client_event_msg(SUID, timestamp, payload)
```

以下各节描述客户端应用程序、客户端管理器和传感器之间交换的消息类型。

## 客户端消息

客户端应用程序通过三种消息类型与 QSH 框架交互：

1. 通过 `sendRequest()` API 发送 `sns_client_request_msg` 请求消息。此消息的 payload 字段携带 protocol buffer 编码的、传感器特定的请求。

2. 接收 `sns_client_resp_msg` 响应消息。客户端管理器在收到请求后立即发送此确认。它确认请求已正确编码且目标 SUID 可达。此阶段仅执行极少的处理。

3. 使用通过 `setCallback()` 注册的回调接收一条或多条 `sns_client_event_msg` 事件消息。每条事件消息属于单个 SUID。如果客户端在多个 SUID 上有活跃请求，则每个 SUID 的事件将在单独的消息中传递。单条事件消息可以携带一个或多个传感器样本。

### 请求消息字段

发送给客户端管理器的所有请求都使用 `sns_client_request_msg` 作为最外层的 protocol buffer 消息。此消息包含以下字段：

* SUID：请求的目标地址。客户端管理器会拒绝发送到无效或不可用 SUID 的任何请求。

* msg\_id：一个数字标识符，告知目标传感器如何解释编码的负载。消息 ID 在一个传感器内是唯一的，但两个不同的传感器可能对不同的消息类型使用相同的 ID。例如：

  * `SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_CONFIG`：客户端发送给传感器的标准流请求。
  * `SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_EVENT`：来自数据源传感器的标准传感器事件。

* 请求（`sns_std_request`）：发送给传感器的信息包。`sns_std_request::payload` 字段携带传感器特定的配置，编码为与 `msg_id` 对应的 protocol buffer 消息。有关详细信息，请参阅传感器特定的 `.proto` 文件。

* 重采样器配置（可选）：控制客户端接收传感器数据的速率。使用以下选项之一：

  * `SNS_RESAMPLER_RATE_FIXED`：以精确的速率传递数据。例如，如果客户端请求 200 Hz 而传感器以 240 Hz 运行，则样本会被插值降至 200 Hz。
  * `SNS_RESAMPLER_RATE_MINIMUM`：以不低于请求的速率传递数据。例如，如果客户端请求 200 Hz 而传感器以 240 Hz 运行，则样本以 240 Hz 传递。

* 阈值配置（可选）：过滤事件，使客户端仅在满足条件时才接收数据。支持以下阈值类型：

  * `SNS_THRESHOLD_TYPE_RELATIVE_VALUE`：当当前值与上次报告值之间的差值超过配置的阈值时触发。
  * `SNS_THRESHOLD_TYPE_RELATIVE_PERCENT`：当当前值与上次报告值之间的差值超过上次报告值的某个百分比时触发。
  * `SNS_THRESHOLD_TYPE_ABSOLUTE`：当当前值越过固定阈值时触发。
  * `SNS_THRESHOLD_TYPE_ANGLE`：当当前四元数与上次报告的四元数之间的角度超过配置的阈值（以弧度为单位）时触发。仅适用于四元数传感器。

* 挂起配置（可选）：控制客户端处理器挂起时的系统行为。它具有以下子字段：

  * `client_proc_type`：标识运行客户端的处理器。来自该处理器上任何客户端的刷新请求都会导致该处理器上的所有客户端收到刷新。

  * `delivery_type`：指定处理器挂起期间是否传递事件：

    * `SNS_STD_DELIVERY_WAKEUP`：事件可用时立即传递，与处理器状态无关。如果请求的 `batch_period` 超过系统容量，则在缓冲区满时发送数据。使用此选项时，`flush_period` 实际上被忽略。
    * `SNS_STD_DELIVERY_NO_WAKEUP`：在处理器挂起期间保留事件，并在处理器恢复时传递所有待处理事件。

  * `nowakeup_msg_ids`：不得唤醒客户端处理器的消息 ID 列表。这些消息仅在其他具有唤醒能力的事件已经在发送时才会被传递。

  有关完整的字段参考，请参阅 `<workspace>/build-qcom-wayland/workspace/sources/sensinghub/sensing-hub/apis/proto/sns_client.proto`，其中 <code>\<workspace></code> 是您的工作目录。

### 批处理

在 `sns_client_request_msg::sns_std_request` 中，客户端可以使用以下批处理字段控制数据的传递方式和时间：

* `batching::batch_period`：两次数据传递之间的最大时间，以微秒为单位。自上次传递以来生成的所有事件都会被保留，直到此定时器触发。在并发场景中，事件可能会更早传递。刷新请求会立即覆盖此定时器。批处理默认禁用（`batch_period = 0`）。

* `batching::flush_period`：向客户端管理器和物理传感器提示应保留多少历史数据，以微秒为单位。早于此值的数据可能会被丢弃。如果未设置，则默认为 `batch_period`，即只保留一个批次。设置时，`flush_period` 必须大于或等于 `batch_period`。

* `batching::flush_only`：如果为 `True`，则客户端管理器仅在客户端显式发送刷新请求时才传递事件。否则，批处理将持续到达到 `flush_period`，此时最旧的数据将被丢弃。

* `batching::max_batch`：如果为 `True`，则指示传感器使用其最大硬件批处理容量。如果 `flush_only` 和 `max_batch` 均为 `True`，则 `flush_only` 优先。

### 事件消息字段

传递给客户端的所有事件都使用 `sns_client_event_msg` 作为最外层的 protocol buffer 消息。此消息携带在 `sns_client_report_ind_msg` 的 payload 字段内，后者是传输层的指示包装器。`sns_client_event_msg` 消息包含以下字段：

* SUID：标识生成事件的数据源。如果客户端在多个 SUID 上有活跃请求，则每个 SUID 的事件将在单独的 `sns_client_event_msg` 消息中传递。

* `events::msg_id`：标识事件的类型，并使用与请求相同的数字 ID 空间——它告知客户端在解码负载时应使用哪个 proto 消息。

* `events::timestamp`：事件发生的时间，以 QTimer 时钟周期为单位。对于传感器数据事件，这是物理样本在硬件中被捕获的时间。对于框架生成的事件（配置更新、错误、刷新完成），这是事件被创建的时间。

* `events::payload`：编码后的事件数据。使用与 `msg_id` 对应的传感器特定 proto buffer 对此字段进行解码。

事件在以下情况下生成：

* 以配置的采样率或批处理周期出现新的传感器数据。
* 处理了传感器配置更改（`SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_PHYSICAL_CONFIG_EVENT`）。
* 刷新请求完成（`SNS_STD_MSGID_SNS_STD_FLUSH_EVENT`）。
* 传感器、传感器实例或框架中发生错误（`SNS_STD_MSGID_SNS_STD_ERROR_EVENT`）。

### 消息负载

请求或事件中传感器特定的内容分别携带在 `sns_std_request` 和 `sns_client_event` 的 `payload` 字段中。这些字段包含 protocol buffer 编码的消息，其结构由 `msg_id` 定义。如果消息类型不携带附加数据，该字段也可以为空。

客户端使用与其通信的传感器的 `.proto` 文件。每种传感器类型都有一个对应的 `.proto` 文件。例如，`sns_accel.proto` 描述了如何启用加速度计数据流。每个传感器都将其 `.proto` 文件列表作为其属性的一部分发布。

* 数据类型：每个传感器都会公布一个数据类型属性。数据类型映射到定义传感器特定 API 的一组唯一的 `.proto` 文件。同一类型的所有传感器必须支持一组最基本的请求和事件消息，并可以定义特定于其实现的其他可选消息。

* 标准化消息：以下 Qualcomm 定义的消息可以发送给任何传感器。它们在 `sns_std.proto` 文件中定义。

  * `SNS_STD_MSGID_SNS_STD_ATTR_REQ`：查询传感器已发布的属性。传感器以包含所有属性的 `SNS_STD_MSGID_SNS_STD_ATTR_EVENT` 进行响应。

    * `sns_std_attr_req::register_updates`：如果为 `True`，则每当传感器的属性发生变化时，客户端会收到 `sns_std_attr_event` 通知。
    * `sns_std_attr_event::attributes`：传感器发布的所有属性的列表，作为对 `sns_std_attr_req` 的响应或在属性变化时返回。

  * `SNS_STD_MSGID_SNS_STD_FLUSH_REQ`：强制将该传感器的所有批处理数据立即传递给客户端。这会同时刷新硬件缓冲区（例如加速度计上的物理 FIFO）以及客户端管理器保留的任何数据。对于诸如游戏旋转矢量（GRV）之类的算法传感器，这还会刷新所有底层物理传感器（加速度计、陀螺仪）的 FIFO。

  * `SNS_CLIENT_MSGID_SNS_CLIENT_DISABLE_REQ`：取消对该传感器的活跃请求。例如，如果客户端之前发送了 `SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_CONFIG` 以启用加速度计数据流，发送 `DISABLE_REQ` 将停止该客户端的数据流。

  * `SNS_STD_MSGID_SNS_STD_FLUSH_EVENT`：由传感器响应刷新请求而发送。表示与刷新对应的所有数据均已传递完毕，之后不会再有刷新事件。

  * `SNS_STD_MSGID_SNS_STD_ERROR_EVENT`：由传感器、传感器实例或框架生成的错误事件。

  有关完整的消息参考，请参阅 `<workspace>/build-qcom-wayland/workspace/sources/sensinghub/sensing-hub/apis/proto/sns_client.proto`。

* 标准化传感器消息：除上述消息外，以下 Qualcomm 推荐的消息适用于标准传感器。这些消息是可选的；您也可以定义自己的请求和事件消息。它们在 `sns_std_sensor.proto` 中定义：

  * `SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_CONFIG`：为物理传感器（加速度计、陀螺仪、磁力计）和某些算法传感器（旋转矢量、重力、线性加速度）启用数据流。
  * `SNS_STD_SENSOR_MSGID_SNS_STD_ON_CHANGE_CONFIG`：为变化触发（on-change）型传感器（例如接近传感器、环境光和步伐检测）启用数据流。
  * `SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_PHYSICAL_CONFIG_EVENT`：由物理传感器在处理客户端请求后发送。指示实际的运行参数，例如传感器产生的采样率。
  * `SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_EVENT`：传感器响应活跃流请求而生成的数据样本。

* SUID 查询：在发送任何请求之前，客户端必须知道目标传感器的 SUID。SUID 查询传感器提供此功能。客户端发送指定数据类型字符串（例如 `accel`）的 `sns_suid_req` 消息，并收到所有匹配的 SUID。空的数据类型字符串会返回系统上的所有 SUID。

  SUID 查询传感器具有一个固定的、众所周知的 SUID，发布在 `sns_suid.proto` 中。

  如果客户端希望在给定类型的新传感器可用时收到通知，可将 `register_updates` 字段设置为 `True`。收到 SUID 后，客户端可以向每个 SUID 发送 `sns_std_attr_req` 以检查属性并选择最合适的传感器。

  `sns_suid_req` 消息包含以下字段。有关更多信息，请参阅 `sns_suid.proto` 文件和示例代码。

**表：SUID 请求**

<table>
  <thead>
    <tr><th>字段</th><th>必选或可选</th><th>数据类型</th><th>描述</th></tr>
  </thead>

  <tbody>
    <tr><td><code>data\_type</code></td><td>必选</td><td>String</td><td>要查询的传感器的数据类型，例如 <code>accel</code> 或 <code>gyro</code>。</td></tr>
    <tr><td><code>register\_updates</code></td><td>可选</td><td>Boolean</td><td>如果为 <code>True</code>，则每当公布此数据类型的传感器变为可用或不可用时，客户端都会收到新的 SUID 事件。</td></tr>
    <tr><td><code>default\_only</code></td><td>可选</td><td>Boolean</td><td><ul><li>如果为 <code>True</code> 且已为该数据类型显式配置了默认传感器，则仅返回该传感器的 SUID。如果未配置默认传感器，则返回第一个匹配传感器的 SUID。</li><li>如果为 <code>False</code>，则在匹配该数据类型的所有 SUID 可用时全部返回。</li></ul></td></tr>
  </tbody>
</table>

### 传感器属性

每个传感器都会发布由数字 ID 标识的属性列表。属性描述了传感器的能力、运行参数以及它接受的取值范围。客户端使用 `SNS_STD_MSGID_SNS_STD_ATTR_REQ` 查询属性，并在 `SNS_STD_MSGID_SNS_STD_ATTR_EVENT` 响应中接收属性。

下表列出了标准传感器属性：

**表：传感器属性**

<table>
  <thead>
    <tr><th>属性 ID</th><th>属性名称</th><th>是否必需？</th><th>数据类型</th><th>描述</th></tr>
  </thead>

  <tbody>
    <tr><td>0</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_NAME</code></td><td>是</td><td>String</td><td>人类可读的传感器名称。</td></tr>
    <tr><td>1</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_VENDOR</code></td><td>是</td><td>String</td><td>人类可读的供应商名称。</td></tr>
    <tr><td>2</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_TYPE</code></td><td>是</td><td>String</td><td>此传感器使用的数据类型，在传感器 proto 文件中定义。</td></tr>
    <tr><td>3</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_AVAILABLE</code></td><td>是</td><td>Boolean</td><td>指示此传感器当前是否可供客户端使用。</td></tr>
    <tr><td>4</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_VERSION</code></td><td>是</td><td>Integer</td><td>表示传感器驱动版本的 64 位整数，格式为 <code>major\[31:16].minor\[15:8].revision\[7:0]</code>。例如：major <code>0x0002</code>、minor <code>0x00</code>、revision <code>0x36</code> 得到 <code>DRIVER\_VERSION 0x00020036</code>。</td></tr>
    <tr><td>5</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_API</code></td><td>是</td><td>String</td><td>此传感器使用的 <code>.proto</code> 文件名列表。其他 proto 依赖项通过这些文件中的导入指定。主要用于测试自动化。</td></tr>
    <tr><td>6</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_RATES</code></td><td>否</td><td>Float</td><td>传感器支持的采样率列表，以 Hz 为单位。</td></tr>
    <tr><td>7</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_RESOLUTIONS</code></td><td>否</td><td>Float</td><td>传感器支持的采样分辨率列表。</td></tr>
    <tr><td>8</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_FIFO\_SIZE</code></td><td>否</td><td>Integer</td><td>支持的 FIFO 深度，以样本数量表示。</td></tr>
    <tr><td>9</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_ACTIVE\_CURRENT</code></td><td>否</td><td>Integer</td><td>活跃电流值数组，以 µA 为单位。</td></tr>
    <tr><td>10</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_SLEEP\_CURRENT</code></td><td>否</td><td>Integer</td><td>非活跃（睡眠）电流，以 µA 为单位。</td></tr>
    <tr><td>11</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_RANGES</code></td><td>否</td><td>Float</td><td>支持的运行测量范围。</td></tr>
    <tr><td>12</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_OP\_MODES</code></td><td>否</td><td>String</td><td>传感器支持的运行模式，例如 <code>\[LPM, HIGH\_PERF, NORMAL, OFF]</code>。</td></tr>
    <tr><td>13</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_DRI</code></td><td>否</td><td>Boolean</td><td>支持的中断类型：<code>True</code> = 数据就绪中断（DRI）；<code>False</code> = 带内中断（IBI）。</td></tr>
    <tr><td>14</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_STREAM\_SYNC</code></td><td>否</td><td>Boolean</td><td>指示传感器是否支持同步流。</td></tr>
    <tr><td>15</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_EVENT\_SIZE</code></td><td>否</td><td>Integer</td><td>此传感器生成的 protocol buffer 编码数据事件的大小（以字节为单位）。HAL 使用它来确定最大批处理容量。</td></tr>
    <tr><td>16</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_STREAM\_TYPE</code></td><td>是</td><td>Integer</td><td>流类型：<code>0</code> = 连续周期性，<code>1</code> = 变化触发，<code>2</code> = 单次。</td></tr>
    <tr><td>17</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_DYNAMIC</code></td><td>否</td><td>Boolean</td><td>如果传感器可以在运行时连接或断开，则为 <code>True</code>。</td></tr>
    <tr><td>18</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_HW\_ID</code></td><td>否</td><td>Integer</td><td>用于区分多个同类型传感器的硬件标识符。</td></tr>
    <tr><td>19</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_RIGID\_BODY</code></td><td>否</td><td>Integer</td><td>传感器的物理位置：<code>0</code> = 显示屏侧，<code>1</code> = 键盘侧，<code>2</code> = 外部设备。</td></tr>
    <tr><td>21</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_PHYSICAL\_SENSOR</code></td><td>否</td><td>Boolean</td><td>如果这是物理传感器则为 <code>True</code>；如果是虚拟（算法）传感器则为 <code>False</code>。</td></tr>
    <tr><td>22</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_PHYSICAL\_SENSOR\_TESTS</code></td><td>否</td><td>Integer</td><td>支持的物理传感器自检列表，使用 <code>sns\_physical\_sensor\_test\_type</code> 中的枚举值。</td></tr>
    <tr><td>23</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_SELECTED\_RESOLUTION</code></td><td>否</td><td>Float</td><td>每个已配置动态范围值的活跃测量分辨率。</td></tr>
    <tr><td>24</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_SELECTED\_RANGE</code></td><td>否</td><td>Float\[2]</td><td>活跃动态范围。有关默认值，请参阅供应商提供的传感器硬件要求规范。</td></tr>
    <tr><td>25</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_ADDITIONAL\_LOW\_LATENCY\_RATES</code></td><td>否</td><td>Float</td><td>可供低延迟专用客户端使用的附加采样率，以 Hz 为单位。这些采样率是对 <code>SNS\_STD\_SENSOR\_ATTRID\_RATES</code> 中速率的扩展，如果被非专用客户端使用，可能会影响系统性能。</td></tr>
    <tr><td>26</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_PASSIVE\_REQUEST</code></td><td>否</td><td>Boolean</td><td>如果传感器支持被动请求，则为 <code>True</code>。如果为 <code>False</code>，则所有请求均视为主动请求。</td></tr>
    <tr><td>29</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_TRANSPORT\_MTU\_SIZE</code></td><td>否</td><td>Integer</td><td>传输传感器的最大传输单元（MTU）大小，以字节为单位。</td></tr>
    <tr><td>30</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_HLOS\_INCOMPATIBLE</code></td><td>否</td><td>Boolean</td><td>如果传感器与其数据类型的 HLOS 规范不兼容，则为 <code>True</code>。</td></tr>
    <tr><td>31</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_SERIAL\_NUM</code></td><td>否</td><td>String</td><td>传感器序列号。</td></tr>
    <tr><td>32</td><td><code>SNS\_STD\_SENSOR\_ATTRID\_TECH\_USED</code></td><td>否</td><td>Integer 数组</td><td>此传感器使用的技术。有关值的列表，请参阅 <code>sns\_std\_type.proto</code> 中的 <code>sns\_tech</code>。</td></tr>
  </tbody>
</table>

<Note>
  属性 ID 20 为保留项，当前未分配。
</Note>

有关更多信息，请参阅 `sns_std_sensor.proto` 文件和示例代码。proto 文件位于设备上的 `/etc/sensors/proto/` 目录中。

## Protocol buffers

QSH 客户端请求和事件消息是包含 protocol buffer 编码负载的不透明内存缓冲区。客户端可以使用 nanopb 库支持的任何语言生成这些消息，将其编码为字节流，并将该流复制到 `sns_client_request_msg` 的 payload 字段中。同样，客户端从 `sns_client_report_ind_msg` 中提取负载，并使用相应的 `.proto` 定义对其进行解码。

有关更多信息，请参阅 [Protocol Buffers](https://developers.google.com/protocol-buffers/) 和 [nanopb](https://github.com/nanopb/nanopb)。
