Skip to content

[TrimUI Smart Pro S] Fix RGB LED live preview in PyUI, joystick ring driver, and cooling fan max speed / kernel lock - #1681

Open
leslieclemence wants to merge 1 commit into
spruceUI:Developmentfrom
leslieclemence:fix/smartpros-osd-fan-rgb
Open

leslieclemence wants to merge 1 commit into
spruceUI:Developmentfrom
leslieclemence:fix/smartpros-osd-fan-rgb

Conversation

@leslieclemence

@leslieclemence leslieclemence commented Sep 14, 2026

Copy link
Copy Markdown

Resolves #1673
Ref: Sundownersport's request in #1673 (comment)

Overview

As requested by @chrisj951, this pull request has been separated to focus exclusively on the non-controversial hardware fixes for the TrimUI Smart Pro S (TG5050 / Allwinner A133P):

  1. Real-time RGB LED Configuration & Joystick Ring Support:
    • Automatically applies LED color, brightness, effects (e.g. Blink, Breathe, Static), and duration dynamically when changed in the PyUI settings menu without requiring a reboot or toggling the physical switch.
    • Adds support for the joystick rings (_lr sysfs node), space formatting required by the kernel driver (printf "%s " "$color"), and trigger reset (echo 0 > effect_$zone).
  2. Cooling Fan Maximum Speed & Kernel Lock Fix:
    • Fixes an issue where setting the fan to level 6 (Max) was rejected by the kernel with -EINVAL due to user_max_state ordering.
    • Maps level 6 to speed 31 (true 100% duty cycle / PWM 255).
    • Robustly terminates competing thermal processes (adaptive_fan.py and thermal-watchdog by PID) so manual levels or Off are not overwritten.
    • Restores the saved fan level on system boot and waking from sleep.

(Note: The stock TrimUI OSDD overlay and watchdog event routing have been split off completely from this PR as requested).


Root Cause Analysis & Technical Details

1. Live RGB LED Preview in PyUI & Driver Formatting

  • Root Cause 1 (Missing changeCmd): In App/PyUI/main-ui/utils/cfw_system_config.py, when a setting changes, PyUI checks for a changeCmd attribute to execute. The "RGB LED Settings" options lacked changeCmd, meaning changes were only saved to JSON.
  • Root Cause 2 (sysfs driver protocol): The kernel led_anim driver requires:
    • Space-terminated RGB hex strings (printf "%s " "$color" > /sys/class/led_anim/effect_rgb_hex_$zone).
    • Cycling effect_$zone through 0 to rearm the animation engine when an effect or color is changed.
    • Joystick rings on Smart Pro & Smart Pro S use the combined _lr sysfs node (effect_rgb_hex_lr), which was previously ignored.
  • Fix:
    • Created spruce/scripts/applySetting/applyRgbLed.sh hook.
    • Added changeCmd to disableLEDs, LEDmaxScale, defaultLEDcolor, defaultLEDeffect, defaultLEDduration, and emulator LED settings in Saves/spruce/spruce-config.json.
    • Updated rgb_led_trimui() to support _lr zones, space formatting, and animation retriggering (echo 0 > effect_$zone).

2. Fan Maximum Speed & Kernel Lock

  • Root Cause 1 (user_max_state lock): In the Linux kernel driver for the TG5050 pwm-fan (/sys/devices/platform/soc@3000000/soc@3000000:pwm_fan/hwmon/hwmon0/user_max_state), any write to cur_state greater than user_max_state is rejected with -EINVAL. In trimui_delegate.sh, cur_state was previously written before unlocking user_max_state.
  • Root Cause 2 (Level 6 speed mapping): The TG5050 DTS defines 32 fan levels (0..31). Level 6 was previously capped at 24 (~86% duty cycle). Speed 31 represents the true 100% duty cycle (PWM 255/255).
  • Root Cause 3 (Process race): In BusyBox/Linux, Python scripts show up under comm as python3. Simple pgrep -f or killall calls could fail to terminate adaptive_fan.py, which would continue running in the background and overwrite cur_state back to 0 every second.
  • Fix:
    • apply_fan_level() unlocks user_max_state (echo -1 > user_max_state) before writing cur_state.
    • Level 6 now maps to 31 (100% duty cycle / PWM 255).
    • Robust PID lookup in ps reliably terminates adaptive_fan.py and thermal-watchdog when manual fan levels or Off are selected.
    • Saved fan level is restored on boot (init_gpio_SmartProS) and resume (device_exit_sleep).

Files Modified

  • spruce/scripts/applySetting/applyRgbLed.sh: [NEW] Hook script to apply LED settings dynamically from PyUI.
  • Saves/spruce/spruce-config.json: Added changeCmd to RGB LED settings.
  • spruce/scripts/platform/device_functions/trimui_delegate.sh:
    • apply_fan_level(): Fix kernel -EINVAL ordering, add level 6 = 31, robustly terminate competing thermal daemons.
    • save_fan_level(): Persist fan level to SYSTEM_JSON and /mnt/UDISK/system.json.
    • rgb_led_trimui(): Support _lr joystick ring zone, format color with trailing space, reset effect trigger.
  • spruce/scripts/platform/device_functions/SmartProS.sh:
    • toggle_led(): Check effect_rgb_hex_lr.
    • init_gpio_SmartProS: Restore saved fan level on boot.
    • device_init: Fixed bluetooth comparison check (-eq 1), run thermal manager if fan_lvl == -1.
    • device_exit_sleep: Restore saved fan level after resume.
  • spruce/smartpros/bin/update-thermal-watchdog-to-setting: Reset user_max_state to -1 before daemon start, improve process termination.

Verification & Testing

  • Shell syntax verified on all scripts (sh -n).
  • JSON syntax validated with python3 -m json.tool.
  • Tested directly on TrimUI Smart Pro S hardware:
    • Changing RGB LED settings (Color, Effect like Blink/Breathe, Duration, Brightness) in PyUI applies immediately in real time.
    • Fan reaches full speed (audible high RPM, level 31 / PWM 255).
    • Fan level persists across reboot and sleep/wake cycles.

Comment thread spruce/scripts/button_actions.sh Outdated
}

show_trimui_osd() {
log_message "button_actions.sh: Toggling TrimUI OSD"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Sundownersport , @ryanmsartor did we intentionally have this disabled? I remember going back and forth on whether or not the trimui OSDD was going to be used

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I believe Sun disabled it due to aesthetic conflicts with spruce and other themes. The TrimUI OSD should probably follow user theme colors at the very least

Comment thread spruce/scripts/volume_sync_watchdog.sh Outdated
unmute_if_raised "$new_vol"
}

handle_fan_event() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is fan event in a volume_sync watchdog?

Comment thread spruce/scripts/volume_sync_watchdog.sh Outdated
esac
}

handle_led_on_event() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same with some of these other ones. We either need to rename this file or break these out somewhere else

@chrisj951

Copy link
Copy Markdown
Contributor

@leslieclemence do you mind splitting off the stock OSDD and the rest of the changes? (e.g. the LED fixes)

… and fan max speed

- Fix cooling fan not reaching maximum speed (level 6) due to driver user_max_state locking and process conflict with adaptive_fan.py/thermal-watchdog; map level 6 to full speed 31 (PWM 255/100%).
- Ensure user_max_state is set to -1 prior to writing cur_state to prevent kernel -EINVAL.
- Add changeCmd hooks in spruce-config.json and applyRgbLed.sh to instantly apply RGB LED brightness, color, effect, and duration when changed in PyUI.
- Format LED color with trailing space ("%s ") and toggle effect trigger to force driver animation update; support joystick ring zone "lr".
- Restore saved fan level on system boot and wake from sleep.
@leslieclemence
leslieclemence force-pushed the fix/smartpros-osd-fan-rgb branch from 02ccd0b to 6400a7a Compare September 19, 2026 18:49
@leslieclemence leslieclemence changed the title [TrimUI Smart Pro S] Fix OSD status sync (Fan, LED, Wi-Fi, BT), full fan max speed (level 31/PWM 255), and live RGB LED settings preview in PyUI [TrimUI Smart Pro S] Fix RGB LED live preview in PyUI, joystick ring driver, and cooling fan max speed / kernel lock Sep 19, 2026
@leslieclemence

Copy link
Copy Markdown
Author

@chrisj951 I have split off the stock TrimUI OSDD and volume_sync watchdog changes as requested!

This PR now strictly contains the non-controversial core hardware improvements:

  1. Live RGB LED Configuration: Dynamic preview in PyUI, support for the joystick ring (_lr), space formatting, and animation trigger reset.
  2. Cooling Fan Maximum Speed: Unlocks user_max_state before writing cur_state to fix the kernel -EINVAL rejection, maps level 6 to speed 31 (PWM 255 / 100% duty cycle), and terminates competing background thermal daemons when manual fan levels or Off are selected.

@leslieclemence

Copy link
Copy Markdown
Author

@chrisj951 Following up on your request to separate concerns into clean, modular PRs:

  1. This PR ([TrimUI Smart Pro S] Fix RGB LED live preview in PyUI, joystick ring driver, and cooling fan max speed / kernel lock #1681) remains strictly focused on the core Cooling Fan levels (0..6 / PWM 31) and real-time RGB LED applying (removing the device.sh stub override).
  2. Smart Pro S: enable RetroArch rumble and set default DualShock/Rumble Pak #1702 has been opened for enabling RetroArch device vibration and setting default DualShock/Rumble Pak mappings on the Smart Pro S.
  3. TrimUI: dedicated OSD event watchdog and FN switch status synchronization #1703 has been opened for the dedicated osd_sync_watchdog.sh (TrimUI OSD events and FN switch synchronization), keeping volume_sync_watchdog.sh 100% untouched.

Thank you!

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants