Sign in Sign up
1 branch
README.md

HyperX Cloud Alpha 2 Wireless: base station lighting without NGENUITY

#

Notes and tools for controlling the Cloud Alpha 2 base station ("puck") lighting on Linux and Windows without NGENUITY: a steady dim ring and dimmed button LEDs, stored on the device, so nothing has to keep running on the PC.

Current setup

#
What State Where it lives
Ring brightness 5 / 255 (~2%) Settings sector 0x1F000
Ring color Steady white Stored frame at 0x17C00, ring idle mode 6 (written 2026-09-29; survives unplug/replug)
Button LEDs 10% of the original colors Settings sector 0x1F000

Button colors (original → now):

Button Original Now
top-left (60, 10, 90) (6, 1, 9)
top-middle (0, 30, 200) (0, 3, 20)
top-right (24, 255, 240) (2, 26, 24)
bottom-left (200, 20, 0) (20, 2, 0)
bottom-middle (247, 173, 0) (25, 17, 0)
bottom-right (60, 220, 20) (6, 22, 2)

Button colors and brightness were first written with peroxy/hyperx-cloud-alpha2-lightsout. Starting NGENUITY resets the brightness to 100% and the buttons to the original colors (and probably the ring mode too), so keep its auto-launch off.

Protocol

#

Full details, with firmware addresses as evidence:

  • docs/firmware-protocol.md: every command the base station firmware accepts, the flash map and the ring effect engine (static analysis of the firmware).
  • docs/ngenuity-protocol.md: how NGENUITY 3.0.2 encodes the same commands (generic HyperX protocol), effect ids, and what it uses for this device.

Where the firmware comes from

#

NGENUITY ships it: %LOCALAPPDATA%\NGENUITY\current\Assets\Services\Libs\Outlaw\.

File Device
03F008BE.hex Base station controller (Sonix SN32F24xC, Cortex-M0, code 0x0–0xC103)
03F00ABE.bin Wireless radio (Airoha)
03F00BBEA.bin, 03F00BBEP.bin Headset (Airoha)

NGENUITY flashes the base station through the Sonix ISP bootloader (SnxHidLib.dll, code security 0), so this .hex is exactly what runs on the puck.

The firmware and NGENUITY files are not in this repo: the NGENUITY EULA forbids redistributing them. docs/sources.md records the exact package, download URL, file paths and SHA-256 hashes, and tools/extract-firmware.sh recreates the local analysis files (in the git-ignored fw/) from HyperX's own package.

Devices

#
USB ID What
03f0:08be Base station controller: lighting, buttons, dial. Vendor HID on interface 2 (usage page 0xFF13), 64-byte reports, first byte = report ID = command
03f0:0abe Wireless radio (audio)
03f0:0bbe Headset over the USB cable
0c45:7160 Sonix mask-ROM ISP bootloader the base station re-enumerates as

Main lighting commands

#

Every request is padded to 64 bytes. The puck acks each one with FF 01 … [0x0e]=cmd [0x0f]=sub (FF 02 = error or unknown), and some also send a reply report (first byte = cmd + 1). ⚠ = writes flash.

Bytes Meaning
40 01 S 00 LL Ring brightness LL. ⚠ saved only if S = 01
40 02 Read brightness → 41 02 LL
42 02 01 00 MM PL PH ⚠ Ring idle mode MM and effect period (PH<<8|PL, 0 = keep). Always saved
42 03 Read effect period → 43 03 00 PL PH (default 50)
44 01 NP Start a live frame of NP packets (NGENUITY: 44 01 02)
44 02 II 00 [R G B]×20 Live frame packet II (21 ring LEDs: 20 in packet 0, 1 in packet 1)
44 03 NF CL CH 01 Open a flash upload of NF frames / CL|CH<<8 packets
44 04 IL IH [R G B]×20 ⚠ Upload packet IH<<8|IL to 0x17C00 + idx×0x40 (packet 0 erases the 1 KB sector). Raw flash writer: never send indexes beyond the upload
44 03 01 01 00 02 + 44 04 00 00 [R G B]×6 + 42 02 02 00 06 ⚠ Stage and save the six button colors (lightsout)
44 05 Read stored animation header → 45 05 00 02 (1 frame, 2 packets) or FF FF (none)
44 06 01 IL IH Read flash page 0x17C00 + idx×0x40, bytes 4..63 only

Ring idle modes (what the ring shows when no live frames arrive for 2 s):

Mode Effect
3 Factory default: rotating wave from a 64-color palette stored at 0x1F004
6 Play the animation stored at 0x17C00 (one frame = static color)
7, 8 Temporary level bar for headset events (volume)
other (e.g. 0) Draw nothing, so the ring keeps its last frame

Other notes:

  • The brightness scales all ring output, including stored frames.
  • Frames are ignored for ~0.5 s after boot and while the mic-mute flag is set.
  • Button colors are stored as B,R,G at 0x1F104. Pure black (0,0,0) is skipped by the firmware, so "off" is (1,1,1).
  • 0x1E400 holds a factory-default copy of the settings (restored by the factory-reset report 07 02 ff; don't send report IDs 01 or 07 casually).
  • Headset battery: send 50 02; the base station relays it to the headset and answers 51 02 PP 00 xx 00 FF …, where PP is the percentage and bit 7 of FF means charging (layout from HaloBattery PR #127; confirmed 2026-09-29: 51 02 57 … = 87%). An fb 13 00 PP report with the same percentage comes along. 52 01 → 53 01 01 looks like a connection status (unconfirmed).

How the static white ring was stored (2026-09-29)

#

Flash backup taken first (fw/flash-backup-2026-09-29.txt, all 528 data pages). Then:

44 03 01 02 00 01            -> FF 01
44 04 00 00 [FF FF FF]×20    -> 45 04 00 00 00
44 04 01 00 FF FF FF         -> 45 04 01 00 00
44 05                        -> 45 05 00 02
42 02 01 00 06 00 00         -> FF 01

Afterwards brightness, effect period and all settings pages read back identical to the backup. After unplugging and replugging, the ring came back white with nothing running on the PC. Undo: 42 02 01 00 03 00 00 (back to the rainbow wave).

Tools

#

alpha2 (Windows and Linux)

#

One program with a settings window and a command line. Rust workspace: crates/alpha2 is the device library (no dependencies), crates/alpha2-app the alpha2 program (egui with OpenGL, ~5.8 MB; on Windows the C runtime is linked statically, see .cargo/config.toml, so the .exe runs on its own). Everything is stored on the base station, so nothing has to keep running afterwards.

cargo build --release          # -> target/release/alpha2(.exe)

Settings window: double-click alpha2.exe, or run alpha2 / alpha2 gui. It is a console program with an embedded manifest (consoleAllocationPolicy = detached), so a double-click opens no console window on Windows 11 24H2 and later (older Windows: a brief flash). It shows the headset battery (refreshed every 30 s), reads the stored settings, previews ring color and brightness live on the device, and only writes flash when you click Save to device (only what changed). Closing without saving restores the stored brightness. ~80 MB RAM while open (mostly the OpenGL driver).

Command line:

alpha2 status                  # battery, brightness, effect period, stored ring, button colors
alpha2 battery                 # headset battery, e.g. "87%" or "87% (charging)"
alpha2 ring static ffffff      # ⚠ store a static ring color (or 21 colors, one per LED)
alpha2 ring rainbow            # ⚠ back to the factory rainbow wave (--period N, lower = faster)
alpha2 ring off                # ⚠ ring off
alpha2 ring preview ff0000 5   # show a color for 5 s without storing anything
alpha2 brightness 5            # ⚠ ring brightness 0-255 or 2% (--no-save: until power-off)
alpha2 buttons 060109 000314 021a18 140200 191100 061602   # ⚠ top-left ... bottom-right
alpha2 backup                  # dump the readable flash data area to a text file

⚠ = writes the base station's flash. Tested on the device (2026-09-29): all read commands, backup, ring preview, the write paths of brightness, ring static and buttons, and saving ring color, brightness and a button color from the settings window. Not yet run: ring rainbow and ring off.

On Linux, install linux/70-hyperx-alpha2.rules so it works without root. If the old Python ring service (hyperx-alpha2-ring.service) is still installed on a Linux machine, disable it: it is no longer needed.

tools/extract-firmware.sh gets the base station firmware from HyperX's NGENUITY package for analysis (see docs/sources.md).

Findings

#

Windows VM capture

#
  • USB passthrough (virt-manager host device): Windows' transfers to the base station hang for about 34 s and get cancelled. Doesn't work.
  • SPICE USB redirection: sending works, but no replies come back, so NGENUITY shows the headset as "stealth". Use native Windows (Wireshark + USBPcap) for captures.

NGENUITY

#
  • Its "Static" ring is a software effect: the light engine streams frames 5 times a second. NGENUITY never uses the firmware's onboard modes for this device's ring.
  • 40 01 00 00 LL does not save by itself. It only persisted because lightsout's 42 02 02 commit saves the whole settings sector.

Other projects (checked 2026-09-29)

#
  • Sapd/HeadsetControl (C, GPL-3.0): the big multi-brand CLI (battery, sidetone, lights, chatmix, EQ, ...), with separate GUI companion projects. HyperX: Cloud Alpha Wireless, Cloud Flight Wireless, Cloud II Wireless. No Cloud Alpha 2.
  • LennardKittner/HyperHeadset (Rust, MIT): HyperX-only CLI + tray app (battery, mute, sidetone, EQ, ...) for Cloud II/III, Stinger 2, Flight, Alpha Wireless and more. No lighting, no Cloud Alpha 2.
  • HeyOkay/HaloBattery: battery monitor. Its open PR #127 adds the Cloud Alpha 2 battery over the base station (50 02), which is where our battery command comes from.
  • OpenRGB: no Alpha 2 device or issue. Nobody else controls the Alpha 2 lighting.

Next steps

#
  1. Click through the GUI (preview, save, revert) and test ring rainbow, ring off and new button colors on the device.
  2. Per-LED ring colors and multi-frame stored animations in the GUI.
  3. Other headset-side settings relayed by the puck (mute, sidetone, EQ: 60 xx, 62 xx, 66 xx), and Alpha 2 support in HyperHeadset or HeadsetControl.
  4. PR the stored-ring method to lightsout.