Skip to content

Conversation

@obra
Copy link
Member

@obra obra commented Aug 16, 2025

Summary

This PR adds comprehensive USB/BLE connection detection and automatic switching for the Keyboardio Preonic, along with improved LED indicators that provide clear visual feedback for connection status.

Major Features

1. USB Connection Detection and Auto-Switching

  • Added USB connection state detection to nRF52840 MCU driver
  • Implemented host connection status tracking with new event system
  • Created USBAutoSwitcher plugin that automatically switches between USB and BLE based on connection status
  • When USB is disconnected, automatically switches to last-used BLE device

2. Enhanced LED Indicators System

  • Added comprehensive LED indicators for all connection states
  • Implemented global indicators that control all 4 LEDs atomically for USB events
  • Different visual effects for different states:
    • USB Power-only: Orange pulse (3 times)
    • USB Connected: Green grow effect
    • USB Disconnected: Orange shrink effect
    • BLE Connecting/Advertising: Blue blink
    • BLE Connected: Blue grow effect
    • BLE Device Selected: Brief blue grow

3. Smart Indicator Management

  • BLE indicators now use dynamic delays based on active USB indicators
  • Prevents indicator conflicts and ensures smooth transitions
  • Added hasActiveIndicatorForLED() API for better coordination between plugins

4. Boot Greeting Improvements

  • Fixed interaction between boot greeting and LED indicators
  • Simplified rainbow effect for clarity (all LEDs show same color)
  • Properly respects active indicators and turns off LEDs when complete
  • Fixed KeyAddr usage to correctly detect indicator conflicts

5. Bug Fixes

  • Fixed BLE device switching issues with proper bond management
  • Fixed USB wake-from-sleep using polling after waitForEvent()
  • Fixed WS2812 driver brightness calculation to prevent overflow
  • Fixed race conditions in indicator timing

Technical Details

New APIs

  • onHostConnectionStatusChanged() hook for plugins to react to connection events
  • hasActiveIndicatorForLED() to check if an LED has an active indicator
  • Global indicator support in LEDIndicators plugin

Files Changed

  • MCU driver: Added USB connection detection
  • Device driver: Added connection event broadcasting
  • LEDIndicators: Global indicators, smart delays, new colors
  • USBAutoSwitcher: New plugin for automatic switching
  • PreonicBootGreeting: Fixed indicator interaction
  • WS2812: Fixed brightness calculations

Testing

  • Tested USB connect/disconnect with synchronized LED effects
  • Verified automatic switching between USB and BLE modes
  • Confirmed boot greeting respects indicators
  • Tested all connection states and transitions
  • Verified no race conditions or LED conflicts

Backwards Compatibility

All changes are backwards compatible. The USBAutoSwitcher plugin is optional and can be disabled if manual control is preferred.

🤖 Generated with Claude Code

obra and others added 23 commits August 13, 2025 12:09
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
When the Preonic enters deep sleep (LOWPWR mode), FreeRTOS tasks are
suspended so the SOC event handler cannot process USB power events.
However, the SoftDevice's internal USB detection interrupt still fires
and briefly wakes the CPU from waitForEvent().

This commit leverages that wake behavior by polling the USB power status
register (USBREGSTATUS) after each waitForEvent() in the sleep loop. If
USB power is detected, we set input_event_pending_ to exit sleep.

This elegant solution requires only 3 lines of code and works perfectly
with the existing power-efficient LOWPWR sleep mode.

Also includes:
- Orange LED indicator for USB power-only connections (vs red)
- USB connection event cleanup in nRF52840.cpp
- SoftDevice API setup for USB VBUS detection

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
USBAutoSwitcher improvements:
- Add afterEachCycle handler to detect "no USB at startup" condition
- Previously only handled USB power-only; now handles no USB at all
- After 1-second startup delay, switches to BLE device 1 if no USB detected
- Minimal performance impact: single boolean check after first second

LED indicator improvements:
- Changed USB connection effect from green pulse to smooth green fade-up
- Uses Grow effect for 1-second fade instead of 3 pulses
- Provides cleaner visual feedback for USB data connection

The afterEachCycle overhead is minimal - just a boolean check that returns
immediately after the startup switching is complete.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
…nic device

- Move USB power-only detection logic to Preonic device's betweenCycles()
  This is the proper location for device-specific startup checks
- Remove misplaced USB checking methods from LEDIndicators plugin
  LED plugins should only handle visual indications, not USB state detection
- Remove debug tone code from nRF52840.cpp MCU driver
  Debug code doesn't belong in production MCU drivers
- The Preonic device now properly checks for USB power-only state after 2s
  and triggers the appropriate LED indication through the event system

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Move USB power-only detection logic from device layer to MCU driver
  where it architecturally belongs
- Implement checkUSBPowerOnlyStatus() in nRF52840 MCU driver
- Call the MCU's check method from Preonic's betweenCycles()
- Avoid circular dependencies by implementing in .cpp file where
  hooks are already available
- Triggers orange LED indication when USB has power but no data
  connection after 2 second startup delay

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Fix selectDevice() to not start advertising when already connected
  This was causing incorrect LED indications when switching back to
  an already-connected BLE device
- Only start advertising if not already connected to the requested device
- Add BLE disconnect when switching to USB mode for clean transitions
  Ensures we don't have simultaneous USB and BLE connections

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Fix sleep timeout from 2s to 10s so LED indicators don't get cut off mid-animation
- Add delay support to LED indicators to prevent USB/BLE indicators from overlapping
- Clear existing indicators when USB connects/disconnects for uniform LED display
- BLE indicators now wait 1.5s to let USB indicators complete first
- Delayed indicators don't block their LED slot until the delay expires
- USB indicators show for 1 second (green on connect, red on disconnect)

This improves the visual feedback when switching between USB and Bluetooth
connections, ensuring all status LEDs display clearly without interference.
When switching between BLE connection states, multiple indicators were
overlapping on the same LED, causing visual confusion. Now each status
change clears any existing indicator before setting a new one.

- Clear advertising indicator when connection succeeds
- Clear device selected indicator when advertising starts
- Clear any previous indicator when setting a new one
- Change USB disconnect from red to orange (less alarming)

This prevents the issue where DeviceSelected, Advertising, and Connected
indicators were all active simultaneously on the same LED.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Add hasActiveIndicators() helper to check if indicators are active
in a given slot range. Use this to only delay BLE indicators when
USB indicators are actively showing, avoiding unnecessary waits.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Add a global indicator type that controls all configured LED slots
with a single indicator. This prevents issues with individual indicators
getting out of sync or interfering with each other.

USB connection events (connecting, connected, disconnected) now use
global indicators that atomically control all 4 LEDs.

Also improve BLE indicator delays to wait only as long as needed for
any active global indicators to finish, rather than a fixed 1500ms.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
This commit improves the interaction between the boot greeting effect and LED indicators:

1. Fix KeyAddr usage in PreonicBootGreeting
   - Previously used KeyAddr(LED_INDEX) which incorrectly created KeyAddr(row=LED_INDEX, col=0)
   - Now uses the correct KeyAddr(0, slot) values that match indicator configuration
   - Maps LED indices to their corresponding indicator slots correctly

2. Simplify boot greeting rainbow effect
   - All 4 LEDs now show the same rainbow color (instead of butterfly pattern)
   - Makes it easier to identify when an LED is showing something different
   - Continues to respect active indicators by not overwriting them

3. Ensure LEDs turn off after boot greeting
   - Boot greeting now continues to turn off LEDs even after it's done
   - Prevents LEDs from staying on after the effect completes

4. Add hasActiveIndicatorForLED() to LEDIndicators
   - Allows other plugins to check if a specific LED has an active indicator
   - Used by PreonicBootGreeting to avoid overwriting indicator LEDs

5. Fix WS2812 driver brightness calculation
   - Prevent potential integer overflow in brightness calculation
   - Use 16-bit intermediate values for safer math

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Fixed else-if brace consistency issue on line 102 where the if block had braces but the else-if didn't.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Changed from device-specific nRF52840 friend declaration to the templated MCU Base class to avoid breaking compilation for non-nRF52 devices.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Removed unnecessary Base class friend declaration
- Added conditional nRF52840 friend declaration only for nRF52 builds
- Uses ARDUINO_NRF52_ADAFRUIT define which is standard for Adafruit nRF52 boards

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@obra obra force-pushed the f/nrf52-usb-detect branch from 87ae758 to a03e56d Compare August 16, 2025 04:06
@obra obra merged commit 5bc8cfc into master Aug 16, 2025
15 checks passed
@obra obra deleted the f/nrf52-usb-detect branch August 16, 2025 04:25
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.

2 participants