)
Wio Terminal 调用 Custom Vision REST API 分类水果图片实战指南IoT-For-Beginners【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners本篇文章是 IoT-For-Beginners 仓库「制造业」单元第二课《从物联网设备检查水果质量》的 Wio Terminal 硬件实现章节。你将学习如何在 Wio TerminalArduino 平台上建立 HTTPS 安全连接调用 Azure Custom Vision 的预测 REST API把 ArduCam 相机拍摄的 JPEG 图片上传到云端完成熟果/生果二分类并把每个标签的置信度输出到串口监视器。读完本文你将掌握单片机 HTTPS 证书配置、HTTP POST 二进制上传与 JSON 响应解析的完整实战方案。从相机到云端为什么单片机需要手动配置证书Custom Vision 服务的预测接口本质是一个 REST API通过 HTTPS安全 HTTP连接访问。当浏览器访问 HTTPS 端点时它会在握手阶段自动向服务器索取公钥证书并用它加密后续流量但微控制器如 Wio Terminal不具备这种自动能力需要开发者手动获取服务器证书、写入代码再用它建立安全连接。这里有两个关键事实证书是公开的HTTPS 证书只包含公钥不需要保密可以安全地硬编码进源码甚至公开分享到 GitHub证书很少变化根证书长期有效一次写入即可长期使用无需频繁更新。在 Wio Terminal 上这个工作由WiFiClientSecure类完成——它是WiFiClient的安全增强版本专门用于通过 HTTPS 与 Web 端点通信。任务一在 Wio Terminal 上配置 SSL 客户端本节完成三个步骤把 Azure 根证书写进config.h、引入WiFiClientSecure、在 WiFi 连接成功后绑定证书。1. 将证书硬编码到 config.h打开fruit-quality-detector项目中的config.h头文件完整工程见 code-classify/wio-terminal/fruit-quality-detector添加以下常量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 服务使用Custom Vision 的预测端点也位于其信任链之下。仓库中的完整工程在 config.h 里已包含该证书的最终版本。验证证书的正确性可选在 macOS 或 Linux 终端Windows 可用 WSL运行以下命令查看api.cognitive.microsoft.com实际下发的证书链openssl s_client -showcerts -verify 5 -connect api.cognitive.microsoft.com:443输出中会列出 DigiCert Global Root G2 证书与上面硬编码的证书一致。2. 引入 WiFiClientSecure 并绑定证书打开main.cpp在文件顶部的 include 区加入#include WiFiClientSecure.h在 include 语句下方声明一个全局的 WiFiClientSecure 实例WiFiClientSecure client;然后在connectWiFi()函数中、确认 WiFi 连接成功之后调用setCACert绑定证书client.setCACert(CERTIFICATE);从源码结构看仓库完整实现的connectWiFi()位于 main.cpp它先在一个while循环里反复尝试WiFi.begin(SSID, PASSWORD)直到WiFi.status() WL_CONNECTED随后立刻执行client.setCACert(CERTIFICATE)并打印Connected!。此后所有经由该client发起的 HTTPS 请求都会使用此根证书完成服务端身份验证与流量加密。任务二调用 Custom Vision 预测 API 分类图片SSL 客户端就绪后接下来把图片二进制数据 POST 到 Custom Vision 预测端点并解析返回的 JSON 结果。1. 在 platformio.ini 中添加 ArduinoJson 依赖在platformio.ini的lib_deps列表中追加一行bblanchon/ArduinoJson 6.17.3该库用于解码 REST API 返回的 JSON 响应。仓库中 platformio.ini 的完整依赖列表如下除了 ArduinoJson还包括 rpcWiFiWiFi 连接、Seeed 的 FS/SFUD/rpcUnified/RTC 以及 mbedtlsTLS 底层等 Wio Terminal 基础库[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其中build_flags的ARDUCAM_SHIELD_V2与OV2640_CAM用于把 ArduCam 库裁剪为仅支持 OV2640 传感器详见前一课 wio-terminal-camera.md 的相机接线与摄像头初始化说明。2. 配置预测 URL 与预测密钥在 Custom Vision 门户的Performance页签发布模型迭代后点击Prediction URL按钮可获取调用地址形如https://location.api.cognitive.microsoft.com/customvision/v3.0/Prediction/id/classify/iterations/Iteration2/image以及对应的Prediction-Key。将这两项写入config.hconst char *PREDICTION_URL PREDICTION_URL; const char *PREDICTION_KEY PREDICTION_KEY;把PREDICTION_URL替换为预测 URLPREDICTION_KEY替换为预测密钥。预测密钥是访问模型的凭证只有携带正确密钥的应用才被允许调用模型其他请求会被拒绝。3. 实现 classifyImage 函数在main.cpp顶部为 ArduinoJson 添加 include注意实际库的头文件名是ArduinoJson.h仓库源码已按此编写#include ArduinoJson.h然后在buttonPressed函数上方添加classifyImage函数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(); }这段代码的调用链如下与仓库 main.cpp 完全一致建立连接HTTPClient httpClient通过httpClient.begin(client, PREDICTION_URL)使用先前配置了 Azure 根证书的WiFiClientSecure实例连接预测 URL设置请求头Content-Type: application/octet-stream声明请求体是原始二进制数据JPEG 图片Prediction-Key头传递 Custom Vision 预测密钥POST 上传httpClient.POST(buffer, length)上传相机捕捉到的 JPEG 字节数组。POST 请求用于发送数据并获取响应与之相对的是用于取数据的 GET 请求浏览器加载网页就是 GET检查状态码POST 返回 HTTP 响应状态码200表示OK请求成功解析 JSON成功时用httpClient.getString()读取文本响应通过 ArduinoJson 的DynamicJsonDocument doc(1024)与deserializeJson反序列化再遍历predictions数组把每个标签名与概率格式化%.2f%%即保留两位小数的百分比输出到串口。 图片分类器会为所有已训练标签都返回一个置信度各标签概率之和约为 1100%。4. 认识预测响应的 JSON 结构REST API 返回的响应是如下格式的 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匹配概率。概率是 0~1 之间的浮点数0表示 0% 匹配该标签1表示 100% 匹配。classifyImage正是读取这两个字段将其换算成百分比打印到串口监视器。5. 接入按钮触发流程在buttonPressed函数中将原来保存图片到 SD 卡的代码替换为调用classifyImage或把调用添加在图片写入之后、缓冲区被delete释放之前classifyImage(buffer, length); 如果选择直接替换 SD 卡保存逻辑可以顺带清理代码删除setupSDCard与saveToSDCard函数。仓库完整实现位于 buttonPressedcamera.startCapture()启动 ArduCam 采集轮询camera.captureReady()等待 JPEG 就绪camera.readImageToBuffer(buffer, length)把相机 FIFO 中的 JPEG 数据读入堆内存camera.h中通过 SPI 以 256 字节分块搬移数据见 camera.h随后立即调用classifyImage(buffer, length)最后delete(buffer)释放内存。6. 上传运行与结果验证编译、上传并运行代码。把相机对准水果按下 Wio Terminal 顶部的C 按钮靠近电源开关的那个串口监视器会输出Connecting to WiFi.. Connected! Image captured Image read to buffer with length 8200 ripe: 56.84% unripe: 43.16%输出表明设备成功连接 WiFi、完成图像采集与缓冲区读取本次 JPEG 长 8200 字节随后把图片上传 Custom Vision模型判定该图片 56.84% 为成熟ripe、43.16% 为未成熟unripe。同一张图片及其预测值也可以在 Custom Vision 门户的Predictions页签中查看源码佐证完整工程位置本课完整的可编译工程位于 code-classify/wio-terminal/fruit-quality-detector包含src/main.cppWiFi 连接、SSL 证书绑定、classifyImage与按钮触发主流程src/config.hWiFi 凭据、预测 URL/密钥与 DigiCert Global Root G2 证书常量含SSID、PASSWORD等占位符需替换为实际值src/camera.hArduCam/OV2640 相机的 SPI 初始化、采集与 JPEG 缓冲读取platformio.iniPlatformIO 工程配置与全部库依赖。延伸阅读相机接线、SPI/I²C 引脚对应表与图像采集部分见 wio-terminal-camera.md上一课训练的fruit-quality-detector图像分类模型与重新训练方法见 1-train-fruit-detector发布模型迭代、获取预测 URL 与 Prediction-Key 的完整步骤以及 Raspberry Pi/虚拟设备上实现同一功能的写法见课程主页 2-check-fruit-from-device 与 single-board-computer-classify-image.md若设备端拍摄的图片与训练数据差异较大导致预测不准可在 Custom Vision 门户的Predictions页签用设备拍摄的照片重新训练并发布新迭代再更新代码中的PREDICTION_URL即可。【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考