When using the Keyboard BLE feature, pressing ALT-TAB to cycle through
windows was not possible. This is now fixed.
OPT-L (for screen lock) also works.
(Previously ALT and OPT/META keys never set _modifier_mask, so they were
sent as raw scan codes (0xe2/0xe3) in HID keycode slots instead of
setting the modifier byte. The USB sender now skips keycode for
modifier-only events.)
(On non-modifier key release, both BLE and USB senders sent an all-zero
HID report, dropping any held modifiers (e.g. ALT released on TAB-up).
Release now always sends getModifierMask() in the modifier byte so held
modifiers stay active -- enables ALT-TAB window cycling.)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When using the Keyboard BLE feature, the DEL keypress produced ^H
instead of ESC[3~ (forward DEL). This is now fixed.
(Previously, DEL was not a possible key so this went unnoticed.
Now with Fn enabling the RED keys, DEL is a valid keypress.)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous commit replaces Fn-as-shift with Aa-as-shift. But because
Aa and A are close to each other, that makes pressing Shift-A hard. As a
feature, Fn will do Shift for A-Z.
(No third symbol is printed on letter keys, so this feature made sense.)
The shift is injected via a new extraModifiers field in KeyEvent_t so the
HID modifier byte is set correctly over both BLE and USB, rather than
relying on the physical Aa (shift) key state.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This change remaps some keyboard modifier keys:
- The front panel shows BLUE for the Shift keys, so it makes sense to
have the BLUE Aa (row 2, col 1) key mean Shift.
- The Fn key (row 2, col 0) is RED, so it makes sense to have it modify
to the red ARROW, DEL and ESC keys.
- Caps lock support (previous Aa behaviour) is dropped for now (could be
reinstated by Fn+Aa).
This results in the following behaviour:
- Fn (2,0): does red keys: (0,0)=ESC, (0,13)=DEL, (2,11)=UP, (3,10)=LEFT,
(3,11)=DOWN, (3,12)=RIGHT
- Aa (2,1): does Shift: uppercase A-Z, symbols above 0-9, etc.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Peripheral Preferred Connection Parameters (PPCP) characteristic was
previously unset. It is now optimized for a (low-power) keyboard.
Both mechanisms are needed because they serve different roles:
- sdkconfig PPCP (CONFIG_BT_NIMBLE_SVC_GAP_PPCP_*): populates the
Peripheral Preferred Connection Parameters GATT characteristic in the
GAP service. A central reads this during service discovery and may
apply the preferences. Acting on PPCP is optional for the central.
- ble_gap_update_params() in code: sends an active L2CAP connection
parameter update request immediately after connect. This forces
negotiation rather than just advertising a preference; most hosts
accept reasonable HID parameters when actively requested.
Some hosts honour PPCP but ignore the update request; others do the
reverse. Keeping both and setting all values to something sane maximises
compatibility across Linux, macOS, Windows, and Android.
NOTE: Remove any stale 'sdkconfig' in the directory before rebuilding.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without this, NimBLE held bond and key material only in RAM, so every
power cycle might required re-pairing. In practice this seemed to
auto-pair partially. The keyboard would connect and disconnect often and
keys would be dropped.
Setting CONFIG_BT_NIMBLE_NVS_PERSIST=y writes bond data to the NVS
partition (0x9000-0xCFFF). That partition is not touched by a normal
app-only flash (write_flash 0x10000), so bonds survive both power
cycles and iterative firmware updates.
After a full erase-flash or first flash of new firmware the NVS
partition is wiped and the host must be told to forget the old bond:
bluetoothctl remove <MAC>
Then re-pair normally.
NOTE: Remove any stale 'sdkconfig' in the directory before rebuilding.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>