Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Resolved 2 Arduino's Leonardo as joystick inputs, only one inputs it's axis.

Discussion in 'Input System' started by koen-rm, Oct 5, 2021.

  1. koen-rm

    koen-rm

    Joined:
    Jan 4, 2021
    Posts:
    10
    For a vehicle based project we are using 2 Arduino's. They are set up using a joystick library and get their inputs in Windows are fine.
    When using on or the other the inputs in Unity are also correct but when both are plugged in some of the axis inputs are broken. The Stick/x and Stick/y inputs are fine, but the Z, Rx, Ry and Rz of only one of the arduino's gets read into the new input system.
    The arduino's are setup so that there is no overlap between these axis, Rx and Ry are for pedals on one board while Rz is used for the steering wheel on the other.

    When reading the raw values from the Joystick component I can see that the input of one board is set to 0 for all axis.
    Code (CSharp):
    1.         for (int i = 0; i < Joystick.all.Count; i++)
    2.         {
    3.             Debug.Log($"stick {i}: " + Joystick.all[i].name);
    4.             Debug.Log("Z :" + Joystick.all[i].allControls.Where((x) => x.displayName == "Z").FirstOrDefault().ReadValueAsObject().ToString());
    5.             Debug.Log("Rx :" + Joystick.all[i].allControls.Where((x) => x.displayName == "Rx").FirstOrDefault().ReadValueAsObject().ToString());
    6.             Debug.Log("Ry :" + Joystick.all[i].allControls.Where((x) => x.displayName == "Ry").FirstOrDefault().ReadValueAsObject().ToString());
    7.             Debug.Log("Rz :" + Joystick.all[i].allControls.Where((x) => x.displayName == "Rz").FirstOrDefault().ReadValueAsObject().ToString());
    8.         }
    When I disable and re-enable both the Joysticks the raw input seems fine, yet the input manager doesn't seem to get the correct values.

    I was thinking that this might be something to do with Joystick.current, that one of them doesn't get said to current when the axis value is changed, but using MakeCurrent() doesn't seem to be doing much for them. The weirdest part to me is that all the button inputs work fine regardless of what arduino is the current one.

    I'm currently on Unity 2020.3.8f1 and input system version 1.0.2
     
  2. dmytro_at_unity

    dmytro_at_unity

    Unity Technologies

    Joined:
    Feb 12, 2021
    Posts:
    212
    Try upgrading to input system 1.1.1 and see if this changes anything (probably will need to update manifest.json line in your project to 1.1.1)

    Also in input debugger you can double click on a device and then double click on events in the trace to see raw bytes, are they valid for both devices?
     
  3. koen-rm

    koen-rm

    Joined:
    Jan 4, 2021
    Posts:
    10
    Just tried updating to 1.1.1, and sadly nothing changed.
    upload_2021-10-5_15-55-44.png
    These are the values I get from de debug view, the rz on the left is the steering wheel is getting correct input, the rx, ry and z on the right are also getting their correct inputs here. But when asked from the input manager the values are wrong.
     
  4. dmytro_at_unity

    dmytro_at_unity

    Unity Technologies

    Joined:
    Feb 12, 2021
    Posts:
    212
    > But when asked from the input manager the values are wrong.

    You mean UnityEngine.Input? Or how do you ask for the values?
     
  5. koen-rm

    koen-rm

    Joined:
    Jan 4, 2021
    Posts:
    10
    I'm using an input action asset and reading the value in script during FixedUpdate().

    Code (CSharp):
    1. float arduinoGas = InputManager.instance.Controls.TruckInteractions.GasArduino.ReadValue<float>();
    upload_2021-10-5_16-12-23.png
     

    Attached Files:

  6. dmytro_at_unity

    dmytro_at_unity

    Unity Technologies

    Joined:
    Feb 12, 2021
    Posts:
    212
    Try reading from normal update and check that input system update setting set to dynamic.
    We might have a bag of other issues in FixedUpdate so it would be good to bisect this first.

    Your input asset screenshot looks like you're binding to Ry, but that only binds to one axis, what is your expectation there? To bind to two axis and threat them as one?
     
  7. koen-rm

    koen-rm

    Joined:
    Jan 4, 2021
    Posts:
    10
    Update was already set to Process Events in Dynamic update.
    I get the same issue in Update. Also tried it in a clean project, a build and a build on another computer. The problem persist throughout.

    Original project:
    upload_2021-10-6_9-1-6.png

    New project:
    upload_2021-10-6_9-3-39.png

    Code I used to print in original project:
    Code (CSharp):
    1.     private void Update()
    2.     {
    3.         Debug.Log($"gas/Ry: {InputManager.instance.Controls.TruckInteractions.GasArduino.ReadValue<float>()}");
    4.         Debug.Log($"break/Rx: {InputManager.instance.Controls.TruckInteractions.BreakArduino.ReadValue<float>()}");
    5.         Debug.Log($"MastLift/Z: {InputManager.instance.Controls.TruckInteractions.MastLiftArduino.ReadValue<float>()}");
    6.         Debug.Log($"Steering/Rz: {InputManager.instance.Controls.TruckInteractions.SteeringArduino.ReadValue<float>()}");
    7.     }
    Maybe I've used the wrong terminology here, sorry if I did.
    The idea here is that the Ry is used for a gas pedal that has a range from -1 to 1.
    Rz is the steering wheel, Rx is the break and Z is a minilever that moves back and forth.
    The Joystick x and y axis are 2 more minilevers, but they seem to be working fine all the time.
     
  8. koen-rm

    koen-rm

    Joined:
    Jan 4, 2021
    Posts:
    10
    I tried changing the Arduino to a Gamepad instead of a Joystick hoping that would change anything.
    But the autogenerated HID doesn't match the HID that gets generated in the Arduino INO file.
    upload_2021-10-6_11-52-32.png
    upload_2021-10-6_11-53-26.png
    It's not picking up as a gamepad in Unity and I also disabled the Hat switches yet they still show up in the input Debugger.

    EDIT: I looked in the HID.cs code and it seems like the arduino showing op as a Joystick is intended behaviour. If I print all the controls from the Joystick object it also seens that it does not have the hatswitches in there anymore.
     
    Last edited: Oct 6, 2021
  9. dmytro_at_unity

    dmytro_at_unity

    Unity Technologies

    Joined:
    Feb 12, 2021
    Posts:
    212
    If I understand correctly, you have two separate devices with same layout, and you also have one action with one binding, it obviously getting resolved to only one of the devices. To support two devices bound to same action you need an action with two separate bindings.

    Could be wrong regarding what is the problem here though
     
  10. koen-rm

    koen-rm

    Joined:
    Jan 4, 2021
    Posts:
    10
    I don't fully understand your answer here. I have two Arduino Leonardo's that are setup as joysticks(gamepads). Their layout is indeed the same, but their buttons and axis don't overlap. So the steering arduino has the steering wheel on Rz and the other board doesn't set any data to Rz.

    Code (CSharp):
    1.  for (int i = 0; i < Joystick.all.Count; i++)
    2.             {
    3.                 // Look if it's the pedal/minilever arduino
    4.                 float? miniLever = Joystick.all[i].allControls.Where((x) => x.displayName == "Button 32").FirstOrDefault()?.ReadValueAsObject() as float?;
    5.                 Debug.Log(miniLever);
    6.                 if (miniLever.HasValue && miniLever.Value == 1)
    7.                 {
    8.                     Rx = Joystick.all[i].allControls.Where((x) => x.displayName == "Rx").FirstOrDefault() as AxisControl;
    9.                     Ry = Joystick.all[i].allControls.Where((x) => x.displayName == "Ry").FirstOrDefault() as AxisControl;
    10.                     Z = Joystick.all[i].allControls.Where((x) => x.displayName == "Z").FirstOrDefault() as AxisControl;
    11.  
    12.                     setup = true;
    13.                 }
    14.                 else
    15.                 {
    16.                     Rz = Joystick.all[i].allControls.Where((x) => x.displayName == "Rz").FirstOrDefault() as AxisControl;
    17.                 }
    18.             }
    19.  
    20.             if (setup)
    21.             {
    22.                 if (Ry != null)
    23.                     Controls.Arduion.TestThrottle.AddBinding(Ry);
    24.                 if (Z != null)
    25.                     Controls.Arduion.TestZ.AddBinding(Z);
    26.                 if (Rx != null)
    27.                     Controls.Arduion.TestZ.AddBinding(Rx);
    28.                 if (Rz != null)
    29.                     Controls.Arduion.TestRx.AddBinding(Rz);
    30.             }
    I've tried to setup the binding in code, I set an unused button on one of the boards so I know with what board I'm dealing. But this doesn't seem to be solving it either.

    upload_2021-10-7_9-13-27.png

    If I want to add a binding I have only one Arduino to add a binding off. Do I need to add the same binding twice then?
     
  11. dmytro_at_unity

    dmytro_at_unity

    Unity Technologies

    Joined:
    Feb 12, 2021
    Posts:
    212
    Ok, I assume device support works fine from what you're writing as input system sees two separate device instances and the corresponding input controls seem to work as intendent in input debugger.

    What you're probably hitting is when you add an action with a binding, we resolve a binding to a device, not a control.
    So in this case all your bindings resolve to first device in the chain, and second is ignored? It's like binding to "<Gamepad>/leftStick" and plugging in two gamepads, the input system will only bind to first gamepad and input from second will be only activated if second gamepad somehow becomes current, e.g. a button was pressed or something else.

    I think the solution here is to use wildcard bindings, e.g. to bind on Rz on _all_ devices at a same time, you should be able to achieve this by changing your binding to "*/Rz" and then in runtime in input debugger you should see the action being bound to both devices at a same time
     
  12. koen-rm

    koen-rm

    Joined:
    Jan 4, 2021
    Posts:
    10
    First of all, thank you for all the help already. It's been quite the week getting this to work.

    upload_2021-10-7_14-3-57.png
    You mean like this? It was already doing this before.

    It might be depending on wich controller is current, but then I wonder why the X and Y axis are still being registered even if that controller isn't the current one.

    Using wildcards doesn't seem to do anything diffrently.
    upload_2021-10-7_14-5-16.png

    I found a solution that partialy works for me, but it's without using the new input system and breaks part of the UI interactions because of that.

    I get the the controllers in Update because the first few frames the values aren't correct yet, once I get the valid values in I use properties to get the values when I need them.
    Code (CSharp):
    1. for (int i = 0; i < Joystick.all.Count; i++)
    2.   private void Update()
    3.     {
    4.          if (!_setup)
    5.         {
    6.             {
    7.                 // Look if it's the pedal/minilever arduino
    8.                 float? miniLever = Joystick.all[i].allControls.Where((x) => x.displayName == "Button 32").FirstOrDefault()?.ReadValueAsObject() as float?;
    9.                 //Debug.Log(miniLever);
    10.  
    11.                 // Setup correct Axis
    12.                 if (miniLever.HasValue && miniLever.Value == 1)
    13.                 {
    14.                     Rx = Joystick.all[i].allControls.Where((x) => x.displayName == "Rx").FirstOrDefault() as AxisControl;
    15.                     Ry = Joystick.all[i].allControls.Where((x) => x.displayName == "Ry").FirstOrDefault() as AxisControl;
    16.                     Z = Joystick.all[i].allControls.Where((x) => x.displayName == "Z").FirstOrDefault() as AxisControl;
    17.  
    18.                     _setup = true;
    19.                 }
    20.                 else
    21.                 {
    22.                     Rz = Joystick.all[i].allControls.Where((x) => x.displayName == "Rz").FirstOrDefault() as AxisControl;
    23.                 }
    24.             }
    25.   }
    Code (CSharp):
    1.  AxisControl Rx;
    2.     AxisControl Ry;
    3.     AxisControl Rz;
    4.     AxisControl Z;
    5.  
    6.     public float Brake
    7.     {
    8.         get
    9.         {
    10.             if (_setup)
    11.                 return Rx.ReadValue();
    12.             else
    13.                 return -1;
    14.         }
    15.     }
    16.  
    17.     public float Gas
    18.     {
    19.         get
    20.         {
    21.             if (_setup)
    22.                 return Ry.ReadValue();
    23.             else
    24.                 return 1;
    25.         }
    26.     }
    27.  
    28.     public float Lift
    29.     {
    30.         get
    31.         {
    32.             if (_setup && !DisableLift)
    33.                 return Z.ReadValue();
    34.             else
    35.                 return 0;
    36.         }
    37.     }
    38.  
    39.     public bool DisableLift = false;
    40.  
    41.     public float Steering
    42.     {
    43.         get
    44.         {
    45.             if (_setup)
    46.                 return Rz.ReadValue();
    47.             else
    48.                 return 0;
    49.         }
    50.     }
     
    Last edited: Oct 7, 2021
  13. dmytro_at_unity

    dmytro_at_unity

    Unity Technologies

    Joined:
    Feb 12, 2021
    Posts:
    212
    Tbh I'm out of ideas and can't really help further without being able to debug it :(

    My only idea so far is that somehow one device overtakes another one and either action binding resolution takes precedence over one, or action value conflict resolution ignores data from one or another. You can try debugging it yourself by placing breakpoints in ProcessControlStateChange and ShouldIgnoreControlStateChange and seeing if first of all, does data from both devices get there, and if yes, why it decides to ignore one.

    Another approach could be to avoid the problem in a first place, from my understanding what you have is two devices that look the same from VID/PID perspective, but really are two independent types of input (wheel and pedals). You can just assign a usage type to both (so one is {Wheel} and another is {Pedals}) and make input binding include the usage, that way you can isolate inputs before they even get to actions, that should be more sound design over all.
     
  14. koen-rm

    koen-rm

    Joined:
    Jan 4, 2021
    Posts:
    10
    How would you go about doing that?
     
  15. dmytro_at_unity

    dmytro_at_unity

    Unity Technologies

    Joined:
    Feb 12, 2021
    Posts:
    212
    Listen to devices (onDeviceChange), identify which one is which, use InputSystem.SetDeviceUsage to set usages.
    Then if you press "T" in asset editor you can manually type binding as a string to something like "<Joystick>{MyUsage}/Rz"

    Usages are custom tags on devices, so that way you can separate them.
     
  16. koen-rm

    koen-rm

    Joined:
    Jan 4, 2021
    Posts:
    10
    upload_2021-10-8_9-6-5.png
    This seemed to have worked like a charm in editor, thank you!

    My only issue now is how can I be certain wich controller is wich? Is there a constant in the data on wich I can check?
    For the moment I check the DeviceID, but this seems to changed when the controllers gets plugged in again.

    The documentation on this states:
    Remarks
    This is only assigned once a device has been added to the system. No two devices will receive the same ID and no device will receive an ID that another device used before even if the device was removed. The only exception to this is if a device gets re-created as part of a layout change. For example, if a new layout is registered that replaces the Mouse layout, all Mouse devices will get recreated but will keep their existing device IDs.

    IDs are assigned by the input runtime.​

    So for this I check the unused button again from my previous posts and that seems to be able to identify wich controller is wich.

    After further testing this doesn't seem to work in build.
    upload_2021-10-8_10-5-52.png
    On start the device is not there. When losing and then getting back into focus the game picks up the controllers but on the Enabled state the Value of the button is for both controllers 0.
     
    Last edited: Oct 8, 2021
  17. dmytro_at_unity

    dmytro_at_unity

    Unity Technologies

    Joined:
    Feb 12, 2021
    Posts:
    212
    Hm, device id is just an ever-increasing number for every device connected.

    Now I wonder now what is easier (sorry :D) because to correctly identify a device it seems like you either need to listen data from device and see if pedal is pressed or not, or separate them by USB PID, or add an extra bit somewhere (in HID descriptor maybe?). You can achieve this by listening to device state changes, and if Rz is changed than assign "pedal" usage to it and so on. That way you will postpone binding resolution until devices are used which might and might not be problematic

    Though on another hand figuring out why two bindings on same action didn't work feels like kinda same amount of work.
     
  18. koen-rm

    koen-rm

    Joined:
    Jan 4, 2021
    Posts:
    10
    I think I've got it to a somewhat stable now. I do need to tab in and out of the application for it to work though.
    When I just start the app it looks like this:
    upload_2021-10-8_10-11-4.png
    Once I retabbed you can see OnDeviceChange worked:
    upload_2021-10-8_10-11-12.png

    I had to add a small delay because on Enabled the correct input wasn't there yet.
    Code (CSharp):
    1.     private void InputSystem_onDeviceChange(InputDevice arg1, InputDeviceChange arg2)
    2.     {
    3.         Debug.Log($"DeviceName: {arg1.displayName}; DeviceId: {arg1.deviceId}; InputChange: {arg2};");
    4.         OnDeviceChange.text += $"DeviceName: {arg1.displayName}; DeviceId: {arg1.deviceId}; InputChange: {arg2};";
    5.  
    6.         if (arg1.displayName == "Arduino Leonardo" && (arg2 == InputDeviceChange.Added || arg2 == InputDeviceChange.Enabled))
    7.         {
    8.             StartCoroutine(WaitForSettingDevice(arg1));
    9.         }
    10.         OnDeviceChange.text += "\n";
    11.     }
    12.  
    13.     private IEnumerator WaitForSettingDevice(InputDevice arg1)
    14.     {
    15.         yield return new WaitForSeconds(0.25f);
    16.  
    17.         float? btnValue = arg1.allControls.Where((x) => x.displayName == "Button 32").FirstOrDefault()?.ReadValueAsObject() as float?;
    18.         if (btnValue.HasValue)
    19.         {
    20.             Debug.Log($"Value: {btnValue.Value}");
    21.             OnDeviceChange.text += $"Value: {btnValue.Value};";
    22.             if (btnValue.Value == 1)
    23.             {
    24.                 InputSystem.SetDeviceUsage(arg1, "Pedals");
    25.             }
    26.             else
    27.             {
    28.                 InputSystem.SetDeviceUsage(arg1, "Wheel");
    29.             }
    30.         }
    31.     }
    To get it working from the moment the application opens I set them on Awake. Looping over all the connected Joysticks.
    Code (CSharp):
    1.         foreach(Joystick j in Joystick.all)
    2.         {
    3.             StartCoroutine(WaitForSettingDevice(j));
    4.         }
     
    Last edited: Oct 8, 2021
  19. Fenrisul

    Fenrisul

    Joined:
    Jan 2, 2010
    Posts:
    618
    Chiming in here because this has been the bane of my existence this week...

    Using USB PID is non-viable for things like Flight Sim setups where you may have 2 or more identical devices that need to be consistently mapped to actions and is impractical to do on App Start because Unity decided to enumerate the devices in whatever F***ing order it wanted to instead of respecting the Windows DirectInput priority list. The correct way to do this either respect the priority list or enumerate by USB physical Port ID. </rant>
     
    koen-rm likes this.
  20. dmytro_at_unity

    dmytro_at_unity

    Unity Technologies

    Joined:
    Feb 12, 2021
    Posts:
    212
    @Fenrisul our backend on Windows for HID stuff is RawInput, not DirecInput, so enumeration is a bit tricky. And not all devices give us something meaningful to identify them, e.g. if you plug two gamepads of the same model, unless we get a serial number, there is not much to differentiate them next time app is running.

    Do you have suggestions what we should do here instead? I understand this is a tricky configuration situation for sims in general
     
  21. Fenrisul

    Fenrisul

    Joined:
    Jan 2, 2010
    Posts:
    618
    You can get physical port # from the USB Location Information field from the parent of an HID instance.

    upload_2021-10-8_11-4-19.png

    These typically will persist beyond rebooting unless USB hub/mobo firmware changes them for some reason. You should be able to enumerate and associate otherwise identical devices with physical ports this way.

    Even exposing more of the USB device information through InputSystem would allow developers to do it however they like themselves.
     
    dmytro_at_unity likes this.
  22. dmytro_at_unity

    dmytro_at_unity

    Unity Technologies

    Joined:
    Feb 12, 2021
    Posts:
    212
    Agree, that is good idea, and if you plug a device in another port, it might be reasonable to expect to need to remap keys in game settings afterwards. We could probably extend our device descriptor to include a port if known.

    It seems like that is a bit more involved on Windows, we currently use HID API from https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/_hid/ specifically
    HidD_GetAttributes
    and
    HidP_GetCaps
    to get device info, obviously this two won't tell us which hardware port device is connected to.
    SetupDiEnumDeviceInterfaces
    should be able to do it, but don't see a clear way to associate one with another.
     
  23. Fenrisul

    Fenrisul

    Joined:
    Jan 2, 2010
    Posts:
    618
    https://docs.microsoft.com/en-us/wi...nf-setupapi-setupdigetdeviceregistrypropertya
    https://www.pinvoke.net/default.aspx/setupapi.SetupDiGetDeviceRegistryProperty

    It's been a bit, but setupapi should be able to grab most of what you need including the device Parent instance ID to go from HID to USB with the appropriate Location Information field.

    It's worth mentioning that the Location Information isn't necessarily *physical* but rather defined by the system registry upon first plugging devices in (ie: if you plug/unplug/update windows/reboot with the device unplugged) you could potentially lose the consistent mapping.

    @dmytro_at_unity do we want to start another thread?
     
  24. dmytro_at_unity

    dmytro_at_unity

    Unity Technologies

    Joined:
    Feb 12, 2021
    Posts:
    212
    @Fenrisul yes another thread would be nice, I need to check
    SetupDiGetDeviceRegistryProperty


    Though it feels like it's something to be opt-in rather opt-out, not sure if we want every Windows game made with Unity to deeply inspect connected USB devices :) The usecase seems to make much more sense for sims games if I understand correctly?
     
  25. Fenrisul

    Fenrisul

    Joined:
    Jan 2, 2010
    Posts:
    618
    Honestly it applies to any situation where you need to pair a device input to an action and have that device stick with that action. That was the whole point of the new InputSystem's "Action" obfuscation... Right now its almost entirely ephemeral and is forgotten on app-restart, with even rebinding being in an absolute shambles "Do it yourself with JSON" state at 1.0 release (read: for over a year now). Beyond sims, it has extreme importance in kiosks, deployed installations, arcade cabinets, etc. Good old XInput (or Windows Game Center) and Wii U had a concept of "Player 1's Controller" built into the drivers themselves, but obviously that doesn't cover all the bases that InputSystem is built for.

    An opt-in checkbox to make Unity consistently enumerate devices at least as consistently as the Windows 95 "USB Game Controllers" with its modifiable priority list (through DInput) would be a huge step forward.

    For total clarity - Input System fails at consistently mapping situations where more than one of the same device is plugged in. There are many cases where a user will have more than one of the same type of input device plugged in even for regular gaming uses (*ESPECIALLY* in the field of Disability compliance).
     
  26. dmytro_at_unity

    dmytro_at_unity

    Unity Technologies

    Joined:
    Feb 12, 2021
    Posts:
    212
    Hey I do agree it's an important use case, I'm just trying to figure out if it's 1) possible to do on all platforms we support, e.g. is it possible on iOS? on PS5? on Stadia? on UWP? etc 2) is it gonna do something that end users (gamers) might not like, scanning all USB ports can be seen as collecting way too much data about the system (it's also probably slow), so a better solution would be if we somehow could get info about device directly via HidP handles.

    I see on Android there is
    InputDevice.getDescriptor()
    precisely for this.
     
  27. Fenrisul

    Fenrisul

    Joined:
    Jan 2, 2010
    Posts:
    618
    I would argue that there are different paradigms for different platforms. Bluetooth based platforms (consoles, mobiles, etc) you have unique radio hardware addresses for every single device, but in general the user workflow for input devices in those cases *are* ephemeral and users are expected to "Press A to Join" while being prompted.

    Off the top of my head... if you wanted to implement a "Device priority list" for...

    Android/iOS - basing it on the date/timestamp the device was paired, but manual is OK too because every bluetooth peripheral has a radio HWID. Most applications here don't use wired devices.
    Stadia - Doesn't really support multi input device (yet!?) and each device is literally treated like another instance on a cloud server.
    Consoles - who boy...
    PlayStation Ecosystem - One Gamepad = One Sony Account, period. Wired peripherals are few and far between here, and I'm not actually aware of any that support splitscreen+multi-race-wheel configurations, and if they did it would likely follow the "Press A to Join" paradigm by requirement from Sony's QA process.
    XBox Ecosystem - XBox controllers are enumerated in the order in which they join. Difficulty is PlayStation-lite, and has a similar QA requirement.
    Switch - has its own "intuitive" way to decide which player uses what device mapping system, just respect that one.
    WebGL - whenever Chrome and Mozilla agree on what input looks like here, agree with them too. Too much trouble. Can adopt the "Press A to Join".
    MacOS - Mmm, can't honestly say I know enough about modern MacOS' exposed HID stuff.
    Windows (non UWP)
    • Old Unity Input Manager simply respected the Windows USB Game Controller list, which does understand registry based Hub/Port mapping. New InputSystem has lost this feature entirely.
    • Resolving Bluetooth HWID to device # could work, but same as before the common use case for battery powered wireless devices is still "Press A to Join".
    Linux - Should be doable the same way as Windows on most Linux flavors, given certain OS permissions.

    XR - I have completely given up hope on Unity XR and Open XR being able to be as good as a per-ecosystem implementation any time in the near future. OpenVR/SteamVR's implementation is extremely rich and diverse and just better than the rest. I have a project that uses upward of 25 Vive Trackers/devices simultaneously on the same system and there is zero support for uniquely mapping them through InputSystem. Can't use USB port/hub, can't use wireless unique HWID, can't use OpenVR supplied DeviceSerial.

    Sorry if I come off as brash on this topic - its been an up hill battle lobbying Unity to make any progress on input for a very, very long time.
     
    dmytro_at_unity likes this.