Skip to content

feat(redragon): add the M690 PRO driver (SinoWealth 258a:002e/002f) - #163

Merged
snekxs merged 2 commits into
OpenMouse-Project:mainfrom
hypn:feat/redragon-m690-pro
Oct 6, 2026
Merged

snekxs merged 2 commits into
OpenMouse-Project:mainfrom
hypn:feat/redragon-m690-pro

Conversation

@hypn

@hypn hypn commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds a driver for the Redragon M690 PRO (258a:002e cable, 258a:002f 2.4 GHz receiver): DPI stages, polling rate, lighting and button remapping, over both paths. It is a SinoWealth design, unrelated to the Holtek protocol of the existing Redragon M612/M724 driver, so it gets its own client beside it and shares the @openmouse/protocol/redragon entry point. Verified on three units; one browser limitation (battery and link status need OpenMouse Bridge) is explained below.

Device

Model Redragon M690 PRO ("MIRAGE PRO"), 8000 DPI, PAW3104
USB IDs 0x258a:0x002e (USB cable), 0x258a:0x002f (2.4 GHz receiver)
Firmware mouse bcdDevice 2.95 and 2.97, receiver 6.05 (three units)
Config collection usagePage 0xFF00, usage 0x0001 on interface MI_01
Protocol the host sends a one-byte command (feature report 5, 05 <cmd> 00 ..); short answers such as identity, battery and link come back on report 5, the settings and button blocks on report 8 (519 bytes)

What this adds

  • src/redragon/m690-pro.ts (re-exported from src/redragon/index.ts): the pure codec.
    • Product table, one settings bank per path.
    • Command and block framing; the block write ([3] = length - 8, the vendor's a5 trailer on the button block).
    • Settings decoder: polling, DPI stages and active stage, DPI stage colours, lighting.
    • Read-modify-write encoders that only touch decoded bytes; the vendor DPI list; button-slot encoding.
  • src/redragon/keys.ts: the keyboard key, modifier and shortcut tables the M612 already used, moved unchanged out of src/redragon/index.ts so both Redragon drivers share them, plus the numpad keys the M690 PRO's app offers. Internal, not a new export; the M612 tests pass unchanged.
  • src/drivers/redragon/m690-pro-hid.ts: RedragonM690ProHidClient.
    • Matches 258a:002e/002f with the 0xFF00 collection carrying reports 5 and 8, then refuses any device whose settings block is not the M690 PRO's (258a:002f is a generic SinoWealth receiver id).
    • Serialised queue. Each write re-reads both blocks, writes them in the vendor app's order, and reads both back before reporting success.
    • A failed settings read gives an identity-only status (settingsReady: false and a status note) instead of throwing; the next refresh retries, and setters refuse meanwhile.
    • Through the receiver it checks the link (0x80) where it can and refuses writes while the mouse is off, as the vendor app does. Where it cannot (plain Chrome, below), the status line warns that changes made while the mouse is off are undone when it reconnects.
  • src/drivers/registry.ts, src/drivers/vendors.ts: registration (VENDOR_ID.redragonM690Pro, REDRAGON_M690_PRO_HID_FILTERS). 0x258a is shared with the Glorious classic line, which claims other product ids.
  • src/drivers/redragon/m690-pro.test.ts: codec tests that re-encode the vendor app's captured writes byte for byte, plus fake-device tests (both paths, receiver bank, mouse off, a browser-like transport whose report-5 reads fail, a non-M690 settings block, lighting mode changes, the last-left-click guard, read retries).
  • captures/redragon-m690-pro/: text exports of the vendor-channel traffic (23 files, about 40 KB, including Chrome's refused reads and OpenMouse sessions) with a README.
  • docs/redragon-m690-pro-testing.md: framing, settings and button maps, evidence for each field, and the hardware checklist.

Battery and connection status need OpenMouse Bridge

In plain Chrome the driver can read and change every setting, but it cannot read the battery level, the charging state or whether the mouse is awake: the mouse rejects the way Chrome asks for those short answers. Through OpenMouse Bridge all of them work.

The details: the vendor app reads short answers (identify 0x01, receiver link 0x80, battery 0x90) with GET_REPORT(Feature 5, wLength 8). Chrome sends every feature read with the device's largest feature length (520), and this firmware STALLs any report-5 read that is not exactly 8 bytes (USBPcap of Chrome on Windows: captures/redragon-m690-pro/chrome-report5-stall.hex). Chrome on macOS behaves the same, and also needs Input Monitoring permission to open the device, because it shares its interface with a keyboard collection.

Pages cannot choose the read length, so the driver:

  • identifies the mouse from its settings block instead (byte 9 = 0x13 and the a5 end marker, in every captured block);
  • tries battery and link once per connection and reports them as unknown when the read fails, instead of retrying (each failure is a STALL).

Through OpenMouse Bridge, which reads each feature report at its own length, battery and the link check work with the driver unchanged (verified through the receiver).

Features supported

  • DPI: 5 stages, the vendor app's 24 values (250-8000), active stage selection, per-stage indicator colour.
  • Polling rate: 125, 250, 500, 1000 Hz.
  • Lighting: the vendor app's three effects plus Off:
    • Static (vendor "Steady"): colour, 4 brightness levels.
    • Wave (vendor "Colorful Streaming"): speed, brightness.
    • Breathing random (vendor "Breathing"): speed, brightness.
    • Off: Steady at brightness 0, which keeps the colour.
    • With the mouse's switch on ECO, the body's effects stay off; only the scroll wheel lights, in the active DPI stage's colour.
  • Buttons: the eight vendor-app buttons, with mouse buttons, DPI up/down, Three click, lighting on/off, disabled, 18 consumer keys, and keyboard keys and shortcuts (the M612's named shortcuts and single keys, plus the numpad). Other key combinations set in the vendor app show by name (e.g. Keys Ctrl+Shift+R); macro slots show as Macro n and are never written.
  • Status: through OpenMouse Bridge (not plain Chrome, see above), battery percent and link state through the receiver, and charging / charged over the cable. Connection type from the PID.
  • Cable and receiver each keep their own settings bank; each path reads and writes its own, as the vendor app does.

Evidence & protocol discovery

  • USBPcap captures (Windows) on three units, one setting per Apply, over the cable and the receiver. Text exports in captures/redragon-m690-pro/.
  • The installer's Cfg.ini (VID/PIDs, sensor, the DPISET list, button-slot table, factory DPI colours) and Text/en/text.xml (effect and action names).
  • libratbag's driver-sinowealth.c (MIT) for the framing this matches (report 8 here instead of its 4/6). Where they differ the captures win: libratbag's "profile 2" commands 0x21/0x22 are this mouse's receiver bank, and its 0x31 is the app's macro read.
  • Polling codes confirmed by the mouse's own report interval: 125 / 250 / 500 Hz as 8 / 4 / 2 ms over the cable, 125 / 250 Hz through the receiver after the app's writes, and 500 / 1000 Hz through the receiver after this driver's writes.

Verified on hardware

Three units (mouse firmware 2.95 and 2.97, receiver 6.05), with the OpenMouse dev build against the local package in Chrome 154 on Windows and USBPcap recording the wire; read-only on Chrome for macOS.

Detection 002e over the cable on both firmware revisions; 002f through the receiver
DPI stage 1 set to 8000 and back to 500, felt; active stage selected from the panel, felt
Polling 125 / 250 / 500 / 1000 Hz written; the mouse's report rate followed each one
Lighting Static colours and brightness, Wave and Breathing speeds (5 fastest), Off, by eye
DPI indicator colours shown in the panel as on the LEDs (the scroll wheel shows the active DPI stage's colour); the colour write re-encodes the vendor app's byte for byte
Buttons wheel click -> Right click and -> Volume up, felt, then restored; keyboard keys (single keys, Print Screen) and Copy (Ctrl+C) assigned and felt
Read-back every write read back identically over both paths; changes made in OpenMouse appear in the vendor app afterwards
Receiver, mouse off the receiver accepts a write and reads it back, and the mouse restores its own settings on reconnect; the status line says so
Battery through OpenMouse Bridge: 99 % through the receiver, link check gating writes; not readable in plain Chrome (see above)

OpenMouse's built-in hardware test passed on all four paths (captures/redragon-m690-pro/openmouse-hardware-test-{wired,wireless,bridge-wired,bridge-wireless}.json): identity, DPI, polling, DPI stages and the wireless link read back; 800 DPI / 1000 Hz written, read back and restored; battery (99 %, receiver) and charging status (cable) through Bridge.

Not settled

  • Through Bridge the polling sampler saw dropouts (60 Hz against 500 Hz), as in the Diamondback Chroma report, so there the rate was read back but not measured; in Chrome it measured 452 Hz at 99 % stability over the cable.
  • Signal strength: none found.

Safety

  • Only byte sequences the vendor app was captured sending are sent; no guessed commands (libratbag documents 0x75 as a DFU entry on this family; it is never used).
  • Blocks are read, changed only at decoded offsets, written back whole, and read back; unknown bytes are preserved.
  • Parameters are validated before anything is sent; the last left click cannot be removed; macros are never written (an empty assigned macro made the vendor app report a settings error).
  • Reads are retried a bounded number of times; writes are never retried.

Not supported

  • Writing macros (assigned macros show as Macro n).
  • Keyboard keys with the right-hand Ctrl, Shift, Alt or Win (shown as raw bytes).
  • Disabling DPI stages.
  • In plain Chrome only: battery, charging state and the awake check. They work through OpenMouse Bridge (see above).

The testing notes document each of these.

Companion PR

Pending, but see hypn/openmouse/feat/redragon-m690-pro

@hypn
hypn marked this pull request as ready for review October 5, 2026 15:18
The only conflict was the SupportedClient union: main added SteelSeriesAerox3WirelessHidClient in the same hunk. Resolution keeps main's list and inserts RedragonM690ProHidClient after RedragonHidClient. tsc clean; 63 focused redragon + registry tests pass.
@snekxs
snekxs merged commit cc17fbb into OpenMouse-Project:main Oct 6, 2026
5 checks passed
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 0.25.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants