Skip to content

port to Waveshare S3 Touch AMOLED 2.06"#525

Open
Loelin wants to merge 1 commit into
TactilityProject:mainfrom
Loelin:Waveshare-S3-Touch-AMOLED-2.06
Open

port to Waveshare S3 Touch AMOLED 2.06"#525
Loelin wants to merge 1 commit into
TactilityProject:mainfrom
Loelin:Waveshare-S3-Touch-AMOLED-2.06

Conversation

@Loelin
Copy link
Copy Markdown

@Loelin Loelin commented May 22, 2026

Checklist

Description

initialized Waveshare S3 Touch AMOLED 2.06" port

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for Waveshare ESP32-S3 Touch AMOLED display devices (1.8" and 2.06" variants)
    • New Audio app with volume and mute controls
    • New Dialog Box app for system messages
    • Audio playback with speaker and microphone support
    • New Miscellaneous Settings app
    • RTC time synchronization service
    • Motion-based screensaver wake detection
    • Charging status notifications
    • Enhanced statusbar with date/time and sync indicators
  • Improvements

    • Reduced power app refresh rate for better efficiency
    • Enhanced display idle and screensaver logic

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

📝 Walkthrough

Walkthrough

Adds full support for the Waveshare ESP32-S3 Touch AMOLED 2.06, including BSP, display/touch/audio/power drivers and device configs. Refactors ESP32 I2C to master-bus API and extends sensors/power drivers. Introduces Audio and DialogBox apps, RTC time-sync service, launcher charging notifications, statusbar time/NT P dialogs, and enhanced display idle behavior. Updates LVGL config, fonts, and stop logic. Adds build dependencies, sdkconfig, toolchain flags, linker/gdb helpers, and comprehensive flash args/templates. Also adds a minimal Waveshare 1.8 device slice and documentation for LVGL v9 desync fixes.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Tactility/Source/app/timezone/TimeZone.cpp (1)

132-138: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix fgets length to match line buffer in TimeZone.cpp (lines 132-138)

line is char line[128], but the loop reads with fgets(line, 96, file), so longer entries are still truncated to 95 chars.

💡 Suggested fix
-        while (fgets(line, 96, file)) {
+        while (fgets(line, sizeof(line), file)) {
bootloader-prefix/tmp/bootloader-mkdirs.cmake (1)

1-28: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Stop committing generated CMake bootloader build artifacts (remove and ignore bootloader-prefix/)

  • bootloader-prefix/tmp/bootloader-mkdirs.cmake is tracked in git and contains developer-specific absolute paths (/Users/oneillchampagnie/...), so it should not be committed.
  • Also tracked: bootloader-prefix/tmp/bootloader-cfgcmd.txt and bootloader-prefix/src/bootloader-stamp/{bootloader-patch-info.txt,bootloader-source_dirinfo.txt,bootloader-update-info.txt}.
  • Add .gitignore rules for bootloader-prefix/ (at least bootloader-prefix/tmp/ and bootloader-prefix/src/bootloader-stamp/) to prevent these regenerated files from being reintroduced.
🟠 Major comments (31)
Devices/waveshare-s3-touch-amoled-18/Source/devices/Display.cpp-64-85 (1)

64-85: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make display lifecycle symmetric and idempotent.

Resources are created in start()/startLvgl() but stop() and stopLvgl() currently do nothing. Re-entering startup can leave stale LVGL registrations and repeat side effects (e.g., SD mount), which is a reliability risk.

Also applies to: 122-139, 146-150, 155-157

Devices/waveshare-s3-touch-amoled-18/Source/devices/InternalFlash.cpp.disabled-6-7 (1)

6-7: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Resolve linkage mismatch for mounted_wl_handle/is_mounted in InternalFlash.

InternalFlash.h.disabled declares extern wl_handle_t mounted_wl_handle; and extern bool is_mounted;, but InternalFlash.cpp.disabled defines them as static, which is inconsistent linkage and will fail once the file is enabled (and can break any TUs including the header).

Suggested fix
-static wl_handle_t mounted_wl_handle = WL_INVALID_HANDLE;
-static bool is_mounted = false;
+wl_handle_t mounted_wl_handle = WL_INVALID_HANDLE;
+bool is_mounted = false;
Devices/waveshare-s3-touch-amoled-206/Source/devices/AudioDevice.cpp-54-95 (1)

54-95: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Add rollback in initI2S() failure paths.

Partial init failures currently return early without cleaning up created/enabled channels/interface, leaving leaked state for later retries.

Devices/waveshare-s3-touch-amoled-206/Source/devices/Display.cpp-150-155 (1)

150-155: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

stop() doesn’t release hardware resources.

panelHandle/ioHandle are kept alive and never nulled or deinitialized, so repeated start/stop cycles can leak resources and leave stale handles.

Devices/waveshare-s3-touch-amoled-206/Source/devices/AudioDevice.cpp-343-355 (1)

343-355: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Rollback speaker open if microphone open fails.

If Line 351 fails, speaker remains open and the function returns false with a partial audio state.

Proposed fix
     if (micHandle) {
         esp_err_t err = esp_codec_dev_open(micHandle, &DEFAULT_FS);
         if (err != ESP_OK) {
             LOG_E(TAG, "Failed to open microphone: %d", err);
+            if (spkHandle) {
+                esp_codec_dev_close(spkHandle);
+            }
             return false;
         }
     }
Devices/waveshare-s3-touch-amoled-206/CMakeLists.txt-1-3 (1)

1-3: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

SOURCE_FILES gets overwritten, dropping .c sources.

Line 3 replaces the result from Line 1, so any C files are excluded from SRCS. Keep a single glob (or append) so both C and C++ files are compiled.

Proposed fix
-file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
-
-file(GLOB_RECURSE SOURCE_FILES "Source/*.cpp")
+file(GLOB_RECURSE SOURCE_FILES
+    "Source/*.c"
+    "Source/*.cc"
+    "Source/*.cxx"
+    "Source/*.cpp"
+)
Devices/waveshare-s3-touch-amoled-206/Source/Configuration.cpp-165-177 (1)

165-177: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle initAudioCodecs failure before registering devices.

If codec init fails, speaker/microphone objects may still be pushed as valid handles but unusable runtime devices. Gate registration on successful initialization.

Proposed fix
-    if (speaker || microphone) {
-        waveshare::audio::initAudioCodecs(speaker, microphone);
-    }
+    bool audioReady = true;
+    if (speaker || microphone) {
+        audioReady = waveshare::audio::initAudioCodecs(speaker, microphone);
+    }
 
-    if (speaker && speaker->isValid()) {
+    if (audioReady && speaker && speaker->isValid()) {
         speaker->setVolume(audioSettings.volume);
         speaker->setMuted(audioSettings.muted);
         devices.push_back(speaker);
     }
 
-    if (microphone && microphone->isValid()) {
+    if (audioReady && microphone && microphone->isValid()) {
         devices.push_back(microphone);
     }
Devices/waveshare-s3-touch-amoled-206/Source/devices/Display.cpp-118-145 (1)

118-145: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use lvgl_port_remove_disp instead of lv_display_delete for lvgl_port_add_disp displays
In Devices/waveshare-s3-touch-amoled-206/Source/devices/Display.cpp (Co5300Display::stopLvgl()), lvglDisplay is created via lvgl_port_add_disp(&disp_cfg), but it’s destroyed with lv_display_delete(lvglDisplay). Switch to lvgl_port_remove_disp(lvglDisplay) (then set lvglDisplay = nullptr) so the esp_lvgl_port layer can do its port-level cleanup; other displays in this repo already follow this pattern.

Devices/waveshare-s3-touch-amoled-206/Source/Configuration.cpp-127-132 (1)

127-132: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix ES7210 I2C probe: 0x80 is invalid for ESP-IDF 7-bit addressing
ESP-IDF I2C master in 7-bit mode expects device_address to be the 7-bit value 0x00–0x7F (no R/W bit). 0x80 is outside that range, so probing at 0x80 cannot succeed. Update the probe argument to the ES7210’s correct 7-bit slave address (based on its AD1/AD0 configuration) instead of 0x80.

Devices/waveshare-s3-touch-amoled-206/Source/devices/Touch.cpp-142-149 (1)

142-149: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Delete touchHandle before ioHandle in Co5300Touch::stop()

touchHandle is created with esp_lcd_touch_new_i2c_ft5x06(ioHandle, ...), but stop() currently calls esp_lcd_panel_io_del(ioHandle) before esp_lcd_touch_del(touchHandle)—swap the order to avoid touch teardown using freed/deinitialized IO resources.

Proposed fix
-    if (ioHandle != nullptr) {
-        esp_lcd_panel_io_del(ioHandle);
-        ioHandle = nullptr;
-    }
-
     if (touchHandle != nullptr) {
         esp_lcd_touch_del(touchHandle);
         touchHandle = nullptr;
     }
+
+    if (ioHandle != nullptr) {
+        esp_lcd_panel_io_del(ioHandle);
+        ioHandle = nullptr;
+    }
Devices/waveshare-s3-touch-amoled-206/Source/devices/Touch.cpp-176-193 (1)

176-193: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use lvgl_port_remove_touch() in Co5300Touch::stopLvgl() for correct LVGL touch teardown/ownership

stopLvgl() manually unregisters the interrupt callback, free(lvglTouchCtx), and lv_indev_delete(lvglTouchIndev). That duplicates esp_lvgl_port’s internal lvgl_port_remove_touch() cleanup (including freeing the touch context obtained via lv_indev_get_driver_data()), so it can break with allocator/ownership changes.

  • Diff (teardown via port API):

    Proposed fix
     bool Co5300Touch::stopLvgl() {
         if (lvglTouchIndev == nullptr) {
             return false;
         }
    -
    -    // Unregister interrupt callback (same as lvgl_port_remove_touch internals)
    -    if (touchHandle != nullptr && touchHandle->config.int_gpio_num != GPIO_NUM_NC) {
    -        esp_lcd_touch_register_interrupt_callback(touchHandle, NULL);
    -    }
    -
    -    // Free allocated touch context
    -    if (lvglTouchCtx != nullptr) {
    -        free(lvglTouchCtx);
    -        lvglTouchCtx = nullptr;
    -    }
    -
    -    lv_indev_delete(lvglTouchIndev);
    +    lvgl_port_remove_touch(lvglTouchIndev);
         lvglTouchIndev = nullptr;
    +    lvglTouchCtx = nullptr;
     
         return true;
     }
  • In Co5300Touch::stop(), esp_lcd_panel_io_del(ioHandle) runs before esp_lcd_touch_del(touchHandle). Prefer deleting the touch handle first when both share the same panel IO transport.

Drivers/AXP2101/Source/Axp2101.cpp-42-42 (1)

42-42: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Charging enable write is inverted.

isChargingEnabled() treats bit 0b10 as enabled, but Line 42 writes 0x00 when enabled == true. This flips behavior for callers like setAllowedToCharge(true).

Proposed fix
 bool Axp2101::setChargingEnabled(bool enabled) const {
-    return writeRegister8(0x18, enabled ? 0x00 : 0x02);
+    return writeRegister8(0x18, enabled ? 0x02 : 0x00);
 }
Drivers/AXP2101/Source/Axp2101Power.cpp-36-43 (1)

36-43: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t trust 100% when the sanity-check read fails.

If getBatteryVoltage() fails, the current logic still returns 100%. That bypasses the fallback estimate and can overreport charge.

Proposed fix
                 if (percentage == 100) {
                     float vbatMillis = 0.0f;
-                    if (axpDevice->getBatteryVoltage(vbatMillis) && vbatMillis < 4150.0f) {
-                        // fall through to voltage calculation below
-                    } else {
+                    if (axpDevice->getBatteryVoltage(vbatMillis)) {
+                        if (vbatMillis >= 4150.0f) {
+                            data.valueAsUint8 = percentage;
+                            return true;
+                        }
+                        // fall through to voltage calculation below
+                    } else {
+                        // fall through to voltage calculation below
+                    }
+                } else {
                         data.valueAsUint8 = percentage;
                         return true;
-                    }
-                } else {
-                    data.valueAsUint8 = percentage;
-                    return true;
                 }
Modules/lvgl-module/source/arch/lvgl_esp32.c-55-63 (1)

55-63: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Recovery path leaves initialized in inconsistent state.

If lvgl_port_deinit() fails and on_start() is called to recover, initialized remains false. This means subsequent lvgl_lock() calls will return fake locks even though LVGL has been restarted, potentially causing concurrent access issues.

🐛 Proposed fix
     // Set initialized to false BEFORE deinit to prevent concurrent
     // lock calls from accessing the mutex after it's destroyed.
     initialized = false;

     if (lvgl_port_deinit() != ESP_OK) {
         // Call on_start again to recover
-        if (lvgl_module_config.on_start) lvgl_module_config.on_start();
+        if (lvgl_module_config.on_start) {
+            lvgl_module_config.on_start();
+            initialized = true;  // Restore state after recovery
+        }
         return ERROR_RESOURCE;
     }
Tactility/Source/PartitionsEsp.cpp-151-170 (1)

151-170: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Propagate partition mount/registration failures instead of always returning success.

Line 169 always returns ESP_OK, even when mount fails (Lines 152/161) or filesystem registration fails (Lines 157/166). That can leave startup in a partial FS state while reporting success.

Suggested fix
-    err = esp_vfs_fat_spiflash_mount_rw_wl("/system", "system", &mount_config, &system_wl_handle);
-    if (err != ESP_OK) {
+    esp_err_t system_err = esp_vfs_fat_spiflash_mount_rw_wl("/system", "system", &mount_config, &system_wl_handle);
+    if (system_err != ESP_OK) {
         LOGGER.error("Failed to mount system partition: %s", esp_err_to_name(err));
     } else {
         LOGGER.info("System partition mounted at /system");
-        system_mounted = true;
         system_fs = file_system_add(&system_fs_api, nullptr);
+        system_mounted = (system_fs != nullptr);
+        if (!system_mounted) {
+            LOGGER.error("Failed to register system filesystem");
+            system_err = ESP_FAIL;
+        }
     }
 
-    err = esp_vfs_fat_spiflash_mount_rw_wl("/data", "data", &mount_config, &data_wl_handle);
-    if (err != ESP_OK) {
+    esp_err_t data_err = esp_vfs_fat_spiflash_mount_rw_wl("/data", "data", &mount_config, &data_wl_handle);
+    if (data_err != ESP_OK) {
         LOGGER.error("Failed to mount data partition: %s", esp_err_to_name(err));
     } else {
         LOGGER.info("Data partition mounted at /data");
-        data_mounted = true;
         data_fs = file_system_add(&data_fs_api, nullptr);
+        data_mounted = (data_fs != nullptr);
+        if (!data_mounted) {
+            LOGGER.error("Failed to register data filesystem");
+            data_err = ESP_FAIL;
+        }
     }
 
-    return ESP_OK;
+    return (system_err == ESP_OK && data_err == ESP_OK) ? ESP_OK : ESP_FAIL;
Tactility/Source/TactilityEsp.cpp-30-31 (1)

30-31: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Wrap esp_netif_init() with ESP_ERROR_CHECK() to handle init failures.

Tactility/Source/TactilityEsp.cpp:31 calls esp_netif_init() without checking its returned esp_err_t, so network init failures can be lost and surface later. Replace with ESP_ERROR_CHECK(esp_netif_init());.

Suggested fix
     ESP_ERROR_CHECK(esp_event_loop_create_default());
-    esp_netif_init();
+    ESP_ERROR_CHECK(esp_netif_init());
Tactility/Source/network/Ntp.cpp-27-28 (1)

27-28: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Thread resource leak on shutdown.

rtcSyncThread is allocated and started but never joined or freed. When the module is deinitialized or on restart, this leaks resources. Add a cleanup function to stop and free the thread.

Proposed approach

Add a deinit() function or ensure cleanup in an existing shutdown path:

void deinit() {
    stopRtcSync = true;
    if (rtcSyncThread) {
        thread_join(rtcSyncThread);
        thread_free(rtcSyncThread);
        rtcSyncThread = nullptr;
    }
}

Also applies to: 120-132

Tactility/Source/hal/sdcard/SpiSdCardDevice.cpp-52-75 (1)

52-75: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Hardcoded GPIO pins reduce reusability.

The SPI1 bus initialization hardcodes GPIO pins (MOSI=1, MISO=3, SCLK=2), but this is a generic SD card device class. These values should come from the configuration struct, similar to how other SPI properties are configured.

Proposed approach

Add GPIO pin fields to the configuration struct and use them here:

     if (config->spiHost == SPI1_HOST) {
         spi_bus_config_t buscfg = {
-            .mosi_io_num = GPIO_NUM_1,
-            .miso_io_num = GPIO_NUM_3,
-            .sclk_io_num = GPIO_NUM_2,
+            .mosi_io_num = config->spiPinMosi,
+            .miso_io_num = config->spiPinMiso,
+            .sclk_io_num = config->spiPinSclk,
             .quadwp_io_num = -1,
             .quadhd_io_num = -1,
             .max_transfer_sz = 4092,
             .flags = 0,
             .intr_flags = 0
         };
bootloader-prefix/src/bootloader-stamp/bootloader-source_dirinfo.txt-7-7 (1)

7-7: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove hardcoded user-specific absolute path.

Line 7 contains a hardcoded absolute path to a specific user's home directory (/Users/oneillchampagnie/...). This build artifact should not be committed to version control as it:

  • Will not work for other developers with different usernames or directory structures
  • Exposes developer-specific environment details
  • Is typically regenerated during build

Add bootloader-prefix/ to .gitignore to prevent committing machine-specific build artifacts.

bootloader-prefix/tmp/bootloader-cfgcmd.txt-1-1 (1)

1-1: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove build artifact with multiple hardcoded paths.

This file contains a CMake command string with numerous hardcoded absolute paths specific to a developer's machine (multiple /Users/oneillchampagnie/... references). This is a generated build artifact that:

  • Must be regenerated for each developer's environment
  • Exposes environment-specific details unnecessarily
  • Should be excluded via .gitignore (add bootloader-prefix/ pattern)

Same root cause as the issue in bootloader-source_dirinfo.txt.

components/waveshare__esp32_s3_touch_amoled_2_06/esp32_s3_touch_amoled_2_06.c-338-341 (1)

338-341: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Propagate brightness init errors instead of always returning ESP_OK.

Line 340 currently discards failure from bsp_display_brightness_set(100).

Suggested fix
 esp_err_t bsp_display_brightness_init(void)
 {
-    bsp_display_brightness_set(100);
-    return ESP_OK;
+    return bsp_display_brightness_set(100);
 }
components/waveshare__esp32_s3_touch_amoled_2_06/esp32_s3_touch_amoled_2_06.c-365-365 (1)

365-365: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Check DCS brightness write result.

esp_lcd_panel_io_tx_param(...) is unchecked, so I/O failures are silently reported as success.

Suggested fix
-    esp_lcd_panel_io_tx_param(io_handle, lcd_cmd, &param, 1);
-
-    return ESP_OK;
+    return esp_lcd_panel_io_tx_param(io_handle, lcd_cmd, &param, 1);
components/waveshare__esp32_s3_touch_amoled_2_06/esp32_s3_touch_amoled_2_06.c-236-244 (1)

236-244: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clear global I2S handles on init failure.

After i2s_del_channel(...), the globals remain non-null. A retry can falsely pass the if (i2s_tx_chan && i2s_rx_chan) guard and leave audio in an invalid state.

Suggested fix
 err:
     if (i2s_tx_chan)
     {
         i2s_del_channel(i2s_tx_chan);
+        i2s_tx_chan = NULL;
     }
     if (i2s_rx_chan)
     {
         i2s_del_channel(i2s_rx_chan);
+        i2s_rx_chan = NULL;
     }
+    i2s_data_if = NULL;

     return ret;
components/waveshare__esp32_s3_touch_amoled_2_06/esp32_s3_touch_amoled_2_06.c-426-470 (1)

426-470: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Stop aborting in bsp_display_new; propagate esp_err_t failures instead (major)

  • bsp_display_new() returns esp_err_t and callers handle bsp_display_new(...)’s return value, but it currently uses ESP_ERROR_CHECK(...) (e.g., spi_bus_initialize, esp_lcd_new_panel_io_spi, esp_lcd_new_panel_sh8601), which aborts the application instead of returning the error.
  • esp_lcd_panel_reset/init/set_gap/disp_on_off also return esp_err_t, but their results are ignored.
Suggested fix
-    ESP_ERROR_CHECK(spi_bus_initialize(BSP_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO));
+    ESP_RETURN_ON_ERROR(spi_bus_initialize(BSP_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "spi_bus_initialize failed");
@@
-    ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)BSP_LCD_SPI_NUM, &io_config, &io_handle));
+    ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)BSP_LCD_SPI_NUM, &io_config, &io_handle), TAG, "new_panel_io_spi failed");
@@
-    ESP_ERROR_CHECK(esp_lcd_new_panel_sh8601(io_handle, &panel_config, &panel_handle));
-    esp_lcd_panel_reset(panel_handle);
-    esp_lcd_panel_init(panel_handle);
-    esp_lcd_panel_set_gap(panel_handle, 0x16, 0);
-    esp_lcd_panel_disp_on_off(panel_handle, true);
+    ESP_RETURN_ON_ERROR(esp_lcd_new_panel_sh8601(io_handle, &panel_config, &panel_handle), TAG, "new_panel_sh8601 failed");
+    ESP_RETURN_ON_ERROR(esp_lcd_panel_reset(panel_handle), TAG, "panel_reset failed");
+    ESP_RETURN_ON_ERROR(esp_lcd_panel_init(panel_handle), TAG, "panel_init failed");
+    ESP_RETURN_ON_ERROR(esp_lcd_panel_set_gap(panel_handle, 0x16, 0), TAG, "panel_set_gap failed");
+    ESP_RETURN_ON_ERROR(esp_lcd_panel_disp_on_off(panel_handle, true), TAG, "panel_disp_on_off failed");
config.env-2-11 (1)

2-11: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove machine-specific paths and user-identifying data from committed config.

Lines 2-11 embed absolute local paths (for example under /Users/oneillchampagnie/...). This leaks workstation-specific info and makes the file non-portable in CI/other dev environments. Please keep this file generated locally (or templated with env vars) and out of source control.

esp-idf/Tactility/data-flash_args.in-1-1 (1)

1-1: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid hardcoding flash mode/frequency/size in shared flash-args templates.

esp-idf/Tactility/data-flash_args.in:1, esp-idf/Tactility/system-flash_args.in:1, and esp-idf/bootloader/bootloader-flash_args.in:1 all hardcode the same values:

--flash_mode dout --flash_freq 80m --flash_size 32MB

Meanwhile Devices/**/device.properties declares varying flashSize and esptoolFlashFreq across devices and even includes different flashMode values (e.g., flashMode=DIO), so these shared template defaults can diverge from device-specific settings and lead to incorrect flashing parameters for boards that don’t match the hardcoded profile.

esp-idf/esptool_py/app-flash_args.in-1-1 (1)

1-1: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Stop hardcoding esptool flash parameters in shared esp-idf templates.

Hardcoded dout/80m/32MB is present not only in esp-idf/esptool_py/app-flash_args.in but also in esp-idf/esptool_py/flasher_args.json.in (e.g., write_flash_args / flash_mode|flash_size|flash_freq). Parameterize these values per target instead of embedding fixed strings.

Suggested direction
---flash_mode dout --flash_freq 80m --flash_size 32MB
+--flash_mode `@ESPTOOLPY_FLASHMODE`@ --flash_freq `@ESPTOOLPY_FLASHFREQ`@ --flash_size `@ESPTOOLPY_FLASHSIZE`@
gdbinit/symbols-19-20 (1)

19-20: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Replace hardcoded absolute path with relative path.

Line 20 contains a hardcoded absolute path that breaks portability for other developers.

🔧 Proposed fix using relative path
 # Load application symbols
-file /Users/oneillchampagnie/Tactility/Tactility.elf
+file Tactility.elf

GDB will resolve this relative to the working directory (typically the project root).

gdbinit/symbols-14-17 (1)

14-17: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Replace hardcoded absolute path with relative path.

Line 16 contains a hardcoded absolute path that breaks portability for other developers.

🔧 Proposed fix using relative path
 # Load bootloader symbols
 set confirm off
-    add-symbol-file /Users/oneillchampagnie/Tactility/bootloader/bootloader.elf
+    add-symbol-file bootloader/bootloader.elf
 set confirm on

GDB will resolve this relative to the working directory (typically the project root).

gdbinit/gdbinit-1-2 (1)

1-2: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Replace hardcoded absolute paths with relative paths.

The hardcoded absolute path /Users/oneillchampagnie/Tactility/ prevents other developers from using these GDB scripts and breaks portability.

🔧 Proposed fix using relative paths
-source /Users/oneillchampagnie/Tactility/gdbinit/symbols
-source /Users/oneillchampagnie/Tactility/gdbinit/connect
+source gdbinit/symbols
+source gdbinit/connect

GDB will resolve these relative to the working directory (typically the project root). Alternatively, if these paths need to be dynamic, consider using environment variables or generating this file as part of the build process.

gdbinit/symbols-2-11 (1)

2-11: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Replace hardcoded absolute path with portable solution.

Line 6 contains a hardcoded absolute path /Users/oneillchampagnie/.espressif/... that prevents other developers from using this debug configuration.

🔧 Proposed fix using environment variable
 define target hookpost-remote
 set confirm off
   # if $_streq((char *) 0x3ff194ad, "Mar  1 2021")
   if (*(int*) 0x3ff194ad) == 0x2072614d && (*(int*) 0x3ff194b1) == 0x32203120 && (*(int*) 0x3ff194b5) == 0x313230
-    add-symbol-file /Users/oneillchampagnie/.espressif/tools/esp-rom-elfs/20241011esp32s3_rev0_rom.elf
+    python gdb.execute("add-symbol-file " + os.path.expandvars("$HOME/.espressif/tools/esp-rom-elfs/20241011esp32s3_rev0_rom.elf"))
   else
     echo Warning: Unknown esp32s3 ROM revision.\n
   end

Alternatively, if IDF_PATH is set, you could reference that environment variable, or generate this file as part of the build process with the correct paths injected.

🟡 Minor comments (13)
Devices/waveshare-s3-touch-amoled-18/CMakeLists.txt-1-3 (1)

1-3: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix SOURCE_FILES overwrite in globbing.

Line 3 overwrites the list built at Line 1, so .c sources are excluded from SRCS. Merge these into one glob assignment.

Suggested fix
-file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
-
-file(GLOB_RECURSE SOURCE_FILES "Source/*.cpp")
+file(GLOB_RECURSE SOURCE_FILES
+    "Source/*.c"
+    "Source/*.cpp"
+)
Firmware/idf_component.yml-42-49 (1)

42-49: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Pin dependency versions (avoid version: "*") for the Waveshare touch + CO5300 LCD drivers

Firmware/idf_component.yml sets version: "*" for waveshare/esp32_s3_touch_amoled_2_06 and espressif/esp_lcd_co5300, while other components in the same file use fixed versions. * can change what gets pulled over time and cause unexpected build/runtime issues when upstream releases are updated. Attempts to fetch version metadata via the earlier registry URL pattern return 404 here, so pick concrete versions based on what you’ve tested (or via the IDF Component Manager).

  • Replace version: "*" with specific version numbers for both components.
Tactility/Source/app/audio/Audio.cpp-52-55 (1)

52-55: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reset the dirty flag across app lifecycle.

audioSettingsUpdated is never cleared after load/save, so later hides can re-save unchanged settings.

💡 Suggested fix
 void onShow(AppContext& app, lv_obj_t* parent) override {
     audioSettings = settings::audio::loadOrGetDefault();
+    audioSettingsUpdated = false;
     auto ui_density = lvgl_get_ui_density();
@@
 void onHide(AppContext& app) override {
     if (audioSettingsUpdated) {
         const settings::audio::AudioSettings settings_to_save = audioSettings;
         getMainDispatcher().dispatch([settings_to_save] {
             settings::audio::save(settings_to_save);
         });
     }
+    audioSettingsUpdated = false;
 }

Also applies to: 104-110

Tactility/Source/app/dialogbox/DialogBox.cpp-22-27 (1)

22-27: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard timeout narrowing before storing into Bundle.

Tactility/Source/app/dialogbox/DialogBox.cpp (lines 22-27 and 34-38) casts uint32_t timeoutMs to int32_t for bundle->putInt32(...). DialogBoxApp::onShow reads it back as int32_t and only starts the timer when timeoutMs > 0, so values > INT32_MAX wrap negative and disable auto-dismiss.

Suggested fix
 LaunchId start(const std::string& title, const std::string& message, uint32_t timeoutMs) {
     auto bundle = std::make_shared<Bundle>();
+    const auto safeTimeoutMs = static_cast<int32_t>(std::min<uint32_t>(timeoutMs, INT32_MAX));
     bundle->putString(PARAMETER_BUNDLE_KEY_TITLE, title);
     bundle->putString(PARAMETER_BUNDLE_KEY_MESSAGE, message);
-    bundle->putInt32(PARAMETER_BUNDLE_KEY_TIMEOUT, (int32_t)timeoutMs);
+    bundle->putInt32(PARAMETER_BUNDLE_KEY_TIMEOUT, safeTimeoutMs);
     return app::start(manifest.appId, bundle);
 }
@@
 LaunchId start(const std::string& message, uint32_t timeoutMs) {
     auto bundle = std::make_shared<Bundle>();
+    const auto safeTimeoutMs = static_cast<int32_t>(std::min<uint32_t>(timeoutMs, INT32_MAX));
     bundle->putString(PARAMETER_BUNDLE_KEY_MESSAGE, message);
-    bundle->putInt32(PARAMETER_BUNDLE_KEY_TIMEOUT, (int32_t)timeoutMs);
+    bundle->putInt32(PARAMETER_BUNDLE_KEY_TIMEOUT, safeTimeoutMs);
     return app::start(manifest.appId, bundle);
 }
Tactility/Source/hal/sdcard/SpiSdCardDevice.cpp-67-67 (1)

67-67: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Incorrect format specifier for Logger.

The Logger class uses fmt-style formatting with {}, not printf-style %s.

Proposed fix
-            LOGGER.error("Failed to init SPI1 bus: %s", esp_err_to_name(ret));
+            LOGGER.error("Failed to init SPI1 bus: {}", esp_err_to_name(ret));
Tactility/Source/service/rtctime/RtcTimeService.cpp-67-73 (1)

67-73: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

localtime() is not thread-safe.

Use localtime_r() for thread-safe conversion.

Proposed fix
     time_t now = time(nullptr);
-    tm* tm_struct = localtime(&now);
-    if (!tm_struct) {
+    tm tm_buf;
+    tm* tm_struct = localtime_r(&now, &tm_buf);
+    if (!tm_struct) {
Tactility/Source/service/rtctime/RtcTimeService.cpp-110-118 (1)

110-118: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Publishing Time event triggers redundant RTC write.

After setting system time from RTC in onStart(), publishing SystemEvent::Time immediately triggers onTimeChanged(), which writes the same time back to the RTC. Consider either skipping the event publish when time comes from RTC, or adding a guard to avoid the redundant write.

Proposed fix option

Add a flag to skip the first event after reading from RTC:

+    bool skipNextTimeEvent = false;
+
     void onTimeChanged(kernel::SystemEvent event) {
         if (event == kernel::SystemEvent::Time) {
+            if (skipNextTimeEvent) {
+                skipNextTimeEvent = false;
+                return;
+            }
             Device* rtc = findRtcDevice();
             if (rtc) {
                 writeRtcFromSystemTime(rtc);
             }
         }
     }

     // In onStart():
     if (setSystemTimeFromRtc(rtc)) {
+        skipNextTimeEvent = true;
         kernel::publishSystemEvent(kernel::SystemEvent::Time);
     }
Tactility/Source/network/Ntp.cpp-36-40 (1)

36-40: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

localtime() is not thread-safe.

Use localtime_r() for thread-safe time conversion in background threads.

Proposed fix
     time_t now = time(nullptr);
-    tm* tm_struct = localtime(&now);
-    if (!tm_struct) {
-        return;
-    }
+    tm tm_struct;
+    if (localtime_r(&now, &tm_struct) == nullptr) {
+        return;
+    }

Then use tm_struct. instead of tm_struct-> below.

Tactility/Source/lvgl/Statusbar.cpp-64-93 (1)

64-93: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

localtime() is not thread-safe.

localtime() returns a pointer to static internal storage that may be overwritten by subsequent calls from other threads. Consider using localtime_r() for thread-safe conversion.

Proposed fix
 static std::string formatDateTimeString() {
     time_t now = ::time(nullptr);
-    tm* tm_struct = localtime(&now);
+    tm tm_struct;
+    localtime_r(&now, &tm_struct);

     settings::SystemSettings sysSettings;
     settings::loadSystemSettings(sysSettings);

-    int month = tm_struct->tm_mon + 1;
-    int day = tm_struct->tm_mday;
-    int year = tm_struct->tm_year + 1900;
+    int month = tm_struct.tm_mon + 1;
+    int day = tm_struct.tm_mday;
+    int year = tm_struct.tm_year + 1900;
     // ... and similar changes for tm_hour, tm_min
Tactility/Source/settings/AudioSettings.cpp-20-26 (1)

20-26: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Treat malformed persisted volume as invalid input.

Line 23 currently turns non-numeric values into 0, which can silently mute audio instead of using a safe default.

🔧 Proposed fix
     auto volume_entry = map.find(SETTINGS_KEY_VOLUME);
     int volume = 70;
     if (volume_entry != map.end()) {
-        volume = atoi(volume_entry->second.c_str());
-        if (volume < 0) volume = 0;
-        if (volume > 100) volume = 100;
+        const auto& raw = volume_entry->second;
+        if (raw.empty() || raw.find_first_not_of("0123456789") != std::string::npos) {
+            volume = 70;
+        } else {
+            volume = atoi(raw.c_str());
+            if (volume < 0) volume = 0;
+            if (volume > 100) volume = 100;
+        }
     }
components/waveshare__esp32_s3_touch_amoled_2_06/include/bsp/display.h-62-63 (1)

62-63: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update backlight docs to match implementation path.

These comments still describe PWM pin control, but implementation uses panel DCS brightness commands. The API docs should reflect that to avoid integration confusion.

Also applies to: 73-74, 88-89, 100-101

components/waveshare__esp32_s3_touch_amoled_2_06/idf_component.yml-6-12 (1)

6-12: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace wildcard * dependency versions with bounded semver ranges for reproducible builds

In components/waveshare__esp32_s3_touch_amoled_2_06/idf_component.yml (lines 6-12), * is an open-ended wildcard that can match “anything”, so dependency resolution can drift over time. Use bounded ranges instead:

  • esp_lcd_touch_ft5x06: ^1.0.0 (or ~1)
  • waveshare/esp_lcd_sh8601: ^1.0.2 (or ~1)
toolchain/toolchain-esp32s3.cmake-2-2 (1)

2-2: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add an explicit IDF_PATH guard before the ESP-IDF toolchain include.

toolchain/toolchain-esp32s3.cmake currently does an unguarded:

include($ENV{IDF_PATH}/tools/cmake/toolchain.cmake)

Add a fail-fast FATAL_ERROR when IDF_PATH is missing/empty to make misconfiguration easier to diagnose.

Proposed change
 set(_CMAKE_TOOLCHAIN_PREFIX xtensa-esp32s3-elf-)
-include($ENV{IDF_PATH}/tools/cmake/toolchain.cmake)
+if(NOT DEFINED ENV{IDF_PATH} OR "$ENV{IDF_PATH}" STREQUAL "")
+  message(FATAL_ERROR "IDF_PATH is not set. Please source ESP-IDF export script first.")
+endif()
+include("$ENV{IDF_PATH}/tools/cmake/toolchain.cmake")
🧹 Nitpick comments (7)
Devices/waveshare-s3-touch-amoled-18/devicetree.yaml (1)

3-3: ⚡ Quick win

Use a 1.8-specific DTS filename for this device slice.

Line 3 references a ...206... DTS from the ...-18 target, which makes board identity ambiguous and fragile for maintenance/tooling.

Suggested fix
-dts: waveshare,s3-touch-amoled-206.dts
+dts: waveshare,s3-touch-amoled-18.dts
Devices/waveshare-s3-touch-amoled-18/Source/devices/InternalFlash.h.disabled (1)

15-23: ⚡ Quick win

Move extern mounted_wl_handle/extern is_mounted above InternalFlashDevice
InternalFlashDevice inline methods reference mounted_wl_handle before it’s declared (lines 15 and 18–19 vs extern at 22). Reorder the extern declarations so enabling the header won’t hit name-lookup/compile errors; this *.h.disabled file isn’t referenced by non-disabled sources right now.

Suggested fix
 `#include` <memory>
 `#include` <esp_vfs_fat.h>
 
+extern wl_handle_t mounted_wl_handle;
+extern bool is_mounted;
+
 class InternalFlashDevice {
 public:
@@
-extern wl_handle_t mounted_wl_handle;
-extern bool is_mounted;
-
 extern wl_handle_t getInternalFlashWlHandle();
 extern bool isInternalFlashMounted();
 extern std::shared_ptr<InternalFlashDevice> createInternalFlash();
Tactility/Source/app/localesettings/LocaleSettings.cpp (1)

109-111: ⚡ Quick win

Remove the duplicate textResources.load() call in onShow.

It is already loaded at the beginning of onShow, so this second load is redundant.

💡 Suggested fix
 auto* region_label = lv_label_create(region_wrapper);
- textResources.load();
 lv_label_set_text(region_label, textResources[i18n::Text::REGION].c_str());
Tactility/Source/app/wifimanage/WifiManage.cpp (1)

7-7: 💤 Low value

Unused include.

DialogBox.h is included but not used in this file. Remove it to keep includes clean.

Tactility/Source/network/Ntp.cpp (1)

67-71: 💤 Low value

Exact time equality check may cause repeated syncs.

Comparing rtc_time == now for exact equality is fragile due to timing differences between reading system time and RTC. Consider allowing a small tolerance (e.g., ±2 seconds).

Tactility/Source/service/displayidle/DisplayIdle.cpp (1)

31-65: ⚡ Quick win

Duplicate IMU device lookups per tick.

findQmi8658() is called separately in both checkMotionWake() and checkFaceDown(), resulting in two device lookups per tick when both are called. Consider caching the device pointer or combining the checks into a single read.

Proposed approach

Create a combined check function that reads once and returns both results:

struct MotionState {
    bool motionWake = false;
    bool faceDown = false;
};

static MotionState checkMotionState() {
    MotionState state;
    Device* imu = findQmi8658();
    if (!imu) return state;

    Qmi8658Data data;
    if (qmi8658_read(imu, &data) != ERROR_NONE) return state;

    float magnitude = std::sqrt(data.ax * data.ax + data.ay * data.ay + data.az * data.az);
    state.motionWake = std::abs(magnitude - 1.0f) > 0.35f;
    state.faceDown = data.az > 0.9f;
    return state;
}
Tactility/Source/service/statusbar/Statusbar.cpp (1)

240-240: ⚖️ Poor tradeoff

30-second update interval may delay status changes.

Increasing the statusbar update from 1 second to 30 seconds significantly delays UI feedback for battery level changes, charging state transitions, WiFi signal strength, and SD card mount/unmount events. Consider a shorter interval (e.g., 5-10 seconds) or event-driven updates for immediate state changes.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 380d9139-4e83-4809-8bd8-593148df5849

📥 Commits

Reviewing files that changed from the base of the PR and between 895e6bc and d9ad30c.

⛔ Files ignored due to path filters (1)
  • partitions-32mb.csv is excluded by !**/*.csv
📒 Files selected for processing (147)
  • .clangd
  • .opencode/plans/lvgl9-desync-fix.md
  • Buildscripts/sdkconfig/default.properties
  • Data/user/app/ImuBall/manifest.properties
  • Devices/waveshare-s3-touch-amoled-18/CMakeLists.txt
  • Devices/waveshare-s3-touch-amoled-18/Source/Configuration.cpp
  • Devices/waveshare-s3-touch-amoled-18/Source/devices/Display.cpp
  • Devices/waveshare-s3-touch-amoled-18/Source/devices/Display.h
  • Devices/waveshare-s3-touch-amoled-18/Source/devices/InternalFlash.cpp.disabled
  • Devices/waveshare-s3-touch-amoled-18/Source/devices/InternalFlash.h.disabled
  • Devices/waveshare-s3-touch-amoled-18/Source/devices/Sdcard.cpp
  • Devices/waveshare-s3-touch-amoled-18/Source/devices/Sdcard.h
  • Devices/waveshare-s3-touch-amoled-18/Source/module.cpp
  • Devices/waveshare-s3-touch-amoled-18/device.properties
  • Devices/waveshare-s3-touch-amoled-18/devicetree.yaml
  • Devices/waveshare-s3-touch-amoled-18/waveshare,s3-touch-amoled-206.dts
  • Devices/waveshare-s3-touch-amoled-206/CMakeLists.txt
  • Devices/waveshare-s3-touch-amoled-206/Source/Configuration.cpp
  • Devices/waveshare-s3-touch-amoled-206/Source/devices/AudioDevice.cpp
  • Devices/waveshare-s3-touch-amoled-206/Source/devices/AudioDevice.h
  • Devices/waveshare-s3-touch-amoled-206/Source/devices/Display.cpp
  • Devices/waveshare-s3-touch-amoled-206/Source/devices/Display.h
  • Devices/waveshare-s3-touch-amoled-206/Source/devices/Power.cpp
  • Devices/waveshare-s3-touch-amoled-206/Source/devices/Power.h
  • Devices/waveshare-s3-touch-amoled-206/Source/devices/Sdcard.cpp
  • Devices/waveshare-s3-touch-amoled-206/Source/devices/Sdcard.h
  • Devices/waveshare-s3-touch-amoled-206/Source/devices/Touch.cpp
  • Devices/waveshare-s3-touch-amoled-206/Source/devices/Touch.h
  • Devices/waveshare-s3-touch-amoled-206/Source/module.cpp
  • Devices/waveshare-s3-touch-amoled-206/device.properties
  • Devices/waveshare-s3-touch-amoled-206/devicetree.yaml
  • Devices/waveshare-s3-touch-amoled-206/waveshare,s3-touch-amoled-206.dts
  • Drivers/AXP2101/Source/Axp2101.cpp
  • Drivers/AXP2101/Source/Axp2101.h
  • Drivers/AXP2101/Source/Axp2101Power.cpp
  • Drivers/AXP2101/bindings/axp2101.h
  • Drivers/AXP2101/devicetree.yaml
  • Drivers/bm8563-module/bindings/nxp,pcf85063.yaml
  • Drivers/bm8563-module/bindings/nxp,pcf8563.yaml
  • Drivers/bm8563-module/source/bm8563.cpp
  • Drivers/qmi8658-module/source/qmi8658.cpp
  • Firmware/idf_component.yml
  • Modules/hal-device-module/include/tactility/hal/Device.h
  • Modules/lvgl-module/source/arch/lvgl_esp32.c
  • Platforms/platform-esp32/CMakeLists.txt
  • Platforms/platform-esp32/include/tactility/drivers/esp32_i2c.h
  • Platforms/platform-esp32/source/drivers/esp32_i2c.cpp
  • Tactility/CMakeLists.txt
  • Tactility/Include/Tactility/app/dialogbox/DialogBox.h
  • Tactility/Include/Tactility/hal/audio/AudioDevice.h
  • Tactility/Include/Tactility/service/displayidle/DisplayIdleService.h
  • Tactility/Include/Tactility/settings/AudioSettings.h
  • Tactility/Private/Tactility/app/files/SupportedFiles.h
  • Tactility/Source/PartitionsEsp.cpp
  • Tactility/Source/Tactility.cpp
  • Tactility/Source/TactilityEsp.cpp
  • Tactility/Source/app/audio/Audio.cpp
  • Tactility/Source/app/dialogbox/DialogBox.cpp
  • Tactility/Source/app/files/SupportedFiles.cpp
  • Tactility/Source/app/launcher/Launcher.cpp
  • Tactility/Source/app/localesettings/LocaleSettings.cpp
  • Tactility/Source/app/miscsettings/MiscSettings.cpp
  • Tactility/Source/app/power/Power.cpp
  • Tactility/Source/app/timezone/TimeZone.cpp
  • Tactility/Source/app/wifimanage/WifiManage.cpp
  • Tactility/Source/hal/sdcard/SpiSdCardDevice.cpp
  • Tactility/Source/lvgl/Statusbar.cpp
  • Tactility/Source/network/Ntp.cpp
  • Tactility/Source/service/displayidle/DisplayIdle.cpp
  • Tactility/Source/service/rtctime/RtcTimeService.cpp
  • Tactility/Source/service/statusbar/Statusbar.cpp
  • Tactility/Source/service/wifi/WifiEsp.cpp
  • Tactility/Source/settings/AudioSettings.cpp
  • Tactility/Source/settings/DisplaySettings.cpp
  • TactilityKernel/include/tactility/bindings/bm8563.h
  • TactilityKernel/include/tactility/bindings/qmi8658.h
  • TactilityKernel/include/tactility/concurrent/thread.h
  • TactilityKernel/include/tactility/drivers/bm8563.h
  • TactilityKernel/include/tactility/drivers/gpio.h
  • TactilityKernel/include/tactility/drivers/qmi8658.h
  • TactilityKernel/source/concurrent/thread.cpp
  • TactilityKernel/source/kernel_symbols.c
  • app-flash_args
  • bootloader-flash_args
  • bootloader-prefix/src/bootloader-stamp/bootloader-patch-info.txt
  • bootloader-prefix/src/bootloader-stamp/bootloader-source_dirinfo.txt
  • bootloader-prefix/src/bootloader-stamp/bootloader-update-info.txt
  • bootloader-prefix/tmp/bootloader-cfgcmd.txt
  • bootloader-prefix/tmp/bootloader-mkdirs.cmake
  • build.ninja
  • compile_commands.json
  • component_manager_run_counter.43315
  • components/waveshare__esp32_s3_touch_amoled_2_06/.component_hash
  • components/waveshare__esp32_s3_touch_amoled_2_06/CHECKSUMS.json
  • components/waveshare__esp32_s3_touch_amoled_2_06/CMakeLists.txt
  • components/waveshare__esp32_s3_touch_amoled_2_06/Kconfig
  • components/waveshare__esp32_s3_touch_amoled_2_06/LICENSE
  • components/waveshare__esp32_s3_touch_amoled_2_06/README.md
  • components/waveshare__esp32_s3_touch_amoled_2_06/esp32_s3_touch_amoled_2_06.c
  • components/waveshare__esp32_s3_touch_amoled_2_06/idf_component.yml
  • components/waveshare__esp32_s3_touch_amoled_2_06/include/bsp/config.h
  • components/waveshare__esp32_s3_touch_amoled_2_06/include/bsp/display.h
  • components/waveshare__esp32_s3_touch_amoled_2_06/include/bsp/esp-bsp.h
  • components/waveshare__esp32_s3_touch_amoled_2_06/include/bsp/esp32_s3_touch_amoled_2_06.h
  • components/waveshare__esp32_s3_touch_amoled_2_06/include/bsp/touch.h
  • components/waveshare__esp32_s3_touch_amoled_2_06/priv_include/bsp_err_check.h
  • config.env
  • config/kconfig_menus.json
  • config/sdkconfig.cmake
  • config/sdkconfig.h
  • config/sdkconfig.json
  • data-flash_args
  • device.py
  • esp-idf/Tactility/data-flash_args.in
  • esp-idf/Tactility/system-flash_args.in
  • esp-idf/bootloader/bootloader-flash_args.in
  • esp-idf/esp_system/ld/linker_script_generator.cmake
  • esp-idf/esptool_py/app-flash_args.in
  • esp-idf/esptool_py/flasher_args.json.in
  • esp-idf/partition_table/partition-table-flash_args.in
  • esp_idf_project_configuration.json
  • flash_app_args
  • flash_args
  • flash_args.in
  • flash_bootloader_args
  • flash_project_args
  • flasher_args.json
  • gdbinit/connect
  • gdbinit/gdbinit
  • gdbinit/prefix_map
  • gdbinit/py_extensions
  • gdbinit/symbols
  • kconfigs.in
  • kconfigs_projbuild.in
  • ldgen_libraries
  • ldgen_libraries.in
  • lv_conf.h
  • main/idf_component.yml
  • partition-table-flash_args
  • project_description.json
  • session-ses_28c2.md
  • system-flash_args
  • toolchain/asmflags
  • toolchain/cflags
  • toolchain/cxxflags
  • toolchain/ldflags
  • toolchain/toolchain-esp32s3.cmake

@@ -0,0 +1 @@
0 No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Build artifact should not be committed to version control.

This is an ESP-IDF component manager runtime counter file (note the process-ID-like suffix .43315). Such files are generated during the build process and should be excluded from version control.

assert(cfg != NULL);
BSP_ERROR_CHECK_RETURN_NULL(lvgl_port_init(&cfg->lvgl_port_cfg));

BSP_NULL_CHECK(disp = bsp_display_lcd_init(cfg), NULL);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Show declaration and call sites for bsp_display_lcd_init
rg -n 'static lv_display_t \*bsp_display_lcd_init\(|bsp_display_lcd_init\(' \
  components/waveshare__esp32_s3_touch_amoled_2_06/esp32_s3_touch_amoled_2_06.c -C2

Repository: TactilityProject/Tactility

Length of output: 410


Fix argument mismatch at Line 623 (compile blocker).

bsp_display_lcd_init is declared as static lv_display_t *bsp_display_lcd_init() (no parameters) but is called as bsp_display_lcd_init(cfg), causing a signature mismatch.

Minimal compile fix
-    BSP_NULL_CHECK(disp = bsp_display_lcd_init(cfg), NULL);
+    BSP_NULL_CHECK(disp = bsp_display_lcd_init(), NULL);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
BSP_NULL_CHECK(disp = bsp_display_lcd_init(cfg), NULL);
BSP_NULL_CHECK(disp = bsp_display_lcd_init(), NULL);

Comment on lines +15 to +17
struct Module waveshare_s3_touch_amoled_206_module = {
.name = "waveshare-s3-touch-amoled-206",
.start = start,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix module identity to match the 1.8 target.

In Devices/waveshare-s3-touch-amoled-18, Line 15 and Line 16 still use the ...206... identity. With a real 206 module also present in this PR, this risks duplicate symbols and incorrect module registration.

Suggested fix
-struct Module waveshare_s3_touch_amoled_206_module = {
-    .name = "waveshare-s3-touch-amoled-206",
+struct Module waveshare_s3_touch_amoled_18_module = {
+    .name = "waveshare-s3-touch-amoled-18",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
struct Module waveshare_s3_touch_amoled_206_module = {
.name = "waveshare-s3-touch-amoled-206",
.start = start,
struct Module waveshare_s3_touch_amoled_18_module = {
.name = "waveshare-s3-touch-amoled-18",
.start = start,

Comment on lines +33 to +36
static void IRAM_ATTR gpio0_reset_isr(void* arg) {
// Stop the top app, or restart if no app is running
tt::app::stop();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Don’t call app-stop logic directly from ISR context.

tt::app::stop() in gpio0_reset_isr can execute non-ISR-safe code. Use ISR to signal/defer work to a task (queue/event flag), then stop the app from task context.

Comment thread kconfigs.in
Comment on lines +1 to +66
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/driver/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/efuse/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp-tls/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_adc/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_coex/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_common/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_ana_cmpr/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_bitscrambler/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_dac/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_gpio/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_gptimer/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_i2c/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_i2s/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_ledc/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_mcpwm/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_parlio/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_pcnt/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_rmt/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_sdm/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_spi/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_tsens/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_twai/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_uart/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_driver_usb_serial_jtag/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_event/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_http_client/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_http_server/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_hw_support/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_lcd/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_mm/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_netif/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_partition/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_phy/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_pm/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_psram/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_ringbuf/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_rom/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_security/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_system/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_timer/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/esp_wifi/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/fatfs/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/freertos/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/hal/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/heap/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/log/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/lwip/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/mbedtls/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/newlib/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/nvs_flash/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/pthread/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/soc/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/spi_flash/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/spiffs/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/tcp_transport/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/usb/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/vfs/Kconfig"
source "/Users/oneillchampagnie/.espressif/v5.5.4/esp-idf/components/wear_levelling/Kconfig"
source "/Users/oneillchampagnie/Tactility/Firmware/Kconfig"
source "/Users/oneillchampagnie/Tactility/Libraries/elf_loader/Kconfig"
source "/Users/oneillchampagnie/Tactility/components/waveshare__esp32_s3_touch_amoled_2_06/Kconfig"
source "/Users/oneillchampagnie/Tactility/managed_components/espressif__cmake_utilities/Kconfig"
source "/Users/oneillchampagnie/Tactility/managed_components/espressif__esp_codec_dev/Kconfig"
source "/Users/oneillchampagnie/Tactility/managed_components/espressif__esp_lcd_touch/Kconfig"
source "/Users/oneillchampagnie/Tactility/managed_components/espressif__esp_tinyusb/Kconfig"
source "/Users/oneillchampagnie/Tactility/managed_components/lvgl__lvgl/Kconfig" No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Remove machine-local Kconfig paths from committed sources.

Line 1 through Line 66 hardcode /Users/... absolute paths. This makes Kconfig resolution non-portable (breaks on CI/other developers) and leaks local user path information. These files should be generated per-environment or rewritten to project/IDF-root-relative references before committing.

Comment on lines +57 to +67
i2c_device_config_t dev_cfg = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = address,
.scl_speed_hz = driver_data->clock_frequency_hz,
};
i2c_master_dev_handle_t dev_handle = nullptr;
esp_err_t err = i2c_master_bus_add_device(driver_data->bus_handle, &dev_cfg, &dev_handle);
if (err == ESP_OK) {
err = i2c_master_receive(dev_handle, data, data_size, ticks_to_timeout_ms(timeout));
i2c_master_bus_rm_device(dev_handle);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

🧩 Analysis chain

🌐 Web query:

ESP-IDF i2c_master_bus_add_device performance overhead

💡 Result:

The function i2c_master_bus_add_device() in the ESP-IDF I2C master driver is primarily an initialization and resource allocation function, not a routine performed during high-frequency data transmission [1]. Consequently, its performance overhead is typically negligible in application contexts because it is designed to be called once per device during the system setup phase [1]. Regarding the broader performance of the ESP-IDF I2C driver (introduced in v5.2 and now the standard, as the legacy driver is EOL) [2][3]: 1. Architectural Overhead: The driver utilizes a bus-device model [1]. While this abstraction provides better thread safety and resource management, it introduces some overhead compared to legacy implementations due to internal semaphore management and interrupt handling [4][5][6]. 2. Transaction Latency: Developers have reported that transaction times can be affected by interrupt handling and task context switching [5][6]. Specifically, gaps between I2C master transmit and receive operations can occur due to task scheduling or the way the driver handles synchronization [5]. 3. Optimizations: - Synchronous transactions in newer ESP-IDF versions have been optimized to avoid unnecessary busy-waiting, improving efficiency by allowing tasks to yield while waiting for completion [6]. - If extremely low latency is required, users have successfully reduced overhead by minimizing interrupts or tuning task priorities and synchronization methods (e.g., using task notifications instead of semaphores) [5][6]. - Configuration settings like CONFIG_I2C_ISR_IRAM_SAFE can be enabled to keep I2C interrupts functional when the cache is disabled, though this increases IRAM consumption [4][7]. In summary, i2c_master_bus_add_device() itself does not create a performance bottleneck for runtime operations. The primary performance considerations for the I2C driver relate to transaction scheduling, interrupt latency, and proper configuration for real-time constraints [4][5][6].

Citations:


Avoid per-transaction i2c_master_bus_add_device() / i2c_master_bus_rm_device(); cache device handles instead.

i2c_master_bus_add_device() is an initialization/resource-allocation path intended to be called once per device during setup; creating/removing the device handle for every transaction adds avoidable overhead. Create the device handle once (or lazily cache by address) and reuse it for subsequent reads/writes; remove it when the driver stops.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant