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

+36
View File
@@ -0,0 +1,36 @@
/* before memory.x to allow override */
ENTRY(ESP32Reset)
/* after memory.x to allow override */
PROVIDE(__pre_init = DefaultPreInit);
PROVIDE(__zero_bss = default_mem_hook);
PROVIDE(__init_data = default_mem_hook);
PROVIDE(__post_init = default_post_init);
PROVIDE(__level_1_interrupt = handle_interrupts);
PROVIDE(__level_2_interrupt = handle_interrupts);
PROVIDE(__level_3_interrupt = handle_interrupts);
INCLUDE exception.x
/* ESP32 fixups */
INCLUDE "fixups/rtc_fast_rwdata_dummy.x"
/* END ESP32 fixups */
/* Shared sections - ordering matters */
INCLUDE "rwtext.x"
INCLUDE "text.x"
INCLUDE "rwdata.x"
INCLUDE "rodata.x"
INCLUDE "rtc_fast.x"
INCLUDE "rtc_slow.x"
INCLUDE "stack.x"
INCLUDE "dram2.x"
/* End of Shared sections */
EXTERN(DefaultHandler);
EXTERN(WIFI_EVENT); /* Force inclusion of WiFi libraries */
INCLUDE "device.x"
+5
View File
@@ -0,0 +1,5 @@
INCLUDE "memory.x"
INCLUDE "alias.x"
INCLUDE "esp32.x"
INCLUDE "hal-defaults.x"
INCLUDE "rom-functions.x"
+61
View File
@@ -0,0 +1,61 @@
/* This memory map assumes the flash cache is on;
the blocks used are excluded from the various memory ranges
see: https://github.com/espressif/esp-idf/blob/5b1189570025ba027f2ff6c2d91f6ffff3809cc2/components/heap/port/esp32/memory_layout.c
for details
*/
/* override entry point */
ENTRY(ESP32Reset)
INCLUDE "memory_extras.x"
/* Specify main memory areas */
MEMORY
{
reserved_cache_seg : ORIGIN = 0x40070000, len = 64k /* SRAM0; reserved for usage as flash cache*/
vectors_seg ( RX ) : ORIGIN = 0x40080000, len = 1k /* SRAM0 */
iram_seg ( RX ) : ORIGIN = 0x40080400, len = 128k-0x400 /* SRAM0 */
reserved_for_rom_seg : ORIGIN = 0x3FFAE000, len = 8k /* SRAM2; reserved for usage by the ROM */
dram_seg ( RW ) : ORIGIN = 0x3FFB0000 + RESERVE_DRAM, len = 176k - RESERVE_DRAM /* SRAM2+1; first 64kB used by BT if enable */
/*
* The following values come from the heap allocator in esp-idf: https://github.com/espressif/esp-idf/blob/ab63aaa4a24a05904da2862d627f3987ecbeafd0/components/heap/port/esp32/memory_layout.c#L137-L157
* The segment dram2_seg after the rom data space is not mentioned in the esp32 linker scripts in esp-idf, instead the space after is used as heap space.
* It seems not all rom data space is reserved, but only "core"/"important" ROM functions that may be called after booting from ROM.
*/
reserved_rom_data_pro : ORIGIN = 0x3ffe0000, len = 1088
reserved_rom_data_app : ORIGIN = 0x3ffe3f20, len = 1072
/*
* The following values are derived from the __stack and _stack_sentry values from ROM.
* They represent the stacks used for each core setup by ROM code. In theory both of these
* can be reclaimed once both cores are running, but for now we play it safe and reserve them both.
*/
reserved_rom_stack_pro : ORIGIN = 0x3ffe1320, len = 11264
reserved_rom_stack_app : ORIGIN = 0x3ffe5230, len = 11264
dram2_seg : ORIGIN = 0x3ffe7e30, len = 98767 /* the rest of DRAM after the rom data segments and rom stacks in the middle */
/* external flash
The 0x20 offset is a convenience for the app binary image generation.
Flash cache has 64KB pages. The .bin file which is flashed to the chip
has a 0x18 byte file header, and each segment has a 0x08 byte segment
header. Setting this offset makes it simple to meet the flash cache MMU's
constraint that (paddr % 64KB == vaddr % 64KB).)
*/
irom_seg ( RX ) : ORIGIN = 0x400D0020, len = 3M - 0x20
drom_seg ( R ) : ORIGIN = 0x3F400020, len = 4M - 0x20
/* RTC fast memory (executable). Persists over deep sleep. Only for core 0 (PRO_CPU) */
rtc_fast_iram_seg(RWX) : ORIGIN = 0x400C0000, len = 8k
/* RTC fast memory (same block as above), viewed from data bus. Only for core 0 (PRO_CPU) */
rtc_fast_dram_seg(RW) : ORIGIN = 0x3FF80000, len = 8k
/* RTC slow memory (data accessible). Persists over deep sleep. */
rtc_slow_seg(RW) : ORIGIN = 0x50000000, len = 8k
}
+9
View File
@@ -0,0 +1,9 @@
INCLUDE "rom/esp32.rom.api.ld"
INCLUDE "rom/esp32.rom.eco3.ld"
INCLUDE "rom/esp32.rom.ld"
INCLUDE "rom/esp32.rom.libgcc.ld"
INCLUDE "rom/esp32.rom.redefined.ld"
INCLUDE "rom/esp32.rom.spiflash_legacy.ld"
INCLUDE "rom/esp32.rom.syscalls.ld"
INCLUDE "rom/additional.ld"
+20
View File
@@ -0,0 +1,20 @@
memcmp = 0x4000c260;
memcpy = 0x4000c2c8;
memmove = 0x4000c3c0;
memset = 0x4000c44c;
PROVIDE ( strcpy = 0x400013ac );
PROVIDE ( abs = 0x40056340 );
PROVIDE ( strncpy = 0x400015d4 );
PROVIDE ( strncmp = 0x4000c5f4 );
PROVIDE ( bzero = 0x4000c1f4 );
PROVIDE ( strcat = 0x4000c518 );
PROVIDE ( strcmp = 0x40001274 );
PROVIDE ( strchr = 0x4000c53c );
PROVIDE ( strlcpy = 0x4000c584 );
PROVIDE ( strstr = 0x4000c674 );
PROVIDE ( strcasecmp = 0x400011cc );
PROVIDE ( strdup = 0x4000143c );
PROVIDE ( atoi = 0x400566c4 );
+62
View File
@@ -0,0 +1,62 @@
/**
* ROM APIs
*/
PROVIDE ( esp_rom_crc32_le = crc32_le );
PROVIDE ( esp_rom_crc16_le = crc16_le );
PROVIDE ( esp_rom_crc8_le = crc8_le );
PROVIDE ( esp_rom_crc32_be = crc32_be );
PROVIDE ( esp_rom_crc16_be = crc16_be );
PROVIDE ( esp_rom_crc8_be = crc8_be );
PROVIDE ( esp_rom_gpio_pad_select_gpio = gpio_pad_select_gpio );
PROVIDE ( esp_rom_gpio_pad_pullup_only = gpio_pad_pullup );
PROVIDE ( esp_rom_gpio_pad_set_drv = gpio_pad_set_drv );
PROVIDE ( esp_rom_gpio_pad_unhold = gpio_pad_unhold );
PROVIDE ( esp_rom_gpio_connect_in_signal = gpio_matrix_in );
PROVIDE ( esp_rom_gpio_connect_out_signal = gpio_matrix_out );
PROVIDE ( esp_rom_efuse_mac_address_crc8 = esp_crc8 );
PROVIDE ( esp_rom_efuse_get_flash_gpio_info = ets_efuse_get_spiconfig );
PROVIDE ( esp_rom_efuse_is_secure_boot_enabled = ets_efuse_secure_boot_enabled );
PROVIDE ( esp_rom_uart_flush_tx = uart_tx_flush );
PROVIDE ( esp_rom_uart_tx_one_char = uart_tx_one_char );
PROVIDE ( esp_rom_uart_tx_wait_idle = uart_tx_wait_idle );
PROVIDE ( esp_rom_uart_rx_one_char = uart_rx_one_char );
PROVIDE ( esp_rom_uart_rx_string = UartRxString );
PROVIDE ( esp_rom_uart_set_as_console = uart_tx_switch );
PROVIDE ( esp_rom_uart_putc = ets_write_char_uart );
PROVIDE ( esp_rom_uart_switch_buffer = uart_buff_switch );
PROVIDE ( esp_rom_output_flush_tx = uart_tx_flush );
PROVIDE ( esp_rom_output_tx_one_char = uart_tx_one_char );
PROVIDE ( esp_rom_output_tx_wait_idle = uart_tx_wait_idle );
PROVIDE ( esp_rom_output_rx_one_char = uart_rx_one_char );
PROVIDE ( esp_rom_output_rx_string = UartRxString );
PROVIDE ( esp_rom_output_set_as_console = uart_tx_switch );
PROVIDE ( esp_rom_output_putc = ets_write_char_uart );
PROVIDE ( esp_rom_output_switch_buffer = uart_buff_switch );
/* wpa_supplicant re-implements the MD5 functions: MD5Init, MD5Update, MD5Final */
/* so here we directly assign the symbols with the ROM API address */
PROVIDE ( esp_rom_md5_init = 0x4005da7c );
PROVIDE ( esp_rom_md5_update = 0x4005da9c );
PROVIDE ( esp_rom_md5_final = 0x4005db1c );
PROVIDE ( esp_rom_software_reset_system = software_reset );
PROVIDE ( esp_rom_software_reset_cpu = software_reset_cpu );
PROVIDE ( esp_rom_printf = ets_printf );
PROVIDE ( esp_rom_delay_us = ets_delay_us );
PROVIDE ( esp_rom_install_uart_printf = ets_install_uart_printf );
PROVIDE ( esp_rom_get_reset_reason = rtc_get_reset_reason );
PROVIDE ( esp_rom_route_intr_matrix = intr_matrix_set );
PROVIDE ( esp_rom_get_cpu_ticks_per_us = ets_get_cpu_frequency );
PROVIDE ( esp_rom_spiflash_set_bp = esp_rom_spiflash_lock );
PROVIDE ( esp_rom_spiflash_write_enable = SPI_write_enable);
PROVIDE ( esp_rom_regi2c_read = rom_i2c_readReg );
PROVIDE ( esp_rom_regi2c_read_mask = rom_i2c_readReg_Mask );
PROVIDE ( esp_rom_regi2c_write = rom_i2c_writeReg );
PROVIDE ( esp_rom_regi2c_write_mask = rom_i2c_writeReg_Mask );
+10
View File
@@ -0,0 +1,10 @@
/*
ESP32 ECO3 ROM address table
Secure Boot Version 2 API's imported from the ROM
*/
PROVIDE ( ets_secure_boot_verify_signature = 0x4006543c);
PROVIDE ( ets_secure_boot_verify_boot_bootloader = 0x400655ec);
PROVIDE ( ets_use_secure_boot_v2 = 0x4000f8d4);
PROVIDE ( ets_rsa_pss_verify = 0x40065310);
PROVIDE ( ets_mgf1_sha256 = 0x400651a8);
PROVIDE ( ets_emsa_pss_verify = 0x4006520c);
File diff suppressed because it is too large. Load diff
+94
View File
@@ -0,0 +1,94 @@
/* Unlike other ROM functions which are exported using PROVIDE, which declares
weak symbols, these libgcc functions are exported using assignment,
which declares strong symbols. This is done so that ROM functions are always
used instead of the ones provided by libgcc.a.
*/
__absvdi2 = 0x4006387c;
__absvsi2 = 0x40063868;
__adddf3 = 0x40002590;
__addsf3 = 0x400020e8;
__addvdi3 = 0x40002cbc;
__addvsi3 = 0x40002c98;
__ashldi3 = 0x4000c818;
__ashrdi3 = 0x4000c830;
__bswapdi2 = 0x40064b08;
__bswapsi2 = 0x40064ae0;
__clrsbdi2 = 0x40064b7c;
__clrsbsi2 = 0x40064b64;
__clzdi2 = 0x4000ca50;
__clzsi2 = 0x4000c7e8;
__cmpdi2 = 0x40063820;
__ctzdi2 = 0x4000ca64;
__ctzsi2 = 0x4000c7f0;
__divdc3 = 0x400645a4;
__divdf3 = 0x40002954;
__divdi3 = 0x4000ca84;
__divsi3 = 0x4000c7b8;
__eqdf2 = 0x400636a8;
__eqsf2 = 0x40063374;
__extendsfdf2 = 0x40002c34;
__ffsdi2 = 0x4000ca2c;
__ffssi2 = 0x4000c804;
__fixdfdi = 0x40002ac4;
__fixdfsi = 0x40002a78;
__fixsfdi = 0x4000244c;
__fixsfsi = 0x4000240c;
__fixunsdfsi = 0x40002b30;
__fixunssfdi = 0x40002504;
__fixunssfsi = 0x400024ac;
__floatdidf = 0x4000c988;
__floatdisf = 0x4000c8c0;
__floatsidf = 0x4000c944;
__floatsisf = 0x4000c870;
__floatundidf = 0x4000c978;
__floatundisf = 0x4000c8b0;
__floatunsidf = 0x4000c938;
__floatunsisf = 0x4000c864;
__gcc_bcmp = 0x40064a70;
__gedf2 = 0x40063768;
__gesf2 = 0x4006340c;
__gtdf2 = 0x400636dc;
__gtsf2 = 0x400633a0;
__ledf2 = 0x40063704;
__lesf2 = 0x400633c0;
__lshrdi3 = 0x4000c84c;
__ltdf2 = 0x40063790;
__ltsf2 = 0x4006342c;
__moddi3 = 0x4000cd4c;
__modsi3 = 0x4000c7c0;
__muldc3 = 0x40063c90;
__muldf3 = 0x4006358c;
__muldi3 = 0x4000c9fc;
__mulsf3 = 0x400632c8;
__mulsi3 = 0x4000c7b0;
__mulvdi3 = 0x40002d78;
__mulvsi3 = 0x40002d60;
__nedf2 = 0x400636a8;
__negdf2 = 0x400634a0;
__negdi2 = 0x4000ca14;
__negsf2 = 0x400020c0;
__negvdi2 = 0x40002e98;
__negvsi2 = 0x40002e78;
__nesf2 = 0x40063374;
__nsau_data = 0x3ff96544;
__paritysi2 = 0x40002f3c;
__popcount_tab = 0x3ff96544;
__popcountdi2 = 0x40002ef8;
__popcountsi2 = 0x40002ed0;
__powidf2 = 0x400638e4;
__subdf3 = 0x400026e4;
__subsf3 = 0x400021d0;
__subvdi3 = 0x40002d20;
__subvsi3 = 0x40002cf8;
__truncdfsf2 = 0x40002b90;
__ucmpdi2 = 0x40063840;
__udiv_w_sdiv = 0x40064bec;
__udivdi3 = 0x4000cff8;
__udivmoddi4 = 0x40064bf4;
__udivsi3 = 0x4000c7c8;
__umoddi3 = 0x4000d280;
__umodsi3 = 0x4000c7d0;
__umulsidi3 = 0x4000c7d8;
__unorddf2 = 0x400637f4;
__unordsf2 = 0x40063478;
+38
View File
@@ -0,0 +1,38 @@
/*
ROM Functions defined in this file are not used in ESP-IDF as is,
and different definitions for functions with the same names are provided.
This file is not used when linking ESP-IDF and is intended for reference only
*/
PROVIDE ( abort = 0x4000bba4 );
PROVIDE ( aes_128_cbc_decrypt = 0x4005cc7c );
PROVIDE ( aes_128_cbc_encrypt = 0x4005cc18 );
PROVIDE ( aes_unwrap = 0x4005ccf0 );
PROVIDE ( base64_decode = 0x4005ced8 );
PROVIDE ( base64_encode = 0x4005cdbc );
PROVIDE ( ets_isr_mask = 0x400067fc );
PROVIDE ( ets_isr_unmask = 0x40006808 );
PROVIDE ( ets_timer_arm = 0x40008368 );
PROVIDE ( ets_timer_arm_us = 0x400083ac );
PROVIDE ( ets_timer_disarm = 0x400083ec );
PROVIDE ( ets_timer_done = 0x40008428 );
PROVIDE ( ets_timer_init = 0x400084e8 );
PROVIDE ( ets_timer_handler_isr = 0x40008454 );
PROVIDE ( ets_timer_setfn = 0x40008350 );
PROVIDE ( hmac_md5 = 0x4005d264 );
PROVIDE ( hmac_md5_vector = 0x4005d17c );
PROVIDE ( hmac_sha1 = 0x40060acc );
PROVIDE ( hmac_sha1_vector = 0x400609e4 );
PROVIDE ( hmac_sha256 = 0x40060d58 );
PROVIDE ( hmac_sha256_vector = 0x40060c84 );
PROVIDE ( MD5Final = 0x4005db1c );
PROVIDE ( MD5Init = 0x4005da7c );
PROVIDE ( MD5Update = 0x4005da9c );
PROVIDE ( md5_vector = 0x4005db80 );
PROVIDE ( pbkdf2_sha1 = 0x40060ba4 );
PROVIDE ( rc4_skip = 0x40060928 );
PROVIDE ( sha1_prf = 0x40060ae8 );
PROVIDE ( sha1_vector = 0x40060b64 );
PROVIDE ( sha256_prf = 0x40060d70 );
PROVIDE ( sha256_vector = 0x40060e08 );
PROVIDE ( uart_tx_wait_idle = 0x40009278 );
@@ -0,0 +1,29 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
Address table for SPI driver functions in ESP32 ROM.
These functions are only linked from ROM when SPI_FLASH_ROM_DRIVER_PATCH is not set in configuration.
*/
PROVIDE ( esp_rom_spiflash_write_encrypted = 0x40062e78 );
PROVIDE ( esp_rom_spiflash_erase_area = 0x400631ac );
PROVIDE ( esp_rom_spiflash_erase_block = 0x40062c4c );
PROVIDE ( esp_rom_spiflash_erase_chip = 0x40062c14 );
PROVIDE ( esp_rom_spiflash_erase_sector = 0x40062ccc );
PROVIDE ( esp_rom_spiflash_attach = 0x40062a6c );
PROVIDE ( esp_rom_spiflash_lock = 0x400628f0 );
PROVIDE ( esp_rom_spiflash_read = 0x40062ed8 );
PROVIDE ( esp_rom_spiflash_config_readmode = 0x40062b64 ); /* SPIMasterReadModeCnfig */
PROVIDE ( esp_rom_spiflash_read_status = 0x4006226c );
PROVIDE ( esp_rom_spiflash_read_statushigh = 0x40062448 );
PROVIDE ( esp_rom_spiflash_write = 0x40062d50 );
PROVIDE ( esp_rom_spiflash_enable_write = 0x40062320 );
PROVIDE ( esp_rom_spiflash_write_status = 0x400622f0 );
/* always using patched versions of these functions
PROVIDE ( esp_rom_spiflash_wait_idle = 0x400622c0 );
PROVIDE ( esp_rom_spiflash_unlock = 0x400????? );
*/
+66
View File
@@ -0,0 +1,66 @@
/* These ROM functions call respective entries in the syscall table.
They are called by other ROM functions (mostly from newlib).
We don't link to them directly, since in IDF there are actual
implementations of these functions, with same names.
I.e.:
times (in ROM) -> _times_r (in ROM) -> syscall table entry _times_r -> _times_r (in IDF)
Hence the following entries are provided only for reference
and commented out.
*/
/* <--- the following lines are commented out
PROVIDE ( calloc = 0x4000bee4 );
PROVIDE ( free = 0x4000beb8 );
PROVIDE ( _free_r = 0x4000bbcc );
PROVIDE ( _getpid_r = 0x4000bcfc );
PROVIDE ( __getreent = 0x4000be8c );
PROVIDE ( _gettimeofday_r = 0x4000bc58 );
PROVIDE ( _kill_r = 0x4000bd10 );
PROVIDE ( _lock_acquire = 0x4000be14 );
PROVIDE ( _lock_acquire_recursive = 0x4000be28 );
PROVIDE ( _lock_close = 0x4000bdec );
PROVIDE ( _lock_close_recursive = 0x4000be00 );
PROVIDE ( _lock_init = 0x4000bdc4 );
PROVIDE ( _lock_init_recursive = 0x4000bdd8 );
PROVIDE ( _lock_release = 0x4000be64 );
PROVIDE ( _lock_release_recursive = 0x4000be78 );
PROVIDE ( _lock_try_acquire = 0x4000be3c );
PROVIDE ( _lock_try_acquire_recursive = 0x4000be50 );
PROVIDE ( malloc = 0x4000bea0 );
PROVIDE ( _malloc_r = 0x4000bbb4 );
PROVIDE ( _raise_r = 0x4000bc70 );
PROVIDE ( realloc = 0x4000becc );
PROVIDE ( _realloc_r = 0x4000bbe0 );
PROVIDE ( _sbrk_r = 0x4000bce4 );
PROVIDE ( _system_r = 0x4000bc10 );
PROVIDE ( _times_r = 0x4000bc40 );
PROVIDE ( _close_r = 0x4000bd3c );
PROVIDE ( _exit_r = 0x4000bd28 );
PROVIDE ( _fstat_r = 0x4000bccc );
PROVIDE ( _link_r = 0x4000bc9c );
PROVIDE ( _lseek_r = 0x4000bd8c );
PROVIDE ( _open_r = 0x4000bd54 );
PROVIDE ( _read_r = 0x4000bda8 );
PROVIDE ( _rename_r = 0x4000bc28 );
PROVIDE ( _unlink_r = 0x4000bc84 );
PROVIDE ( _write_r = 0x4000bd70 );
---> end commented out block
*/
/* These are the non-reentrant versions of syscalls present in the ROM.
They call the reentrant versions, passing the pointer returned by __getreent
as the first argument.
*/
close = 0x40001778;
open = 0x4000178c;
read = 0x400017dc;
sbrk = 0x400017f4;
times = 0x40001808;
write = 0x4000181c;