When writing to factory (0x20000), automatically erase the otadata
partition (0xF000, 8KB) by writing 0xFF blocks. This forces the
bootloader to fall back to factory on next boot, instead of
continuing to boot from ota_0.
Without this, flashing factory "succeeds" but the ESP keeps
booting the old firmware from ota_0.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: missing SPI_SET_PARAMS (CMD 0x0B) before flash_begin.
ROM bootloader defaults to 2MB flash size, silently truncating
writes to 16MB flash. Progress bar showed success but firmware
was corrupt.
Changes:
- Add spi_set_params() with 16MB geometry before flash_begin
- Add MD5 verification after write (SPI_FLASH_MD5, CMD 0x13)
- Add retry logic (3 attempts per block)
- Pad firmware to 4-byte boundary
- flash_end(reboot=false) then separate hard reset after MD5 check
- Pure Rust MD5 implementation (no external crate)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OTA protocol (text-based, uses existing keyboard connection):
1. Send "OTA <size>"
2. Wait for firmware OTA_READY
3. Send firmware in 4096-byte chunks
4. Progress bar with chunk counter
5. OTA_DONE on completion
No programming cable needed - uses the same USB port as
keyboard communication. Browse for .bin file, click Flash OTA.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
mix() was not rendering correctly. Replaced with stepped thresholds:
- >80%: bright red (#ff0000)
- >60%: red-orange (#ff4400)
- >40%: orange (#ff8800)
- >20%: yellow (#ffcc00)
- >5%: cool blue (#446688)
- <=5%: very cold (#2d2d44)
Dark text when heat > 20%. Removed debug eprintln.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Gradient: dark purple -> orange -> red (more contrast than blue->yellow)
- Text switches to dark (#1a1a2e) when heat > 0.3
- Bold text in heatmap mode
- Sublabel opacity reduced in heatmap
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Firmware v2 doesn't respond to text "MACROS?" query.
Now uses binary LIST_MACROS (0x30) for refresh, with proper
parse_macros_binary() for the response payload.
Save still uses text MACROSEQ, then binary refresh after 100ms.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Macro slot auto-assigned from macros.len() (no manual slot selection)
- Heatmap toggle auto-loads KEYSTATS? from firmware
- Debug logs for macro save/refresh
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Firmware sends: "KO2: 0B+02->4C+00"
Parser expected: "KO0: trigger=2A mod=02 -> result=4C resmod=00"
Rewrote parse_ko_lines to match real format:
<trigger_hex>+<mod_hex>-><result_hex>+<mod_hex>
Also skips "KO 0 deleted" and "KOSET 1:OK" lines.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
KO list now displays modifier names alongside key names:
- "Shift+H" instead of just "H"
- Uses mod_name() to decode the bitmask
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace ModComboBox with Ctrl/Shift/Alt checkboxes for KO mods
- Build HID bitmask from checkboxes (Ctrl=0x01, Shift=0x02, Alt=0x04)
- Add DarkCheckbox component (Dracula theme)
- Descriptive label: "When you press [From] key, output [To] key instead"
- Read all KO fields from properties (no args in callback)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Index 255 does not mean "auto-assign" in the firmware.
Must use combo_data.len() as next index (matching egui behavior).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause of keyboard malfunction: binary SET/DELETE commands
followed by text QUERY commands confused the firmware state machine.
Changes:
- All create/delete (combo, leader, KO) back to TEXT protocol
(matching stable egui desktop behavior)
- TD_SET kept binary (isolated, no text query after)
- 50ms delay between all consecutive serial commands
- Removed dead v2 check in refresh_advanced
- Added delay between KEYSTATS? and BIGRAMS? queries
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
TAMA? and AUTOSHIFT? commands were causing keyboard to malfunction.
Added 50ms delay between serial queries to prevent buffer corruption.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Combo, Leader, KO create/delete all use send_binary() now:
- COMBO_SET (0x60), COMBO_DELETE (0x62)
- LEADER_SET (0x70), LEADER_DELETE (0x72)
- KO_SET (0x91), KO_DELETE (0x93)
Text protocol commands (COMBOSET, COMBODEL, etc.) were not working
with the v2 firmware.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Combo picker: pass key index directly instead of relying on selected_key_index
- TD actions: use DarkButton for consistent visibility
- Combo Add: validation message when keys not picked
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tap Dance:
- Each TD action is now a clickable button that opens the key selector
- Clicking an action -> pick key from popup -> sends TD_SET binary to firmware
- Labels show 1-tap / 2-tap / 3-tap / hold columns
Combo creation:
- Added validation: "Pick both keys first" message if keys not selected
- Debug log for COMBOSET command
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- button-bg lighter (#565970) to stand out from bg-secondary
- Border on DarkButton and DarkComboBox (#6272a4)
- Primary button: dark text on purple bg
- Tab titles: inactive #9a9ebb (was #6272a4), 13px, bold 700 when active
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- DarkComboBox: PopupWindow dropdown with Dracula colors, replaces all std ComboBox
- DarkTab: custom tab bar with purple underline, replaces std TabWidget
- All 7 .slint files updated, zero std-widgets Button/ComboBox/TabWidget remaining
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Custom DarkButton component with Dracula theme colors:
- bg-secondary base, button-hover on hover
- primary variant with accent-purple
- Disabled state with reduced opacity
- Consistent 28px height, 4px border-radius
Also fixes PickDarkButton/KeyDarkButton naming errors from agent.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Full port of the KaSe/KeSp split keyboard configurator from egui to Slint:
- 6 tabs: Keymap, Advanced, Macros, Stats, Settings, Flash
- Responsive keyboard view with scale-to-fit and key rotations
- Key selector popup with categorized grid, MT/LT builders, hex input
- Combo key picker with inline keyboard visual
- Macro step builder with visual tags
- Serial communication via background threads + mpsc polling
- Heatmap overlay with blue-yellow-red gradient
- OTA flasher with prog port VID filtering and partition selector
- WPM polling, Tamagotchi, Autoshift controls
- Dracula theme matching egui version
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>