Reading HID data from Arctis 7+ headphones
Getting Chatmix dial position using Rust language.
Little backstory
So, I used to have SteelSeries Arctis 7 headset. It was great, but it had one annoying thing - microUSB charging port. So when Arctis 7 was announced with USB-C and better battery life I was like: bet. It is a great headset, but there was one change that wasn't mentioned. Chatmix dial is no longer hardware based, but software based.
What is Chatmix dial?
This thing:
On right headphone there is a dial that lets you change volume of game and chat audio independently.
Which is all cool and dandy, but since it is software dependent you'll need to install SteelSeriesGG to use it.
So what's the problem?
Subprogram "SteelSeries Sonar" which is used to make this dial work isn't bad software. BUT
using it only to make this small feature work is a bit overkill.
This software creates virtual audio devices to divide audio on your PC. It was working really well at first but after some updates it would cause audio to crackle every once in a while.
How to eat a cake and have it too?
Looking for a solution I stumbled upon this GitHub issue. Inside I found out that it is possible to get the dial's position using HID events. Perfect! Let's get to work.
Side note: Isn't it cool that you can ask for help on GitHub and get it from the manufacturer itself?
Eating the cake
So knowing that headset sends HID event on interface #5
when the dial is moved we need to listen to them.
We will be using Rust and HIDAPI crate to do that.
Let's prepare our needed data:
const VENDOR_ID: u16 = 4152;
const PRODUCT_ID: u16 = 8758;
const INTERFACE: i32 = 5;
You can easily find out your headset's vendor and product ID in device manager.
I will slap everything into main()
and use unwrap()
just to make the code shorter, but you should handle errors properly.
const VENDOR_ID: u16 = 4152;
const PRODUCT_ID: u16 = 8758;
const INTERFACE: i32 = 5;
fn main() {
let api = hidapi::HidApi::new().unwrap();
for device in api.device_list() {
if device.vendor_id() == VENDOR_ID &&
device.product_id() == PRODUCT_ID &&
device.interface_number() == INTERFACE
{
let arctis = device.open_device(&api).unwrap();
loop {
let mut buffer = [0u8; 8];
let response = arctis.read(&mut buffer[..]).unwrap();
let payload = &buffer[..response];
println!("game: {} | chat: {}", payload[1], payload[2]);
}
}
}
}
To summarize what we did:
- We created new
HidApi
instance. - We iterated over all devices.
- We checked if
VENDOR_ID
,PRODUCT_ID
andINTERFACE
match our headset. - We read data from the device.
- We printed the data to see if it works.
Aaaand it works!
Of course, it doesn't really do anything other than printing data, but it is a start.
Next steps will be to implement ability to change volume of other audio devices using this dial.
But that's for another me to figure out.
I should go,
Krystian