FastBond4挑战部分-基于ESP32实现无线摄像头图传显示
该项目使用了2*ESP32,实现了计算机视觉的设计,它的主要功能为:摄像头拍照、OLED显示。
标签
ESP32
OLED
摄像头
FastBond4
、计算机视觉
--小灰灰--
更新2026-03-30
3
KiCad文件
全屏

项目介绍

本次活动的主题是嵌入式系统中的计算机视觉。使用两个ESP32实现。

第一块ESP32开发板被配置为专用的图像采集节点,使用0.3百万像素的GC0308摄像头,支持图像采集和红外传输控制。

第二块ESP32开发板则担任智能显示终端的角色。接收来自采集节点的压缩数据流,进行实时解码,并驱动外接的LCD屏幕进行画面还原。

此外,为了满足不同应用场景的多样化需求,ESP开发板不仅自己支持扩展,还为该整个系统绘制了一块扩展板

硬件原理图PCB介绍

下面就是使用到的两个ESP32-S3-PICO-1-N8R8,性能和功能都非常的强大

image.png

image.png

ESP32组成模组的功能如下:

image.png

通过扩展板将它们连接在一起,更方便的使用,并且新增了电源保护、两个用户按键和一个复位按键。

其中F1保险丝使用的是四大原厂供应商之一的Fittelfuse、U1是四大原厂供应商之一的ADI。

image.png

image.png

image.png

image.png

设计思路

本次“基于双ESP32的嵌入式计算机视觉系统”的设计,核心思路可以概括为:“软硬解耦、分布协同、模块化扩展”。在资源受限的嵌入式微控制器上实现计算机视觉,最大的挑战在于算力瓶颈内存限制。单颗ESP32很难同时高质量地完成“高分辨率图像采集 + 复杂图像处理/压缩 + 无线传输 + 屏幕高帧率刷新”这一整套流程。

  1. 采集端:摄像头 → ESP32压缩编码 → 无线发送
  2. 传输层:UDP协议 + JPEG压缩流 (带帧序号)
  3. 显示端:无线接收 → ESP32解码 → 屏幕渲染

image.png

软件流程

ESP32摄像头系统启动流程

┌─────────────────────────────────────────────────────────────────┐
│ 系统上电启动 │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
app_main() 入口 │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 1. 配置日志格式 │ │
│ │ spdlog::set_pattern("[%H:%M:%S] [%L] %v") │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
依赖注入 (DI)
│ ┌─────────────────────────┐ ┌─────────────────────────────┐ │
│ │ shared_data_injection() │ │ asset_pool_injection() │ │
│ │ - SharedData::Inject() │ │ - 映射 AssetPool 分区 │ │
│ │ - 创建共享数据单例 │ │ - AssetPool::InjectStatic │ │
│ └─────────────────────────┘ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ 硬件初始化 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │enable_camera │ │ i2c_init() │ │ imu_init() │ │
│ │ power() │ │ - I2C0: IMU │ │ - BMI270 │ │
│ │ - GPIO18 │ │ - I2C1: 预留 │ │ - BMM150 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ ir_init() │ │ camera_init()│ │
│ │ - GPIO47 │ │ - XCLK, PCLK│ │
│ │ - NEC 协议 │ │ - DVP 接口 │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ 启动服务 │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ start_service_uvc() │ │start_service_web_ │ │
│ │ - UVC 设备初始化 │ │ server() │ │
│ │ - 注册回调函数 │ │ - 创建 AP (CAM-WiFi)│ │
│ │ - 启动 UVC 任务 │ │ - 注册 Web API │ │
│ └─────────────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
主循环 (Main Loop)
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ while(1) { │ │
│ │ vTaskDelay(1000); │ │
│ │ cleanup_imu_ws_client(); // 清理断开的 WebSocket 客户端 │ │
│ │ } │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

ESP32摄像头的关键代码,程序的入口(usb_webcam_main.cpp)

extern "C" int app_main(void)
{
spdlog::set_pattern("[%H:%M:%S] [%L] %v");

/* 依赖注入 */
shared_data_injection(); // SharedData 单例
asset_pool_injection(); // AssetPool 资源池

/* 硬件初始化 */
enable_camera_power(); // GPIO18 摄像头电源
i2c_init(); // I2C 总线 (IMU)
imu_init(); // BMI270 + BMM150
ir_init(); // IR 发射 (GPIO47)
camera_init(); // 摄像头初始化

/* 启动服务 */
start_service_uvc(); // UVC 摄像头服务
start_service_web_server(); // Web 服务器

/* 主循环 */
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
cleanup_imu_ws_client(); // 清理 WebSocket 客户端
}
}



ESP32显示屏系统启动流程

┌─────────────────────────────────────────────────────────────────┐
上电启动 (Power On)
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
ESP32-S3 Bootloader │
(ESP-IDF 二级引导程序)
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
app_main() 入口 │
- 设置日志格式 spdlog::set_pattern()
- 创建 SetupCallback_t 回调结构 │
- 定义 AssetPoolInjection 回调 │
- 定义 HalInjection 回调 │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ AssetPoolInjection 回调执行 │
- nvs_flash_init() 初始化 NVS
- 查找 assetpool 分区 (type=0x233, subtype=0x23)
- esp_partition_mmap() 内存映射 2MB 分区 │
- AssetPool::InjectStaticAsset() 注入静态资源 │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ HalInjection 回调执行 │
HAL::Inject(new HAL_AtomS3R)
- 创建 HAL_AtomS3R 实例 │
- 调用 init() 初始化硬件: │
│ · watch_dog_init() 看门狗 │
│ · i2c_init() I2C 总线 │
│ · led_controller_init() LP5562 LED 控制器 │
│ · imu_init() BMI270 IMU 传感器 │
│ · disp_init() 显示屏驱动 │
│ · ir_init() 红外发射器 │
│ · initArduino() Arduino 核心 │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
APP::Setup(callback)
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 1. AssetPool 注入 (已执行) │ │
│ │ 2. SharedData 注入 (默认或自定义) │ │
│ │ 3. HAL 注入 (已执行) │ │
│ │ 4. HAL::LvglInit() 初始化 LVGL 图形库 │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Mooncake 应用框架初始化 │ │
│ │ - new Mooncake() 创建框架实例 │ │
│ │ - mooncake->init() 初始化 │ │
│ │ - app_run_startup_anim() 运行启动动画 │ │
│ │ - app_install_default_startup_app() 安装主应用 │ │
│ │ · AppUserDemo (用户演示应用) │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
主循环 (while(1))
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ APP::Loop() │ │
│ │ ↓ │ │
│ │ mooncake->update() 更新所有应用状态 │ │
│ │ - 检查当前应用生命周期 │ │
│ │ - 调用 onResume()/onRunning()/onDestroy() │ │
│ │ ↓ │ │
│ │ HAL::FeedTheDog() 喂看门狗 │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

ESP32显示屏的关键代码,程序的入口(app_main.cpp)

extern "C" void app_main(void)
{
// 1. 配置日志格式
spdlog::set_pattern("[%H:%M:%S] [%L] %v");

// 2. 创建回调结构
APP::SetupCallback_t callback;

// 3. AssetPool 注入回调 - 从 Flash 分区加载资源
callback.AssetPoolInjection = []() {
char* static_asset;
const esp_partition_t* part;
spi_flash_mmap_handle_t handler;

// 初始化 NVS
nvs_flash_init();

// 查找 assetpool 分区 (type=0x233, subtype=0x23)
part = esp_partition_find_first(
(esp_partition_type_t)233,
(esp_partition_subtype_t)0x23,
NULL
);

// 内存映射 2MB 分区到地址空间
err = esp_partition_mmap(
part, 0, 2 * 1024 * 1024,
ESP_PARTITION_MMAP_DATA,
(const void**)&static_asset,
&handler
);

// 注入到 AssetPool 单例
AssetPool::InjectStaticAsset((StaticAsset_t*)static_asset);
};

// 4. HAL 注入回调 - 创建 AtomS3R 专用 HAL
callback.HalInjection = []() {
HAL::Inject(new HAL_AtomS3R);
};

// 5. 初始化应用框架
APP::Setup(callback);

// 6. 主循环
while (1) {
APP::Loop();
}
}

功能展示

  • 硬件功能展示图及说明

image.png

image.png

心得体会

此次活动的基础非常简单,进阶活动非常的难,有点两极分化的感觉。进阶的创意非常的先进很贴合当前的热门主题,但是似乎有点太超前了,本来看到无线的WIFI-7挺感兴趣的,但是搜索了一下这类芯片和模组对个人现在似乎还挺少的。

没有使用过ESP-IDF,本次想通过AI完成全部代码,发现还是有点小问题,来来回回对话改了相似的问题得改好长一段时间。

代码附件下载

链接:https://www.123684.com/s/FbfSvd-R6KF3?pwd=zV4I#

提取码:zV4I

软硬件
电路图
附件下载
ProPrj_FastBond4.zip
SCH、PCB
代码下载网盘链接.txt
团队介绍
个人
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号