bitmouse: support the ATK ZERO on wired and receiver transports - #50
Open
itsnttt wants to merge 2 commits into
Open
bitmouse: support the ATK ZERO on wired and receiver transports#50itsnttt wants to merge 2 commits into
itsnttt wants to merge 2 commits into
Conversation
ATK's newer mice use a different configuration channel from the A9
family already in atk/hid.ts: 63-byte frames on report 0x08 over a
vendor collection at usage page 0xff05 / usage 0x0001, rather than
16-byte EEPROM commands on 0xff02 / 0x0002. The existing ATK request
filter matches only the older collection, so an ATK ZERO never reaches
the browser picker at all.
Add the framing as a codec under src/bitmouse -- the name the vendor
software gives this protocol family -- and a driver for the two ATK ZERO
USB ids, wired and behind its 8K receiver. Only those two products are
claimed; the same protocol drives many more models, but each needs its
own hardware check first.
Verified on an ATK ZERO, mouse firmware 3.0.3, receiver firmware 3.0.2,
over both transports:
reads device version, cid/mid (1,1), device type, battery level and
charging state, the config block, the DPI stage table
writes each confirmed by reading back, then restored: polling rate
2000 -> 1000 Hz, DPI 800 -> 1600, debounce 4 -> 8 ms, sleep
1800 -> 600 s, motion sync and ripple control on -> off
Two firmware behaviours the tests pin down: a reply leaves the previous
exchange's bytes past its own length, so payloads are trimmed to the
length the device reports; and the DPI table's count byte reads 8 while
only two stages hold a value, so the usable stages are the leading run
with a non-zero DPI.
Left unreported rather than guessed at: lift-off distance, where the
config byte reads zero on both transports and the vendor's own field map
overlaps a 16-bit DPI value with it; angle snapping, which has a command
but was not exercised; chip id and the online-status byte, which both
answer zero on hardware that is plainly online.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ong range
Driving the ATK ZERO from the OpenMouse app rather than a unit test turned
up a defect the read-back verification could not catch, plus four settings
the first pass either mis-read or left out.
The DPI enable bit. A stage record is eight bytes and a write prefixes it
with the stage index, so the write byte lining up with the record's last
byte is a literal zero at +8, and `enable` at +9 has no counterpart in the
stored table at all. Reading that last byte back as an enable flag and
feeding it to the next write meant every DPI write sent enable=0: the mouse
stored the value, answered the read-back with it, and never moved the sensor
onto it. `enable` is a command bit, set when the stage being written is the
active one, so it is computed that way now. The byte the table does store is
renamed `reserved` so it is not mistaken for a flag again.
Lift-off. The level is not in the config block's silentHeight byte, which
reads zero: the vendor reads it as offsetCalibration + 1 and writes it as
{ height: 0, offsetCalibration: level - 1 }. The level is the A9 register
scale, tenths of a millimetre offset by six, so codes 1 to 11 are 0.7 mm to
1.7 mm — a range, not the three stops first assumed. Both are reported: the
nearest stop for the shared control, and the range in `liftOffScale` for a
mouse-specific slider.
Straight line correction is angle snapping under the vendor's name, so it is
reported and written through the shared control instead of hidden.
Sensor sampling mode and Ultra Long Range are read with getAddressData at
addresses 74 and 75 and written with commands 31 and 27. Long range needs a
field of its own, so `longRangeMode` joins the shared status.
`setDpiStageValue` implements the stage editor the driver already advertised
through `ui.dpiStageEditor` but could not service.
Verified on an ATK ZERO over its receiver: DPI 800 -> 1600, polling
2000 -> 1000 Hz, debounce 4 -> 8 ms, sleep 1800 -> 600 s, motion sync and
ripple on -> off, each read back and restored. Lift-off, angle snapping,
sensor mode and long range are decoded from the vendor software and build
clean, but have not yet been exercised on hardware.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #48.
What this adds
ATK's current mice speak a configuration protocol this repository does not
implement. Their vendor software calls the family BITMOUSE; it shares
nothing with the 16-byte EEPROM protocol in
src/atk/but the vendor id.SUPPORTED_HID_FILTERSonly asks ATK for{ 0x373b, usagePage 0xff02, usage 2 },and an ATK ZERO exposes no
0xff02at all — so it never reaches the browser'sdevice picker. Vendor id alone was never enough to conclude this family was
covered.
This adds the codec under
src/bitmouse/, a driver atsrc/drivers/atk/bitmouse-hid.ts, and PID-scoped discovery filters.Devices
0x373b:0x1154target = 00x373b:0x1155target = 1Both answer
cid,mid = 1,1. Sensor is a PAW3950 Ultra (10–42 000 DPI, stepping10 / 50 / 100).
Only these two products are claimed. The same protocol drives much of the
current ATK and VXE range, but each model wants its own hardware check first —
the table in
src/bitmouse/index.tsis where one goes.Framing
63-byte frames on report
0x08, over the vendor collection at usage page0xff05/ usage0x0001.A receiver answers its own commands on
target 0and relays mouse commands ontarget 1.Three firmware behaviours worth knowing
Replies are not zero-padded. Bytes past the reported length are whatever the
previous exchange left behind:
bitmouseDecodeReplytrims to the reported length, and a test pins this capture.The DPI table's count byte is the table size, not the enabled count. It reads
8 on a mouse holding two configured stages; unused slots read 0 DPI while keeping
stale colour bytes.
enableon a DPI write is a command bit, not a stored flag. This one cost around trip. A stage record is eight bytes and a write prefixes it with the stage
index, so the write byte that lines up with the record's last byte is a literal
zero at +8 — and
enable, at +9, has no counterpart in the table at all. Anearly version of this driver read that last byte back as an enable flag and fed
it to the next write, so every DPI write sent
enable=0. The mouse stored thevalue, answered the read-back with it, and never moved the sensor. Read-back
verification passed the whole time; only using the mouse showed it.
enableistrue when the stage being written is the active one, and the byte the table does
store is named
reservedso it is not mistaken for a flag again.Lift-off is a range, not three stops
The level is not the config block's
silentHeightbyte, which reads zero: thevendor reads it as
offsetCalibration + 1and writes it as{ height: 0, offsetCalibration: level - 1 }. The level is the same registerscale the A9 family already uses in
src/atk/index.ts— tenths of a millimetreoffset by six — so codes 1 to 11 are 0.7 mm to 1.7 mm, which is the slider
the vendor software shows.
MouseStatus.liftOffDistancecarries three stops, which would throw away eightof the eleven settings, so the driver reports both: the nearest stop for the
shared control, and the range in a new optional
liftOffScalefor a controlthat can use it. Drivers that set no scale are unaffected.
Verified on hardware
An ATK ZERO, over its receiver and wired. Every write is confirmed by reading
the value back, and each was restored afterwards.
Reads exercised on both transports: device version, cid/mid, device type,
battery level and charging state, the config block, and the DPI stage table.
Deliberately not decoded
GetChipId(71) andGetMouseOnlineStatus(35) answer zero onhardware that is plainly online, so neither is used.
cmdSnis a constant0x3ain every reply captured. Not validated against.lift-off byte in the vendor's own accessors. DPI comes from the address sweep
the vendor software uses.
Evidence
Framing, command ids, field offsets and request lengths were read out of the
vendor's own WebHID configurator (ATK HUB v3.2.21, served publicly from
hub.atk.pro), and every value used here was then confirmed against the device.No vendor code is copied; the implementation and its naming are this
repository's own.
The
mouseCidMidcommand (74) is the same identification route as #44,approached from this protocol's side.
Checks
npm run checkhere: build clean, 848 tests,npm packfine.openmousebuilt against this package withnpm install --no-save --package-lock=false ../mouse-protocol:npm run checkclean, 112 tests, and the app was driven against the mousefor the table above.
registry.test.tsneeded0xff05inUSAGE_PAGES, exactly as its ownassertion message instructs, so the new driver is covered by the overlap
matrix.
Matching application PR
The app needs changes to reach this driver at all, and to render the lift-off
range — see OpenMouse-Project/openmouse#159.