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

+6
View File
@@ -0,0 +1,6 @@
/* an uninitialized section of RAM otherwise not useable */
SECTIONS {
.dram2_uninit (NOLOAD) : ALIGN(4) {
*(.dram2_uninit)
} > dram2_seg
}
+27
View File
@@ -0,0 +1,27 @@
SECTIONS {
.rodata_dummy (NOLOAD) :
{
/* This dummy section represents the .flash.text section but in RODATA.
* Thus, it must have its alignment and (at least) its size.
*/
/* Start at the same alignment constraint than .flash.text */
. = ALIGN(ALIGNOF(.text));
/* Create an empty gap as big as .text section */
. = . + SIZEOF(.text);
/* Prepare the alignment of the section above. Few bytes (0x20) must be
* added for the mapping header.
*/
. = ALIGN(0x10000) + 0x20;
_rodata_reserved_start = .;
} > RODATA
}
INSERT BEFORE .rodata;
@@ -0,0 +1,14 @@
/*
This fix up is required when the RTC fast memory is split across two address spaces.
This fix up pads the _data bus_ address space by the size of the code accessed by the instruction bus.
*/
SECTIONS {
.rtc_fast.dummy (NOLOAD) :
{
_rtc_dummy_start = ABSOLUTE(.); /* needed to make section proper size */
. = . + SIZEOF(.rtc_fast.text);
_rtc_dummy_end = ABSOLUTE(.); /* needed to make section proper size */
} > RTC_FAST_RWDATA
}
INSERT BEFORE .rtc_fast.data;
+20
View File
@@ -0,0 +1,20 @@
SECTIONS {
.rodata : ALIGN(4)
{
. = ALIGN (4);
_rodata_start = ABSOLUTE(.);
*(.rodata .rodata.*)
*(.srodata .srodata.*)
. = ALIGN(4);
_rodata_end = ABSOLUTE(.);
} > RODATA
.rodata.wifi : ALIGN(4)
{
. = ALIGN(4);
*( .rodata_wlog_*.* )
. = ALIGN(4);
} > RODATA
}
+36
View File
@@ -0,0 +1,36 @@
SECTIONS {
.rtc_fast.text : {
. = ALIGN(4);
*(.rtc_fast.literal .rtc_fast.text .rtc_fast.literal.* .rtc_fast.text.*)
. = ALIGN(4);
} > RTC_FAST_RWTEXT AT > RODATA
.rtc_fast.data :
{
. = ALIGN(4);
_rtc_fast_data_start = ABSOLUTE(.);
*(.rtc_fast.data .rtc_fast.data.*)
_rtc_fast_data_end = ABSOLUTE(.);
. = ALIGN(4);
} > RTC_FAST_RWDATA AT > RODATA
.rtc_fast.bss (NOLOAD) :
{
. = ALIGN(4);
_rtc_fast_bss_start = ABSOLUTE(.);
*(.rtc_fast.bss .rtc_fast.bss.*)
_rtc_fast_bss_end = ABSOLUTE(.);
. = ALIGN(4);
} > RTC_FAST_RWDATA
.rtc_fast.persistent (NOLOAD) :
{
. = ALIGN(4);
_rtc_fast_persistent_start = ABSOLUTE(.);
*(.rtc_fast.persistent .rtc_fast.persistent.*)
_rtc_fast_persistent_end = ABSOLUTE(.);
. = ALIGN(4);
} > RTC_FAST_RWDATA
}
+36
View File
@@ -0,0 +1,36 @@
SECTIONS {
.rtc_slow.text : {
. = ALIGN(4);
*(.rtc_slow.literal .rtc_slow.text .rtc_slow.literal.* .rtc_slow.text.*)
. = ALIGN(4);
} > rtc_slow_seg AT > RODATA
.rtc_slow.data :
{
. = ALIGN(4);
_rtc_slow_data_start = ABSOLUTE(.);
*(.rtc_slow.data .rtc_slow.data.*)
_rtc_slow_data_end = ABSOLUTE(.);
. = ALIGN(4);
} > rtc_slow_seg AT > RODATA
.rtc_slow.bss (NOLOAD) :
{
. = ALIGN(4);
_rtc_slow_bss_start = ABSOLUTE(.);
*(.rtc_slow.bss .rtc_slow.bss.*)
_rtc_slow_bss_end = ABSOLUTE(.);
. = ALIGN(4);
} > rtc_slow_seg
.rtc_slow.persistent (NOLOAD) :
{
. = ALIGN(4);
_rtc_slow_persistent_start = ABSOLUTE(.);
*(.rtc_slow.persistent .rtc_slow.persistent.*)
_rtc_slow_persistent_end = ABSOLUTE(.);
. = ALIGN(4);
} > rtc_slow_seg
}
+63
View File
@@ -0,0 +1,63 @@
SECTIONS {
.data : ALIGN(4)
{
_data_start = ABSOLUTE(.);
. = ALIGN (4);
#IF ESP_HAL_PLACE_SWITCH_TABLES_IN_RAM
*(.rodata.*_esp_hal_internal_handler*)
*(.rodata..Lswitch.table.*)
*(.rodata.cst*)
#ENDIF
#IF ESP_HAL_PLACE_ANON_IN_RAM
*(.rodata..Lanon .rodata..Lanon.*)
#ENDIF
*(.sdata .sdata.* .sdata2 .sdata2.*);
*(.data .data.*);
*(.data1)
_data_end = ABSOLUTE(.);
. = ALIGN(4);
} > RWDATA AT > RODATA
/* LMA of .data */
_sidata = LOADADDR(.data);
.bss (NOLOAD) : ALIGN(4)
{
_bss_start = ABSOLUTE(.);
. = ALIGN (4);
*(.dynsbss)
*(.sbss)
*(.sbss.*)
*(.gnu.linkonce.sb.*)
*(.scommon)
*(.sbss2)
*(.sbss2.*)
*(.gnu.linkonce.sb2.*)
*(.dynbss)
*(.sbss .sbss.* .bss .bss.*);
*(.share.mem)
*(.gnu.linkonce.b.*)
*(COMMON)
_bss_end = ABSOLUTE(.);
. = ALIGN(4);
} > RWDATA
.noinit (NOLOAD) : ALIGN(4)
{
. = ALIGN(4);
*(.noinit .noinit.*)
. = ALIGN(4);
} > RWDATA
.data.wifi :
{
. = ALIGN(4);
*( .dram1 .dram1.*)
. = ALIGN(4);
} > RWDATA AT > RODATA
}
+22
View File
@@ -0,0 +1,22 @@
SECTIONS {
.rwtext : ALIGN(4)
{
. = ALIGN (4);
*(.rwtext.literal .rwtext .rwtext.literal.* .rwtext.*)
. = ALIGN(4);
} > RWTEXT
.rwtext.wifi :
{
. = ALIGN(4);
*( .wifi0iram .wifi0iram.*)
*( .wifirxiram .wifirxiram.*)
*( .wifislprxiram .wifislprxiram.*)
*( .wifislpiram .wifislpiram.*)
*( .phyiram .phyiram.*)
*( .iram1 .iram1.*)
*( .wifiextrairam.* )
*( .coexiram.* )
. = ALIGN(4);
} > RWTEXT AT > RODATA
}
+43
View File
@@ -0,0 +1,43 @@
#IF flip-link
/* no Xtensa chip is supported - so we can assume RISC-V */
SECTIONS {
/* must be last segment using RWDATA */
.stack (NOLOAD) : ALIGN(4)
{
_stack_end = ABSOLUTE(.);
_stack_end_cpu0 = ABSOLUTE(.);
/* Since we cannot know how much the alignment padding of the sections will add we shrink the stack for "the worst case"
*/
. = . + LENGTH(RWDATA) - (SIZEOF(.trap) + SIZEOF(.rwtext) + SIZEOF(.rwtext.wifi) + SIZEOF(.data) + SIZEOF(.bss) + SIZEOF(.noinit) + SIZEOF(.data.wifi)) - 304;
. = ALIGN (4);
_stack_start = ABSOLUTE(.);
_stack_start_cpu0 = ABSOLUTE(.);
} > RWDATA
}
INSERT BEFORE .trap;
#ELSE
SECTIONS {
/* must be last segment using RWDATA */
.stack (NOLOAD) : ALIGN(4)
{
. = ALIGN (4);
_stack_end = ABSOLUTE(.);
_stack_end_cpu0 = ABSOLUTE(.);
} > RWDATA
}
PROVIDE(_stack_start = ORIGIN(RWDATA) + LENGTH(RWDATA));
PROVIDE(_stack_start_cpu0 = ORIGIN(RWDATA) + LENGTH(RWDATA));
#ENDIF
/*
Provide the stack_guard for `stack-protector`
Ideally the offset should be configurable - should be done once we have https://github.com/esp-rs/esp-hal/issues/1111
*/
PROVIDE(__stack_chk_guard = _stack_end + 4096);
+15
View File
@@ -0,0 +1,15 @@
SECTIONS {
.text : ALIGN(4)
{
#IF riscv
KEEP(*(.init));
KEEP(*(.init.rust));
KEEP(*(.text.abort));
#ENDIF
*(.literal .text .literal.* .text.*)
} > ROTEXT
}