How to queue delta events for partial CAN bus device states?

Hello, I’m writing an custom InputDevice for a CAN bus device. In case you don’t know (I didn’t) CAN stands for controller area network and is used extensively in automotive applications. I’m using drivers, plugins, and the CANlib SDK by Kvaser to read the messages from this device into Unity. No problem there, getting messages streaming into InputSystem and even updating one StickControl of my custom InputDevice correctly.

But this CAN device does not have just one joystick. It has 5. And ~20 buttons. Therefore, it does not report its state all in one CAN message, but in about 4. So that’s where my issue arises. I get 4 messages which I filter by its messageID. Here’s an example of the two main ones:

0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

So in my code, I filter each message.

  • Message 1

  • bytes 0 and 1 go to a VC2B stick control.

  • bytes 2 and 3 go to a VC2B stick control.

  • byte 4 goes to a bunch of BIT aligned button controls

  • etc

  • Message 2

  • bytes 0 and 1 go to a VC2B stick control.

  • bytes 2 and 3 go to a VC2B stick control.

  • byte 4 goes to a bunch of BIT aligned button controls

  • etc

  • etc

Initially, I was using InputSystem.QueueStateEvent(myDevice,new MyDeviceState(){...});, just setting the fields I could at the time with the message I received. Of course, I realized that the other fields were getting set to their defaults and this was no good. So most recently, I’ve been trying to use InputSystem.QueueDeltaStateEvent(myDevice.Joystick,Vector2.zero);, but when I do that my device gets removed. Is this a bug? Am I doing it wrong?

I haven’t found many examples of using QueueDeltaStateEvent, except in this script on GitHub and this guide and I thought I did it the same way. One thing - I’m using async while loops to read messages coming in, so QueueDeltaStateEvent might be getting called on a different thread, but my understanding was that these methods were thread safe, so I don’t know. Any guidance on how I’m supposed to use this or if there is a better way to do partial state updates would be very appreciated.

Bonus Question: I can also send commands to the device to turn on/off LEDs and make beeps, etc. How do I correctly override ExecuteCommand to send these commands via my Canlib library? So far, I’ve just worked around this method by calling my library methods directly, but it would be nice to know if there was an “InputSystem way” I could hook that up.

Update: I think I figured out the answer to my bonus question. You can intercept device commands:

InputSystem.onDeviceCommand += OnCustomCommandHandler;

private unsafe long? OnCustomCommandHandler(InputDevice device, InputDeviceCommand* command)
        {
            if (device.GetType() != typeof(MyDevice)) return null; // Not our device, so do nothing with this command
            if (command->type == MyCustomCommand.Type)
            {
                //Send your device the command via your third-party API
                return InputDeviceCommand.GenericSuccess //or fail if command sent unsuccessfully
            }
            return null; // Don't have a handler for this command, so do nothing with this command
        }

As for QueueDeltaStateEvent, I’m still unsure how to do it. I see some examples where it’s used to update a StickControl with a Vector2, but not sure how to do it with 2 bytes and calling it from my async message reading loop removes the device anyway. Also don’t know what to do about buttons since you can’t queue delta events for bits.

So currently thinking I’ll build a full device state from the incoming partial states and then QueueStateEvent. Concerned I’ll miss button clicks if I don’t build and queue full state often enough, but looks to be my only option right now given the lack of delta examples and docs.

1 Like