Skip to content

Commit cc28c3e

Browse files
jessebrahamMabezDevSergioGasquez
authored
Add the esp-println package to the repository (#1575)
* Add the `esp-println` package to the repository * Update esp-println version in README.md Co-authored-by: Sergio Gasquez Arcos <sergio.gasquez@gmail.com> --------- Co-authored-by: Scott Mabin <scott@mabez.dev> Co-authored-by: Sergio Gasquez Arcos <sergio.gasquez@gmail.com>
1 parent 5facb75 commit cc28c3e

File tree

8 files changed

+745
-2
lines changed

8 files changed

+745
-2
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ exclude = [
1010
"esp-ieee802154",
1111
"esp-lp-hal",
1212
"esp-metadata",
13+
"esp-println",
1314
"esp-riscv-rt",
1415
"examples",
1516
"hil-test",

esp-println/Cargo.toml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
[package]
2+
name = "esp-println"
3+
version = "0.9.1"
4+
edition = "2021"
5+
description = "Provides `print!` and `println!` implementations various Espressif devices"
6+
repository = "https://github.com/esp-rs/esp-hal"
7+
license = "MIT OR Apache-2.0"
8+
9+
[package.metadata.docs.rs]
10+
cargo-args = ["-Z", "build-std=core"]
11+
default-target = "riscv32imc-unknown-none-elf"
12+
features = ["esp32c3"]
13+
14+
[dependencies]
15+
critical-section = { version = "1.1.2", optional = true }
16+
defmt = { version = "0.3.7", optional = true }
17+
log = { version = "0.4.21", optional = true }
18+
portable-atomic = { version = "1.6.0", optional = true, default-features = false }
19+
20+
[build-dependencies]
21+
esp-build = { version = "0.1.0", path = "../esp-build" }
22+
23+
[features]
24+
default = ["dep:critical-section", "colors", "uart"]
25+
log = ["dep:log"]
26+
27+
# You must enable exactly 1 of the below features to support the correct chip:
28+
esp32 = []
29+
esp32c2 = []
30+
esp32c3 = []
31+
esp32c6 = []
32+
esp32h2 = []
33+
esp32p4 = []
34+
esp32s2 = []
35+
esp32s3 = []
36+
37+
# You must enable exactly 1 of the below features to enable to intended
38+
# communication method (note that "uart" is enabled by default):
39+
jtag-serial = ["dep:portable-atomic"] # C3, C6, H2, P4, and S3 only!
40+
no-op = []
41+
uart = []
42+
43+
# Enables a `defmt` backend usable with espflash. We force rzcobs encoding to simplify implementation
44+
defmt-espflash = ["dep:defmt", "defmt?/encoding-rzcobs"]
45+
46+
# logging sub-features
47+
colors = []

esp-println/README.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# esp-println
2+
3+
A library that provides `print!`, `println!`, `dbg!` implementations and
4+
logging capabilities for Espressif devices.
5+
6+
- Supports all Espressif ESP32 family devices.
7+
- Supports different communication methods:
8+
- UART (Default)
9+
- JTAG-Serial (Only available in ESP32-C3, ESP32-C6, ESP32-H2, ESP32-S3)
10+
- No-op: Turns printing into a no-op
11+
- Supports [`defmt`] backend
12+
13+
# Usage
14+
15+
```toml
16+
esp-println = { version = "0.9.1", features = ["esp32c2"] }
17+
```
18+
19+
or `cargo add esp-println --features esp32c2`
20+
It's important to specify your target device as feature.
21+
22+
Then in your program:
23+
24+
```rust
25+
use esp_println::println;
26+
```
27+
28+
You can now `println!("Hello world")` as usual.
29+
30+
# Features
31+
32+
- There is one feature for each supported target: `esp32`, `esp32c2`,
33+
`esp32c3`, `esp32c6`, `esp32h2`, `esp32s2`, and `esp32s3`.
34+
- One of these features must be enabled.
35+
- Only one of these features can be enabled at a time.
36+
- There is one feature for each supported communication method: `uart`, `jtag-serial` and `no-op`.
37+
- Only one of these features can be enabled at a time.
38+
- `log`: Enables logging using [`log` crate].
39+
- `colors` enable colored logging.
40+
- Only effective when using the `log` feature.
41+
- `critical-section` enables critical sections.
42+
- `defmt-espflash`: This is intended to be used with [`espflash`], see `-L/--log-format` argument
43+
of `flash` or `monitor` subcommands of `espflash` and `cargo-espflash`. Uses [rzCOBS] encoding
44+
and adds framing.
45+
46+
## Default Features
47+
48+
By default, we use the `uart`, `critial-section` and `colors` features.
49+
Which means that it will print to the UART, use critical sections and output
50+
messages will be colored.
51+
If we want to use a communication method that is not `uart`, the default
52+
one, we need to [disable the default features].
53+
54+
## Logging
55+
56+
With the feature `log` activated you can initialize a simple logger like this
57+
58+
```rust
59+
init_logger(log::LevelFilter::Info);
60+
```
61+
62+
There is a default feature `colors` which enables colored log output.
63+
64+
Additionally, you can use
65+
66+
```rust
67+
init_logger_from_env();
68+
```
69+
70+
In this case the following environment variables are used:
71+
72+
- `ESP_LOGLEVEL` sets the log level, use values like `trace`, `info` etc.
73+
- `ESP_LOGTARGETS` if set you should provide the crate names of crates (optionally with a path e.g. `esp_wifi::compat::common`) which should get logged, separated by `,` and no additional whitespace between
74+
75+
If this simple logger implementation isn't sufficient for your needs, you can implement your own logger on top of `esp-println`. See [Implementing a Logger section log documentaion]
76+
77+
## `defmt`
78+
79+
Using the `defmt-espflash` feature, `esp-println` will install a `defmt` global logger. The logger will
80+
output to the same data stream as `println!()`, and adds framing bytes so it can be used even with
81+
other, non-`defmt` output. Using the `defmt-espflash` feature automatically uses the [rzCOBS] encoding and does
82+
not allow changing the encoding.
83+
84+
Follow the [`defmt` book's setup instructions] on how to
85+
set up `defmt`. Remember, the global logger is already installed for you by `esp-println`!
86+
87+
Please note that `defmt` does _not_ provide MSRV guarantees with releases, and as such we are not able to make any MSRV guarantees when this feature is enabled. For more information refer to the MSRV section of `defmt`'s README:
88+
https://github.com/knurling-rs/defmt?tab=readme-ov-file#msrv
89+
90+
[`defmt`]: https://github.com/knurling-rs/defmt
91+
[`log` crate]: https://github.com/rust-lang/log
92+
[rzCOBS]: https://github.com/Dirbaio/rzcobs
93+
[`espflash`]: https://github.com/esp-rs/espflash
94+
[disable the default features]: https://doc.rust-lang.org/cargo/reference/features.html#the-default-feature
95+
[Implementing a Logger section log documentaion]: https://docs.rs/log/0.4.17/log/#implementing-a-logger
96+
[`defmt` book's setup instructions]: https://defmt.ferrous-systems.com/setup
97+
98+
# Troubleshooting linker errors
99+
100+
If you experience linker errors, make sure you have _some_ reference to `esp_println` in your code.
101+
If you don't use `esp_println` directly, you'll need to add e.g. `use esp_println as _;` to your
102+
import statements. This ensures that the global logger will not be removed by the compiler.
103+
104+
# License
105+
106+
Licensed under either of:
107+
108+
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
109+
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
110+
111+
at your option.
112+
113+
# Contribution
114+
115+
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
116+
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
117+
any additional terms or conditions.

esp-println/build.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use esp_build::assert_unique_used_features;
2+
3+
fn main() {
4+
// Ensure that only a single chip is specified
5+
assert_unique_used_features!(
6+
"esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4", "esp32s2", "esp32s3"
7+
);
8+
9+
// Ensure that only a single communication method is specified
10+
assert_unique_used_features!("jtag-serial", "uart");
11+
12+
// Ensure that, if the `jtag-serial` communication method feature is enabled,
13+
// either the `esp32c3`, `esp32c6`, `esp32h2`, or `esp32s3` chip feature is
14+
// enabled.
15+
if cfg!(feature = "jtag-serial")
16+
&& !(cfg!(feature = "esp32c3")
17+
|| cfg!(feature = "esp32c6")
18+
|| cfg!(feature = "esp32h2")
19+
|| cfg!(feature = "esp32p4")
20+
|| cfg!(feature = "esp32s3"))
21+
{
22+
panic!(
23+
"The `jtag-serial` feature is only supported by the ESP32-C3, ESP32-C6, ESP32-H2, ESP32-P4, and ESP32-S3"
24+
);
25+
}
26+
27+
// Ensure that, if the `colors` is used with `log`.`
28+
if cfg!(feature = "colors") && !cfg!(feature = "log") {
29+
println!(
30+
"cargo:warning=The `colors` feature is only effective when using the `log` feature"
31+
);
32+
}
33+
}

esp-println/src/defmt.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//! defmt global logger implementation.
2+
// Implementation taken from defmt-rtt, with a custom framing prefix
3+
4+
#[cfg(feature = "critical-section")]
5+
use critical_section::RestoreState;
6+
7+
use super::Printer;
8+
9+
/// Global logger lock.
10+
#[cfg(feature = "critical-section")]
11+
static mut TAKEN: bool = false;
12+
#[cfg(feature = "critical-section")]
13+
static mut CS_RESTORE: RestoreState = RestoreState::invalid();
14+
static mut ENCODER: defmt::Encoder = defmt::Encoder::new();
15+
16+
#[defmt::global_logger]
17+
pub struct Logger;
18+
unsafe impl defmt::Logger for Logger {
19+
fn acquire() {
20+
#[cfg(feature = "critical-section")]
21+
unsafe {
22+
// safety: Must be paired with corresponding call to release(), see below
23+
let restore = critical_section::acquire();
24+
25+
// safety: accessing the `static mut` is OK because we have acquired a critical
26+
// section.
27+
if TAKEN {
28+
panic!("defmt logger taken reentrantly")
29+
}
30+
31+
// safety: accessing the `static mut` is OK because we have acquired a critical
32+
// section.
33+
TAKEN = true;
34+
35+
// safety: accessing the `static mut` is OK because we have acquired a critical
36+
// section.
37+
CS_RESTORE = restore;
38+
}
39+
40+
// If not disabled, write a non-UTF8 sequence to indicate the start of a defmt
41+
// frame. We need this to distinguish defmt frames from other data that
42+
// might be written to the printer.
43+
do_write(&[0xFF, 0x00]);
44+
45+
// safety: accessing the `static mut` is OK because we have acquired a critical
46+
// section.
47+
unsafe { ENCODER.start_frame(do_write) }
48+
}
49+
50+
unsafe fn release() {
51+
// safety: accessing the `static mut` is OK because we have acquired a critical
52+
// section.
53+
ENCODER.end_frame(do_write);
54+
55+
Printer.flush();
56+
57+
#[cfg(feature = "critical-section")]
58+
{
59+
// We don't need to write a custom end-of-frame sequence because:
60+
// - using `defmt`, the rzcobs encoding already includes a terminating zero
61+
// - using `defmt-raw`, we don't add any additional framing data
62+
63+
// safety: accessing the `static mut` is OK because we have acquired a critical
64+
// section.
65+
TAKEN = false;
66+
67+
// safety: accessing the `static mut` is OK because we have acquired a critical
68+
// section.
69+
let restore = CS_RESTORE;
70+
71+
// safety: Must be paired with corresponding call to acquire(), see above
72+
critical_section::release(restore);
73+
}
74+
}
75+
76+
unsafe fn flush() {
77+
Printer.flush();
78+
}
79+
80+
unsafe fn write(bytes: &[u8]) {
81+
// safety: accessing the `static mut` is OK because we have acquired a critical
82+
// section.
83+
ENCODER.write(bytes, do_write);
84+
}
85+
}
86+
87+
fn do_write(bytes: &[u8]) {
88+
Printer.write_bytes_assume_cs(bytes)
89+
}

0 commit comments

Comments
 (0)