资讯动态

Wio Terminal 调用 Custom Vision REST API 实现水果图像分类:HTTPS 证书配置与 ArduinoJson 解析实战

发布时间:2026/9/16 19:06:46 来源:尧图企业网站定制
Wio Terminal 调用 Custom Vision REST API 实现水果图像分类HTTPS 证书配置与 ArduinoJson 解析实战【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners本文是 IoT-For-Beginners 制造课程第 2 课2-check-fruit-from-deviceWio Terminal 分支的核心实战指南教你如何将 ArduCAM 摄像头拍摄的 JPEG 图像通过 HTTPS 安全连接上传到微软 Azure Custom Vision 服务完成分类并把成熟/未成熟等标签的置信度打印到串口监视器。读完本文你将掌握微控制器上手工配置 SSL 根证书的原理、WiFiClientSecure安全客户端的建立、HTTP POST 二进制流上传、以及用 ArduinoJson 解码预测结果 JSON 的完整代码实现。为什么微控制器需要手动配置 SSL 证书Custom Vision 服务的图像分类功能通过 REST API 对外提供调用方式是一个基于HTTPS安全 HTTP连接的 POST 请求。HTTPS 的核心是 TLS 加密客户端代码必须先向目标服务器请求其公钥证书再用该证书加密所有传输流量。普通 Web 浏览器会自动完成证书校验但微控制器如 Wio Terminal没有浏览器也不会自动携带系统根证书库。因此我们必须手动获取服务器证书链中信任锚点根证书将该根证书硬编码进固件在连接时用它校验服务器身份并协商加密。证书包含的是公钥不属于机密信息可以直接写进源码并在 GitHub 等公开平台共享。而且这类根证书长期不变一次获取即可长期复用。补充说明HTTPS 中的证书校验可防止中间人攻击——没有正确 CA 证书的客户端无法验证服务器身份容易被伪基站截获上传的图像数据。这正是本步骤存在的安全意义。任务一建立 SSL 客户端整个配置涉及三个文件config.h证书与密钥常量、main.cpp网络与主逻辑、platformio.ini依赖声明。1. 在 config.h 中加入 Azure 根证书打开fruit-quality-detector项目的 config.h添加如下证书常量仓库中即完整 PEM 文本const char *CERTIFICATE -----BEGIN CERTIFICATE-----\r\n MIIF8zCCBNugAwIBAgIQAueRcfuAIek/4tmDg0xQwDANBgkqhkiG9w0BAQwFADBh\r\n MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\r\n d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\r\n MjAeFw0yMDA3MjkxMjMwMDBaFw0yNDA2MjcyMzU5NTlaMFkxCzAJBgNVBAYTAlVT\r\n MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKjAoBgNVBAMTIU1pY3Jv\r\n c29mdCBBenVyZSBUTFMgSXNzdWluZyBDQSAwNjCCAiIwDQYJKoZIhvcNAQEBBQAD\r\n ggIPADCCAgoCggIBALVGARl56bx3KBUSGuPc4H5uoNFkFH4e7pvTCxRi4j/zXb\r\n wjEz5CipDOqjx9/jWjskL5dk7PaQkzItidsAAnDCW1leZBOIi68Lff1bjTeZgMY\r\n iwdRd3Y39b/lcGpiuP2d23W95YHkMMT8IlWosYIX0f4kYb62rphyfnAjYb/4Od99\r\n ThnhlAxGtfvSbXcBVIKCYfZgqRvV5lReUnd1aNjRYVzPOoifgSx2fRyy1pO1Uz\r\n aMMNnIOE71bVYW0A1hr19w7kOb0KkJXoALTDDj1ukUEDqQuBfBxReL5mXiu1O7WG\r\n 0vltg0VZ/SZzctBsdBlx1BkmWYBW261KZgBivrql5ELTKKd8qgtHcLQA5fl6JB0Q\r\n gs5XDaWehN86Gps5JW8ArjGtjcWAIPX8CQaWfaCnuRm6Bk/03PQWhgdi84qwA0s\r\n sRfFJwHUPTNSnE8EiGVk2frt0u8PG1pwSQsFuNJfcYIHEv1vOzP7uEOuDydsmCjh\r\n lxuoK2n5/2aVR3BMTup4gl8alXoBycyLmj3J/PUgqD8SL5fTCUegGsdia/Sa60\r\n N2oV7vQ17wjMNLXa2rjj/b4ZlZgXVojDmAjDwIRdDUujQu0RVsJqFLMzSIHpp2C\r\n Zp7mIoLrySay2YYBu7SiNwL95X6He2kS8eefBBHjzwW/9FxGqry57i71c2cDAgMB\r\n AAGjggGtMIIBqTAdBgNVHQ4EFgQU1cFnOsKjnfR3UltZEjgp5lVou6UwHwYDVR0j\r\n BBgwFoAUTiJUIBiV5uNu5g/6rkS7QYXjzkwDgYDVR0PAQH/BAQDAgGGMB0GA1Ud\r\n JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMHYG\r\n CCsGAQUFBwEBBGowaDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu\r\n Y29tMEAGCCsGAQUFBzAChjRodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln\r\n aUNlcnRHbG9iYWxSb290RzIuY3J0MHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6Ly9j\r\n cmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwN6A1oDOG\r\n MWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RHMi5j\r\n cmwwHQYDVR0gBBYwFDAIBgZngQwBAgEwCAYGZ4EMAQICMBAGCSsGAQQBgjcVAQQD\r\n AgEAMA0GCSqGSIb3DQEBDAUAA4IBAQB2oWc93fB8esci/8esixjN22meiGDjgF\r\n rA2LUK5IOQOgcUSTGKSqF9lYfAxPjrqPjDCUPHCURv26ad5P/BYtXtbmtxJWu\r\n cS5BhMDPPeG3oPZwXRHBJFAkY4O4AF7RIAAUW6EzDflUoDHKv83zOiPfYGcpHc9s\r\n kxAInCedk7QSgXvMARjjOqdakor21DTmNIUotxo8kHv5hwRlGhBJwps6fEVi1Bt0\r\n trpM/3wYxlr473WSPUFZPgP1j519kLpWOJ8z09wxayBr29irPcBYv0GMXlHqThy\r\n 8y4m/HyTQeI2IMvMrQnwqPpYrLIXyviI2vLoI4xKE4Rn38ZZ8m\r\n -----END CERTIFICATE-----\r\n;这是Microsoft Azure DigiCert Global Root G2 根证书——全球众多 Azure 服务共同使用的信任锚点之一api.cognitive.microsoft.com的证书链最终收敛于它。如何验证该证书确实正确在 macOS 或 Linux 上执行Windows 可用 WSL 运行openssl s_client -showcerts -verify 5 -connect api.cognitive.microsoft.com:443输出中的证书链会列出 DigiCert Global Root G2与你硬编码的 PEM 一致即可确认无误。2. 在 main.cpp 中声明 WiFiClientSecure 客户端打开 main.cpp添加头文件#include WiFiClientSecure.h在所有 include 之后声明全局实例WiFiClientSecure client;WiFiClientSecure封装了通过 HTTPS 与 Web 端点通信的全部逻辑是后续 HTTP POST 的传输层基础。3. 连接 WiFi 时绑定根证书在connectWiFi方法中即WiFi.begin(SSID, PASSWORD)成功后调用client.setCACert(CERTIFICATE);从仓库完整实现看connectWiFi的最终形态如下main.cppvoid connectWiFi() { while (WiFi.status() ! WL_CONNECTED) { Serial.println(Connecting to WiFi..); WiFi.begin(SSID, PASSWORD); delay(500); } client.setCACert(CERTIFICATE); Serial.println(Connected!); }setCACert将该根证书注入 TLS 握手过程之后client发起的每个 HTTPS 连接都会用此 CA 校验对端证书链。任务二编写图像分类逻辑1. 添加 ArduinoJson 依赖在 platformio.ini 的lib_deps列表中追加一行bblanchon/ArduinoJson 6.17.3ArduinoJson 是 Arduino 生态最常用的 JSON 解析库用于解码 Custom Vision REST API 返回的预测结果。仓库中该文件的完整依赖如下已含本课新增项[env:seeed_wio_terminal] platform atmelsam board seeed_wio_terminal framework arduino lib_deps seeed-studio/Seeed Arduino rpcWiFi 1.0.5 seeed-studio/Seeed Arduino FS 2.1.1 seeed-studio/Seeed Arduino SFUD 2.0.2 seeed-studio/Seeed Arduino rpcUnified 2.1.3 seeed-studio/Seeed_Arduino_mbedtls 3.0.1 seeed-studio/Seeed Arduino RTC 2.0.0 bblanchon/ArduinoJson 6.17.3 build_flags -w -DARDUCAM_SHIELD_V2 -DOV2640_CAM注意Seeed_Arduino_mbedtls是 Wio Terminal 平台 HTTPS 所需的 TLS 底层库与本课的WiFiClientSecure配套使用缺一不可。build_flags中的-DARDUCAM_SHIELD_V2与-DOV2640_CAM则用于裁剪 ArduCAM 库代码以适配 OV2640 摄像头。2. 配置预测 URL 与密钥在config.h中添加两个占位常量与 SSID/PASSWORD 并列仓库中完整 config.h 即此结构const char *PREDICTION_URL PREDICTION_URL; const char *PREDICTION_KEY PREDICTION_KEY;PREDICTION_URL替换为 Custom Vision 门户中Prediction URL按钮给出的 URL形如https://location.api.cognitive.microsoft.com/customvision/v3.0/Prediction/id/classify/iterations/Iteration2/image详见上一课发布模型的步骤PREDICTION_KEY替换为同对话框中的Prediction-Key值这是访问模型的鉴权凭据未携带正确 Key 的调用会被拒绝。3. 在 main.cpp 中引入 ArduinoJson 头文件#include ArduinoJSON.h4. 实现 classifyImage 函数在buttonPressed函数上方添加如下函数仓库 main.cpp 中的实现与文档完全一致void classifyImage(byte *buffer, uint32_t length) { HTTPClient httpClient; httpClient.begin(client, PREDICTION_URL); httpClient.addHeader(Content-Type, application/octet-stream); httpClient.addHeader(Prediction-Key, PREDICTION_KEY); int httpResponseCode httpClient.POST(buffer, length); if (httpResponseCode 200) { String result httpClient.getString(); DynamicJsonDocument doc(1024); deserializeJson(doc, result.c_str()); JsonObject obj doc.asJsonObject(); JsonArray predictions obj[predictions].asJsonArray(); for(JsonVariant prediction : predictions) { String tag prediction[tagName].asString(); float probability prediction[probability].asfloat(); char buff[32]; sprintf(buff, %s:\t%.2f%%, tag.c_str(), probability * 100.0); Serial.println(buff); } } httpClient.end(); }逐段拆解HTTPClient httpClient;声明 REST 客户端对象提供与 HTTP API 交互的方法。httpClient.begin(client, PREDICTION_URL)将 HTTP 客户端绑定到先前配置好 CA 证书的WiFiClientSecure实例并对接预测 URL——HTTPS 与证书链路在此接通。请求头设置Content-Type: application/octet-stream声明请求体为原始二进制数据即 JPEG 字节流Prediction-Key: KEY向 Custom Vision 传递预测密钥完成鉴权。httpClient.POST(buffer, length)发起 POST 请求并上传字节数组。POST 用于提交数据并取回响应与浏览器加载网页使用的 GET仅取数据不同。返回值是 HTTP 状态码200表示OK成功。若返回 4xx/5xx 则说明鉴权失败、URL 错误或服务异常。成功分支的 JSON 解码getString()读取 REST 响应文本DynamicJsonDocument doc(1024)分配 1024 字节动态 JSON 文档deserializeJson解析随后定位predictions数组。遍历预测结果每个元素取tagName标签名与probability0~1 的小数置信度乘以 100 后格式化为百分比输出到串口例如ripe: 56.84%。5. 理解响应 JSON 结构Custom Vision 分类接口返回的典型 JSON 如下{ id:45d614d3-7d6f-47e9-8fa2-04f237366a16, project:135607e5-efac-4855-8afb-c93af3380531, iteration:04f1c1fa-11ec-4e59-bb23-4c7aca353665, created:2021-06-10T17:58:58.959Z, predictions:[ { probability:0.5582016, tagId:05a432ea-9718-4098-b14f-5f0688149d64, tagName:ripe }, { probability:0.44179836, tagId:bb091037-16e5-418e-a9ea-31c6a2920f17, tagName:unripe } ] }核心是predictions数组分类器会为训练时用到的每一个标签返回一条记录包含tagName与probability。probability是 0~1 的浮点数0 表示 0% 匹配1 表示 100% 匹配。所有标签概率之和约为 1。代码中正是从这个数组逐条取出标签与概率打印到串口。6. 在按钮回调中接入分类逻辑在buttonPressed函数中将原来保存到 SD 卡的代码替换为classifyImage调用或将其加在图像写入之后、buffer 删除之前否则数据已被释放classifyImage(buffer, length);如果选择替换 SD 卡保存逻辑可顺手删除setupSDCard与saveToSDCard两个函数精简代码。仓库 main.cpp 中buttonPressed的最终形态为void buttonPressed() { camera.startCapture(); while (!camera.captureReady()) delay(100); Serial.println(Image captured); byte *buffer; uint32_t length; if (camera.readImageToBuffer(buffer, length)) { Serial.print(Image read to buffer with length ); Serial.println(length); classifyImage(buffer, length); delete (buffer); } }整条链路为startCapture触发 ArduCAM 后台拍摄 →captureReady轮询等待完成 →readImageToBuffer通过 SPI 将相机 FIFO 中的 JPEG 拷入内存 buffer相机底层实现见 camera.h→classifyImage上传分类 →delete释放内存。loop中检测到 WIO_KEY_C 按键按下INPUT_PULLUP模式下为低电平即触发整个流程。运行与验证编译上传固件后将摄像头对准水果并按下 C 键串口监视器输出示例Connecting to WiFi.. Connected! Image captured Image read to buffer with length 8200 ripe: 56.84% unripe: 43.16%注意Image read to buffer with length后的字节数会随画面内容变化——JPEG 是压缩格式同等分辨率下复杂画面体积更大。同时你上传的这张图片及其预测值也会出现在 Custom Vision 门户的Predictions标签页中可对照查看如果预测结果不理想通常是因为训练样本与设备摄像头成像差异过大分辨率、光线、白平衡。可参考本课 README4-manufacturing/lessons/2-check-fruit-from-device/README.md中的Improve the model部分用设备实拍的图像重新训练迭代并重新发布再更新PREDICTION_URL即可持续优化。完整示例代码位置本课完整可运行的工程位于仓库 code-classify/wio-terminal 目录下包含src/main.cppWiFi 连接、HTTPS 客户端、分类逻辑与按键触发src/config.hWiFi 凭据、预测 URL/Key、Azure 根证书src/camera.hArduCAM/OV2640 摄像头 SPI 采集封装platformio.iniPlatformIO 环境与依赖配置。对照此工程修改配置即可在自己的设备上复现完整的拍照 → 上传 → 分类 → 串口输出流程。如果你使用的是 Raspberry Pi 或虚拟设备仓库中还提供了对应实现pi/app.py 与 virtual-iot-device/app.py二者用 Custom Vision Python SDK 走classify_image接口逻辑等价。【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价