[GH-ISSUE #34] DS4 mode has the gyro roll axis inverted vs an original controller #17

Closed
opened 2026-10-22 04:57:48 +00:00 by roman · 15 comments
Owner

Originally created by @Fob0bqAd34 on GitHub (Jun 13, 2026).
Original GitHub issue: https://github.com/safijari/openpuck/issues/34

When using DS4 mode the gyro roll axis has inverted output when compared to the output of an original DS4 controller. Only tested on Windows 11.

Originally created by @Fob0bqAd34 on GitHub (Jun 13, 2026). Original GitHub issue: https://github.com/safijari/openpuck/issues/34 When using DS4 mode the gyro roll axis has inverted output when compared to the output of an original DS4 controller. Only tested on Windows 11.
roman closed this issue 2026-10-22 04:57:48 +00:00
Author
Owner

@safijari commented on GitHub (Jun 14, 2026):

should be fixed in #38

<!-- gh-comment-id:4702351207 --> @safijari commented on GitHub (Jun 14, 2026): should be fixed in #38
Author
Owner

@Fob0bqAd34 commented on GitHub (Jun 15, 2026):

Apologies I made a mistake on the report. On the main branch:

  • HID gyro(DS4) mode had yaw inverted
  • PS5 Dualsense mode had roll inverted.

In the new one:

  • HID gyro(DS4) mode is now working as expected
  • PS5 Dualsense mode now has both yaw and roll inverted vs how they normally work in steam.
<!-- gh-comment-id:4703667883 --> @Fob0bqAd34 commented on GitHub (Jun 15, 2026): Apologies I made a mistake on the report. On the main branch: - HID gyro(DS4) mode had yaw inverted - PS5 Dualsense mode had roll inverted. In the new one: - HID gyro(DS4) mode is now working as expected - PS5 Dualsense mode now has both yaw and roll inverted vs how they normally work in steam.
Author
Owner

@safijari commented on GitHub (Jun 16, 2026):

ok, I'll get it in the next patch

<!-- gh-comment-id:4714525730 --> @safijari commented on GitHub (Jun 16, 2026): ok, I'll get it in the next patch
Author
Owner

@safijari commented on GitHub (Jun 16, 2026):

Ok I'm very lost. I have no good way to test this and in both DS5 and DS5 modes Steam shows me negative values for each axis when I rotate the controller clockwise around it which fits with the right hand rule and is consistent between the two modes.

<!-- gh-comment-id:4714578793 --> @safijari commented on GitHub (Jun 16, 2026): Ok I'm very lost. I have no good way to test this and in both DS5 and DS5 modes Steam shows me negative values for each axis when I rotate the controller clockwise around it which fits with the right hand rule and is consistent between the two modes.
Author
Owner

@Fob0bqAd34 commented on GitHub (Jun 16, 2026):

I fiddled around with the files and below is what made the gyro work the same for me in all 3 modes. I commented the lines with yaw, pitch, roll to which I think is which. I tried to upload the files to github but for some reason they keep failing. Note I have very little knowledge of code at all I just moved things around until they worked how I expect.

In mode_ps5.cpp:

static void ps5Build(uint8_t out[63]){
uint32_t b=psButtonsFromSteam(g_in.buttons);
bool lTouch=(b&TB_LPADT)||(b&TB_LPADC), rTouch=(b&TB_RPADT)||(b&TB_RPADC);
memset(out,0,63);
out[0]=swStick(g_in.lx,false); out[1]=swStick(g_in.ly,true);
out[2]=swStick(g_in.rx,false); out[3]=swStick(g_in.ry,true);
out[4]=g_in.lt; out[5]=g_in.rt;
static uint8_t seq=0; out[6]=seq++;
out[7]=psHatNibble(b)|psFaceNibble(b);
out[8]=psShouldersByte(b);
out[9]=((b&TB_STEAM)?0x01:0)|((b&TB_TOUCH||b&TB_LPADC||b&TB_RPADC)?0x02:0)|((b&TB_MUTE)?0x04:0);
out[15]=g_in.gx&0xFF; out[16]=g_in.gx>>8; //pitch
out[17]=g_in.gz&0xFF; out[18]=g_in.gz>>8; //yaw
out[19]=(-g_in.gy)&0xFF; out[20]=(-g_in.gy)>>8; //roll
out[21]=g_in.ax&0xFF; out[22]=g_in.ax>>8;
out[23]=g_in.ay&0xFF; out[24]=g_in.ay>>8;
out[25]=g_in.az&0xFF; out[26]=g_in.az>>8;
uint16_t lx, ly, rx, ry;
steamPadsToTouch(b, PS5_TOUCH_H, g_in.lpx, g_in.lpy, g_in.rpx, g_in.rpy, &lx, &ly, &rx, &ry);
touchPackPads(out+32, lTouch, rTouch, lx, ly, rx, ry);
out[52]=PS5_STATUS_USB;
}

In mode_hidgyro.cpp:

static void hidGyroBuild(uint8_t out[63]){
uint32_t b=psButtonsFromSteam(g_in.buttons);
bool lTouch=(b&TB_LPADT)||(b&TB_LPADC), rTouch=(b&TB_RPADT)||(b&TB_RPADC);
memset(out,0,63);
out[0]=swStick(g_in.lx,false); out[1]=swStick(g_in.ly,true);
out[2]=swStick(g_in.rx,false); out[3]=swStick(g_in.ry,true);
out[4]=psHatNibble(b)|psFaceNibble(b);
out[5]=psShouldersByte(b);
static uint8_t ctr=0; out[6]=((ctr++&0x0F)<<4)|((b&TB_TOUCH||b&TB_LPADC||b&TB_RPADC)?0x02:0)|((b&TB_STEAM)?0x01:0);
out[7]=g_in.lt; out[8]=g_in.rt;
out[12]=g_in.gx&0xFF; out[13]=g_in.gx>>8; //pitch
out[14]=g_in.gy&0xFF; out[15]=g_in.gy>>8; //roll
out[16]=(-g_in.gz)&0xFF; out[17]=(-g_in.gz)>>8; //yaw
out[18]=g_in.ax&0xFF; out[19]=g_in.ax>>8;
out[20]=g_in.ay&0xFF; out[21]=g_in.ay>>8;
out[22]=g_in.az&0xFF; out[23]=g_in.az>>8;
out[29]=DS4_STATUS_USB;
if(lTouch || rTouch){
uint16_t lx, ly, rx, ry;
steamPadsToTouch(b, DS4_TOUCH_H, g_in.lpx, g_in.lpy, g_in.rpx, g_in.rpy, &lx, &ly, &rx, &ry);
static uint8_t tstamp=0;
out[32]=1; out[33]=tstamp++;
touchPackPads(out+34, lTouch, rTouch, lx, ly, rx, ry);
} else {
out[32]=0;
touchPackPads(out+34, false, false, 0, 0, 0, 0); // contact 0x80 -- memset(0) reads as touch @0,0
}
}

In mode_switch_pro.cpp:

static void switchProBuild(uint8_t out[63]){
memset(out,0,63);
jcInputPrefix(out);
for(int k=0;k<3;k++){
int o=12+k*12;
out[o+0]=g_in.ax&0xFF; out[o+1]=g_in.ax>>8;
out[o+2]=g_in.ay&0xFF; out[o+3]=g_in.ay>>8;
out[o+4]=g_in.az&0xFF; out[o+5]=g_in.az>>8;
out[o+6]=g_in.gy&0xFF; out[o+7]=g_in.gy>>8; //roll
out[o+8]=(-g_in.gx)&0xFF; out[o+9]=(-g_in.gx)>>8; //pitch
out[o+10]=g_in.gz&0xFF; out[o+11]=g_in.gz>>8; //yaw
}
}

<!-- gh-comment-id:4718422744 --> @Fob0bqAd34 commented on GitHub (Jun 16, 2026): I fiddled around with the files and below is what made the gyro work the same for me in all 3 modes. I commented the lines with yaw, pitch, roll to which I think is which. I tried to upload the files to github but for some reason they keep failing. Note I have very little knowledge of code at all I just moved things around until they worked how I expect. In mode_ps5.cpp: static void ps5Build(uint8_t out[63]){ uint32_t b=psButtonsFromSteam(g_in.buttons); bool lTouch=(b&TB_LPADT)||(b&TB_LPADC), rTouch=(b&TB_RPADT)||(b&TB_RPADC); memset(out,0,63); out[0]=swStick(g_in.lx,false); out[1]=swStick(g_in.ly,true); out[2]=swStick(g_in.rx,false); out[3]=swStick(g_in.ry,true); out[4]=g_in.lt; out[5]=g_in.rt; static uint8_t seq=0; out[6]=seq++; out[7]=psHatNibble(b)|psFaceNibble(b); out[8]=psShouldersByte(b); out[9]=((b&TB_STEAM)?0x01:0)|((b&TB_TOUCH||b&TB_LPADC||b&TB_RPADC)?0x02:0)|((b&TB_MUTE)?0x04:0); out[15]=g_in.gx&0xFF; out[16]=g_in.gx>>8; //pitch out[17]=g_in.gz&0xFF; out[18]=g_in.gz>>8; //yaw out[19]=(-g_in.gy)&0xFF; out[20]=(-g_in.gy)>>8; //roll out[21]=g_in.ax&0xFF; out[22]=g_in.ax>>8; out[23]=g_in.ay&0xFF; out[24]=g_in.ay>>8; out[25]=g_in.az&0xFF; out[26]=g_in.az>>8; uint16_t lx, ly, rx, ry; steamPadsToTouch(b, PS5_TOUCH_H, g_in.lpx, g_in.lpy, g_in.rpx, g_in.rpy, &lx, &ly, &rx, &ry); touchPackPads(out+32, lTouch, rTouch, lx, ly, rx, ry); out[52]=PS5_STATUS_USB; } In mode_hidgyro.cpp: static void hidGyroBuild(uint8_t out[63]){ uint32_t b=psButtonsFromSteam(g_in.buttons); bool lTouch=(b&TB_LPADT)||(b&TB_LPADC), rTouch=(b&TB_RPADT)||(b&TB_RPADC); memset(out,0,63); out[0]=swStick(g_in.lx,false); out[1]=swStick(g_in.ly,true); out[2]=swStick(g_in.rx,false); out[3]=swStick(g_in.ry,true); out[4]=psHatNibble(b)|psFaceNibble(b); out[5]=psShouldersByte(b); static uint8_t ctr=0; out[6]=((ctr++&0x0F)<<4)|((b&TB_TOUCH||b&TB_LPADC||b&TB_RPADC)?0x02:0)|((b&TB_STEAM)?0x01:0); out[7]=g_in.lt; out[8]=g_in.rt; out[12]=g_in.gx&0xFF; out[13]=g_in.gx>>8; //pitch out[14]=g_in.gy&0xFF; out[15]=g_in.gy>>8; //roll out[16]=(-g_in.gz)&0xFF; out[17]=(-g_in.gz)>>8; //yaw out[18]=g_in.ax&0xFF; out[19]=g_in.ax>>8; out[20]=g_in.ay&0xFF; out[21]=g_in.ay>>8; out[22]=g_in.az&0xFF; out[23]=g_in.az>>8; out[29]=DS4_STATUS_USB; if(lTouch || rTouch){ uint16_t lx, ly, rx, ry; steamPadsToTouch(b, DS4_TOUCH_H, g_in.lpx, g_in.lpy, g_in.rpx, g_in.rpy, &lx, &ly, &rx, &ry); static uint8_t tstamp=0; out[32]=1; out[33]=tstamp++; touchPackPads(out+34, lTouch, rTouch, lx, ly, rx, ry); } else { out[32]=0; touchPackPads(out+34, false, false, 0, 0, 0, 0); // contact 0x80 -- memset(0) reads as touch @0,0 } } In mode_switch_pro.cpp: static void switchProBuild(uint8_t out[63]){ memset(out,0,63); jcInputPrefix(out); for(int k=0;k<3;k++){ int o=12+k*12; out[o+0]=g_in.ax&0xFF; out[o+1]=g_in.ax>>8; out[o+2]=g_in.ay&0xFF; out[o+3]=g_in.ay>>8; out[o+4]=g_in.az&0xFF; out[o+5]=g_in.az>>8; out[o+6]=g_in.gy&0xFF; out[o+7]=g_in.gy>>8; //roll out[o+8]=(-g_in.gx)&0xFF; out[o+9]=(-g_in.gx)>>8; //pitch out[o+10]=g_in.gz&0xFF; out[o+11]=g_in.gz>>8; //yaw } } <!-- Failed to upload "mode_ps5.cpp" --> <!-- Failed to upload "mode_hidgyro.cpp" --> <!-- Failed to upload "mode_switch_pro.cpp" -->
Author
Owner

@safijari commented on GitHub (Jun 16, 2026):

ok, I can integrate this but it would be really nice if you can tell me how to test this in a consistent way

<!-- gh-comment-id:4721401251 --> @safijari commented on GitHub (Jun 16, 2026): ok, I can integrate this but it would be really nice if you can tell me how to test this in a consistent way
Author
Owner

@Fob0bqAd34 commented on GitHub (Jun 16, 2026):

When I map a controller in steam or JoyShockMapper custom curve no matter the controller the the gyro always behaves the same. ie. regardless of whether I use an original steam controller, new steam controller, ds4 or Aolion j20 pro(third party switch controller in switch mode) the same gyro setup in steam would result in the same cursor movement from the same controller movements.

To test in steam:

  • Pick a game
  • Make a layout for the controller(you can use a blank template).
  • Set "Gyro Behavior" to "Gyro To Mouse"
  • In "Gyro To Mouse Settings"(any settings should work here but I'll give what I used):
    -set "Choose Gyro Button" to the south face button e.g. Cross for playstation or B for nintendo
    -set "3DOF to 2D conversion style" to "Yaw + Roll"
    -set "Roll Axis Contribution" to -100%.
    -everything else left at default
    -Test that regardless of the controller mode you are in the same steam settings result in the same movement

Currently in the main branch of OpenPuck the modes ds4, dualsense and switch + gyro all move differently with the same gyro settings in steam which I think is unexpected because every other supported controller I have tried moves the same. If no one else has mentioned it maybe this expectation is wrong or there is something peculiar I have done.

<!-- gh-comment-id:4722716993 --> @Fob0bqAd34 commented on GitHub (Jun 16, 2026): When I map a controller in steam or JoyShockMapper custom curve no matter the controller the the gyro always behaves the same. ie. regardless of whether I use an original steam controller, new steam controller, ds4 or Aolion j20 pro(third party switch controller in switch mode) the same gyro setup in steam would result in the same cursor movement from the same controller movements. To test in steam: - Pick a game - Make a layout for the controller(you can use a blank template). - Set "Gyro Behavior" to "Gyro To Mouse" - In "Gyro To Mouse Settings"(any settings should work here but I'll give what I used): -set "Choose Gyro Button" to the south face button e.g. Cross for playstation or B for nintendo -set "3DOF to 2D conversion style" to "Yaw + Roll" -set "Roll Axis Contribution" to -100%. -everything else left at default -Test that regardless of the controller mode you are in the same steam settings result in the same movement Currently in the main branch of OpenPuck the modes ds4, dualsense and switch + gyro all move differently with the same gyro settings in steam which I think is unexpected because every other supported controller I have tried moves the same. If no one else has mentioned it maybe this expectation is wrong or there is something peculiar I have done.
Author
Owner

@Fob0bqAd34 commented on GitHub (Jun 16, 2026):

If you use joyshockmapper custom curve(using SDL) the following for a config.txt might be easier than setting up different controllers in steam.

Required Settings

RESET_MAPPINGS
TELEMETRY_ENABLED = ON
TELEMETRY_PORT = 8974

Gyro Behavior

REAL_WORLD_CALIBRATION = 151.5
IN_GAME_SENS = 1.2
GYRO_SPACE = YAW_PLUS_ROLL
TICK_TIME = 1

Noise & Steadying

GYRO_CUTOFF_SPEED = 0

Sensitivity

ROLL_CONTRIBUTION = -100
GYRO_SENS = 1 1

Face Buttons

GYRO_ON = S

<!-- gh-comment-id:4722777275 --> @Fob0bqAd34 commented on GitHub (Jun 16, 2026): If you use joyshockmapper custom curve(using SDL) the following for a config.txt might be easier than setting up different controllers in steam. # Required Settings RESET_MAPPINGS TELEMETRY_ENABLED = ON TELEMETRY_PORT = 8974 # Gyro Behavior REAL_WORLD_CALIBRATION = 151.5 IN_GAME_SENS = 1.2 GYRO_SPACE = YAW_PLUS_ROLL TICK_TIME = 1 # Noise & Steadying GYRO_CUTOFF_SPEED = 0 # Sensitivity ROLL_CONTRIBUTION = -100 GYRO_SENS = 1 1 # Face Buttons GYRO_ON = S
Author
Owner

@Fob0bqAd34 commented on GitHub (Jun 16, 2026):

gyro only test OpenPuck.txt

<!-- gh-comment-id:4722783588 --> @Fob0bqAd34 commented on GitHub (Jun 16, 2026): [gyro only test OpenPuck.txt](https://github.com/user-attachments/files/29018490/gyro.only.test.OpenPuck.txt)
Author
Owner

@Fob0bqAd34 commented on GitHub (Jun 16, 2026):

For some reason files with a .txt extension work but not as .cpp. These are the files I changed:

mode_ps5.cpp.txt
mode_hidgyro.cpp.txt
mode_switch_pro.cpp.txt

<!-- gh-comment-id:4722808500 --> @Fob0bqAd34 commented on GitHub (Jun 16, 2026): For some reason files with a .txt extension work but not as .cpp. These are the files I changed: [mode_ps5.cpp.txt](https://github.com/user-attachments/files/29018573/mode_ps5.cpp.txt) [mode_hidgyro.cpp.txt](https://github.com/user-attachments/files/29018574/mode_hidgyro.cpp.txt) [mode_switch_pro.cpp.txt](https://github.com/user-attachments/files/29018575/mode_switch_pro.cpp.txt)
Author
Owner

@safijari commented on GitHub (Jun 17, 2026):

Can you suggest a game or app that I can test these with (like not by seeing the numbers but something where the view moves around with the controller gyro inputs)

<!-- gh-comment-id:4736385579 --> @safijari commented on GitHub (Jun 17, 2026): Can you suggest a game or app that I can test these with (like not by seeing the numbers but something where the view moves around with the controller gyro inputs)
Author
Owner

@Fob0bqAd34 commented on GitHub (Jun 18, 2026):

Any game that has mouse look would work for gyro to mouse e.g. Overwatch(it is free and has a practice range where things can be tested). For native in game gyro support I believe Fortnite has the best support.

<!-- gh-comment-id:4739578898 --> @Fob0bqAd34 commented on GitHub (Jun 18, 2026): Any game that has mouse look would work for gyro to mouse e.g. Overwatch(it is free and has a practice range where things can be tested). For native in game gyro support I believe Fortnite has the best support.
Author
Owner

@safijari commented on GitHub (Jun 18, 2026):

Oh I see you're testing with Steam input. I thought you were seeing that in games with native support.

I actually tested Fortnite and found that it can't find the controllers. I will look at it more deeply.

<!-- gh-comment-id:4741799227 --> @safijari commented on GitHub (Jun 18, 2026): Oh I see you're testing with Steam input. I thought you were seeing that in games with native support. I actually tested Fortnite and found that it can't find the controllers. I will look at it more deeply.
Author
Owner

@safijari commented on GitHub (Jun 18, 2026):

Please test against #59

<!-- gh-comment-id:4744709414 --> @safijari commented on GitHub (Jun 18, 2026): Please test against #59
Author
Owner

@Fob0bqAd34 commented on GitHub (Jun 18, 2026):

All seems good now, thanks.

<!-- gh-comment-id:4746792433 --> @Fob0bqAd34 commented on GitHub (Jun 18, 2026): All seems good now, thanks.
Sign in to join this conversation.
No labels
pull-request
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
safijari/openpuck#17
No description provided.