ESP32 ESP-IDF自定义组件
简介
官方文档
示例说明
此示例在《ESP32 smart_config和airkiss配网》
https://zhuanlan.zhihu.com/p/440454542
https://link.zhihu.com/?target=https%3A//blog.csdn.net/chentuo2000/article/details/121687760
基础上,增加连接成功后点亮板载LED功能。
实现所需功能后将各功能代码分离,再将分离后的代码构造成组件,使得项目有清晰的结构,方便功能代码移植.
开发环境
以下有三种方法:
《Win10启用Linux子系统安装Ubuntu》
https://link.zhihu.com/?target=https%3A//blog.csdn.net/chentuo2000/article/details/112131624《用乐鑫国内Gitee镜像搭建ESP32开发环境》
https://link.zhihu.com/?target=https%3A//blog.csdn.net/chentuo2000/article/details/113424934《ESP32环境搭建》(自己写的环境搭建)
https://xuan9527.github.io/2024/02/19/ESP32%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/
构建项目
拷贝 && 初始化例程
将例子项目hello_world复制到ESP-IDF开发工具之外,更名为components_demo:
cd ~/esp
cp -r ~/esp/esp-adf/esp-idf/examples/get-started/hello_world ./components_demo
清空build目录:
cd ~/esp/components_demo
rm -r build/*
注意,每当添加了新组件就要删除build目录下的全部内容,或者执行下面这条命令:
idf.py fullclean
清除以前的构建。
添加组件letter_shell
idf.py -C components create-component letter_shell
该命令会创建一个新组件,新组件将包含构建组件所需的一组空文件。我们的工作就是在这一组空文件中写上我们的代码。
如果熟悉了组件结构,也可以直接在项目中手工创建。
项目树
构建好的项目结构如下:

注意:
- 组件目录
components名字不能改,其下的组件名可以随意取。build目录是编译时生成的,编译的结果都放在其中。dependencies.lock是随原来的项目复制过来的不要改。sdkconfig文件可以用idf.py menuconfig命令修改。 idf_component.yml是新版自动链接下载文件,以下是esp32-camera依赖组件,下拉在managed_components里。1
2
3dependencies:
esp32-camera:
git: git@github.com:espressif/esp32-camera.git
代码和说明
各文件的位置关系很重要,请对照前面的项目树看代码文件。
项目的根CMakeLists.txt文件
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(components_demo)
只需要修改project中的项目名称。
main目录
CMakeLists.txt
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "."
PRIV_REQUIRES letter_shell
REQUIRES nvs_flash)
main.c
1 |
|
头文件nvs_flash.h是对系统组件的引用,shell_port.h是对自定义组件的引用。
letter_shell组件
CMakeLists.txt:
1 | idf_component_register( |
说明:
1、PRIV_REQUIRES
该参数指定对其它自定义组件的依赖,即私有依赖项。
PRIV_REQUIRES led表示指出在smart_config组件中要用到自定义的led组件。组件名字可以加引号,也可以不加。多个组件用空格分开。
2、 REQUIRES
该参数指定对系统组件的依赖,即公共依赖项。
REQUIRES esp_driver_uart 表示在letter_shell组件中要用到系统组件esp_driver_uart。
3、系统组件的确定
对于要依赖的系统组件不像私有依赖项那样一目了然,有时我们并不清楚所要的系统组件名称。比如我们不知道需要组件wpa_supplicant,这时我们可以先编译一次,看看错误信息:

在CMakeLists.txt中添加依赖组件REQUIRES esp_driver_uart,编译通过。
关于CMakeLists.txt文件
根和每个目录都有一个CMakeLists.txt文件,开始遇到的问题是不知道目录结构和怎样写CMakeLists.txt文件,要注意每一层目录中CMakeLists.txt文件的写法,本文的例子给出了一个简单的示范。对于复杂的项目还需要更多编写CMakeLists.txt文件的知识,请看简介中给出的官方文档。
ESP32移植Letter_shell问题
添加shell组件及其log,编译出错
可能原因:
宏使用不正确: 如果
SHELL_FREE旨在实际释放与companions对象关联的内存或资源,则当前定义不正确。它应该调用内存管理函数或执行其他必要的清理任务。编译器警告被视为错误:
-Werror=unused-value标志已启用,它将警告视为错误。即使宏使用本身可能不是关键问题,这也可能导致编译失败。
解决方案:
修复 SHELL_FREE 定义:
- 如果
companions需要内存分配,请更新shell_cfg.h中的 SHELL_FREE 宏以调用适当的内存管理函数,例如free()。 - 如果
companions不需要内存管理,请从shell_companion.c中的第 57 行删除SHELL_FREE调用;或者将shell_cfg.h中的第 36 行SHELL_USING_COMPANION的宏定义改为 0。
禁用 -Werror=unused-value (如果适用):
如果您希望将未使用的值警告视为警告而不是错误,您可以暂时在编译期间禁用 -Werror=unused-value 标志。但是,通常建议修复底层问题以避免潜在的内存泄漏或资源管理问题。
其他提示:
- 提供有关您的项目更多信息,例如具体的 ESP-IDF 版本、涉及的组件以及
SHELL_FREE宏的用途。这将有助于了解根本原因并提供更定制的指导。 - 分享
shell_cfg.h头文件和shell_companion.c文件的相关部分,以便分析代码结构和上下文。
考虑使用调试器逐步执行代码并检查companions在SHELL_FREE调用之前和之后的 值,以了解其使用情况和潜在的内存管理问题。 - 通过遵循这些步骤并提供更多信息,我可以帮助您有效地解决编译错误并确保您的 ESP-IDF 项目成功构建。
配置shell优先级
将shell的freertos优先级设置为 tskIDLE_PRIORITY,为0级,跟空闲函数优先级一样,所有其他优先级任务执行完后才会执行 tskIDLE_PRIORITY优先级任务。
源代码例程
ESP32启动流程解析
ESP32-IDF组件下载安装路径
安装所需组件:
1
2cd ~/esp/esp-adf/esp-idf
./install.sh esp32,esp32s3 # 可按需求安装若下载很慢,可按以下路径在
Windows/Linux下载:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49cd ~/esp/esp-adf/esp-idf/tools
vim tools.json
{
"tools": [
{
"description": "GDB for Xtensa",
"export_paths": [
[
"xtensa-esp-elf-gdb",
"bin"
]
],
"export_vars": {},
"info_url": "https://github.com/espressif/binutils-gdb",
"install": "always",
"license": "GPL-3.0-or-later",
"name": "xtensa-esp-elf-gdb",
"supported_targets": [
"esp32",
"esp32s2",
"esp32s3"
],
"version_cmd": [
"xtensa-esp-elf-gdb-no-python",
"--version"
],
"version_regex": "GNU gdb \\(esp-gdb\\) ([a-z0-9.-_]+)",
"versions": [
{
"linux-amd64": {
"sha256": "b5f7cc3e4b5a58db655754083ed9652e4953e71c3b4922fb624e7a034ec24a64",
"size": 26947336,
"url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v11.2_20220823/xtensa-esp-elf-gdb-11.2_20220823-x86_64-linux-gnu.tar.gz"
},
"linux-arm64": {
"sha256": "816acfae38b6b443f4f1590395f68f079243539259d19c7772ae6416c6519444",
"size": 27134508,
"url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v11.2_20220823/xtensa-esp-elf-gdb-11.2_20220823-aarch64-linux-gnu.tar.gz"
},
"linux-armel": {
"sha256": "4dd1bace0633196fddfdcef3cebcc4bbfce22f5a0d2d1e3d618f3d8a6cbfcacc",
"size": 25205239,
"url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v11.2_20220823/xtensa-esp-elf-gdb-11.2_20220823-arm-linux-gnueabi.tar.gz"
},
"linux-armhf": {
"sha256": "53a142b9a508a8babe6b7edf3090bb49e3714380ba819b54052425fcf1ac6f9c",
"size": 23491575,
"url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v11.2_20220823/xtensa-esp-elf-gdb-11.2_20220823-arm-linux-gnueabihf.tar.gz"
...
ESP32网络问题
基本外设:camera: OV3660 + LCD: ILI9341
- 当前
IDF5.4版本在配置时可以连接下载组件,但是有时候网络不好,连接超时。
修改
Git的URL为镜像源(如Gitee)1
2
3
4
5
6
7
8
9修改 Git 的 URL 为镜像源(如 Gitee)
git config --global url."git@github.com:espressif/esp32-camera.git".insteadOf "https://github.com/espressif/esp32-camera.git"
查看当前 Git 的 URL 替换规则
git config --global --get-regexp "url\..*\.insteadOf"
删除 Gitee 镜像的替换规则
git config --global --unset url."https://gitee.com/espressif/esp32-camera.git".insteadOf
或者直接修改回 GitHub 官方源(可选)
git config --global url."https://github.com/espressif/esp32-camera.git".insteadOf "git@github.com:espressif/esp32-camera.git"GitHub限流:频繁克隆可能触发限流,稍后再试。下载
esp32-camera
- 从
GitHub直接下载压缩包:https://github.com/espressif/esp32-camera/archive/refs/heads/master.zip - 解压后放到
ESP-IDF的组件目录中:1
2unzip esp32-camera-master.zip
mv esp32-camera-master ~/esp/esp-adf/esp-idf/components/esp32-camera
修改项目的 CMakeLists.txt, 确保项目能找到本地组件,例如:
1 | set(EXTRA_COMPONENT_DIRS ~/esp/esp-adf/esp-idf/components/esp32-camera) |
常见问题处理
- 依赖未更新:修改
idf_component.yml后需运行idf.py reconfigure重新解析。 - 本地组件覆盖:若同时存在本地组件和注册表同名组件,优先使用路径引用的本地版本。
- 锁文件冲突:若
dependencies.lock被误改,删除后重新运行reconfigure即可恢复。
ESP32对sdkconfig的解释
当IDF的版本不一样时,sdkconfig如果不加修改就直接用,大概率会跑飞,需要根据实际情况进行修改。
sdkconfig- 实际构建时生成的最终配置
- 合并了所有默认配置和
menuconfig的修改 - 是实际编译使用的配置文件
sdkconfig.defaults- 项目级别的默认配置
- 包含WiFi凭据、分区表、网络设置等应用层配置
- 覆盖芯片特定配置中的部分设置
sdkconfig.defaults.esp32s3- 芯片特定的默认配置(针对
ESP32-S3) - 包含
PSRAM配置、缓存设置、外设配置 - 主要用于定义硬件相关的基础配置
- 芯片特定的默认配置(针对
RTSP推流问题
VLC可能认为192.168.4.2是主机IP,没有请求192.168.4.1,需要注意修改。
ESP32S3开发摄像头模组
编译环境
- Linux Ubuntu 22.04
- ADF v2.7 commit:c732d65dee097a97902041cda6fb14013e99631a
- IDF (HEAD, tag: v5.4) commit:67c1de1eebe095d554d281952fde63c16ee2dca0
创建仓库,设计框架
复制一个
DEMO创建仓库,有main.c函数,能跑通。添加
components:1
2
3idf.py -C components create-component lcd_camera
// 下载esp32-camera组件,地址:git@github.com:espressif/esp32-camera.git
mv -rf esp32-camera ${workspaceFolder}/componentsmain文件夹里注释掉注释掉idf_component.yml和Kconfig.projbuild,我们这里暂时不使用自动下载依赖工具和idf.py menuconfig配置,后发现其他组件如esp32-camera组件中也会有.yml文件,暂时从网上更新拉取依赖,后期再做隔离。编写顶层
CmakeLists.txt,注意,ESP-IDF构建系统并不会自动为每个组件添加所有依赖关系,需要显式地告诉CMake每个组件依赖谁,以下便是会扫描这些路径,看看里面有没有组件(components),但是还需要再文件使用时添加依赖的组件,如:REQUIRES esp_wifi。1
2
3
4
5
6
7
8
9
10
11
12
13cmake_minimum_required(VERSION 3.5)
include($ENV{ADF_PATH}/CMakeLists.txt)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# file(GLOB_RECURSE SOURCES "main/*.c")
# set(EXTRA_COMPONENT_DIRS "components")
set(EXTRA_COMPONENT_DIRS
components
)
project(camera_test)以上可分为三步:
set(EXTRA_COMPONENT_DIRS ...)告诉CMake哪里有组件目录,仅是可见范围,不代表使用。idf_component_register()声明一个组件(模块),必须有,才能被当成组件处理。PRIV_REQUIRES/REQUIRES显式声明我依赖谁,必须手动写,否则链接失败或头文件找不到。
建议:可以保持
EXTRA_COMPONENT_DIRS写法用于组件扫描;但每一个.c文件所在组件,都必须有自己明确依赖的组件声明。
main主文件夹的CmakeLists.txt:1
2
3
4
5
6
7
8
9
10
11idf_component_register(
SRCS "main.c"
INCLUDE_DIRS "."
REQUIRES
lcd_camera
wifi_softap
rtsp_server
http_server
web_mjpeg_server
)
⚡ 为什么 main 里不需要 REQUIRES,在其他组件需要手动添加依赖?
- 因为
main是特殊组件,它会自动看到整个工程的公共include path和链接库。 - 但单独组件要访问其它组件的
API,就必须在idf_component_register里REQUIRES指明依赖。
自动依赖与显式依赖的区别:
main里没写REQUIRES/PRIV_REQUIRES,就像“放开手脚,扫描所有能找到的组件”,所以可能包含了esp_http_server。main里写了REQUIRES/PRIV_REQUIRES,就像“限定只扫描你指定的组件及依赖”,没写的组件不会被扫描。
大致查找顺序是这样:
- 项目本地组件目录
- 默认是项目根目录下的
components/文件夹, 此目录为默认组件目录,set(EXTRA_COMPONENT_DIRS xxx)时可不添加。 - 如果设置了
EXTRA_COMPONENT_DIRS,也会优先按顺序在这些路径里查找
ESP-IDF自带组件目录
- 即
$IDF_PATH/components
- 通过
idf.py add-dependency或dependencies.lock下载的managed_components
- 存放在
managed_components/中,这些优先级比IDF内置高,但低于项目本地组件
现在有两种方法:
- 在线方法:保留
managed_components,在编译的时候会对比网上拉取的最新固件版本,并更新,无网络时有风险。 - 离线方法:新建
offline_components,拷贝managed_components的所有数据到offline_components,删除managed_components,在顶层CmakeLists.txt中添加,但可能组件更新不及时,落后主分支:1
2
3
4
5set(EXTRA_COMPONENT_DIRS
"components"
"offline_components" # 添加离线组件路径
"$ENV{ADF_PATH}/components" # 显式添加ESP-ADF组件路径, 上面 include($ENV{ADF_PATH}/CMakeLists.txt) 未生效
)
**特别的解释(解释ADF_PATH环境中的components未生效)**:
ADF_PATH/CMakeLists.txt→ 加载ADF的构建逻辑(不一定包含组件路径)。EXTRA_COMPONENT_DIRS += ADF_PATH/components→ 确保ADF组件能被找到。- 如果去掉
ADF_PATH/components,可能会导致 部分ADF组件无法被扫描到,编译时报错。
流媒体协议全解析:RTSP/RTP/TCP/UDP
摄像头推流问题汇总
- 可能的问题点:
- 帧率控制问题:
- 当前实现没有稳定的帧率控制
- 帧间隔计算不准确导致画面抖动
- RTP时间戳错误:
- 时间戳单位不正确(应该是
90kHz) - 时间戳增量不固定导致画面抖动
- 时间戳单位不正确(应该是
- JPEG头格式问题:
- 类型特定(
type specific)字段需要正确设置 Q因子设置不合理
- 类型特定(
- 网络稳定性:
UDP发送缓冲区不足- 缺少重传机制
- 分析调试:
frame2jpg(fb, 60, &jpeg_buf, &jpeg_len)这个函数转换需要100ms~150ms,很慢。PIXFORMAT_RGB565适合LCD显示;PIXFORMAT_JPEG,只推流不显示速度会快很多,同时有以下问题。
camera硬件解码(PIXFORMAT_JPEG)VLC显示闪屏:- 已排除
errno=12对应ENOMEM,即系统内存不足(尤其是LWIP UDP发送缓冲区)。这说明UDP发送缓冲区或堆内存压力过大,导致RTP数据包发送失败,从而客户端播放时卡顿闪屏。 - 已排除
Quant Table缺失(q≠0) - 已排除
VLC播放器问题 - 已排除
AP/UDP网络不稳定 - 已排除
JPEG数据不完整
- 已排除
调试发现与
RTP JPEG Type有很大关系,问题测试过程如下:
| 图像来源 | JPEG 类型内容 | RTP JPEG Type | 是否闪屏 | 说明 |
|---|---|---|---|---|
| 摄像头原生 JPEG | 包含 DQT/DHT | Type = 0 |
❌ 不闪屏 | ✅ 正确 |
| 摄像头原生 RGB565 | 无 JPEG | Type = 1 |
❌ 不闪屏 | ✅ 正确 |
| JPEG → RGB565 → RTP | 原图有 DQT/DHT | Type = 0 |
⚠️ 闪屏 | ❌ 错误 |
| JPEG → RGB565 → RTP | 原图有 DQT/DHT | Type = 1 |
❌ 不闪屏 | ✅ 正确 |
| RGB565 → 软件 JPEG 编码 | 编码包含 DQT/DHT | Type = 0 |
❌ 不闪屏 | ✅ 正确 |
| RGB565 → 软件 JPEG 编码 | 编码包含 DQT/DHT | Type = 1 |
⚠️ 闪屏 | ❌ 错误 |
RTP JPEG Type设置错误带来的解码行为:
| Type 值 | VLC 等播放器行为 |
|---|---|
Type = 0 |
播放器使用 JPEG 内嵌的 DQT/DHT 表进行解码(需 JPEG 包含完整头) |
Type = 1 |
播放器使用默认内置表(RFC2435 附录)进行解码,忽略图像中 DQT |
✅核心推论:🚨 不管图像来源是否是 JPEG,如果你用 RTP Type = 0,就必须确保 JPEG 中包含 DQT/DHT!否则 VLC 会闪屏?
- 添加打印以验证分析理论:
- 前置条件:
RGB565→ 软件JPEG编码;type = 0。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47// 检测 JPEG 是否包含 DQT 段 (0xFFDB)
static bool jpeg_has_dqt(const uint8_t *data, size_t len) {
size_t i = 2; // 跳过 SOI 0xFFD8
while (i + 4 <= len) {
if (data[i] != 0xFF) {
// 非法 marker
break;
}
uint8_t marker = data[i + 1];
// 0xFFDA 是 SOS,之后是压缩数据段,不再查找
if (marker == 0xDA) {
break;
}
// 跳过填充的 0xFF(合法 JPEG 允许多个)
while (marker == 0xFF && i + 2 < len) {
i++;
marker = data[i + 1];
}
// marker 长度字段位于 i+2,2 字节
if (i + 4 > len) break;
uint16_t segment_length = (data[i + 2] << 8) | data[i + 3];
if (segment_length < 2) break;
if (marker == 0xDB) {
return true; // DQT 找到了
}
i += 2 + segment_length;
}
return false;
}
... ...
// 检测是否包含 DQT,决定 RTP JPEG Type (在校验完报头时打印)
bool has_dqt = jpeg_has_dqt(jpeg, len);
uint8_t type = has_dqt ? 0 : 1;
ESP_LOGI(TAG, "JPEG header: %02X %02X, has_dqt=%s → type=%d", jpeg[0], jpeg[1], has_dqt ? "YES" : "NO", type);
... ...
// 打印结果说明具有0xFFD8,包含DQT。
I (19451) RTSP_SERVER: JPEG header: FF D8, has_dqt=YES → type=0
- 前置条件:
RGB565→ 软件JPEG编码;type = 1。1
2// 打印结果说明具有0xFFD8,包含DQT。
I (19451) RTSP_SERVER: JPEG header: FF D8, has_dqt=YES → type=0
- 结论:有
DQT ≠可以用type=0,只有 摄像头生成的标准JPEG(完全按MJPEG over RTP规范压缩的)才能稳定使用type=0,否则必须使用type=1。
- 结论总结:
ESP32S3偶发性重启
idf.py menuconfig:
Component config → LWIP → TCP -> (65535) Default send buffer size
Component config —> HTTP Server —> [*] WebSocket server support (CONFIG_HTTPD_WS_SUPPORT=y)
会出现重启、传输时阻塞卡死、画面卡住以下是常见的
error日志:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56I (782633) RTSP_SERVER: Streaming: 10 FPS, 50 pkts, 5619 bytes, 0 errs (0.0%)
W (782833) RTSP_SERVER: Sendto failed at offset 2760, errno=12 (Not enough space)
W (782833) RTSP_SERVER: Non-first packet failed at offset 2760, continuing
W (782843) RTSP_SERVER: Sendto failed at offset 4140, errno=12 (Not enough space)
W (782843) RTSP_SERVER: Non-first packet failed at offset 4140, continuing
W (782853) RTSP_SERVER: Sendto failed at offset 5520, errno=12 (Not enough space)
W (782863) RTSP_SERVER: Non-first packet failed at offset 5520, continuing
W (782963) RTSP_SERVER: Sendto failed at offset 0, errno=12 (Not enough space)
... ...
E (560523) RTSP_SERVER: Fatal send error: errno=-1
W (560523) RTSP_SERVER: Sendto failed at offset 0, errno=-1 ()
W (560523) RTSP_SERVER: Drop frame due to send failure at offset 0 (critical)
... ...
W (1638) cam_hal: NO-SOI
Guru Meditation Error: Core 0 panic'ed (LoadProhibited). Exception was unhandled.
Core 0 register dump:
PC : 0x4037770b PS : 0x00060033 A0 : 0x8201981a A1 : 0x3fc9e0c0
--- 0x4037770b: ll_cam_send_event at /home/ubuntu/esp/work/camera_test/managed_components/esp32-camera/driver/cam_hal.c:107
A2 : 0x00060523 A3 : 0x00000001 A4 : 0x3fc9e0f0 A5 : 0x3fcad5f0
A6 : 0x00060d23 A7 : 0x00000000 A8 : 0x03c9bc78 A9 : 0x01ffffff
A10 : 0x3fc9bc78 A11 : 0x3fc9e0c0 A12 : 0x3fc9e0f0 A13 : 0x00000000
A14 : 0x3fc9c604 A15 : 0x0000cdcd SAR : 0x00000000 EXCCAUSE: 0x0000001c
EXCVADDR: 0x0006054b LBEG : 0x00000000 LEND : 0x00000000 LCOUNT : 0x00000000
Backtrace: 0x40377708:0x3fc9e0c0 0x42019817:0x3fc9e0f0 0x40376ae1:0x3fc9e120 0x40377d3d:0x3fc9e140 0x4037b66b:0x3fcad660 0x42004db6:0x3fcad680 0x403801f1:0x3fcad6a0 0x4037f04d:0x3fcad6c0
--- 0x40377708: ll_cam_send_event at /home/ubuntu/esp/work/camera_test/managed_components/esp32-camera/driver/cam_hal.c:107
--- 0x42019817: ll_cam_vsync_isr at /home/ubuntu/esp/work/camera_test/managed_components/esp32-camera/target/esp32s3/ll_cam.c:106
--- 0x40376ae1: shared_intr_isr at /home/ubuntu/esp/esp-adf/esp-idf/components/esp_hw_support/intr_alloc.c:464
--- 0x40377d3d: _xt_lowint1 at /home/ubuntu/esp/esp-adf/esp-idf/components/xtensa/xtensa_vectors.S:1240
--- 0x4037b66b: xt_utils_wait_for_intr at /home/ubuntu/esp/esp-adf/esp-idf/components/xtensa/include/xt_utils.h:82
--- (inlined by) esp_cpu_wait_for_intr at /home/ubuntu/esp/esp-adf/esp-idf/components/esp_hw_support/cpu.c:55
--- 0x42004db6: esp_vApplicationIdleHook at /home/ubuntu/esp/esp-adf/esp-idf/components/esp_system/freertos_hooks.c:58
--- 0x403801f1: prvIdleTask at /home/ubuntu/esp/esp-adf/esp-idf/components/freertos/FreeRTOS-Kernel/tasks.c:4341 (discriminator 1)
--- 0x4037f04d: vPortTaskWrapper at /home/ubuntu/esp/esp-adf/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:139
ELF file SHA256: 6ae9a007e
Rebooting...
���ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0xc (RTC_SW_CPU_RST),boot:0x28 (SPI_FAST_FLASH_BOOT)
Saved PC:0x40375da1
--- 0x40375da1: esp_restart_noos at /home/ubuntu/esp/esp-adf/esp-idf/components/esp_system/port/soc/esp32s3/system_internal.c:162
... ...
W (18220) cam_hal: FB-OVF
W (18310) cam_hal: FB-OVF
W (18320) cam_hal: NO-EOI
E (18320) lcd_camera: hardware jpeg get failed!
W (18330) cam_hal: FB-OVF
W (18460) cam_hal: FB-OVF
W (18460) cam_hal: NO-EOI
W (18470) cam_hal: FB-OVF注释法,轮流注释
camera和作对比。- 用
JPEG格式(PIXFORMAT_JPEG)采集数据会有概率重启?
- 用
jpeg转换成rgb565,参数跟demo有点不同.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// jpg2rgb565原始函数显示花屏,需要.flags.swap_color_bytes = 1
static uint8_t work[3100];
bool myjpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, esp_jpeg_image_scale_t scale)
{
esp_jpeg_image_cfg_t jpeg_cfg = {
.indata = (uint8_t *)src,
.indata_size = src_len,
.outbuf = out,
.outbuf_size = UINT32_MAX, // @todo: this is very bold assumption, keeping this like this for now, not to break existing code
.out_format = JPEG_IMAGE_FORMAT_RGB565,
.out_scale = scale,
.flags.swap_color_bytes = 1,
.advanced.working_buffer = work,
.advanced.working_buffer_size = sizeof(work),
};
esp_jpeg_image_output_t output_img = {};
if(esp_jpeg_decode(&jpeg_cfg, &output_img) != ESP_OK){
return false;
}
return true;
}
ESP32系列初始化
NVS(Non-Volatile Storage,非易失性存储)初始化:
1 | esp_err_t ret = nvs_flash_init(); |
- 初始化
NVS
nvs_flash_init()会挂载NVS分区(通常是flash里一个叫 “nvs“ 的分区)。NVS是一个key-value存储系统,可以用来保存Wi-Fi配置、设备状态、参数等,断电不会丢失。- ESP32 内部很多系统组件(比如
Wi-Fi)都依赖NVS存储。
- 处理版本或容量异常
ESP_ERR_NVS_NO_FREE_PAGES:表示NVS分区空间不足,无法再写入新的key-value(可能是结构变化后旧数据无效占用空间)。ESP_ERR_NVS_NEW_VERSION_FOUND:表示固件更新后,NVS的结构版本与当前IDF版本不兼容(比如升级了ESP-IDF)。- 这两种情况都需要 擦除
NVS分区 再重新初始化。
- 调用
nvs_flash_erase()
- 会清空整个
NVS分区(相当于恢复出厂设置)。 - 然后再次调用
nvs_flash_init()完成初始化。
ESP_ERROR_CHECK(ret)
- 如果
ret不是ESP_OK,会直接报错并终止程序(方便调试)。
💡 总结
- 这段代码的目的是:确保 NVS 存储可用且结构版本匹配,否则会自动清空并重新初始化。
- 这是很多
ESP-IDF项目都会放在app_main()开头的“惯用初始化代码”,特别是Wi-Fi或BLE工程。 - 如果你不用
NVS,可以不加,但ESP-IDF内部有些API会自动依赖它,比如esp_wifi_init()。
## 摄像头开发
摄像头配置
OV3660(30W像素)和SC101(100W像素)的配置有些不同:OV3660在idf.py menuconfig勾选配置好就行了。SC101需要勾选加配置Component config→Camera configuration->Support SC101IOT HD(SC101iot default regs(xclk20M_720p_15fps)),xclk20M_720p_15fps模式比VGA模式采样频率低,不容易超时。
缓存配置
默认发送缓存太小,需要调整
idf.py menuconfig:1
2
3
4
5
6
7
8
9
10
11
12
13Component config --->
LWIP --->
[*] Enable SO_RCVBUF option # 打开后才能设置缓存大小(这是接收的,设置没用)
LWIP → UDP
(256) Maximum active UDP control blocks
(64) Default UDP receive mail box size
LWIP → TCP
(32768) Default send buffer size
(5760) Default receive window size
(32) Default TCP receive mail box size
(6) Default TCP accept mail box size代码里设置缓存
1
2
3
4// ✅ 设置发送缓冲区大小
int send_buf_size = UDP_SEND_BUF_SIZE;
setsockopt(udp_sock, SOL_SOCKET, SO_SNDBUF, &send_buf_size, sizeof(send_buf_size));
ESP32S3下载模式
USB下载模式(第一次启动需要使用boot0和EN启动?):1
2
3
4
5
6
7
8idf.py menuconfig
(Top) → Component config → ESP System Settings → Channel for console output
( ) Default: UART0
(X) USB CDC
( ) USB Serial/JTAG Controller
( ) Custom UART
( ) None未使用
boot0和EN启动(如果IO0(BOOT)是浮空的,那芯片上电/复位时就可能被误判为“拉低”而进入下载模式):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38Uploading stub...
Running stub...
Stub running...
Changing baud rate to 1152000
Changed.
Configuring flash size...
Flash will be erased from 0x00000000 to 0x00005fff...
Flash will be erased from 0x00010000 to 0x00042fff...
Flash will be erased from 0x00008000 to 0x00008fff...
SHA digest in image updated
Compressed 21008 bytes to 13371...
Writing at 0x00000000... (100 %)
Wrote 21008 bytes (13371 compressed) at 0x00000000 in 0.1 seconds (effective 1155.7 kbit/s)...
Hash of data verified.
Compressed 206832 bytes to 110421...
Writing at 0x0003da2f... (100 %)
Wrote 206832 bytes (110421 compressed) at 0x00010000 in 1.0 seconds (effective 1640.5 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 103...
Writing at 0x00008000... (100 %)
Wrote 3072 bytes (103 compressed) at 0x00008000 in 0.0 seconds (effective 2204.5 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting via RTS pin...
Done
Executing action: monitor
Running idf_monitor in directory /home/ubuntu/esp/work/hello_world
Executing "/home/ubuntu/.espressif/python_env/idf5.4_py3.10_env/bin/python /home/ubuntu/esp/esp-adf/esp-idf/tools/idf_monitor.py -p /dev/ttyACM0 -b 115200 --toolchain-prefix xtensa-esp32s3-elf- --target esp32s3 --revision 0 /home/ubuntu/esp/work/hello_world/build/hello_world.elf -m '/home/ubuntu/.espressif/python_env/idf5.4_py3.10_env/bin/python' '/home/ubuntu/esp/esp-adf/esp-idf/tools/idf.py' '-p' '/dev/ttyACM0'"...
--- esp-idf-monitor 1.6.2 on /dev/ttyACM0 115200
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x15 (USB_UART_CHIP_RESET),boot:0x3 (DOWNLOAD(USB/UART0))
Saved PC:0x40041a76
--- 0x40041a76: ets_delay_us in ROM
waiting for download