# C SDK

C SDK 适合网关、嵌入式 Linux 和需要直接调用串口、GPIO、驱动库或 C/C++ 模块的设备程序。生成包已经处理 MQTT 连接、命令响应和数据上报，主要在 `src/main.c` 中接入真实硬件逻辑。

| 先看文件 | 用途 |
| --- | --- |
| `src/main.c` | 编写命令处理、硬件调用和数据上报 |
| `device-spec.json` | 核对命令、属性和事件定义 |
| `.env.example` | 配置 MQTT 地址和设备身份 |
| `CMakeLists.txt` / `README.md` | 查看依赖、构建选项和平台说明 |

## 构建和启动

1. 下载并解压代码包，进入代码包根目录。
2. 将 `.env.example` 复制为 `.env`，填写连接信息。
3. 安装依赖并使用 CMake 完成首次构建。
4. 在 `src/main.c` 中接入真实硬件逻辑，然后重新构建。
5. 启动 `build/device`，回到设备智能体工作区验证。

最小构建和启动命令如下：

```bash
cp .env.example .env
set -a && source .env && set +a
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
./build/device
```

MQTT 客户端依赖 `libmosquitto`。CMake 会优先使用系统中的 `cJSON`，未找到时自动获取。语音功能依赖 `libwebsockets` 和 OpenSSL，视觉功能依赖 `libcurl`；不需要时可以关闭对应模块：

```bash
cmake -B build \
  -DCMAKE_BUILD_TYPE=Release \
  -DDA_BUILD_VOICE=OFF \
  -DDA_BUILD_VISION=OFF
```

C 程序不会自行读取 `.env`，运行前需要像上面的命令一样将其导出到当前环境。命令名、参数名、属性字段和事件名必须与 `device-spec.json` 保持一致。生成的初始状态会使用属性的 `defaultValue`；没有默认值时按字段类型填充基础值，接入硬件后应改为真实状态。

## 实现命令、状态和事件

设备命令会进入 `src/main.c` 中的 `handle_command()`。在这里解析参数、调用硬件接口、更新状态并返回结果：

```c
static int handle_command(const da_command_t *cmd,
                          da_response_t *out,
                          void *user_data) {
    if (strcmp(cmd->cmd, "power") == 0) {
        // 解析 cmd->params_json，并调用真实硬件接口。
        da_client_send_status(g_client, "online", "{\"power\":true}");
        da_client_send_telemetry(g_client, "state", "{\"power\":true}");

        out->code = 0;
        out->msg = "ok";
        return 0;
    }

    out->code = 501;
    out->msg = "command not implemented";
    return 0;
}
```

三类数据的用途不同：

| 类型 | 何时使用 | C 接口 |
| --- | --- | --- |
| 状态 | 上线或状态快照发生变化 | `da_client_send_status()` |
| 遥测 | 上报温度、开关等当前数据 | `da_client_send_telemetry()` |
| 事件 | 上报告警、按键等独立事件 | `da_client_send_event()` |

事件名和数据字段也需要在 `device-spec.json` 中定义：

```c
da_client_send_status(client, "online", "{\"power\":true}");
da_client_send_telemetry(client, "state", "{\"temperature\":22.5}");
da_client_send_event(client, "button_pressed", "{\"button\":\"A\"}");
```

## 接入语音和视觉

### 语音

`include/device_agent/voice_client.h` 和 `src/voice_client.c` 提供设备端语音客户端，`examples/voice_chat.c` 是可直接运行的示例。示例包含生成时写入的默认地址；如需覆盖，运行前导出 `VOICE_WS_URL`。真实设备需要把麦克风采集、扬声器播放和回调连接到客户端：

```c
da_voice_options_t opts = {
    .ws_url = "ws://<gateway>:3001/ws/voice",
    .device_id = "device-001",
    .product_id = "agent-001",
};

da_voice_client_t *voice = da_voice_client_new(&opts, &callbacks);
da_voice_client_connect(voice);
da_voice_client_start_listening(voice, "manual");
da_voice_client_send_audio(voice, pcm_samples, sample_count);
da_voice_client_stop_listening(voice);
```

### 视觉

`include/device_agent/vision_client.h` 和 `src/vision_client.c` 提供生成包预置的单次拍照识别流程，具体触发命令见生成包 README。在 `src/main.c` 的 `capture_local_vision_image()` 中接入摄像头、截图或图像文件，并通过 `VOICE_CHAT_HOST` 指定服务地址。生成代码会把识别结果作为命令响应返回。

完整的语音和视觉配置见 [语音交互](../../usage/voice.md) 和 [摄像头与视觉识别](../../usage/multimedia.md)。启动后，按 [SDK 接入](../sdk-generation.md) 中的检查项验证设备上线、状态、命令和事件。
