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

"HID write failed: {}" error shows up after connecting gamepad in browser #398

Open
HitBox38 opened this issue Oct 30, 2023 · 21 comments
Open
Assignees

Comments

@HitBox38
Copy link

HitBox38 commented Oct 30, 2023

Describe the bug
After granting permission to connect a DualSense gamepad using controller.hid.provider.getRequest() the following error shows up:
image

To Reproduce
Package version: 5.1.8
steps:

  1. Connect gamepad with Bluetooth.
  2. Grant permission using controller.hid.provider.getRequest().

Expected behavior
Successful connection with no errors.

Additional context

  • Using in a React (18.2.0) TypeScript (5.0.2) app built with Vite (4.4.5).
  • Used ReactContext like in the documentation and zustand (4.4.4) to check if it's a problem in my end.
@nsfm
Copy link
Owner

nsfm commented Oct 30, 2023

@HitBox38 Hey, thanks for opening an issue! I'll have some time to take a closer look tonight or tomorrow and attempt to replicate. I have a few more questions,

  • What version of chrome are you using?
  • Write failures means there was some problem sending rumble or LED updates to the gamepad, are you using these features? Are other inputs working by any chance?
  • If you're not intentionally using rumble, do things work properly if you downgrade the library to 5.0.136 or 5.0.3?

@HitBox38
Copy link
Author

HitBox38 commented Oct 30, 2023

  • I'm using the latest version of Opera (104.0.4944.33 Chromium (118.0.5993.96)).
  • I didn't use any of those features yet I just tried connecting.
  • I want to use rumble but at the moment I can't connect correctly.

I will note that when checking if the controller is connected using the following useEffect I get that the connection state is true:
useEffect(() => { controller.connection.on("change", ({ state }) => setConnected(state)); }, []);

I didn't even think about the rumble or LED updates I was just trying to get button inputs to get myself familiar with this library.

@nsfm
Copy link
Owner

nsfm commented Nov 5, 2023

I tested two controllers but couldn't replicate the HID write error on 5.1.8 with Opera 104.0.4944.36 / Chromium 118.0.5993.118 or Chrome 117.0.5938.149. However the HID writes are not working in the browser anyway, so I'll push a patch to disable them. I'll also try to set up a working example on github pages.

I noticed that inputs were broken for one of the two devices... does it make a difference if you call controller.hid.provider.setWired() after constructing the controller?

edit: Nevermind, I was able to replicate it. When I include the suggested controller.hid.on("error", console.error) I also see the error. The inputs are still working as expected on one controller, and after using controller.hid.provider.setWired() on the other.

@HitBox38
Copy link
Author

HitBox38 commented Nov 5, 2023

I didn't notice that controller.hid.provider.setWired() exists, will look into it.

edit: Wait what do you mean setWired? I thought browser works only with Bluetooth.

edit 2: I don't get any inputs at all. How did you use controller.hid.provider.setWired()?
Here is my code snippet with zustand when constructing:

import { create } from "zustand";
import { Dualsense } from "dualsense-ts";

const controller = new Dualsense();

interface ControllerState {
  controller: Dualsense;
  connected: boolean;
  setConnected: (value: boolean) => void;
}

export const useControllerStore = create<ControllerState>((set) => ({
  controller,
  connected: controller.connection.state,
  setConnected: (value: boolean) => set(() => ({ connected: value })),
}));

@nsfm
Copy link
Owner

nsfm commented Nov 7, 2023

Sorry for the trouble, here's a little more background on the wired/wireless issue...

Early on I found that the format of the updates coming from the controller changed slightly depending on whether it was connected via bluetooth or USB. In Node.js there's a function for detecting the wired/wireless state, but in the browser I couldn't find a good solution. I settled for hardcoding the wireless connection style until I could come up with something.

The other day when I tested using a much newer controller, the results were different - it's sending updates in the same format regardless of whether the connection is wired/wireless.

This controller.hid.provider.setWired() function was only meant for debugging, so I didn't mention it in the docs. But calling this on the newer controller made it work as expected.

I think this might help:

import { create } from "zustand";
import { Dualsense } from "dualsense-ts";

const controller = new Dualsense();
controller.hid.provider.setWired(); // Here

interface ControllerState {
  controller: Dualsense;
  connected: boolean;
  setConnected: (value: boolean) => void;
}

export const useControllerStore = create<ControllerState>((set) => ({
  controller,
  connected: controller.connection.state,
  setConnected: (value: boolean) => set(() => ({ connected: value })),
}));

If that works, I will make this the default connection style.

@HitBox38
Copy link
Author

HitBox38 commented Nov 7, 2023

Nope still doesn't work still has the same issue wired or not.

@HitBox38
Copy link
Author

HitBox38 commented Nov 9, 2023

The other day when I tested using a much newer controller, the results were different - it's sending updates in the same format regardless of whether the connection is wired/wireless.

What's the number model on your controllers? maybe it is a compatibility issue? mine is CFI-ZCT1W.

@daniloarcidiacono
Copy link
Contributor

I get the same error, my controller is also CFI-ZCT1W

@nsfm
Copy link
Owner

nsfm commented Nov 14, 2023

@daniloarcidiacono Thank you for the extra data point 🙏🏻

Both of my controllers list "MODEL: CFI-ZCT1W" as well, but the older one has "FCC ID: AK8CFIZCT1" while the newer one shows "FCC ID: AK8CFIZCT1A". The label layout is also different. I'm going to test/prioritize the newer one from now on.

I'm almost ready with a fix, will wrap up testing and get a patch up ASAP. Apologies for the delay on this, been trying to squeeze the change in between some travel. Really appreciate that you both reached out here, thanks so much for the patience!!

@nsfm
Copy link
Owner

nsfm commented Nov 15, 2023

@daniloarcidiacono @HitBox38 I released 5.2.0 which should solve the problem, please let me know if it helps 🙏🏻

Here's an example app that should react to analog inputs: https://nsfm.github.io/dualsense-ts/

@nsfm
Copy link
Owner

nsfm commented Nov 15, 2023

Hmm, I guess the described issue technically isn't solved since the console error still appears. But at least inputs should be working as expected. Either way I'll leave this open and take another crack at webhid rumble support to deal with that.

@daniloarcidiacono
Copy link
Contributor

daniloarcidiacono commented Nov 15, 2023

Hello @nsfm thanks for the quick response, now I receive data from the controller, however the mapping seems off.
With the controller connected via Bluetooth:

  • R2, LX, LY change continuously;
  • RY is affected by dpad, triangle, square, circle, cross buttons on controller;
  • Triangle, Circle, Cross, Square, Dpad, up are affected by pressing R2 on controller;

Here's the component I use:

import { useContext, useEffect, useState } from "react";
import { DualsenseContext } from "../../context/DualsenseContext";

export const ConnectionComponent = () => {
  const controller = useContext(DualsenseContext);
  const [connected, setConnected] = useState(controller.connection.state);
  const [content, setContent] = useState('');

  useEffect(() => {
    controller.hid.on("error", console.error);
    controller.connection.on("change", ({ state }) => setConnected(state));
    
    controller.hid.register((data) => {
      setContent(JSON.stringify(controller.hid.state, null, 2));
    });
  }, []);

  return (
    <>
        <p>{`Controller ${connected ? "" : "dis"}connected`}</p>
        <pre>
          {content}
        </pre>
    </>
  );
};

Also, this is what's reported on my controller:

IMG_20231115_220058.jpg

@nsfm
Copy link
Owner

nsfm commented Nov 15, 2023

@daniloarcidiacono Oh boy, well at least it's progress...

I don't think this will solve it, but earlier I suggested using controller.hid.provider.setWired() or setWireless(), if you still have these in your code it they should be removed now.

Otherwise I think I'll need to collect some example events from your controller and compare to mine. Tonight I'll update the demo app to print this info, or add another debugging method to the library to help with future support requests.

I know there have been some software updates for the controllers but I don't have a PS5 to try them myself, so possibly even my newer controller is out of date 😅

By the way, is it possible for you to try the library in node.js? I'm curious if the same issue applies there. Either way, thanks for the quick response!

@daniloarcidiacono
Copy link
Contributor

daniloarcidiacono commented Nov 15, 2023

@nsfm, I don't call setWired nor setWireless. I've just tried with NodeJS and the behavior is the same.

Let me know how I can help you debug this issue.

@nsfm
Copy link
Owner

nsfm commented Nov 15, 2023

Appreciate it 🙏🏻 If you're up for it, the object I'd like to inspect is the buffer: DataView argument located here: https://github.com/nsfm/dualsense-ts/blob/main/src/hid/web_hid_provider.ts#L89

If you can edit this file in your node_modules to log buffer.buffer, or clone this repo+edit+build+link locally, that should give me the info I need. If possible, hold X while you capture it so I can make sure the inputs are lining up. (even better if you can capture this while connected via USB as well as bluetooth!)

@daniloarcidiacono
Copy link
Contributor

daniloarcidiacono commented Nov 15, 2023

@nsfm we had the same idea :)

process(buffer) {
      // Bluetooth buffer starts with an extra byte
      console.clear();
      console.log(buffer.toString('hex'));

My findings (connected via Bluetooth):
ps5_mapping

More in depth:

  • hex 0-1: always 0x01
  • hex 2-5: left analog position
  • hex 6-9: right analog position
  • hex 10: triangle (0x8), circle (0x4), cross (0x2), circle (0x1) buttons
  • hex 11: d-pad (0x8 = nothing, up = 0x0, right = 0x2, down = 0x4, left = 0x6)
  • hex 12: create (0x1) and menu (0x2) tiny buttons, left (0x4) and right (0x8) analog click
  • hex 13: L1 (0x1), R1 (0x2), L2 (0x4), R2 (0x8) press
  • hex 14-15: change like crazy
  • hex 16-17: L2 pressure (0x00 - 0xff)
  • hex 18-19: R2 pressure (0x00 - 0xff)

I could not "map" the playstation button, the mute button, and the touchpad.

edit: yikes, the buffer via USB is very different!

  • hex 0-1: always 0x01
  • hex 2-5: left analog
  • hex 6-9: right analog
  • hex 10-11: L2 pressure
  • hex 12-13: R2 pressure
  • hex 14-15: changes like crazy
  • hex 16: triangle, circle, cross, square buttons
  • hex 17: dpad
  • hex 18: left/right analog click, create and menu tiny buttons
  • hex 19: l1 (0x1), r1(0x2),l2(0x4),r2(0x8) press state
  • hex 20: always zero?
  • hex 21: ps button (0x1), click on touchpad (0x2), mute button (0x4)
  • ...
  • hex 66-??: related to touch pad
  • ...
  • seemingly random stuff

I wonder how the PS5 still detects the Playstation button, mute button, touchpad even via Bluetooth, seems there's more than HID?

@daniloarcidiacono
Copy link
Contributor

daniloarcidiacono commented Nov 15, 2023

It seems that the mapping is already correct for the USB case. The only thing that seems suspicius is the "Dpad" attribute which changes when pressing "Triangle", "Square", "Circle" or "Cross" (but "Up", "Down" etc... are not affected).

edit: yes, there are some issues. If I press cross + up buttons, "Up" is set to false

@nsfm
Copy link
Owner

nsfm commented Nov 15, 2023

Super helpful. If the inputs are coming in a different order now, this is gonna be fun... Any chance you can paste over the byte strings or arrays? I can use them to recreate the buffers and set up some unit tests

If it interests you there are a few extra comments around the unmapped bytes in the node HID provider: https://github.com/nsfm/dualsense-ts/blob/main/src/hid/node_hid_provider.ts#L98

For example the one that changes like crazy is probably the counter to help ensure messages are processed in the right order. The dpad and shapes buttons are all included in the same byte as individual bits, even if the format hasn't changed there's a good chance I made some mistake dealing with the masking/shifting there... But otherwise those accessory buttons and touch inputs do appear later in the buffer for me.

So far the main difference between BT/USB has been some extra bytes at the beginning so it's been enough to just offset the buffer by a few bytes. If the order of the inputs has changed, that will definitely create some headaches, but hopefully some of the static bytes act as a flag for this...

I'll also double check the dpad handling on my controllers and review the pydualsense library and linux drivers again tonight. If you play around with the hid_providers and find working arrangements for your controller, feel free to put up a PR and I'll test it on my end to see what we can do about compatibility! Decoding the buffers was equal parts fun and suffering when I worked on it last year, but I'd love to have another contributor!

nsfm added a commit that referenced this issue Nov 17, 2023
* Add basic debugging tools to the example app

* Style debugger

* Improve format

* Add overflow styling
@nsfm
Copy link
Owner

nsfm commented Nov 17, 2023

Hey @daniloarcidiacono, I just released version 5.3.0 which adds some extra debugging utilities. You can get the raw HID buffer now using controller.hid.provider.buffer

I also updated the example app: https://nsfm.github.io/dualsense-ts/

Now it will show the input states, and a buffer you can copy/paste over here that will help me recreate the problem. There's also a slider for adjusting the byte offset that gets applied to the buffer - you might be able to guess and check different values to see if it corrects the mapping issue. Would you mind trying it out and sharing some usb/bluetooth buffer examples?

I was also able to reproduce the dpad bug so I opened a new issue: #408

@nsfm
Copy link
Owner

nsfm commented Nov 17, 2023

Oh, just saw your PR 😅 I'll review

@nsfm
Copy link
Owner

nsfm commented Nov 19, 2023

@HitBox38 Thanks to @daniloarcidiacono, the input problems should be resolved in version 5.4.0 🙏🏻

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

3 participants