Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HD-rumble data #11

Open
a7a00 opened this issue Apr 9, 2017 · 31 comments
Open

HD-rumble data #11

a7a00 opened this issue Apr 9, 2017 · 31 comments

Comments

@a7a00
Copy link

a7a00 commented Apr 9, 2017

I've been wrangling around with the Joy-Con and think I've figured out how the rumble data is sent over, starting from byte 13 in the packet sent from the Switch. (It doesn't look like standard HID...)

Byte 13 is a constant, 0x10. It's probably an identifier, as it's always 0xf for all pings not containing rumble data. Byte 14 looks like some sort of timekeeper in the event of the packets colliding; it starts at 0xa, increments with every ping sent until 0xf, then loops back around to 0x0, 0x1, etc. Changing either of these bits doesn't trigger any rumble at all. There are then 8 bits, 6 of which seem to correspond to the 6 faces of the Joy-Con; but my drivers started crapping out before I could dope out the last 2. The first of the 8 (Byte 15) seems to be some sort of global intensity modifier; as changing it seemed to jolt the whole thing more or less, so that leaves 6 faces, one global modifier, and ?????.

I've managed to trigger some rumble events, but my laptop drivers are suddenly refusing to play nice with the Joy-Cons. I'm not sure if it's hardware failure or something to do with manipulating this data, but I'd love to have some fresh eyes verify it. I verified it against @dekuNukem's BotW data and it seems to check out with respects to which bits change when during the bomb rumble. (I would love to try it with this, because it would let us isolate which bits correspond to which edge of the Joy-Con.)

Once someone else verifies, we can add it to the README?

Oh, and as a clarification because my Bluetooth has been hell, they're sending the same data over BT as they are over serial, right? The only thing that's different is the clock speed?

@riking
Copy link
Contributor

riking commented Apr 9, 2017

When the Bluetooth stack of the joycon and your computer desync (or whatever that is), just do a physical pair with your Switch, long press the sync button, and connect again from your computer.

@fossephate
Copy link

@lyra833 could you post what you sent to the JoyCons / their responses? I haven't been able to get any rumble events to trigger

@shinyquagsire23
Copy link
Contributor

shinyquagsire23 commented Apr 15, 2017

I've been testing vibration a bit today by keeping the baudrate lower over USB HID so that I can race fast enough to keep vibration enabled when transitioning from Bluetooth to UART in the grip and found a few things at least:

For a USB HID packet 80 92 00 0a 00 00 eb 01 10 03 XX YY YY YY XX ZZ ZZ ZZ,

The right Joy-Con is only affected by the ZZ bytes when incrementing each byte one at a time from 0 to FF, and the left Joy-Con is only affected by the YY bytes when doing the same. In this test, XX are both sent to 01.

When leaving YY or ZZ at 0x80 and incrementing the first XX or the second XX respectively, only the first YY or ZZ respond and the frequency of the vibration increases. Leaving YY or ZZ at 0x01 has all of the bytes respond. The first XX seems to control frequency on the left Joy-Con only, and the second the right. This test is demonstrated here and the code here

@CTCaer
Copy link
Contributor

CTCaer commented Sep 16, 2017

The HD-Rumble is well known now. Can we close this?

@facekapow
Copy link

facekapow commented Sep 24, 2017

I've started trying to decode the amplitude algorithm (for high amplitude first), and I've got something that partially works (only for 0.010 through 0.033, though): ((log2(amp * 1000) * 32) - 0x60) / (5 - (amp * 30)). The fact that it works only for these values is probably due to some of the fixed numbers I've used (0x60, 5, 30). Anyone know how the correct equation for frequency was found? Or was it just educated guessing and testing?

EDIT: BTW, the correct values are given if you round() the output

@CTCaer
Copy link
Contributor

CTCaer commented Sep 24, 2017

The freq was indeed a mix of educated guessing (Frequency modulation), testing and later confirmed by RE.

The amplitude works similarly but actually it's broken in the following 3 parts:
0
0.009942 - 0.112491,
0.117471 - 0.224982,
0.229908 - 1.0

or in a 0 1-100 range based on supported amps by JC:
0,
1-16,
17-100

This is the latest data I found out by REing.
You can also see this behavior by the following chart:
Alt
Lastly, don't take my values as the exact real ones. If you can find similar values it's OK.

We would be grateful if you can crack the algorithm.

@facekapow
Copy link

facekapow commented Sep 25, 2017

Alright, so I've found the algorithm(s) for original amplitude to high amplitude, and they are (unsimplified):

if (amp < 0.117) return (((log2(amp * 1000) * 32) - 0x60) / (5 - (2 ** amp)) - 1);
if (amp >= 0.117 && amp < 0.23) return ((log2(amp * 1000) * 32) - 0x60) - 0x5c;
if (amp >= 0.23) return (((log2(amp * 1000) * 32) - 0x60) * 2) - 0xf6;

Each range (0 to 0.117, 0.117 to 0.23, and 0.23 and up) requires it's own variation of the algorithm. Basically, what appears to be the encoded amplitude is ((log2(amp * 1000) * 32) - 0x60). I'm going to start figuring out the algorithm(s) for low amplitude later today/tomorrow.

These equations should obviously be simplified and cleaned up, but I literally just now found them so... I'll leave that to someone else. 😄

@facekapow
Copy link

facekapow commented Sep 26, 2017

Well, finding the algorithm for low amplitude was a lot easier having known the algorithms for high amplitude:

uint8_t encoded = round(highAmplitudeAlgorithm(amp)) / 2;
uint8_t evenOrOdd = encoded % 2;
uint8_t bytes[2];

if (evenOrOdd > 0) {
  // odd
  bytes[0] = 0x80;
  encoded = encoded - 1;
}

encoded = encoded / 2;

bytes[1] = 0x40 + encoded;

// if you wanted to combine them:
uint16_t byte = (bytes[0] << 8) | bytes[1];

@wormyrocks
Copy link
Contributor

where does val come from in this alg?

@facekapow
Copy link

facekapow commented Sep 26, 2017

Sorry, val is supposed to be encoded. I'll edit that.

@wormyrocks
Copy link
Contributor

wormyrocks commented Sep 26, 2017

Also, @CTCaer, I figured I might as well mention this here because I wasn't sure where to put it, but I'm having trouble applying the equations on this page to return the default rumble data [00 01 40 40] from input value of f=320 Hz, amp=0. My low frequency value ends up as 0x60 instead of 0x40 when I pass in f = 320 Hz. Are you certain that the low frequency conversion equation shouldn't be uint8_t lf = encoded_hex_freq - 0x60 rather than uint8_t lf = encoded_hex_freq - 0x40?

@CTCaer
Copy link
Contributor

CTCaer commented Sep 26, 2017

Hmm the equation is correct.
The default values are [00 01 40 40] = [HF: 320, 0.0 | LF: 160, 0.0]

Default LF is 160Hz. That's the Low end resonant frequency.
320Hz is HF default and also High end resonant frequency.

EDIT:
@facekapow
I will try your algorithms when I have time. As, I said, they don't need to be the exact same algorithm that Switch uses. We just need them to spit out similar numbers.

If these are working we can optimize them later.
(I'll also try to optimize them in calculation expense way.
E.g in freq alg: / 10.0 can be * 0.1.
Multiplication is way less expensive than dividing with double/float )

@wormyrocks
Copy link
Contributor

OK, so those two default frequency band values don't correspond to any real initial frequency value?
For instance, I can't start with some overall frequency F to give me LF = 160 and HF = 320. Because if I plug in HF = 320 and LF = 160 and try to solve for encoded_hex_freq I get two different answers.

@CTCaer
Copy link
Contributor

CTCaer commented Sep 26, 2017

Ofcourse. You should get different values for encoded_hex_freq and for encoded LF/HF freq.


OK, let's decode the default vibration [00 01 40 40]:
One way to do this is:
HF_u16 = 0x0001;

HF_freq = (HF_u16 >> 8) & 0xFF | HF_u16 & 0x01; //we need LSB
HF_ amp = HF_u16 & 0xFE; // we mask the LSB out

Another way we can work with HF_amp is (HF_u16 >> 1) & 0xFF and then we get a range of 0-7f with +1 increments.


LF_u16 = 0x4040;

HF_freq = (LF_u16 >> 8) & 0x7F // we mask out x80 which is the intermediate for LF_amp
LF_amp = LF_u16 & 0x80FF // we also get the x80 from HF_freq

Then for LF_amp we can work by splitting the 2 bytes. And if LF_amp >> 8 == 0x80 then we know it's intermediate amplitude and bigger than LF_amp & 0xFF.


I may sound a bit confusing, but that's the math. Please also read here, to understand which byte is which and how they are encoded.

Lastly, don't take my code as the de facto. There are countless ways to smartly optimize, bit mask, bit shift, convert and work on data. This way you can have easier calculations and also less expensive.

The above also means, that we can find better algorithms with new numbers that increment by +1 and then bit shift them to have what JC understands. Bit manipulation is a funny thing..

@CTCaer
Copy link
Contributor

CTCaer commented Sep 26, 2017

OK, I've read your comment again. If understood correctly, you want to work with only one algorithm for both LF/HF.

As you probably saw by now:
No you can't. You need to have 2 different algorithms (functions) when you do the encoding.

@wormyrocks
Copy link
Contributor

OK - it seems like the first code sample on this document converts a single frequency float into hf and lf values. How does this factor in, if the function requires two frequencies?

@CTCaer
Copy link
Contributor

CTCaer commented Sep 26, 2017

Ok I understand now. The code is a RFC to understand how to encode a freq to LF OR HF.

To work correctly you need sth like this:

float real_LF = 0.0f;
float real_HF = 0.0f;

//Get user float LF/HF or from somewhere else or hardcoded
real_LF = 160.0f;
real_HF = 320.0f;
// End - code to get LF/HF

// clamp them to acceptable freq
real_LF = clamp( real_LF, 40.875885f, 626.286133f );
real_HF = clamp( real_HF, 81.75177, 1252.572266f );

uint16_t hf = ((uint8_t)round(log2((double)real_HF * 0.01)*32.0)-0x60)*4;
uint8_t lf = (uint8_t)round(log2((double)real_LF * 0.01)*32.0)-0x40;

@wormyrocks
Copy link
Contributor

wormyrocks commented Oct 25, 2017

I may be extremely late on this, but the default values of 320 / 160 Hz and the setting of low and high vibration frequencies seems to all but confirm that the HD Rumble vibration motors are ALPS Electric Haptic Reactors, was this known?

Discussion here.

@CTCaer
Copy link
Contributor

CTCaer commented Oct 25, 2017

Nope, had no clue.
It's definitely Haptic Reactor. Images, specs and the frequency response graph match.

BTW, I remembered to state something that I always forgot.
As you all saw by now, there are some numbers that are skipped in the High frequencies.
The numbers increment by 4 but actually the in between numbers have specific usage (probably special waves like saw/triangle/etc). If my research goes again that way, I will update with anything I'll find.

EDIT:
The link for the haptic reactor is here.
EDIT2:
The Joy-Con analog sticks.

@riking
Copy link
Contributor

riking commented Oct 25, 2017

Looks like the actual part in the Joy-Cons isn't visible on that page - the ....06 has no center push button.

It's probably the ...05 or ...04 or something like that.

@CTCaer
Copy link
Contributor

CTCaer commented Oct 26, 2017

True, but the other characteristics probably are the same.

@rei-github
Copy link

rei-github commented Jan 25, 2018

Analysing the four unused bit(first byte lowest 2 bit and last byte highest 2bit),
I found there are other types of rumble data.
Though it is not perfect yet, I summarize my analysis here.

basically, the JoyCon HD rumble is a mix of below three elements:

Resonance Frequency Wave
	Sin wave of a resonance of haptic reactor.
	Frequency is 160Hz or 320Hz.
	Amplitude is 4bit.

Arbitary Frequency Wave
	Sin wave of a arbitary frequency.
	Frequency is 7bit, 40-625Hz for low frequency or 80-1250Hz for high frequecy.
	Amplitude is 7bit or 4bit.

Click Pulse
	4 kind of fixed shape transient pulse at the rumble beginning.
	pulse period is 10msec.
	shape is 4 type.
	Amplitude is 4bit or 7bit.

the rumble data is the combination pattern of these three elements in special manner.

TYPE X0: "single wave with resonance"
In this type, you can make one arbitary frequency wave and one resonance wave and two pulse.

BIT PATTERN:
bbbbbbba eeedcccc ihggggfe ?0iiiiii

a	High/Low select bit (0=low channel, 1=high channel)
bbbbbbb	Frequency
cccc	High freq channel amplitude
d	High freq channel switch
eeee	Low freq channel amplitude
f	Low freq channel switch
gggg	pulse1 amplitude
h	pulse1 switch
iiiiiii	pulse2 amplitude

if [a]=0, the [bbbbbbb] is the frequency of "low channel" and "high channel" frequency is 320Hz.
if [a]=1, the [bbbbbbb] is the frequency of "high channel" and "low channel" frequency is 160Hz.
if [d]/[f]/[h] bit is "1", the output of correspond channel is off(become silent).
I cannot figure out the last bit.

TYPE 01-00: "dual wave"
In this type, you can make two arbitary frequency sin wave at the same time.

BIT PATTERN:
aaaaaa00 bbbbbbba dccccccc 01dddddd

aaaaaaa	High channel Frequency
bbbbbbb	High channel Amplitude
ccccccc	Low channel Frequency
ddddddd	Low channel Amplitude

TYPE 01-01: "silent"
no sounds at all.

BIT PATTERN:
??????01 ???????? ???????? 01??????

TYPE 01-01: "dual resonance with 3 pulse"
In this type, you can make 2 resonance frequency sin wave and 3 pulse at the same time.

baaaa?10 eeedcccc ihggggfe 01iiiiii

aaaa	High channel resonant(320Hz) amplitude
b	High channel resonant(320Hz) switch
cccc	Low channel resonant(160Hz) amplitude
d	Low channel resonant(160Hz) switch
eeee	pulse 1 amplitude
f	pulse 1 switch
gggg	pulse 2 amplitude
h	pulse 2 switch
iiiiiii	pulse 3 amplitude

if [b]/[d]/[f]/[h] bit is "1", the output of correspond channel is off(become silent).
I cannot figure out the one bit meaning.

TYPE 11: "dual resonance with 4 pulse"
In this type, you can make 2 frequency sin wave and 4 pulse at the same time.

cccbaaaa gfeeeedc iiiihggg 11lkkkkj

aaaa	High channel resonant(320Hz) amplitude
b	High channel resonant(320Hz) switch
cccc	Low channel resonant(160Hz) amplitude
d	Low channel resonant(160Hz) switch
eeee	pulse 1/400Hz amplitude
f	pulse 1/400Hz switch
gggg	pulse 2 amplitude
h	pulse 2 switch
iiii	pulse 3 amplitude
j	pulse 3 switch
kkkk	pulse 4 amplitude
l	pulse 4 switch

if [b] bit is "1", the "320Hz" is off and "pulse1/400Hz" channel make "400Hz" sin wave.
if [b] bit is "0", the "320Hz" is on and "pulse1/400Hz" channel make "pulse".
if [d]/[f]/[h]/[j]/[l] bit is "1", the output of correspond channel is off(become silent).

Amplitude:
the "Amplitude" bit pattern is 7bit or 4bit.

7bit amplitude:
	"0000000" is minimum and "1111111" is maximum amplitude.
	this pattern is used on "arbitary freq channel" or last "pulse"

4bit amplitude:
	"0000","1100"-"1111" is silent, "0001" is maximum, "1011" is "minimum.
	below table is normalized value measured by oscilloscope.

	0000:	0
	0001:	1
	0010:	0.713429339
	0011:	0.510491764
	0100:	0.364076932
	0101:	0.263212876
	0110:	0.187285343
	0111:	0.128740086
	1000:	0.096642284
	1001:	0.065562582
	1010:	0.047502641
	1011:	0.035863824
	1100:	0
	1101:	0
	1110:	0
	1111:	0

Pulse Shape:
there are 4 pulse shape.

pulse 1:
	three peaks and three valley. length is 10msec.
	(medium peak, medium valley, large peak, large valley, small peak, small valley)
pulse 2:
	one peak and two valley. length is 10msec.
	(valley, peak, valley)
pulse 3:
	time shift of pulse 1.length is 10msec.
pulse 4:
	sum of pulse 1 and pulse2.

@CTCaer
Copy link
Contributor

CTCaer commented Jan 25, 2018

@rei-github
Great work!
I've tested these but couldn't make any sense, because I was using only my hearing and not an oscilloscope.

Is it possible to also record the real frequencies produced so we can update the freq table?

Lastly, I will take a closer look at your findings next Monday and if you want I can do the pull request to include your info.

@rei-github
Copy link

rei-github commented Jan 25, 2018

Yes, but the frequency and the amplitude is almost accurate in my measurement.

oscilloscope is not suitable to measure of amplitude or frequency.
especially for PWM driving motor.
(Haptic actuator is controlled by 250kHz PWM)

Next time when I open joycon, I'll try to measure frequency and amplitude accurately by freq meter or something.

I forgot one thing to note.
the "Phase" of both waves (Hi/Low) always start from "0" and I cannot managed to change.
so I can make "almost saw tooth" wave but cannot change the direction of "SAW" or make "Triangle" wave.
saw tooth wave

@CTCaer
Copy link
Contributor

CTCaer commented Jan 29, 2018

So, if understand correctly and also that they start at 0, the pulses look somewhat like these (I did not use Fourier or anything else, they are by hand):

And pulse 3 has a different phase, correct?

@rei-github
Copy link

rei-github commented Feb 9, 2018

hi, I've made one mistake and forgot one information.
pulse 4 is not the sum of pulse 1+2. it is the time shift of pulse 2.
the time shift of pulse 3 and 4 is 5msec earlier than pulse 1 and 2.

the pulse shape is difficult to describe, so I took some pictures.

pulse 1
pulse1

pulse 2
pulse2

pulse 3
pulse3

pulse 4
pulse4

pulse 1+2
pulse1 2

pulse 1+3
pulse1 3

pulse 1+4
pulse1 4

pulse 2+3
pulse2 3

pulse 2+4
pulse2 4

pulse 3+4
pulse3 4

pulse 1+2+3
pulse1 2 3

pulse 1+2+4
pulse1 2 4

pulse 1+3+4
pulse1 3 4

pulse 2+3+4
pulse2 3 4

pulse 1+2+3+4
pulse1 2 3 4

@rei-github
Copy link

rei-github commented Feb 9, 2018

the HD motor is driven by PWM of 250kHz.
so to get above waves, I use simple CR low pass filter (2kR, 0.1uF i.e. 800Hz cut off).
because of this filter, pulse shape is not the real one.

dsc_0019

@ClearlyClaire
Copy link

Hm, maybe I'm messing something up, but when I try to use pulses, I get a regular vibration, except its frequency shifts down with every (identical) message I send? I can't make sense of it :/

@riking
Copy link
Contributor

riking commented May 9, 2018

@ThibG make sure you're incrementing the rumble frame timer in the packets you send!

@ClearlyClaire
Copy link

The first byte of the HID output report, referenced as GlobalPacketNumber in https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/bluetooth_hid_notes.md? I'm incrementing it at each packet, but maybe it's out of sync from the beginning somehow? Anyway I don't have this issue when outputing “dual wave” rumble data

@CTCaer
Copy link
Contributor

CTCaer commented May 10, 2018

What about the packet frequency and hid output report id?
Try with 5ms/10ms/15ms delay after each packet.
Try with ouput report x10

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants