Rust嵌入式开发库esp-println的使用,专为ESP系列芯片优化的高效日志打印工具
Rust嵌入式开发库esp-println的使用,专为ESP系列芯片优化的高效日志打印工具
esp-println是一个为Espressif设备提供print!
、println!
、dbg!
实现和日志功能的库。
特性
- 支持所有Espressif ESP32系列设备
- 支持不同的通信方式:
- UART(默认)
- JTAG-Serial(仅适用于ESP32-C3、ESP32-C6、ESP32-H2、ESP32-S3)
- No-op:将打印变为无操作
- 支持
defmt
后端
使用方法
在Cargo.toml中添加依赖:
esp-println = { version = "0.11.0", features = ["esp32c2"] }
或者运行命令:
cargo add esp-println --features esp32c2
在代码中使用:
use esp_println::println;
fn main() {
println!("Hello world");
}
日志功能
启用log-04
特性并添加log
crate v0.4依赖后,可以初始化一个简单的日志记录器:
init_logger(log::LevelFilter::Info);
默认启用了colors
特性,可以显示彩色日志输出。
还可以使用:
init_logger_from_env();
此时会使用以下环境变量:
ESP_LOG
:指定要显示的日志消息,类似于RUST_LOG
。不支持正则表达式。例如:warn,test::foo=info,test::foo::bar=debug
defmt
支持
使用defmt-espflash
特性时,esp-println
会安装一个defmt
全局日志记录器。该记录器会输出到与println!()
相同的数据流,并添加帧字节以便与其他非defmt
输出一起使用。
完整示例代码
// 引入必要的库
use esp_println::println;
use log::{info, warn, error};
fn main() {
// 初始化日志记录器,设置日志级别为Info
esp_println::init_logger(log::LevelFilter::Info);
// 使用println!宏打印信息
println!("System started!");
// 使用log宏记录不同级别的日志
info!("This is an info message");
warn!("This is a warning message");
error!("This is an error message");
// 打印变量值
let count = 42;
println!("The answer is: {}", count);
// 使用dbg!宏调试
let result = some_calculation();
dbg!(result);
}
fn some_calculation() -> i32 {
// 模拟一些计算
42
}
注意事项
- 如果遇到链接错误,请确保代码中有对
esp_println
的引用。如果不直接使用esp_println
,可以添加use esp_println as _;
到导入语句中 - 最低支持的Rust版本(MSRV)为1.86.0
扩展完整示例代码
//! esp-println 完整使用示例
//! 演示了打印、日志记录和defmt集成
#![no_std]
#![no_main]
// 引入必要的库
use esp_println::{println, print};
use log::{info, warn, error, debug, trace};
use defmt::{info as defmt_info, warn as defmt_warn};
// 对于某些ESP目标可能需要这个
use esp_backtrace as _;
#[entry]
fn main() -> ! {
// 1. 基本打印功能演示
println!("\n=== 基本打印演示 ===");
print!("Hello "); // 不换行打印
println!("World!"); // 带换行打印
let x = 42;
println!("变量值: x = {}", x);
// 2. 日志功能演示
println!("\n=== 日志功能演示 ===");
// 初始化日志记录器,设置日志级别为Debug
esp_println::init_logger(log::LevelFilter::Debug);
info!("这是信息级别日志");
warn!("这是警告级别日志");
error!("这是错误级别日志");
debug!("这是调试级别日志");
trace!("这是跟踪级别日志 - 不会显示");
// 3. dbg!宏演示
println!("\n=== dbg!宏演示 ===");
let values = [1, 2, 3];
dbg!(values); // 自动打印变量名和值
// 4. 环境变量配置日志演示
println!("\n=== 环境变量配置日志 ===");
// 假设设置了`ESP_LOG=info,my_module=debug`
esp_println::init_logger_from_env();
info!("全局信息日志");
debug!("全局调试日志 - 不会显示");
mod my_module {
use log::debug;
pub fn test() {
debug!("模块内调试日志 - 会显示");
}
}
my_module::test();
// 5. defmt集成演示 (需要启用defmt-espflash特性)
#[cfg(feature = "defmt-espflash")]
{
println!("\n=== defmt集成演示 ===");
defmt_info!("这是defmt信息日志");
defmt_warn!("这是defmt警告日志: {}", x);
}
loop {
// 嵌入式系统通常需要无限循环
}
}
使用建议
-
选择适合的通信方式:
- 常规开发使用UART
- ESP32-C3/C6等支持JTAG-Serial的设备可以获得更好性能
- 发布版本考虑使用No-op减少代码大小
-
日志级别选择:
- 开发阶段使用Debug或更低级别
- 生产环境建议使用Info或更高级别
-
性能考虑:
- 频繁的日志输出可能影响实时性能
- 考虑使用
dbg!
宏仅在调试时启用
-
内存使用:
- 格式化字符串会占用栈空间
- 对于长字符串考虑分多次打印
1 回复
esp-println - 专为ESP系列芯片优化的Rust日志打印工具
介绍
esp-println
是专为ESP系列芯片(如ESP32、ESP32-C3等)设计的Rust日志打印库,提供了高效的println!宏实现。相比标准库的println!,它针对ESP芯片进行了优化,更适合嵌入式开发环境。
主要特点:
- 轻量级实现,减少代码体积
- 支持多种ESP芯片系列
- 与标准println!相似的API,学习成本低
- 可配置的输出目标(串口等)
使用方法
基本使用
首先在Cargo.toml中添加依赖:
[dependencies]
esp-println = "0.3"
然后在代码中使用:
use esp_println::println;
fn main() {
println!("Hello, ESP world!"); // 输出到默认串口
println!("Value: {}", 42); // 支持格式化
}
初始化配置
在使用前通常需要初始化日志系统:
use esp_println::{logger::init_logger, Printer};
// 初始化日志系统,设置波特率
init_logger(Printer::new(115200));
不同日志级别
esp-println
也提供了不同日志级别的宏:
use esp_println::{debug, error, info, warn};
fn some_function() {
debug!("调试信息");
info!("常规信息");
warn!("警告信息");
error!("错误信息");
}
自定义输出目标
可以自定义输出到不同的串口:
use esp_println::{Printer, println};
use embedded_hal::serial::Write;
// 假设uart1是你的串口实例
let printer = Printer::new_with_uart(uart1);
println!(printer, "这条消息会输出到uart1");
示例项目
一个完整的ESP32示例:
#![no_std]
#![no_main]
use esp32_hal::{clock::ClockControl, pac::Peripherals, prelude::*, timer::TimerGroup};
use esp_backtrace as _;
use esp_println::{println, init_logger, Printer};
#[entry]
fn main() -> ! {
let peripherals = Peripherals::take().unwrap();
let system = peripherals.DPORT.split();
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
// 初始化日志系统,波特率115200
init_logger(Printer::new(115200));
// 打印启动信息
println!("系统启动完成");
println!("CPU频率: {} MHz", clocks.cpu_clock.to_MHz());
loop {
println!("心跳...");
TimerGroup::new(peripherals.TIMG1, &clocks).timer0.delay_ms(1000u32);
}
}
完整示例DEMO
以下是一个更完整的ESP32-C3示例,展示了多级日志和自定义串口的使用:
#![no_std]
#![no_main]
use esp32c3_hal::{
clock::ClockControl,
pac::Peripherals,
prelude::*,
timer::TimerGroup,
Rtc,
Uart,
IO,
};
use esp_backtrace as _;
use esp_println::{println, debug, info, warn, error, init_logger, Printer};
#[entry]
fn main() -> ! {
let peripherals = Peripherals::take().unwrap();
let system = peripherals.SYSTEM.split();
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
// 初始化串口0作为默认日志输出
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
let pins = io.pins;
let uart0 = Uart::new(peripherals.UART0, &clocks);
// 初始化日志系统,波特率115200
init_logger(Printer::new_with_uart(uart0));
// 打印启动信息
println!("系统启动完成");
info!("CPU频率: {} MHz", clocks.cpu_clock.to_MHz());
debug!("调试信息只会出现在debug模式下");
// 初始化RTC
let mut rtc = Rtc::new(peripherals.RTC_CNTL);
rtc.rwdt.disable();
// 创建一个计时器组
let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
let mut timer0 = timer_group0.timer0;
let mut counter = 0;
loop {
counter += 1;
match counter % 4 {
0 => println!("循环计数: {}", counter),
1 => debug!("调试计数: {}", counter),
2 => info!("信息计数: {}", counter),
3 => warn!("警告计数: {}", counter),
_ => error!("错误计数: {}", counter),
}
timer0.delay_ms(500u32);
}
}
注意事项
- 确保你的芯片支持所选波特率
- 在no_std环境下使用需要添加
#![no_std]
属性 - 对于复杂项目,考虑结合
log
crate使用更完整的日志系统
esp-println
是ESP嵌入式开发的实用工具,能显著简化调试和日志输出过程。