三步搞定AIoT对接:百宝箱平台与xiaozhi协议的完美融合
本文档介绍了如何基于xiaozhiws协议对接百宝箱AIoT平台,重点关注与标准协议的差异部分。主要修改包括:1)OTA上行需添加Device-Key请求头;2)WebSocket连接时需使用HMAC-SHA256算法生成签名(结合设备MAC地址、devicekey和服务端token)。文档提供了ESP32平台下的C++实现示例,包括签名生成方法和WebSocket连接流程。对接前提是已在百宝箱平
·
本文将介绍基于 xiaozhi-esp32 的 WebSocket 通信协议实现百宝箱 IoT 设备的对接。
前置说明
- 关于 xiaozhi-esp32 WebSocket 协议的详细介绍请参见:协议原文。
- 本文所示代码仍然采用 esp32 ESP-IDF 框架下的 C++ 代码。其他平台移植时,会提供以 HMAC 算法为主的平替内容。
- 在根据本文进行对接操作前,请先确保您已授权了百宝箱 AIoT SDK 渠道的协议,若无,请参见:IoT SDK 授权完成相关操作。
操作说明
本文仅介绍与 xiaozhi-esp32 WebSocket 通信协议存在差异的部分,其余与协议本身相同的内容不再赘述。
差异点 1:OAT 上行
在原协议的基础上增加了 Device-key的参数配置,该参数可登录百宝箱 AIoT 设备页面,从右上角 Device Key除获取。

实现代码如下所示。
Http* Ota::SetupHttp() {
......
auto http = board.CreateHttp();
http->SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str()); // 在你的智能体里做了导入的设备的 mac 地址
http->SetHeader("Device-Key", device_key.c_str()); // 从 https://www.tbox.cn/open/iot 页面右上角复制过来
差异点 2:与 WebSocket 建立连接
在与 WebSocket 建立连接时,客户端需将 HTTP 请求头中的 Authorization 字段替换为一个新的签名。该签名由 HMAC(Hash-based Message Authentication Code)算法生成,其输入数据为设备的 MAC 地址与服务端下发的 Token 的拼接结果。实现代码如下所示。
#define HMAC_OUTPUT_LEN 32 // HMAC-SHA256 输出长度
#define MAC_ADDRESS_LEN 18 // MAC 地址长度
std::vector<unsigned char> hexStringToByteArray(const std::string& hex_str) {
// 每个字节由两个字符表示,因此需要的字节数是字符串长度的一半
std::vector<unsigned char> byteArray;
if (hex_str.length() % 2 != 0) {
throw std::invalid_argument("Hex string must have an even length");
}
for (size_t i = 0; i < hex_str.length(); i += 2) {
// 取出每两个字符
std::string byteString = hex_str.substr(i, 2);
// 将字符转换为十进制整数
unsigned char byte = static_cast<unsigned char>(strtol(byteString.c_str(), nullptr, 16));
byteArray.push_back(byte);
}
return byteArray;
}
void generate_signature(const char *mac, const char *token, const unsigned char *device_key, unsigned char *signature) {
char message[MAC_ADDRESS_LEN + 256]; // 存储 MAC + token
snprintf(message, sizeof(message), "%s%s", mac, token); // 合并 MAC 和 token
// 使用 HMAC-SHA256 生成签名
mbedtls_md_context_t ctx;
const mbedtls_md_info_t *info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
mbedtls_md_init(&ctx);
mbedtls_md_setup(&ctx, info, 1); // 1表示使用HMAC
mbedtls_md_hmac_starts(&ctx, device_key, HMAC_OUTPUT_LEN);
mbedtls_md_hmac_update(&ctx, (const unsigned char *)message, strlen(message));
mbedtls_md_hmac_finish(&ctx, signature);
mbedtls_md_free(&ctx);
}
bool WebsocketProtocol::OpenAudioChannel() {
if (websocket_ != nullptr) {
delete websocket_;
}
// 获取设备唯一标识符
std::string mac_address = SystemInfo::GetMacAddress();
ESP_LOGW(TAG, "Device mac addr: %s\n", mac_address.c_str());
// 生成签名
Settings settings("websocket", false);
std::string url = settings.GetString("url");
std::string token = settings.GetString("token");
auto device_key_array = hexStringToByteArray(device_key);
unsigned char signature[HMAC_OUTPUT_LEN] = { 0 }; // 存储签名
generate_signature(mac_address.c_str(), token.c_str(), device_key_array.data(), signature);
std::string signature_string = bytesToHexString(signature, HMAC_OUTPUT_LEN);
int version = settings.GetInt("version");
if (version != 0) {
version_ = version;
}
error_occurred_ = false;
websocket_ = Board::GetInstance().CreateWebSocket();
if (!signature_string.empty()) {
// If token not has a space, add "Bearer " prefix
if (signature_string.find(" ") == std::string::npos) {
signature_string = "Bearer " + signature_string;
}
// ESP_LOGI(TAG, "signature_string %s", signature_string.c_str());
websocket_->SetHeader("Authorization", signature_string.c_str());
}
更多推荐



所有评论(0)