Skip to content

bitmouse: support the ATK ZERO on wired and receiver transports - #58

Merged
snekxs merged 4 commits into
mainfrom
pr50-fix
Sep 6, 2026
Merged

bitmouse: support the ATK ZERO on wired and receiver transports#58
snekxs merged 4 commits into
mainfrom
pr50-fix

Conversation

@snekxs

Copy link
Copy Markdown
Member

Rebase/merge of #50 onto current main, resolving conflicts in registry.ts/vendors.ts (additive with #53's VXE support) and a duplicate longRangeMode field the merge introduced in mouse-types.ts. Closes#50.

itsntttand others added 4 commits September 5, 2026 07:03
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>
# Conflicts:
#	src/drivers/registry.ts
#	src/drivers/vendors.ts
@snekxs
snekxs merged commit a77cf7f into mainSep 6, 2026
2 checks passed
@snekxs
snekxs deleted the pr50-fix branch September 6, 2026 08:10
Sign up for freeto 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

@snekxs@itsnttt