feat: add display UI with splash screen, dashboard, and RSSI sparkline

Implement full SSD1306 OLED display support:
- Splash screen on boot (1.5s) with project name, version, board
- Dashboard showing radio state, config, counters, RSSI/SNR
- RSSI history sparkline (64-sample ring buffer, 2px-wide bars)
- DisplayOn/DisplayOff host commands routed via dedicated channel
- Pure render functions against generic DrawTarget (no hardware coupling)
- Display on/off control bypasses radio task entirely

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
swaitsandClaude Opus 4.6 committed 2026-03-29 20:42:54 -06:00
1 parent 7064110293
commit 3102fc747a
17624 files changed
+4564928 -38

No files matched your search

+1
View File
@@ -0,0 +1 @@
{"files":{".cargo_vcs_info.json":"d30b7407be6478981c0dc201d015ffb6c232dccb694fb96105424a64d45b1f79","Cargo.toml":"2ea5e892534e0a7e97c3da904f7c3916a342b32744fae57272da5570f5ee96b6","Cargo.toml.orig":"efd36cf07b5596cd8ec5519ebd7198b06929d1b4a6aff38febf1354afa50eac6","README.md":"bfbcd158174f82018d0ec52f42ee871b74087dfe1e3ad63394ade16ad27af1c4","devices/esp32.toml":"857cb2be73cec5646779cf3011b58724bcf7267189420d4faa0809e726ca5321","devices/esp32c2.toml":"fce7a85ee44af770908549755925bbbb55644552d50a07190b3f933bcf6eaa68","devices/esp32c3.toml":"b3c57aeb6cf59c6867e329767f8339dabdee75ee695b2c2fd64d95f0d4e0fd3f","devices/esp32c6.toml":"b29633cbf6304480cbe1d6b456b3b9e6fc270fe1fa470549977a0c2f46055b09","devices/esp32h2.toml":"f85107eccba6688b29122a36af669690b7bc9c39a5088ac281333a01f479febb","devices/esp32p4.toml":"44e640d315f0d791507e2722eadae1cd0535cf20e9d3ddbaac44bccb31e3f607","devices/esp32s2.toml":"c44df0e0c558732aa46e70b4cb19ba2a588505b2b63ca5d17374183bfd83e4d9","devices/esp32s3.toml":"bab017533d3d5ec22a9945219273a491ed42cf0f7d16c6c55e7d39056ace3d29","src/lib.rs":"f3ae5b0fb1ee27882ae477907533160463a824fdaddb2cf6dd9e9cbb315b169e"},"package":"f9972bbb21dcafe430b87f92efc7a788978a2d17cf8f572d104beeb48602482a"}
+6
View File
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "d655fe665faf2ff2d64d4debb7852b003594d3c1"
},
"path_in_vcs": "esp-metadata"
}
+49
View File
@@ -0,0 +1,49 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.70.0"
name = "esp-metadata"
version = "0.4.0"
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Metadata for Espressif devices"
readme = "README.md"
license = "MIT OR Apache-2.0"
repository = "https://github.com/esp-rs/esp-hal"
[lib]
name = "esp_metadata"
path = "src/lib.rs"
[dependencies.anyhow]
version = "1.0.89"
[dependencies.basic-toml]
version = "0.1.9"
[dependencies.clap]
version = "4.5.16"
features = ["derive"]
optional = true
[dependencies.serde]
version = "1.0.210"
features = ["derive"]
[dependencies.strum]
version = "0.26.3"
features = ["derive"]
+33
View File
@@ -0,0 +1,33 @@
# esp-metadata
[![Crates.io](https://img.shields.io/crates/v/esp-metadata?labelColor=1C2C2E&color=C96329&logo=Rust&style=flat-square)](https://crates.io/crates/esp-metadata)
[![docs.rs](https://img.shields.io/docsrs/esp-metadata?labelColor=1C2C2E&color=C96329&logo=rust&style=flat-square)](https://docs.rs/esp-metadata)
![MSRV](https://img.shields.io/badge/MSRV-1.70-blue?labelColor=1C2C2E&style=flat-square)
![Crates.io](https://img.shields.io/crates/l/esp-metadata?labelColor=1C2C2E&style=flat-square)
[![Matrix](https://img.shields.io/matrix/esp-rs:matrix.org?label=join%20matrix&labelColor=1C2C2E&color=BEC5C9&logo=matrix&style=flat-square)](https://matrix.to/#/#esp-rs:matrix.org)
Metadata for Espressif devices, intended for use in [build scripts].
[build scripts]: https://doc.rust-lang.org/cargo/reference/build-scripts.html
## [Documentation](https://docs.rs/crate/esp-metadata)
## Minimum Supported Rust Version (MSRV)
This crate is guaranteed to compile on stable Rust 1.70 and up. It _might_
compile with older versions but that may change in any new patch release.
## License
Licensed under either of:
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
any additional terms or conditions.
+76
View File
@@ -0,0 +1,76 @@
[device]
name = "esp32"
arch = "xtensa"
cores = "multi_core"
peripherals = [
# Peripherals available in the PAC:
"aes",
"apb_ctrl",
"bb",
"dport",
"efuse",
"flash_encryption",
"frc_timer",
"gpio",
"gpio_sd",
"hinf",
"i2c0",
"i2c1",
"i2s0",
"i2s1",
"io_mux",
"ledc",
"mcpwm0",
"mcpwm1",
"nrx",
"pcnt",
"rmt",
"rng",
"rsa",
"rtc_cntl",
"rtc_i2c",
"rtc_io",
"sdhost",
"sens",
"sha",
"slc",
"slchost",
"spi0",
"spi1",
"spi2",
"spi3",
"timg0",
"timg1",
"twai0",
"uart0",
"uart1",
"uart2",
"uhci0",
"uhci1",
]
symbols = [
# Additional peripherals defined by us (the developers):
"adc",
"dac",
"pdma",
"phy",
"bt",
"wifi",
"psram",
"timg_timer1",
"touch",
"large_intr_status",
# ROM capabilities
"rom_crc_le",
"rom_crc_be",
"rom_md5_bsd",
# Wakeup SOC based on ESP-IDF:
"pm_support_ext0_wakeup",
"pm_support_ext1_wakeup",
"pm_support_touch_sensor_wakeup",
"ulp_supported",
]
+54
View File
@@ -0,0 +1,54 @@
[device]
name = "esp32c2"
arch = "riscv"
cores = "single_core"
peripherals = [
# Peripherals available in the PAC:
"apb_ctrl",
"apb_saradc",
"assist_debug",
"dma",
"ecc",
"efuse",
"extmem",
"gpio",
"i2c0",
"interrupt_core0",
"io_mux",
"ledc",
"rng",
"rtc_cntl",
"sensitive",
"sha",
"spi0",
"spi1",
"spi2",
"system",
"systimer",
"timg0",
"uart0",
"uart1",
"xts_aes",
]
symbols = [
# Additional peripherals defined by us (the developers):
"adc",
"assist_debug_sp_monitor",
"gdma",
"phy",
"bt",
"wifi",
# ROM capabilities
"rom_crc_le",
"rom_crc_be",
"rom_md5_mbedtls",
# Wakeup SOC based on ESP-IDF:
"pm_support_wifi_wakeup",
"pm_support_bt_wakeup",
"uart_support_wakeup_int",
"gpio_support_deepsleep_wakeup",
]
+66
View File
@@ -0,0 +1,66 @@
[device]
name = "esp32c3"
arch = "riscv"
cores = "single_core"
peripherals = [
# Peripherals available in the PAC:
"aes",
"apb_ctrl",
"apb_saradc",
"assist_debug",
"dma",
"ds",
"efuse",
"extmem",
"gpio",
"gpio_sd",
"hmac",
"i2c0",
"i2s0",
"interrupt_core0",
"io_mux",
"ledc",
"rmt",
"rng",
"rsa",
"rtc_cntl",
"sensitive",
"sha",
"spi0",
"spi1",
"spi2",
"system",
"systimer",
"timg0",
"timg1",
"twai0",
"uart0",
"uart1",
"uhci0",
"uhci1",
"usb_device",
"xts_aes",
]
symbols = [
# Additional peripherals defined by us (the developers):
"adc",
"assist_debug_sp_monitor",
"assist_debug_region_monitor",
"gdma",
"phy",
"bt",
"wifi",
# ROM capabilities
"rom_crc_le",
"rom_crc_be",
"rom_md5_bsd",
# Wakeup SOC based on ESP-IDF:
"pm_support_wifi_wakeup",
"pm_support_bt_wakeup",
"uart_support_wakeup_int",
"gpio_support_deepsleep_wakeup",
]
+98
View File
@@ -0,0 +1,98 @@
[device]
name = "esp32c6"
arch = "riscv"
cores = "single_core"
peripherals = [
# Peripherals available in the PAC:
"aes",
"apb_saradc",
"assist_debug",
"atomic",
"dma",
"ds",
"ecc",
"efuse",
"extmem",
"gpio",
"gpio_sd",
"hinf",
"hmac",
"hp_apm",
"hp_sys",
"i2c0",
"i2s0",
"interrupt_core0",
"intpri",
"io_mux",
"ledc",
"lp_ana",
"lp_aon",
"lp_apm",
"lp_apm0",
"lp_clkrst",
"lp_i2c0",
"lp_i2c_ana_mst",
"lp_io",
"lp_peri",
"lp_tee",
"lp_timer",
"lp_uart",
"lp_wdt",
"mcpwm0",
"mem_monitor",
"otp_debug",
"parl_io",
"pau",
"pcnt",
"pcr",
"pmu",
"rmt",
"rng",
"rsa",
"sha",
"slchost",
"soc_etm",
"spi0",
"spi1",
"spi2",
"systimer",
"tee",
"timg0",
"timg1",
"trace0",
"twai0",
"twai1",
"uart0",
"uart1",
"uhci0",
"usb_device",
]
symbols = [
# Additional peripherals defined by us (the developers):
"adc",
"assist_debug_sp_monitor",
"assist_debug_region_monitor",
"gdma",
"large_intr_status",
"plic",
"phy",
"bt",
"wifi",
"ieee802154",
"lp_core",
# ROM capabilities
"rom_crc_le",
"rom_crc_be",
"rom_md5_bsd",
# Wakeup SOC based on ESP-IDF:
"pm_support_wifi_wakeup",
"pm_support_beacon_wakeup",
"pm_support_bt_wakeup",
"gpio_support_deepsleep_wakeup",
"uart_support_wakeup_int",
"pm_support_ext1_wakeup",
]
+79
View File
@@ -0,0 +1,79 @@
[device]
name = "esp32h2"
arch = "riscv"
cores = "single_core"
peripherals = [
# Peripherals available in the PAC:
"aes",
"apb_saradc",
"assist_debug",
"dma",
"ds",
"ecc",
"efuse",
"gpio",
"gpio_sd",
"hmac",
"hp_apm",
"hp_sys",
"i2c0",
"i2c1",
"i2s0",
"interrupt_core0",
"intpri",
"io_mux",
"ledc",
"lp_ana",
"lp_aon",
"lp_apm",
"lp_clkrst",
"lp_peri",
"lp_timer",
"lp_wdt",
"mcpwm0",
"mem_monitor",
"modem_lpcon",
"modem_syscon",
"otp_debug",
"parl_io",
"pau",
"pcnt",
"pcr",
"pmu",
"rmt",
"rng",
"rsa",
"sha",
"soc_etm",
"spi0",
"spi1",
"spi2",
"systimer",
"tee",
"timg0",
"timg1",
"trace0",
"twai0",
"uart0",
"uart1",
"uhci0",
"usb_device",
]
symbols = [
# Additional peripherals defined by us (the developers):
"adc",
"assist_debug_sp_monitor",
"assist_debug_region_monitor",
"gdma",
"plic",
"phy",
"bt",
"ieee802154",
# ROM capabilities
"rom_crc_le",
"rom_crc_be",
"rom_md5_bsd",
]
+98
View File
@@ -0,0 +1,98 @@
[device]
name = "esp32p4"
arch = "riscv"
cores = "multi_core"
peripherals = [
# Peripherals available in the PAC:
# "adc",
# "aes",
# "assist_debug",
# "axi_dma",
# "axi_icm",
# "bitscrambler",
# "cache",
# "dma",
# "ds",
# "ecc",
# "ecdsa",
"efuse",
# "gpio_sd",
"gpio",
# "h264_dma",
# "h264",
# "hmac",
# "hp_sys_clkrst",
"hp_sys",
# "i2c0",
# "i2c1",
# "i2s0",
# "i2s1",
# "i2s2",
# "i3c_mst_mem",
# "i3c_mst",
# "i3c_slv",
"interrupt_core0",
"interrupt_core1",
"io_mux",
# "isp",
# "jpeg",
# "lcd_cam",
# "ledc",
# "lp_adc",
# "lp_ana_peri",
# "lp_aon_clkrst",
# "lp_gpio",
# "lp_huk",
# "lp_i2c_ana_mst",
# "lp_i2c0",
# "lp_i2s0",
# "lp_intr",
# "lp_io_mux",
# "lp_peri",
# "lp_sys",
# "lp_timer",
# "lp_touch",
# "lp_tsens",
# "lp_uart",
# "lp_wdt",
# "mcpwm0",
# "mcpwm1",
# "mipi_csi_bridge",
# "mipi_csi_host",
# "mipi_dsi_bridge",
# "mipi_dsi_host",
# "parl_io",
# "pau",
# "pcnt",
# "pmu",
# "ppa",
# "pvt",
# "rmt",
# "rsa",
# "sdhost",
# "sha",
# "soc_etm",
# "spi0",
# "spi1",
# "spi2",
# "spi3",
# "systimer",
# "timg0",
# "timg1",
# "trace0",
# "trace1",
# "twai0",
# "twai1",
# "twai2",
# "uart0",
# "uhci0",
# "usb_device",
# "usb_wrap",
]
symbols = [
# Additional peripherals defined by us (the developers):
"clic",
"very_large_intr_status",
]
+74
View File
@@ -0,0 +1,74 @@
[device]
name = "esp32s2"
arch = "xtensa"
cores = "single_core"
peripherals = [
# Peripherals available in the PAC:
"aes",
"apb_saradc",
"dedicated_gpio",
"ds",
"efuse",
"extmem",
"gpio",
"gpio_sd",
"hmac",
"i2c0",
"i2c1",
"i2s0",
"interrupt_core0",
"io_mux",
"ledc",
"pcnt",
"pms",
"rmt",
"rng",
"rsa",
"rtc_cntl",
"rtc_i2c",
"rtc_io",
"sens",
"sha",
"spi0",
"spi1",
"spi2",
"spi3",
"system",
"systimer",
"timg0",
"timg1",
"twai0",
"uart0",
"uart1",
"uhci0",
"usb0",
"usb_wrap",
"xts_aes",
]
symbols = [
# Additional peripherals defined by us (the developers):
"adc",
"dac",
"pdma",
"phy",
"wifi",
"psram",
"ulp_riscv_core",
"timg_timer1",
"large_intr_status",
# ROM capabilities
"rom_crc_le",
"rom_md5_bsd",
# Wakeup SOC based on ESP-IDF:
"pm_support_ext0_wakeup",
"pm_support_ext1_wakeup",
"pm_support_touch_sensor_wakeup",
"pm_support_wifi_wakeup",
"uart_support_wakeup_int",
"ulp_supported",
"riscv_coproc_supported",
]
+90
View File
@@ -0,0 +1,90 @@
[device]
name = "esp32s3"
arch = "xtensa"
cores = "multi_core"
peripherals = [
# Peripherals available in the PAC:
"aes",
"apb_ctrl",
"apb_saradc",
"assist_debug",
"dma",
"ds",
"efuse",
"extmem",
"gpio",
"gpio_sd",
"hmac",
"i2c0",
"i2c1",
"i2s0",
"i2s1",
"interrupt_core0",
"interrupt_core1",
"io_mux",
"lcd_cam",
"ledc",
"mcpwm0",
"mcpwm1",
"pcnt",
"peri_backup",
"rmt",
"rng",
"rsa",
"rtc_cntl",
"rtc_i2c",
"rtc_io",
"sens",
"sensitive",
"sha",
"spi0",
"spi1",
"spi2",
"spi3",
"system",
"systimer",
"timg0",
"timg1",
"twai0",
"uart0",
"uart1",
"uart2",
"uhci0",
"uhci1",
"usb0",
"usb_device",
"usb_wrap",
"wcl",
"xts_aes",
]
symbols = [
# Additional peripherals defined by us (the developers):
"adc",
"assist_debug_region_monitor",
"gdma",
"phy",
"bt",
"wifi",
"psram",
"octal_psram",
"ulp_riscv_core",
"timg_timer1",
"very_large_intr_status",
# ROM capabilities
"rom_crc_le",
"rom_crc_be",
"rom_md5_bsd",
# Wakeup SOC based on ESP-IDF:
"pm_support_ext0_wakeup",
"pm_support_ext1_wakeup",
"pm_support_touch_sensor_wakeup",
"pm_support_wifi_wakeup",
"pm_support_bt_wakeup",
"uart_support_wakeup_int",
"ulp_supported",
"riscv_coproc_supported",
]
+247
View File
@@ -0,0 +1,247 @@
//! Metadata for Espressif devices, primarily intended for use in build scripts.
use std::sync::OnceLock;
use anyhow::{bail, Result};
use strum::IntoEnumIterator;
macro_rules! include_toml {
($type:ty, $file:expr) => {{
static LOADED_TOML: OnceLock<$type> = OnceLock::new();
LOADED_TOML.get_or_init(|| basic_toml::from_str(include_str!($file)).unwrap())
}};
}
/// Supported device architectures.
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
serde::Deserialize,
serde::Serialize,
strum::Display,
strum::EnumIter,
strum::EnumString,
strum::AsRefStr,
)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum Arch {
/// RISC-V architecture
RiscV,
/// Xtensa architecture
Xtensa,
}
/// Device core count.
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
serde::Deserialize,
serde::Serialize,
strum::Display,
strum::EnumIter,
strum::EnumString,
strum::AsRefStr,
)]
pub enum Cores {
/// Single CPU core
#[serde(rename = "single_core")]
#[strum(serialize = "single_core")]
Single,
/// Two or more CPU cores
#[serde(rename = "multi_core")]
#[strum(serialize = "multi_core")]
Multi,
}
/// Supported devices.
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Deserialize,
serde::Serialize,
strum::Display,
strum::EnumIter,
strum::EnumString,
strum::AsRefStr,
)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")]
pub enum Chip {
/// ESP32
Esp32,
/// ESP32-C2, ESP8684
Esp32c2,
/// ESP32-C3, ESP8685
Esp32c3,
/// ESP32-C6
Esp32c6,
/// ESP32-H2
Esp32h2,
/// ESP32-S2
Esp32s2,
/// ESP32-S3
Esp32s3,
}
impl Chip {
pub fn target(&self) -> &str {
use Chip::*;
match self {
Esp32 => "xtensa-esp32-none-elf",
Esp32c2 | Esp32c3 => "riscv32imc-unknown-none-elf",
Esp32c6 | Esp32h2 => "riscv32imac-unknown-none-elf",
Esp32s2 => "xtensa-esp32s2-none-elf",
Esp32s3 => "xtensa-esp32s3-none-elf",
}
}
pub fn has_lp_core(&self) -> bool {
use Chip::*;
matches!(self, Esp32c6 | Esp32s2 | Esp32s3)
}
pub fn lp_target(&self) -> Result<&str> {
use Chip::*;
match self {
Esp32c6 => Ok("riscv32imac-unknown-none-elf"),
Esp32s2 | Esp32s3 => Ok("riscv32imc-unknown-none-elf"),
_ => bail!("Chip does not contain an LP core: '{}'", self),
}
}
pub fn pretty_name(&self) -> &str {
match self {
Chip::Esp32 => "ESP32",
Chip::Esp32c2 => "ESP32-C2",
Chip::Esp32c3 => "ESP32-C3",
Chip::Esp32c6 => "ESP32-C6",
Chip::Esp32h2 => "ESP32-H2",
Chip::Esp32s2 => "ESP32-S2",
Chip::Esp32s3 => "ESP32-S3",
}
}
pub fn is_xtensa(&self) -> bool {
matches!(self, Chip::Esp32 | Chip::Esp32s2 | Chip::Esp32s3)
}
pub fn is_riscv(&self) -> bool {
!self.is_xtensa()
}
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
struct Device {
pub name: String,
pub arch: Arch,
pub cores: Cores,
pub peripherals: Vec<String>,
pub symbols: Vec<String>,
}
/// Device configuration file format.
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct Config {
device: Device,
}
impl Config {
/// The configuration for the specified chip.
pub fn for_chip(chip: &Chip) -> &Self {
match chip {
Chip::Esp32 => include_toml!(Config, "../devices/esp32.toml"),
Chip::Esp32c2 => include_toml!(Config, "../devices/esp32c2.toml"),
Chip::Esp32c3 => include_toml!(Config, "../devices/esp32c3.toml"),
Chip::Esp32c6 => include_toml!(Config, "../devices/esp32c6.toml"),
Chip::Esp32h2 => include_toml!(Config, "../devices/esp32h2.toml"),
Chip::Esp32s2 => include_toml!(Config, "../devices/esp32s2.toml"),
Chip::Esp32s3 => include_toml!(Config, "../devices/esp32s3.toml"),
}
}
/// The name of the device.
pub fn name(&self) -> String {
self.device.name.clone()
}
/// The CPU architecture of the device.
pub fn arch(&self) -> Arch {
self.device.arch
}
/// The core count of the device.
pub fn cores(&self) -> Cores {
self.device.cores
}
/// The peripherals of the device.
pub fn peripherals(&self) -> &[String] {
&self.device.peripherals
}
/// User-defined symbols for the device.
pub fn symbols(&self) -> &[String] {
&self.device.symbols
}
/// All configuration values for the device.
pub fn all(&self) -> impl Iterator<Item = &str> + '_ {
[
self.device.name.as_str(),
self.device.arch.as_ref(),
self.device.cores.as_ref(),
]
.into_iter()
.chain(self.device.peripherals.iter().map(|s| s.as_str()))
.chain(self.device.symbols.iter().map(|s| s.as_str()))
}
/// Does the configuration contain `item`?
pub fn contains(&self, item: &str) -> bool {
self.all().any(|i| i == item)
}
/// Define all symbols for a given configuration.
pub fn define_symbols(&self) {
define_all_possible_symbols();
// Define all necessary configuration symbols for the configured device:
for symbol in self.all() {
println!("cargo:rustc-cfg={symbol}");
}
}
}
/// Defines all possible symbols that _could_ be output from this crate
/// regardless of the chosen configuration.
///
/// This is required to avoid triggering the unexpected-cfgs lint.
fn define_all_possible_symbols() {
for chip in Chip::iter() {
let config = Config::for_chip(&chip);
for symbol in config.all() {
// https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-check-cfg
println!("cargo:rustc-check-cfg=cfg({})", symbol);
}
}
}