ESP32小智AI设备端接入后台流程详解

小智AI超好玩儿的,最近准备自搭后台让其接入免费听歌。就是想听啥哥就能听啥歌。可以随心DIY...我有后台资源,搭个mcp服务就行了。在物联网时代,设备的远程管理和固件升级能力至关重要。小智ESP32设备采用了一套完善的OTA(Over-The-Air)升级和设备激活机制,确保设备能够安全、可靠地接入云平台并获取最新功能。本文将详细介绍小智ESP32设备的接入流程、协议规范以及核心代码实现。

小智AI项目开源地址:https://github.com/78/xiaozhi-esp32

小智ESP32环境搭建,参加文档《Windows搭建 ESP IDF 5.5.1开发环境以及编译小智

设备接入整体流程

小智ESP32设备的接入流程主要包含以下几个关键步骤:

  1. 设备信息采集:收集设备的硬件信息、固件版本、网络状态等
  2. OTA版本检查:向服务器发送版本检查请求,获取最新固件信息
  3. 设备激活:根据激活版本执行不同的激活流程
  4. 配置更新:获取并应用MQTT、WebSocket等服务器配置
  5. 固件升级:如有新版本,执行OTA升级流程

设备接入操作流程

1. 设备配网

设备配网是接入流程的第一步,使用以下方式:

  • 设备进入AP模式,创建一个临时WiFi热点(名称通常包含设备型号或ID)
  • 用户手机连接到该临时热点
  • 在手机浏览器中访问设备的Web配网页面(通常是192.168.4.1)
  • 输入目标WiFi的SSID和密码,点击确认
  • 设备连接到指定WiFi网络,配网完成

2. 配网成功后的激活码获取

配网成功后,设备会向OTA地址发送POST请求,自动执行以下操作:

  1. 连接服务器:设备使用配置的WiFi网络连接到小智云服务器(api.tenclass.net/xiaozhi/ota/)

  2. 发送设备信息:设备向服务器发送设备ID、MAC地址、固件版本等信息

  3. 获取激活码 :服务器验证设备信息后,返回包含激活码的响应:

    json 复制代码
    {
      "activation": {
        "code": "539000",
        "message": "xiaozhi.me\n539000",
        "challenge": "8dcafd09-fa80-4a4e-b97b-851918ee1b08"
      }
      // 其他配置信息...
    }
  4. 显示激活码:设备通过显示屏、LED指示灯或语音等方式向用户展示激活码(如"539000")

3. 登录xiaozhi.me系统添加设备

获取激活码后,用户需要通过以下步骤完成设备添加:

  1. 访问xiaozhi.me :在浏览器中打开https://xiaozhi.me

  2. 用户登录:使用已注册的账号登录系统

    • 如果没有账号,需要先完成注册
    • 支持手机号、邮箱等多种登录方式
  3. 进入设备管理页面

    • 登录成功后,点击导航栏中的"设备管理"或类似选项
    • 进入设备列表页面
  4. 添加新设备

    • 点击"添加设备"或"注册新设备"按钮
    • 选择设备类型(根据实际设备型号选择)
  5. 输入激活码

    • 在弹出的添加设备表单中,找到"激活码"字段
    • 输入设备显示的激活码(如"539000")
    • 点击"下一步"或"确认"按钮
  6. 完成设备添加

    • 系统验证激活码的有效性
    • 验证通过后,设备将显示在设备列表中
    • 用户可以为设备设置名称、位置等信息
    • 设备状态变为"在线",表示已成功接入

4. 设备激活完成

添加设备成功后,系统会自动完成以下操作:

  1. 后台激活:服务器向设备发送激活确认消息
  2. 设备响应:设备收到确认消息后,完成最终激活流程
  3. 状态同步:设备状态在xiaozhi.me系统中更新为"已激活"
  4. 功能可用:用户可以在系统中查看设备状态、控制设备功能等

OTA接口详细说明

接口地址

复制代码
POST https://api.tenclass.net/xiaozhi/ota/
Content-Type: application/json

请求头参数

参数名 类型 描述 是否必需
Activation-Version String 激活版本,1或2 必需
Device-Id String 设备唯一标识符(MAC地址) 必需
Client-Id String 客户端唯一标识符(UUID v4) 必需
User-Agent String 客户端名称和版本 必需
Accept-Language String 客户端当前语言 可选
Serial-Number String 设备序列号(仅激活版本2需要) 条件必需

注:当前OTA接口地址是固化在固件代码里了。默认值是虾哥的后台接口地址: https://api.tenclass.net/xiaozhi/ota/

后台管理地址是xiaozhi.me, 如果想接入自有平台或其他平台,需要改下代码重新刷固件。

代码中的修改位置:

cpp 复制代码
Ota::~Ota() {
}

std::string Ota::GetCheckVersionUrl() {
    Settings settings("wifi", false);
    std::string url = settings.GetString("ota_url");
    if (url.empty()) {
        url = CONFIG_OTA_URL;
    }
    return url;
}

或者修改配置文件中的值:

注:由于我买的星辰的板子,固件1.6.2以下才需要这么干。如果是新版本如1.9.2或者2.1.0最新固件,则默认就支持在配网的时候去修改ota的url地址。

新固件如2.1.0版本,配网界面有以下高级选项,可以自定义OTA,方便接入其他三方后台服务。

请求体结构

请求体为JSON格式,包含丰富的设备信息:

json 复制代码
{
  "version": 2,
  "flash_size": 4194304,
  "psram_size": 0,
  "minimum_free_heap_size": 123456,
  "mac_address": "00:00:00:00:00:00",
  "uuid": "00000000-0000-0000-0000-000000000000",
  "chip_model_name": "esp32s3",
  "chip_info": {
    "model": 1,
    "cores": 2,
    "revision": 0,
    "features": 0
  },
  "application": {
    "name": "my-app",
    "version": "1.0.0",
    "compile_time": "2021-01-01T00:00:00Z",
    "idf_version": "4.2-dev",
    "elf_sha256": ""
  },
  "partition_table": [
    {
      "label": "app",
      "type": 1,
      "subtype": 2,
      "address": 10000,
      "size": 100000
    }
  ],
  "ota": {
    "label": "ota_0"
  },
  "board": {
    "type": "esp32s3-devkitc",
    "name": "aa",
    "ssid": "aa",
    "rssi": "0",
    "channel": "1",
    "ip": "192.168.1.5",
    "mac": "34:85:18:ab:cd:ef"
  }
}

响应信息格式

服务器响应包含设备所需的各种配置信息:

json 复制代码
{
  "mqtt": {
    "endpoint": "mqtt.xiaozhi.me",
    "client_id": "GID_test@@@34_85_18_ab_cd_ef@@@550e8400-e29b-41d4-a716-446655440000",
    "username": "eyJpcCI6IjIyMC4yMDIuNzIuNTgifQ==",
    "password": "advv6eT5EDtQ4DOZ2QpJiTnla6ar0SZFKRybjj/CuX0=",
    "publish_topic": "device-server",
    "subscribe_topic": "null"
  },
  "websocket": {
    "url": "wss://api.tenclass.net/xiaozhi/v1/",
    "token": "test-token"
  },
  "server_time": {
    "timestamp": 1768454488884,
    "timezone_offset": 480
  },
  "firmware": {
    "version": "1.0.0",
    "url": ""
  },
  "activation": {
    "code": "539000",
    "message": "xiaozhi.me\n539000",
    "challenge": "8dcafd09-fa80-4a4e-b97b-851918ee1b08"
  }
}

完整OTA报文例子

bash 复制代码
### OTA 小智接入API
post https://api.tenclass.net/xiaozhi/ota/
Content-Type: application/json
Activation-Version:1
Device-Id:34:85:18:ab:cd:ee
Client-Id:550e8400-e29b-41d4-a716-446655440000
Accept-Language:zh-CN
#Serial-Number:34:85:18:ab:cd:ee

{
  "version": 2,
  "flash_size": 4194304,
  "psram_size": 0,
  "minimum_free_heap_size": 123456,
  "mac_address": "00:00:00:00:00:00",
  "uuid": "00000000-0000-0000-0000-000000000000",
  "chip_model_name": "esp32s3",
  "chip_info": {
    "model": 1,
    "cores": 2,
    "revision": 0,
    "features": 0
  },
  "application": {
    "name": "my-app",
    "version": "1.0.0",
    "compile_time": "2021-01-01T00:00:00Z",
    "idf_version": "4.2-dev",
    "elf_sha256": ""
  },
  "partition_table": [
    {
      "label": "app",
      "type": 1,
      "subtype": 2,
      "address": 10000,
      "size": 100000
    }
  ],
  "ota": {
    "label": "ota_0"
  },
  "board": {
    "type": "esp32s3-devkitc",
    "name": "aa",
    "ssid": "aa",
    "rssi": "0",
    "channel": "1",
    "ip": "192.168.1.5",
    "mac": "34:85:18:ab:cd:ee"
  }
}

##响应信息
{
  "mqtt": {
    "endpoint": "mqtt.xiaozhi.me",
    "client_id": "GID_test@@@34_85_18_ab_cd_ef@@@550e8400-e29b-41d4-a716-446655440000",
    "username": "eyJpcCI6IjIyMC4yMDIuNzIuNTgifQ==",
    "password": "advv6eT5EDtQ4DOZ2QpJiTnla6ar0SZFKRybjj/CuX0=",
    "publish_topic": "device-server",
    "subscribe_topic": "null"
  },
  "websocket": {
    "url": "wss://api.tenclass.net/xiaozhi/v1/",
    "token": "test-token"
  },
  "server_time": {
    "timestamp": 1768454488884,
    "timezone_offset": 480
  },
  "firmware": {
    "version": "1.0.0",
    "url": ""
  },
  "activation": {
    "code": "539000",
    "message": "xiaozhi.me\n539000",
    "challenge": "8dcafd09-fa80-4a4e-b97b-851918ee1b08"
  }
}

上述报文是第一次请求的报文示例,可以看出后台返回了activation字段信息,包含了激活码code信息,此时设备上会语音提示你请登录xiaozhi.me后台完成设备激活,输入code码。用户登录xiaozhi.me后台完成设备激活后,如果再次请求,则会收到以下响应内容:

bash 复制代码
{
    "mqtt": {
        "endpoint": "mqtt.xiaozhi.me",
        "client_id": "GID_test@@@34_85_18_ab_cd_ee@@@550e8400-e29b-41d4-a716-446655440001",
        "username": "eyJpcCI6IjIyMC4yMDIuNzIuNTgifQ==",
        "password": "eVsbvo+Yu1GFbj1uoq/iVRtrvmHFeHDullacyuGmtFo=",
        "publish_topic": "device-server",
        "subscribe_topic": "null"
    },
    "websocket": {
        "url": "wss://api.tenclass.net/xiaozhi/v1/",
        "token": "test-token"
    },
    "server_time": {
        "timestamp": 1768642695827,
        "timezone_offset": 480
    },
    "firmware": {
        "version": "1.0.0",
        "url": ""
    }
}

可以看出这时少了activation字段信息,设备再次开机不会再走激活流程了:

cpp 复制代码
void Application::CheckNewVersion() {
        //省略......
        //如果没返回激活码则不再展示
        if (!ota_->HasActivationCode() && !ota_->HasActivationChallenge()) {
            // Exit the loop if done checking new version
            break;
        }

        display->SetStatus(Lang::Strings::ACTIVATION);
        // Activation code is shown to the user and waiting for the user to input
        //如果返回了激活码,则再设备上展示
        if (ota_->HasActivationCode()) {
            ShowActivationCode(ota_->GetActivationCode(), ota_->GetActivationMessage());
        }

        // This will block the loop until the activation is done or timeout
        //激活流程
        for (int i = 0; i < 10; ++i) {
            ESP_LOGI(TAG, "Activating... %d/%d", i + 1, 10);

OTA请求代码

cpp 复制代码
std::unique_ptr<Http> Ota::SetupHttp() {
    auto& board = Board::GetInstance();
    auto network = board.GetNetwork();
    auto http = network->CreateHttp(0);
    auto user_agent = SystemInfo::GetUserAgent();
    http->SetHeader("Activation-Version", has_serial_number_ ? "2" : "1");
    http->SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
    http->SetHeader("Client-Id", board.GetUuid());
    if (has_serial_number_) {
        http->SetHeader("Serial-Number", serial_number_.c_str());
        ESP_LOGI(TAG, "Setup HTTP, User-Agent: %s, Serial-Number: %s", user_agent.c_str(), serial_number_.c_str());
    }
    http->SetHeader("User-Agent", user_agent);
    http->SetHeader("Accept-Language", Lang::CODE);
    http->SetHeader("Content-Type", "application/json");

    return http;
}

设备激活机制

小智ESP32设备支持两种激活版本,提供不同级别的安全性和复杂度:

激活版本1(简化流程)

Activation-Version请求头设置为"1"时,设备采用简化的激活流程:

  • 无需发送Serial-Number
  • 无需执行额外的激活请求
  • 设备自动完成激活

这种方式适用于对安全性要求不高的场景,简化了设备接入流程。

激活版本2(完整流程)

Activation-Version请求头设置为"2"时(默认值),设备需要执行完整的激活流程:

  1. 发送带有序列号的版本检查请求

    cpp 复制代码
    http->SetHeader("Activation-Version", "2");
    http->SetHeader("Serial-Number", serial_number_.c_str());
  2. 获取激活挑战码 :服务器在响应中返回activation.challenge字段

  3. 计算HMAC值:使用设备内部密钥对挑战码进行HMAC-SHA256计算

  4. 发送激活请求:将计算得到的HMAC值和相关信息发送给服务器进行验证

  5. 完成激活:服务器验证通过后,设备完成激活流程

在代码中也可以看到,是否启用激活版本2,是根据用户的配置,是否在ESP_EFUSE_BLOCK_USR_DATA用户自定义数据区写入了序列号来区分的:

cpp 复制代码
Ota::Ota() {
#ifdef ESP_EFUSE_BLOCK_USR_DATA
    // Read Serial Number from efuse user_data
    uint8_t serial_number[33] = {0};
    if (esp_efuse_read_field_blob(ESP_EFUSE_USER_DATA, serial_number, 32 * 8) == ESP_OK) {
        if (serial_number[0] == 0) {
        //无序列号
            has_serial_number_ = false;
        } else {
            serial_number_ = std::string(reinterpret_cast<char*>(serial_number), 32);
            //有序列号
            has_serial_number_ = true;
        }
    }
#endif
}

根据这个http->SetHeader("Activation-Version", has_serial_number_ ? "2" : "1");来设置是2还是1。

网上买到的开源硬件,没有给设备刷过序列号设置过秘钥,没有登记过系统。猜测这个地方是1,所以可以很容易的接入xiaozhi.me后台。(实际它并未走设备的激活协议。而只是让输入接口返回的code码,完成了绑定而已,并未做进一步的安全认证激活流程,校验hmac256的操作)。正式商用的话,为了安全接入,才会增加这层安全机制(有了该机制,不在系统登记在册的设备就彻底无法接入了)。

cpp 复制代码
std::unique_ptr<Http> Ota::SetupHttp() {
    auto& board = Board::GetInstance();
    auto network = board.GetNetwork();
    auto http = network->CreateHttp(0);
    auto user_agent = SystemInfo::GetUserAgent();
    http->SetHeader("Activation-Version", has_serial_number_ ? "2" : "1");
    http->SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
    http->SetHeader("Client-Id", board.GetUuid());
    if (has_serial_number_) {
        http->SetHeader("Serial-Number", serial_number_.c_str());
        ESP_LOGI(TAG, "Setup HTTP, User-Agent: %s, Serial-Number: %s", user_agent.c_str(), serial_number_.c_str());
    }
    http->SetHeader("User-Agent", user_agent);
    http->SetHeader("Accept-Language", Lang::CODE);
    http->SetHeader("Content-Type", "application/json");

    return http;
}

HMAC计算与安全机制

在激活版本2中,设备使用HMAC-SHA256算法对挑战码进行加密,确保激活过程的安全性:

核心代码实现

cpp 复制代码
std::string Ota::GetActivationPayload() {
    if (!has_serial_number_) {
        return "{}";
    }

    std::string hmac_hex;
#ifdef SOC_HMAC_SUPPORTED
    uint8_t hmac_result[32]; // SHA-256输出为32字节
    
    // 使用Key0计算HMAC
    esp_err_t ret = esp_hmac_calculate(HMAC_KEY0, (uint8_t*)activation_challenge_.data(), activation_challenge_.size(), hmac_result);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "HMAC calculation failed: %s", esp_err_to_name(ret));
        return "{}";
    }

    for (size_t i = 0; i < sizeof(hmac_result); i++) {
        char buffer[3];
        sprintf(buffer, "%02x", hmac_result[i]);
        hmac_hex += buffer;
    }
#endif

    cJSON *payload = cJSON_CreateObject();
    cJSON_AddStringToObject(payload, "algorithm", "hmac-sha256");
    cJSON_AddStringToObject(payload, "serial_number", serial_number_.c_str());
    cJSON_AddStringToObject(payload, "challenge", activation_challenge_.c_str());
    cJSON_AddStringToObject(payload, "hmac", hmac_hex.c_str());
    auto json_str = cJSON_PrintUnformatted(payload);
    std::string json(json_str);
    cJSON_free(json_str);
    cJSON_Delete(payload);

    ESP_LOGI(TAG, "Activation payload: %s", json.c_str());
    return json;
}

激活报文举例

bash 复制代码
### OTA 小智接入设备激活API
post https://api.tenclass.net/xiaozhi/ota/activate
Content-Type: application/json
Activation-Version:2
Device-Id:34:85:18:ab:cd:ef
Client-Id:550e8400-e29b-41d4-a716-446655440000
Accept-Language:zh-CN
Serial-Number:34:85:18:ab:cd:ef

{
    "algorithm":"hmac-sha256",
    "serial_number":"34:85:18:ab:cd:ef",
    "challenge":"8dcafd09-fa80-4a4e-b97b-851918ee1b08",
    "hmac":"4a11c0babb8b4c443e7ddd36315f073834e66f9cc756abebe3ce14c779007745"
}
##响应信息
{
}

HMAC_KEY0说明

在ESP32平台中,HMAC_KEY0是一个特殊的硬件密钥,存储在设备的eFuse中。它具有以下特点:

  1. 硬件级安全:存储在ESP32的安全eFuse区域,无法通过软件直接读取
  2. 唯一标识:每个设备都有唯一的HMAC_KEY0值
  3. 不可逆性:只能用于HMAC计算,无法反向推导出原始密钥
  4. 硬件加速 :通过esp_hmac_calculate API使用硬件加速计算HMAC值

HMAC_KEY0在ESP-IDF框架中定义,无需用户手动定义。它是ESP32芯片的内置安全特性,确保设备身份验证的安全性。

更多关于HMAC的介绍,参见乐鑫官方文档:https://docs.espressif.com/projects/esp-idf/zh_CN/v5.5.1/esp32s3/api-reference/peripherals/hmac.html

HMAC_KEY0的后台验证机制

关于"HMAC_KEY0是设备硬件密钥,后台如何验证"的猜测?涉及到ESP32的安全设计和设备生产流程:

密钥与序列号的关联机制

在设备生产或烧录阶段,执行以下关键操作:

  1. 生成唯一密钥对:为每个设备生成唯一的HMAC_KEY0和对应的序列号
  2. 安全存储
    • HMAC_KEY0被写入ESP32的eFuse安全区域,无法通过软件读取
    • 序列号也被写入eFuse的用户数据区域(ESP_EFUSE_USER_DATA)
  3. 建立映射关系:将设备的序列号与对应的HMAC_KEY0建立映射关系,并安全存储在后台服务器中

激活验证流程

当设备进行激活时,验证流程如下:

  1. 设备侧操作

    cpp 复制代码
    // 从eFuse读取序列号
    esp_efuse_read_field_blob(ESP_EFUSE_USER_DATA, serial_number, 32 * 8);
    
    // 使用硬件密钥计算HMAC
    esp_hmac_calculate(HMAC_KEY0, (uint8_t*)activation_challenge_.data(), 
                      activation_challenge_.size(), hmac_result);
  2. 激活请求:设备将序列号、挑战码和计算得到的HMAC值发送给服务器

  3. 服务器侧验证

    • 根据收到的序列号,从数据库中查找对应的HMAC_KEY0
    • 使用相同的挑战码和查找到的HMAC_KEY0计算HMAC值
    • 将服务器计算的HMAC值与设备发送的HMAC值进行比较
    • 如果一致,则验证通过;否则,验证失败

安全设计优势

这种设计方案具有以下安全优势:

  1. 密钥永不泄露:HMAC_KEY0永远不会离开设备的eFuse区域,即使设备被破解,也无法获取原始密钥

  2. 双向验证

    • 设备验证服务器(通过挑战码的真实性)
    • 服务器验证设备(通过HMAC值的正确性)
  3. 防止克隆:每个设备的HMAC_KEY0都是唯一的,无法通过复制序列号来克隆设备

  4. 抗重放攻击:挑战码是一次性的,每次激活都会生成新的挑战码

替代方案:密钥派生机制

在某些实现中,序列号和HMAC_KEY0之间可能采用密钥派生机制,而不是直接存储映射关系:

  • 序列号作为基础输入,通过特定算法派生HMAC_KEY0
  • 服务器和设备使用相同的派生算法
  • 这种方式避免了服务器存储密钥的需求,进一步提高安全性

无论采用哪种方式,核心思想都是确保HMAC_KEY0的安全性,同时实现可靠的设备身份验证。

激活请求实现

设备在计算完HMAC值后,需要发送激活请求:

cpp 复制代码
esp_err_t Ota::Activate() {
    if (!has_activation_challenge_) {
        ESP_LOGW(TAG, "No activation challenge found");
        return ESP_FAIL;
    }

    std::string url = GetCheckVersionUrl();
    if (url.back() != '/') {
        url += "/activate";
    } else {
        url += "activate";
    }

    auto http = SetupHttp();

    std::string data = GetActivationPayload();
    http->SetContent(std::move(data));

    if (!http->Open("POST", url)) {
        ESP_LOGE(TAG, "Failed to open HTTP connection");
        return ESP_FAIL;
    }
    
    auto status_code = http->GetStatusCode();
    if (status_code == 202) {
        return ESP_ERR_TIMEOUT;
    }
    if (status_code != 200) {
        ESP_LOGE(TAG, "Failed to activate, code: %d, body: %s", status_code, http->ReadAll().c_str());
        return ESP_FAIL;
    }

    ESP_LOGI(TAG, "Activation successful");
    return ESP_OK;
}

OTA版本检查与升级流程

设备定期执行OTA版本检查,获取最新固件信息和服务器配置:

cpp 复制代码
esp_err_t Ota::CheckVersion() {
    // ...设备信息采集
    
    auto http = SetupHttp();
    std::string data = board.GetSystemInfoJson();
    std::string method = data.length() > 0 ? "POST" : "GET";
    http->SetContent(std::move(data));

    if (!http->Open(method, url)) {
        // ...错误处理
    }
    
    // ...解析响应,获取固件信息、激活码、MQTT配置等
    
    return ESP_OK;
}

配置更新与应用

设备在收到服务器响应后,会自动更新MQTT和WebSocket配置:

cpp 复制代码
cJSON *mqtt = cJSON_GetObjectItem(root, "mqtt");
if (cJSON_IsObject(mqtt)) {
    Settings settings("mqtt", true);
    cJSON *item = NULL;
    cJSON_ArrayForEach(item, mqtt) {
        if (cJSON_IsString(item)) {
            if (settings.GetString(item->string) != item->valuestring) {
                settings.SetString(item->string, item->valuestring);
            }
        } else if (cJSON_IsNumber(item)) {
            if (settings.GetInt(item->string) != item->valueint) {
                settings.SetInt(item->string, item->valueint);
            }
        }
    }
    has_mqtt_config_ = true;
}

总结与最佳实践

小智ESP32设备的接入流程设计考虑了安全性、可靠性和灵活性:

  1. 分层安全机制:通过激活版本选择,平衡安全性和复杂度
  2. 硬件级安全:利用ESP32的HMAC硬件加速和eFuse密钥存储,确保设备身份验证的安全性
  3. 完整的配置管理:支持MQTT、WebSocket等多种通信协议的配置更新
  4. 灵活的升级策略:支持强制升级和条件升级,确保设备始终运行最新固件

最佳实践

  1. 选择合适的激活版本:根据设备使用场景和安全要求选择激活版本
  2. 确保序列号的唯一性:序列号是设备身份的重要标识,必须保证唯一性
  3. 定期执行版本检查:建议设备定期(如每天)执行版本检查,及时获取更新
  4. 处理网络异常:在网络不稳定的环境下,实现重试机制和错误处理
  5. 验证固件完整性:在OTA升级过程中,使用SHA256等算法验证固件完整性

通过遵循这些最佳实践,可以确保小智ESP32设备安全、可靠地接入云平台,并获得最佳的用户体验。

参考资料

相关推荐
华清远见IT开放实验室3 天前
以“科技+教育”双引擎,打造虚实融合的智能化教育新生态——华清远见亮相央广网2025教育年度盛典
科技·stm32·单片机·物联网·esp32·虚拟仿真·非凡就业班
SmartRadio3 天前
精准抑制PA上电冲击方案(正常WIFI大电流无阻碍放行)
单片机·嵌入式硬件·esp32·浪涌·冲击·启动电流
三万棵雪松3 天前
【AI小智硬件程序(九)】
c++·人工智能·嵌入式·esp32·ai小智
weixin_462446234 天前
ESP32 + SSD1306 OLED 显示中文天气与网络时间(U8g2 + WiFi + NTP 完整实战)
物联网·esp32
三万棵雪松4 天前
【AI小智硬件程序(八)】
c++·人工智能·嵌入式·esp32·ai小智
SmartRadio5 天前
ESP32添加修改蓝牙名称和获取蓝牙连接状态的AT命令-完整UART BLE服务功能后的完整`main.c`代码
c语言·开发语言·c++·esp32·ble
三万棵雪松7 天前
【AI小智硬件程序(四)】
人工智能·嵌入式·esp32·ai小智
qq_411262428 天前
纯图像传感器(只出像素),还是 Himax WiseEye/WE1/WE-I Plus 这类带处理器、能在端侧跑模型并输出“metadata”的模块
人工智能·嵌入式硬件·esp32·四博智联
飞睿科技9 天前
乐鑫ESP32-S3-BOX-3,面向AIoT与边缘智能的新一代开发套件
人工智能·嵌入式硬件·esp32·智能家居·乐鑫科技