IoT-For-Beginners 实战为 Wio Terminal 接入 ArduCAM OV2640 摄像头并完成图像采集【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners本指南对应 IoT-For-Beginners 项目中「制造业」单元第 2 课Check fruit quality from an IoT device的 Wio Terminal 硬件篇讲解如何为 Seeed Studio Wio Terminal 接驳 ArduCam Mini 2MP PlusOV2640 传感器摄像头并通过 PlatformIO 编写固件完成 GPIO 接线、SPI/I2C 总线通信、按键触发拍照与串口调试输出。读完本文你将掌握基于 OV2640 的 SPI 摄像头在 Arduino 框架下的接线规范、platformio.ini的编译标志配置、camera.h底层驱动封装思路以及将 JPEG 图像写入 SD 卡进行结果核验的完整流程为后续调用 Custom Vision 图像分类服务见 wio-terminal-classify-image.md打好数据采集基础。本文内容以 translations/bg/4-manufacturing/lessons/2-check-fruit-from-device/wio-terminal-camera.md 及其英文原版 wio-terminal-camera.md 为骨架源码级细节取自仓库中的 code-camera/wio-terminal 完整工程。一、硬件选型为什么选择 ArduCAM Mini 2MP Plus本课使用的摄像头是ArduCam Mini 2MP Plus核心是一颗OV26402 百万像素图像传感器。它的通信方式很有特点SPI 接口负责传输图像数据。图像文件即使是压缩后的 JPEG体积远大于温度、湿度等传感器输出的单个数值SPI 高带宽特性适合搬移大数据量I2C 接口负责配置传感器内部寄存器如分辨率、JPEG 尺寸、光照模式与特效。与 Wio Terminal 上常见的 Grove 传感器不同ArduCAM 摄像头没有 Grove 插座需要直接通过跳线连到 Wio Terminal 的 GPIO 引脚同时挂接在 SPI 与 I2C 两条总线上。 需要留意 SPI 引脚命名的新旧约定ArduCAM 板上的丝印标签与代码中使用的 Wio Terminal 引脚名仍沿用旧命名如 MOSI/MISO而本课文字说明采用新命名Controller Output, Peripheral Input 等。下文接线表中描述一栏即采用新命名。二、硬件接线ArduCAM 与 Wio Terminal 的引脚对照开始接线前建议先贴好 Wio Terminal 随附的 GPIO 引脚贴纸围绕引脚排布贴上即可便于快速定位引脚位置。使用杜邦跳线按下表完成 8 根线的连接ArduCAM 引脚Wio Terminal 引脚描述CS24 (SPI_CS)SPI 片选Chip SelectMOSI19 (SPI_MOSI)SPI 控制器输出、外设输入MISO21 (SPI_MISO)SPI 控制器输入、外设输出SCK23 (SPI_SCLK)SPI 串行时钟GND6 (GND)接地 – 0VVCC4 (5V)5V 电源SDA3 (I2C1_SDA)I2C 串行数据SCL5 (I2C1_SCL)I2C 串行时钟供电注意GND与VCC为 ArduCAM 提供5V供电注意与 Grove 传感器的 3V 不同该电源直接来自为设备供电的 USB-C 连接。因此 5V 供电能力受 USB-C 供电质量影响请确保数据线/电源输出足够稳定。接线完成后即可将 Wio Terminal 通过 USB-C 连接到电脑进入固件开发阶段。三、工程初始化PlatformIO 项目与依赖库3.1 创建工程在 VS Code 中通过 PlatformIO 插件新建 Wio Terminal 工程命名为fruit-quality-detector。仓库中完整的参考工程位于 code-camera/wio-terminal/fruit-quality-detector其 platformio.ini 给出了完整的平台配置[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 build_flags -DARDUCAM_SHIELD_V2 -DOV2640_CAM说明lib_deps中的rpcWiFi、FS、SFUD、rpcUnified、mbedtls、RTC等 Seeed 官方库用于 WiFi 连接、文件系统与 TLS 支持WiFi 凭据不写死在源码中而是集中放在src/config.h参考 config.h#pragma once #include string using namespace std; // WiFi credentials const char *SSID SSID; const char *PASSWORD PASSWORD;3.2 ArduCAM 库的特殊安装方式ArduCAM 官方库无法像普通 Arduino 库那样从platformio.ini的lib_deps直接安装必须从源码引入克隆官方仓库https://github.com/ArduCAM/Arduino.git或访问其 GitHub 页面点击Code按钮下载 zip只需要其中的ArduCAM文件夹整个文件夹复制到工程lib目录下。⚠️ 必须是lib/ArduCam整个文件夹保持代码位于lib/ArduCam之下不能只把ArduCam文件夹的内容散落到lib目录里。3.3 编译标志按需裁剪摄像头驱动ArduCAM 库支持多款摄像头型号。为了在不使用某型号时将其驱动代码从最终固件中剔除、控制体积库通过编译器标志build_flags选择型号。针对 OV2640需在platformio.ini末尾追加build_flags -DARDUCAM_SHIELD_V2 -DOV2640_CAM两个标志的作用ARDUCAM_SHIELD_V2告知库摄像头位于一块 Arduino 扩展板shield上OV2640_CAM仅编译 OV2640 型号对应的驱动代码。 若同时需要关闭编译器警告可参考分类版工程 code-classify/wio-terminal 中额外追加的-w标志。四、核心驱动camera.h源码逐段剖析在src目录新建camera.h完整实现见 code-camera 的 camera.h它封装了与摄像头通信的全部底层细节。文件以#pragma once防止重复包含并引入ArduCAM.h与Wire.h#pragma once #include ArduCAM.h #include Wire.h class Camera { public: Camera(int format, int image_size) : _arducam(OV2640, PIN_SPI_SS) { _format format; _image_size image_size; } ... private: ArduCAM _arducam; int _format; int _image_size; };构造函数将OV2640型号与PIN_SPI_SS片选引脚传入ArduCAM库对象并保存图像格式与尺寸两个参数由调用方在main.cpp中传入JPEG、OV2640_640x480。4.1init()上电自检与传感器配置init()依次完成以下关键步骤每步失败都会返回falsebool init() { // Reset the CPLD _arducam.write_reg(0x07, 0x80); delay(100); _arducam.write_reg(0x07, 0x00); delay(100); // Check if the ArduCAM SPI bus is OK _arducam.write_reg(ARDUCHIP_TEST1, 0x55); if (_arducam.read_reg(ARDUCHIP_TEST1) ! 0x55) { return false; } // Change MCU mode _arducam.set_mode(MCU2LCD_MODE); uint8_t vid, pid; // Check if the camera module type is OV2640 _arducam.wrSensorReg8_8(0xff, 0x01); _arducam.rdSensorReg8_8(OV2640_CHIPID_HIGH, vid); _arducam.rdSensorReg8_8(OV2640_CHIPID_LOW, pid); if ((vid ! 0x26) ((pid ! 0x41) || (pid ! 0x42))) { return false; } _arducam.set_format(_format); _arducam.InitCAM(); _arducam.OV2640_set_JPEG_size(_image_size); _arducam.OV2640_set_Light_Mode(Auto); _arducam.OV2640_set_Special_effects(Normal); delay(1000); return true; }逐步说明复位 CPLD向寄存器0x07依次写入0x80、0x00中间各延时 100ms完成 ArduCAM 板上 CPLD 的复位时序SPI 总线自检向测试寄存器ARDUCHIP_TEST1写入0x55再读回若读回值不一致说明 SPI 链路不通常见于接线错误切换 MCU 模式set_mode(MCU2LCD_MODE)让 MCU 可以访问传感器配置校验传感器型号通过 I2CwrSensorReg8_8/rdSensorReg8_8读取 OV2640 的芯片 ID 高字节与低字节验证是否为 OV2640vid 0x26pid为0x41或0x42应用画质参数set_format设置输出格式JPEG/BMP/RAWInitCAM()初始化OV2640_set_JPEG_size设置 JPEG 分辨率OV2640_set_Light_Mode(Auto)启用自动光照模式OV2640_set_Special_effects(Normal)关闭特殊特效最后延时 1s 等待传感器稳定。4.2 异步采集模型startCapture/captureReady/readImageToBufferArduCAM 的采集模型是异步的主控发出一条开始采集指令摄像头在后台完成曝光、JPEG 编码并把数据存进自己的片上 FIFO 缓冲主控随后轮询状态并搬走数据void startCapture() { _arducam.flush_fifo(); _arducam.clear_fifo_flag(); _arducam.start_capture(); } bool captureReady() { return _arducam.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK); }flush_fifo()与clear_fifo_flag()清空上次残留数据与完成标志避免脏数据start_capture()下发采集指令captureReady()通过ARDUCHIP_TRIG寄存器的CAP_DONE_MASK位判断采集是否完成。数据读取readImageToBuffer的要点bool readImageToBuffer(byte **buffer, uint32_t buffer_length) { if (!captureReady()) return false; uint32_t length _arducam.read_fifo_length(); buffer_length length; if (length MAX_FIFO_SIZE) return false; if (length 0) return false; byte *buf new byte[length]; ... _arducam.CS_LOW(); _arducam.set_fifo_burst(); while (length--) { temp_last temp; temp SPI.transfer(0x00); if ((temp 0xD9) (temp_last 0xFF)) // JPEG 结束标志 FF D9 { buf[buffer_pos] temp; buffer_pos; i; _arducam.CS_HIGH(); } ... } ... *buffer buf; }实现逻辑先通过read_fifo_length()拿到 JPEG 数据的字节数若为 0 或超过MAX_FIFO_SIZE上限则返回falsenew byte[length]在堆上分配接收缓冲区调用方用完必须deleteCS_LOW()拉低片选、set_fifo_burst()进入突发读模式后用SPI.transfer(0x00)逐个时钟读出字节同时识别 JPEG 的FF D8SOI起始标记与FF D9EOI结束标记来界定有效数据边界并按 256 字节一块的方式控制片选翻转读取完成后清空 FIFO 标志并重新下发一次格式/尺寸设置以复位传感器通过出参buffer返回堆缓冲区指针。这是一段与 ArduCAM 硬件强耦合的底层代码本课不需要逐行深究——直接使用即可。你真正需要关心的接口只有三个startCapture()、captureReady()、readImageToBuffer(buffer, length)。五、在main.cpp中集成摄像头5.1 实例化 Camera 对象在main.cpp的 include 语句下方创建全局摄像头实例见 main.cpp#include camera.h Camera camera Camera(JPEG, OV2640_640x480);这里把图像保存为JPEG格式、分辨率640x480。虽然 OV2640 最高支持到 3280x2464但本课后续的图像分类器输入尺寸很小约 227x227采集并上传更大的图片既浪费内存也拖慢网络传输因此 640x480 是性价比最优选择。5.2setupCamera()初始化总线void setupCamera() { pinMode(PIN_SPI_SS, OUTPUT); digitalWrite(PIN_SPI_SS, HIGH); Wire.begin(); SPI.begin(); if (!camera.init()) { Serial.println(Error setting up the camera!); } }该函数按顺序完成把 SPI 片选引脚PIN_SPI_SS设为输出并拉高使 Wio Terminal 处于SPI 控制器Controller地位启动 I2C 总线Wire.begin()与 SPI 总线SPI.begin()调用camera.init()若返回false则在串口打印Error setting up the camera!。在setup()末尾调用setupCamera();即可。仓库参考工程的setup()完整流程为Serial.begin(9600)→ 等待串口就绪 → 连接 WiFiconnectWiFi()内部循环调用WiFi.begin(SSID, PASSWORD)直至WL_CONNECTED→setupCamera()→ 配置按键 →可选setupSDCard()。5.3 编译、烧录与排查编译并上传后打开串口监视器波特率 9600。若看到Error setting up the camera!优先检查8 根跳线是否一一对应接线表中的引脚尤其 CS、MOSI、MISO、SCK 四条 SPI 线跳线是否插牢半松状态会导致信号时序异常供电是否稳定5V 由 USB-C 提供。六、按键触发拍照C 按钮与loop()轮询微控制器是不断循环执行代码的无法像桌面程序那样阻塞等待一个事件。Wio Terminal 自带 3 个顶部按钮本课用**靠近电源开关的那个按钮C 键**作为拍照触发源。6.1 配置按键为上拉输入在setup()末尾加入pinMode(WIO_KEY_C, INPUT_PULLUP);INPUT_PULLUP会反转输入逻辑默认情况下按钮未按下输出低电平、按下输出高电平开启内部上拉后未按下为高电平按下为低电平。因此检测按下的条件就是读到LOW。6.2loop()轮询与防抖void loop() { if (digitalRead(WIO_KEY_C) LOW) { buttonPressed(); delay(2000); } delay(200); }每 200ms 轮询一次按键状态检测到按下就调用buttonPressed()随后延时 2 秒给手指留出松开按钮的时间避免一次长按被判定为多次触发。6.3buttonPressed()拍照核心流程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); delete(buffer); } }startCapture()下发采集指令后摄像头在后台完成曝光、JPEG 编码并暂存到自身 FIFO主控每 100ms 轮询captureReady()直到CAP_DONE_MASK位置位readImageToBuffer把 FIFO 中的 JPEG 字节搬进堆缓冲并把字节数赋给length将长度打印到串口随后delete(buffer)释放堆内存。⚠️ 内存管理纪律每次readImageToBuffer成功都会new出一块缓冲区使用后必须delete否则连续拍照会不断泄漏堆内存最终导致分配失败。6.4 串口验证输出编译上传后每按一次 C 按钮串口监视器应输出类似Connecting to WiFi.. Connected! Image captured Image read to buffer with length 9224 Image captured Image read to buffer with length 11272不同画面的 JPEG 大小天然不同——JPEG 是内容相关的有损压缩相同分辨率下画面细节越丰富体积越大因此长度值不固定是正常现象。七、可选扩展把图片写入 SD 卡核验拍摄效果串口只能看到字节数无法直观确认画面内容。最省事的核验方式是把 JPEG 写到 Wio Terminal 的microSD 卡再插回电脑查看。限制条件Wio Terminal 仅支持16GB 及以下的 microSD 卡更大容量的卡无法工作。卡片需先格式化为FAT32 或 exFATmacOS 用 Disk Utility、Windows 用资源管理器、Linux 用命令行工具。7.1 初始化 SD 卡在main.cpp顶部增加头文件#include SD/Seeed_SD.h #include Seeed_FS.h在setup()前定义初始化函数并在setup()中调用void setupSDCard() { while (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) { Serial.println(SD Card Error); } }SD 卡同样通过 SPI 总线通信SDCARD_SS_PIN与SDCARD_SPI是 Seeed 库提供的板级常量。7.2 写文件saveToSDCard在buttonPressed函数上方加入int fileNum 1; void saveToSDCard(byte *buffer, uint32_t length) { char buff[16]; sprintf(buff, %d.jpg, fileNum); fileNum; File outFile SD.open(buff, FILE_WRITE ); outFile.write(buffer, length); outFile.close(); Serial.print(Image written to file ); Serial.println(buff); }fileNum是全局计数器负责生成递增文件名1.jpg、2.jpg……SD.open(buff, FILE_WRITE)以写模式打开文件outFile.write(buffer, length)将整块 JPEG 二进制数据落盘随后close()。7.3 在buttonPressed中调用必须在 delete 之前Serial.print(Image read to buffer with length ); Serial.println(length); saveToSDCard(buffer, length); delete(buffer);调用顺序很关键saveToSDCard必须在delete(buffer)之前执行否则写的是已释放的悬垂指针。7.4 验证输出与取卡按 C 键后串口输出示例Connecting to WiFi.. Connected! Image captured Image read to buffer with length 16392 Image written to file 1.jpg Image captured Image read to buffer with length 14344 Image written to file 2.jpg随后将 Wio Terminal 断电轻按 microSD 卡使其弹出必要时用指甲或细工具辅助插入电脑即可查看照片。 白平衡校准提示前几张照片可能出现偏色这是摄像头自动白平衡AWB收敛期间的正常现象。若影响核验可修改代码在setup()中静默拍摄几张并丢弃让 AWB 提前稳定。八、下一步衔接把图片交给 Custom Vision 分类本课采集到的 JPEG 图像正是下一环节——wio-terminal-classify-image.md——的输入。分类阶段会在buttonPressed中把buffer与length交给classifyImage(buffer, length)通过 HTTPSWiFiClientSecure 硬编码的 DigiCert 根证书POST 到 Custom Vision 预测端点返回ripe/unripe标签及概率并打印到串口。届时可在platformio.ini的lib_deps中追加bblanchon/ArduinoJson 6.17.3用于解析 JSON 响应完整参考工程见 code-classify/wio-terminal。另外值得注意用设备摄像头实拍的照片与训练集通常来自手机在清晰度、白平衡、光照上存在差异可能拉低分类准确率。若出现偏差可参考本课 README.md 的 Improve the model 章节用设备实拍图片重新训练并发布新迭代。九、小结完成本课你已具备以下能力理解 OV2640 ArduCAM 的 SPI传数据/ I2C配寄存器双总线架构并能按接线表完成 Wio Terminal 的硬件连接掌握 ArduCAM 库的手动安装方式与ARDUCAM_SHIELD_V2、OV2640_CAM编译标志的作用读懂camera.h中init/startCapture/captureReady/readImageToBuffer四个核心接口的异步采集模型用INPUT_PULLUP按键触发拍照正确处理内存释放与防抖延时可选地将 JPEG 写入 microSD 卡完成端到端的拍摄结果核验。本课所有代码均可直接查看/复用仓库中的两个完整工程code-camera/wio-terminal拍照 SD 卡保存与 code-classify/wio-terminal拍照 云端分类。【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考