IDF 监视器

[English]

IDF 监视器是一个串行终端程序,使用了 esp-idf-monitor 包,用于收发目标设备串口的串行数据,IDF 监视器同时还兼具 ESP-IDF 的其他特性。

在 ESP-IDF 中调用 idf.py monitor 可以启用此监视器。

操作快捷键

为了方便与 IDF 监视器进行交互,请使用表中给出的快捷键。这些快捷键可以自定义,请查看 配置文件 章节了解详情。

快捷键

操作

描述

Ctrl + ]

退出监视器程序

Ctrl + T

菜单退出键

按下如下给出的任意键之一,并按指示操作。

  • Ctrl + T

将菜单字符发送至远程

  • Ctrl + ]

将 exit 字符发送至远程

  • Ctrl + P

重置目标设备,进入引导加载程序,通过 RTS 线暂停应用程序

重置目标设备,通过 RTS 线(如已连接)进入引导加载程序,此时开发板不运行任何程序。等待其他设备启动时可以使用此操作。

  • Ctrl + R

通过 RTS 线重置目标设备

重置设备,并通过 RTS 线(如已连接)重新启动应用程序。

  • Ctrl + F

编译并烧录此项目

暂停 idf_monitor,运行 flash 目标,然后恢复 idf_monitor。任何改动的源文件都会被重新编译,然后重新烧录。如果 idf_monitor 是以参数 -E 启动的,则会运行目标 encrypted-flash

  • Ctrl + A (或者 A)

仅编译及烧录应用程序

暂停 idf_monitor,运行 app-flash 目标,然后恢复 idf_monitor。 这与 flash 类似,但只有主应用程序被编译并被重新烧录。如果 idf_monitor 是以参数 -E 启动的,则会运行目标 encrypted-flash

  • Ctrl + Y

停止/恢复在屏幕上打印日志输出

激活时,会丢弃所有传入的串行数据。允许在不退出监视器的情况下快速暂停和检查日志输出。

  • Ctrl + L

停止/恢复向文件写入日志输出

在工程目录下创建一个文件,用于写入日志输出。可使用快捷键停止/恢复该功能(退出 IDF 监视器也会终止该功能)。

  • Ctrl + I (或者 I)

停止/恢复打印时间标记

IDF 监视器可以在每一行的开头打印一个时间标记。时间标记的格式可以通过 --timestamp-format 命令行参数来改变。

  • Ctrl + H (或者 H)

显示所有快捷键

  • Ctrl + X (或者 X)

退出监视器程序

Ctrl + C

中断正在运行的应用程序

暂停 IDF 监视器并运行 GDB 项目调试器,从而在运行时调试应用程序。这需要启用 :ref: CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME 选项。

除了 Ctrl-]Ctrl-T,其他快捷键信号会通过串口发送到目标设备。

兼具 ESP-IDF 特性

自动解码地址

每当芯片输出指向可执行代码的十六进制地址时,IDF 监视器将查找该地址在源代码中的位置(文件名和行号),并在下一行用黄色打印出该位置。

ESP-IDF 应用程序发生 crash 和 panic 事件时,将产生如下的寄存器转储和回溯:

Guru Meditation Error of type StoreProhibited occurred on core  0. Exception was unhandled.
Register dump:
PC      : 0x400f360d  PS      : 0x00060330  A0      : 0x800dbf56  A1      : 0x3ffb7e00
A2      : 0x3ffb136c  A3      : 0x00000005  A4      : 0x00000000  A5      : 0x00000000
A6      : 0x00000000  A7      : 0x00000080  A8      : 0x00000000  A9      : 0x3ffb7dd0
A10     : 0x00000003  A11     : 0x00060f23  A12     : 0x00060f20  A13     : 0x3ffba6d0
A14     : 0x00000047  A15     : 0x0000000f  SAR     : 0x00000019  EXCCAUSE: 0x0000001d
EXCVADDR: 0x00000000  LBEG    : 0x4000c46c  LEND    : 0x4000c477  LCOUNT  : 0x00000000

Backtrace: 0x400f360d:0x3ffb7e00 0x400dbf56:0x3ffb7e20 0x400dbf5e:0x3ffb7e40 0x400dbf82:0x3ffb7e60 0x400d071d:0x3ffb7e90

IDF 监视器为寄存器转储补充如下信息:

Guru Meditation Error of type StoreProhibited occurred on core  0. Exception was unhandled.
Register dump:
PC      : 0x400f360d  PS      : 0x00060330  A0      : 0x800dbf56  A1      : 0x3ffb7e00
0x400f360d: do_something_to_crash at /home/gus/esp/32/idf/examples/get-started/hello_world/main/./hello_world_main.c:57
(inlined by) inner_dont_crash at /home/gus/esp/32/idf/examples/get-started/hello_world/main/./hello_world_main.c:52
A2      : 0x3ffb136c  A3      : 0x00000005  A4      : 0x00000000  A5      : 0x00000000
A6      : 0x00000000  A7      : 0x00000080  A8      : 0x00000000  A9      : 0x3ffb7dd0
A10     : 0x00000003  A11     : 0x00060f23  A12     : 0x00060f20  A13     : 0x3ffba6d0
A14     : 0x00000047  A15     : 0x0000000f  SAR     : 0x00000019  EXCCAUSE: 0x0000001d
EXCVADDR: 0x00000000  LBEG    : 0x4000c46c  LEND    : 0x4000c477  LCOUNT  : 0x00000000

Backtrace: 0x400f360d:0x3ffb7e00 0x400dbf56:0x3ffb7e20 0x400dbf5e:0x3ffb7e40 0x400dbf82:0x3ffb7e60 0x400d071d:0x3ffb7e90
0x400f360d: do_something_to_crash at /home/gus/esp/32/idf/examples/get-started/hello_world/main/./hello_world_main.c:57
(inlined by) inner_dont_crash at /home/gus/esp/32/idf/examples/get-started/hello_world/main/./hello_world_main.c:52
0x400dbf56: still_dont_crash at /home/gus/esp/32/idf/examples/get-started/hello_world/main/./hello_world_main.c:47
0x400dbf5e: dont_crash at /home/gus/esp/32/idf/examples/get-started/hello_world/main/./hello_world_main.c:42
0x400dbf82: app_main at /home/gus/esp/32/idf/examples/get-started/hello_world/main/./hello_world_main.c:33
0x400d071d: main_task at /home/gus/esp/32/idf/components/esp32/./cpu_start.c:254

IDF 监视器在后台运行以下命令,解码各地址:

xtensa-esp32-elf-addr2line -pfiaC -e build/PROJECT.elf ADDRESS

如果在应用程序源代码中找不到匹配的地址,IDF 监视器还会检查 ROM 代码。此时不会打印源文件名和行号,只显示 函数名 in ROM:

abort() was called at PC 0x40007c69 on core 0
0x40007c69: ets_write_char in ROM

Backtrace: 0x40081656:0x3ffb4ac0 0x40085729:0x3ffb4ae0 0x4008a7ce:0x3ffb4b00 0x40007c69:0x3ffb4b70 0x40008148:0x3ffb4b90 0x400d51d7:0x3ffb4c20 0x400e31bc:0x3ffb4c50 0x40087bc5:0x3ffb4c80
0x40081656: panic_abort at /Users/espressif/esp-idf/components/esp_system/panic.c:452
0x40085729: esp_system_abort at /Users/espressif/esp-idf/components/esp_system/port/esp_system_chip.c:90
0x4008a7ce: abort at /Users/espressif/esp-idf/components/newlib/abort.c:38
0x40007c69: ets_write_char in ROM
0x40008148: ets_printf in ROM
0x400d51d7: app_main at /Users/espressif/esp-idf/examples/get-started/hello_world/main/hello_world_main.c:49
0x400e31bc: main_task at /Users/espressif/esp-idf/components/freertos/app_startup.c:208 (discriminator 13)
0x40087bc5: vPortTaskWrapper at /Users/espressif/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:162
.....

ROM ELF 文件会根据 IDF_PATHESP_ROM_ELF_DIR 环境变量的路径自动加载。如需覆盖此行为,可以通过调用 esp_idf_monitor 并指定特定的 ROM ELF 文件路径:python -m esp_idf_monitor --rom-elf-file [ROM ELF 文件的路径]

备注

将环境变量 ESP_MONITOR_DECODE 设置为 0 或者调用 esp_idf_monitor 的特定命令行选项 python -m esp_idf_monitor --disable-address-decoding 来禁止地址解码。

连接时复位目标芯片

默认情况下,IDF 监视器会在目标芯片连接时通过 DTR 和 RTS 串行线自动复位芯片。要防止 IDF 监视器在连接时自动复位,请在调用 IDF 监视器时加上选项 --no-reset,如 idf.py monitor --no-reset

备注

--no-reset 选项在 IDF 监视器连接到特定端口时可以实现同样的效果,如 idf.py monitor --no-reset -p [PORT]

配置 GDBStub 以启用 GDB

GDBStub 支持在运行时进行调试。GDBStub 在目标上运行,并通过串口连接到主机从而接收调试命令。GDBStub 支持读取内存和变量、检查调用堆栈帧等命令。虽然没有 JTAG 调试通用,但由于 GDBStub 完全通过串行端口完成通信,故不需要使用特殊硬件(如 JTAG/USB 桥接器)。

通过设置 CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME,可以将目标配置为在后台运行 GDBStub。GDBStub 将保持在后台运行,直到通过串行端口发送 Ctrl+C 导致应用程序中断(即停止程序执行),从而让 GDBStub 处理调试命令。

此外,还可以通过设置 CONFIG_ESP_SYSTEM_PANICGDBStub on panic 来配置 panic 处理程序,使其在发生 crash 事件时运行 GDBStub。当 crash 发生时,GDBStub 将通过串口输出特殊的字符串模式,表示 GDBStub 正在运行。

无论是通过发送 Ctrl+C 还是收到特殊字符串模式,IDF 监视器都会自动启动 GDB,从而让用户发送调试命令。GDB 退出后,通过 RTS 串口线复位目标。如果未连接 RTS 串口线,请按复位键,手动复位开发板。

备注

IDF 监视器在后台运行如下命令启用 GDB:

xtensa-esp32-elf-gdb -ex "set serial baud BAUD" -ex "target remote PORT" -ex interrupt build/PROJECT.elf :idf_target:`Hello NAME chip`

输出筛选

可以调用 idf.py monitor --print-filter="xyz" 启动 IDF 监视器,其中,--print-filter 是输出筛选的参数。参数默认值为空字符串,即打印所有内容。支持使用环境变量 ESP_IDF_MONITOR_PRINT_FILTER 调整筛选设置。

备注

同时使用环境变量 ESP_IDF_MONITOR_PRINT_FILTER 和参数 --print-filter 时,通过命令行输入的 CLI 参数 --print-filter 优先级更高。

若需对打印内容设置限制,可指定 <tag>:<log_level> 等选项,其中 <tag> 是标签字符串,<log_level>{N, E, W, I, D, V, *} 集合中的一个字母,指的是 日志 级别。

例如,--print_filter="tag1:W" 只匹配并打印 ESP_LOGW("tag1", ...) 所写的输出,或者写在较低日志详细度级别的输出,即 ESP_LOGE("tag1", ...)。请勿指定 <log_level> 或使用详细级别默认值 *

备注

编译时,可以使用主日志在 日志库 中禁用不需要的输出。也可以使用 IDF 监视器筛选输出来调整筛选设置,且无需重新编译应用程序。

应用程序标签不能包含空格、星号 *、冒号 :,以便兼容输出筛选功能。

如果应用程序输出的最后一行后面没有回车,可能会影响输出筛选功能,即,监视器开始打印该行,但后来发现该行不应该被写入。这是一个已知问题,可以通过添加回车来避免此问题(特别是在没有输出紧跟其后的情况下)。

筛选规则示例

  • * 可用于匹配任何类型标签。但 --print_filter="*:I tag1:E" 打印关于 tag1 的输出时会报错,这是因为 tag1 规则比 * 规则的优先级高。

  • 默认规则(空)等价于 *:V,因为在详细级别或更低级别匹配任意标签即意味匹配所有内容。

  • "*:N" 不仅抑制了日志功能的输出,也抑制了 printf 的打印输出。为了避免这一问题,请使用 *:E 或更高的冗余级别。

  • 规则 "tag1:V""tag1:v""tag1:""tag1:*""tag1" 等同。

  • 规则 "tag1:W tag1:E" 等同于 "tag1:E",这是因为后续出现的具有相同名称的标签会覆盖掉前一个标签。

  • 规则 "tag1:I tag2:W" 仅在 Info 详细度级别或更低级别打印 tag1,在 Warning 详细度级别或更低级别打印 tag2

  • 规则 "tag1:I tag2:W tag3:N" 在本质上等同于上一规则,这是因为 tag3:N 指定 tag3 不打印。

  • tag3:N 在规则 "tag1:I tag2:W tag3:N *:V" 中更有意义,这是因为如果没有 tag3:Ntag3 信息就可能打印出来了;tag1tag2 错误信息会打印在指定的详细度级别(或更低级别),并默认打印所有内容。

高级筛选规则示例

如下日志是在没有设置任何筛选选项的情况下获得的:

load:0x40078000,len:13564
entry 0x40078d4c
E (31) esp_image: image at 0x30000 has invalid magic byte
W (31) esp_image: image at 0x30000 has invalid SPI mode 255
E (39) boot: Factory app partition is not bootable
I (568) cpu_start: Pro cpu up.
I (569) heap_init: Initializing. RAM available for dynamic allocation:
I (603) cpu_start: Pro cpu start user code
D (309) light_driver: [light_init, 74]:status: 1, mode: 2
D (318) vfs: esp_vfs_register_fd_range is successful for range <54; 64) and VFS ID 1
I (328) wifi: wifi driver task: 3ffdbf84, prio:23, stack:4096, core=0

--print_filter="wifi esp_image:E light_driver:I" 筛选选项捕获的输出如下所示:

E (31) esp_image: image at 0x30000 has invalid magic byte
I (328) wifi: wifi driver task: 3ffdbf84, prio:23, stack:4096, core=0

--print_filter="light_driver:D esp_image:N boot:N cpu_start:N vfs:N wifi:N *:V" 选项的输出如下:

load:0x40078000,len:13564
entry 0x40078d4c
I (569) heap_init: Initializing. RAM available for dynamic allocation:
D (309) light_driver: [light_init, 74]:status: 1, mode: 2

配置文件

esp-idf-monitor 使用 C0 控制字符 与控制台进行交互。配置文件中的字符会被转换为对应的 C0 控制代码。可用字符包括英文字母 (A-Z) 和特殊符号:[]\^、和 _.

警告

注意,一些字符可能无法在所有平台通用,或被保留作为其他用途的快捷键。请谨慎使用此功能。

文件位置

配置文件的默认名称为 esp-idf-monitor.cfg。首先,在 esp-idf-monitor 路径中检测配置文件并运行。

如果此目录中没有检测到配置文件,则检查当前用户操作系统的配置目录:

  • Linux: /home/<user>/.config/esp-idf-monitor/

  • MacOS /Users/<user>/.config/esp-idf-monitor/

  • Windows: c:\Users\<user>\AppData\Local\esp-idf-monitor\

如仍未检测到配置文件,会最后再检查主目录:

  • Linux: /home/<user>/

  • MacOS /Users/<user>/

  • Windows: c:\Users\<user>\

在 Windows 中,可以使用 HOMEUSERPROFILE 环境变量设置主目录,因此,Windows 配置目录的位置也取决于这些变量。

还可以使用 ESP_IDF_MONITOR_CFGFILE 环境变量为配置文件指定一个不同的位置,例如 ESP_IDF_MONITOR_CFGFILE = ~/custom_config.cfg。这一设置的检测优先级高于上述所有位置检测的优先级。

如果没有使用其他配置文件,esp-idf-monitor 会从其他常用的配置文件中读取设置。如果存在 setup.cfgtox.ini 文件,esp-idf-monitor 会自动从这些文件中读取设置。

配置选项

下表列出了可用的配置选项:

选项名称

描述

默认值

menu_key

访问主菜单

T

exit_key

退出监视器

]

chip_reset_key

初始化芯片重置

R

recompile_upload_key

重新编译并上传

F

recompile_upload_app_key

仅重新编译并上传应用程序

A

toggle_output_key

切换输出显示

Y

toggle_log_key

切换日志功能

L

toggle_timestamp_key

切换时间戳显示

I

chip_reset_bootloader_key

将芯片重置为引导加载模式

P

exit_menu_key

从菜单中退出监视器

X

skip_menu_key

设置使用菜单命令时无需按下主菜单键

False

语法

配置文件为 .ini 文件格式,必须以 [esp-idf-monitor] 标头引入才能被识别为有效文件。以下语法以“配置名称 = 配置值”形式列出。以 #; 开头的行是注释,将被忽略。

# esp-idf-monitor.cfg file to configure internal settings of esp-idf-monitor
[esp-idf-monitor]
menu_key = T
exit_key = ]
chip_reset_key = R
recompile_upload_key = F
recompile_upload_app_key = A
toggle_output_key = Y
toggle_log_key = L
toggle_timestamp_key = I
chip_reset_bootloader_key = P
exit_menu_key = X
skip_menu_key = False

IDF 监视器已知问题

Windows 环境下已知问题

  • 由于 Windows 控制台限制,有些箭头键及其他一些特殊键无法在 GDB 中使用。

  • 偶然情况下,idf.py 退出时,可能会在 IDF 监视器恢复之前暂停 30 秒。

  • GDB 运行时,可能会暂停一段时间,然后才开始与 GDBStub 进行通信。