Skip to content
This repository has been archived by the owner on Jan 31, 2022. It is now read-only.

Driver: communication protocol

Oleg V edited this page Sep 7, 2021 · 3 revisions

V1-stable

This is the version of the communication protocol currently implemented in the latest release. Also the title of this page is kind of a lie, this protocol is comprised out of 2 protocols as of writing this, device protocol and manager protocol. Both will be explained here.

Both protocols are expected to be done with TCP sockets, even more so, you are expected to essentially implement a TCP socket server which listens on 127.0.0.1:6969, the driver will connect to it twice, first with the device socket(which can be identified by the first message: "hello\n") and second with the manager socket(which can be identified by the first message: "monky\n"). Also as a reference you can take a look at the server implemented in virtualreality here

Device protocol

Is a dynamic size flat buffer of floats followed by \t\r\n sent over a TCP socket. The size is this buffer is set by the number and type of devices, which can be set either at runtime or beforehand in the driver config. More info about device types can be found here.

To aid in understand the message format of this protocol and how the said messages will be interpreted, here is an example:


Device setup: hmd, controller, controller, tracker

Device structs:

#pragma pack(push, 1)
struct hmd {
    float pos[3];  // 3D vector
    float ori[4];  // quaternion
    float vel[3];  // 3D vector
    float ang_vel[3];  // 3D vector
};

struct controller {
    float pos[3];  // 3D vector
    float ori[4];  // quaternion
    float vel[3];  // 3D vector
    float ang_vel[3];  // 3D vector

    float inputs[9];
};

struct tracker {
    float pos[3];  // 3D vector
    float ori[4];  // quaternion
    float vel[3];  // 3D vector
    float ang_vel[3];  // 3D vector
};
#pragma pack(pop)

Message struct:

#pragma pack(push, 1)
struct message {
    hmd        d1;
    controller d2;
    controller d3;
    tracker    d4;
    char terminator[3];
};

/*
the above message is only valid for device setup:
hmd, controller, controller, tracker

if you had, lets say, device setup that looks like this:
controller, tracker, tracker

your message struct would look like this:

struct message {
    controller d1;
    tracker    d2;
    tracker    d3;
    char terminator[3];
};

*/

#pragma pack(pop)

Now the size of the buffer can be acquired with a simple sizeof() and the buffer itself can be constructed like so:

message* buffer = new message;

When you're ready to send your message, provided that you have an established device socket connection to the driver on port, it can be sent with the following code:

send(fd, buffer, sizeof(*buffer), 0);
// provided that fd is the socket descriptor connected to the driver

But oh no, the driver rejected your message:

hobovr: driver: bad packet, expected 231, got 280. double check your udu settings

That happen because you didn't tell the driver the new buffer layout, which can be done in a few ways, either by changing the startup default in the driver config by adding a "driver_hobovr" section with a "uduSettings" filed in Steam's steamvr.vrsettings config file with the following content:

...
"driver_hobovr" : {
    "uduSettings" : "h13 c22 c22 t13" // h - hmd, c - controller, t - tracker
},
...

Or by updating it at runtime by sending an Emsg_uduString manager message, which will be explained in detail in the next section.

Other notes:

  • Controller handiness is based on order, all odd numbered controllers are right handed and all even numbered controllers are left handed.

  • The method of delivery and language doesn't matter, as long as its TCP sockets and the format of the messages stays the same.

Manager protocol

Is a 130 uint32_t buffer sent over a TCP socket. Out of those 130 the first sets the type of the message, which defines how the other 129 should be used.

Currently available message types:

enum HobovrManagerMsgType
{
  Emsg_invalid = 0,
  Emsg_ipd = 10,
  Emsg_uduString = 20,
  Emsg_poseTimeOffset = 30,
  Emsg_distortion = 40,
  Emsg_eyeGap = 50,
  Emsg_setSelfPose = 60,
};

Message structs corresponding to the message types:

#pragma pack(push, 1)
struct IpdMessage {
    uint32_t type;  // has to be Emsg_ipd
    uint32_t nominator;
    uint32_t denominator;
    uint32_t rest[127];
    char terminator[3];
};

struct UduStringMessage {
    uint32_t type;  // has to be Emsg_uduString
    uint32_t len; // number of devices
    struct {
        uint32_t device_type; // h - 0, c - 1, t - 2
        uint32_t device_len; // number of floats for this device
    } devices[64];
    char terminator[3];
};

struct PoseTimeOffsetMessage {
    uint32_t type;  // has to be Emsg_poseTimeOffset
    uint32_t nominator;
    uint32_t denominator;
    uint32_t rest[127];
    char terminator[3];
};

struct DistortionMessage {
    uint32_t type;  // has to be Emsg_distortion
    uint32_t k1_nominator;
    uint32_t k1_denominator;
    uint32_t k2_nominator;
    uint32_t k2_denominator;
    uint32_t zoom_width_nominator;
    uint32_t zoom_width_denominator;
    uint32_t zoom_height_nominator;
    uint32_t zoom_height_denominator;
    uint32_t rest[121];
    char terminator[3];
};

struct EyeGapMessage {
    uint32_t type;  // has to be Emsg_eyeGap
    uint32_t width; // in pixels
    uint32_t rest[128];
    char terminator[3];
};

struct SetSelfPoseMessage {
    uint32_t type;  // has to be Emsg_setSelfPose
    uint32_t x_nominator;
    uint32_t x_denominator;
    uint32_t y_nominator;
    uint32_t y_denominator;
    uint32_t z_nominator;
    uint32_t z_denominator;
    uint32_t rest[123];
    char terminator[3];
};

#pragma pack(pop)

The process of sending the messages is identical to the device messages, except with a different socket, it has to be the manager socket connection.

Conclusion

Not the best protocol in world but it works and is currently stable. However there is a work in progress V2 version of the driver protocol which is drastically different from V1 and tries to eliminate the weaknesses of the V1 as well as introduce more flexibility.

V2

coming soon™