How do I make the YEI 3-Space Sensor Bluetooth work with unity?

So I need a way for this,
YEI Sensor to work with unity. I just need for it to tell unity its location and orientation in real time, but I’m not sure how to do that. The sensor installs on the computer as a device,As shown here, but I don’t know how to tell unity to get information from it. I don’t want to buy it without knowing whether or not it can even work with unity. They have all the info for it on the website, so if someone smarter than me could look at it and determined it’s compatibly with unity that would be great.

I apologize for my lack of knowledge in this area; I’m new to both unity and any motion capture devices.

The API returns an integer that can be either a TSS_ID, TSS_ERROR, or boolean type depending on the function. The functions createTSDevice and createTSDeviceStr return a TSS_ID that is used to pass into the sensor functions.

What commands are you trying to use?

Also to use the ThreeSpace_API.dll, you will need Unity Pro because it is an unmanaged dll. If you do not have Unity Pro, you can use one of these simple scripts to connect to the sensor. The COM port name will need to be changed to the COM port your sensor is on. You will also need to make sure that Unity is set to full .Net 2.0 instead of the default subset for the script to work.


Communicating with a wired or Bluetooth 3-Space Sensor

using UnityEngine;
using System;
using System.Collections;
using System.IO.Ports;

public class cube_rotator : MonoBehaviour {
	// Connect to the serial port the 3-Space Sensor is connected to
	public static SerialPort sp = new SerialPort("\\\\.\\COM13");
	
	// Command packet for getting the filtered tared orientation as a quaternion
	// {header byte, command byte, [data bytes], checksum byte}
	// checksum = (command byte + data bytes) % 256
	public static byte[] send_bytes = {0xf7,0x00,000};
	
	// Use this for initialization
	void Start () {
		// Allow the user to set the appropriate properties.
		sp.BaudRate = 115200;
		sp.Parity = Parity.None;
		sp.DataBits = 8;
		sp.StopBits = StopBits.One;
		
		// Set the read/write timeouts
		sp.WriteTimeout = 500;
		sp.ReadTimeout = 500;
		
		sp.Open();
		
		sp.Write(send_bytes,0,3);
	}
	// Helper function for taking the bytes read from the 3-Space Sensor and converting them into a float
	float bytesToFloat(byte[] raw_bytes, int offset){
		byte[] big_bytes = new byte[4];
		big_bytes[0] = raw_bytes[offset+3];
		big_bytes[1] = raw_bytes[offset+2];
		big_bytes[2] = raw_bytes[offset+1];
		big_bytes[3] = raw_bytes[offset+0];
		return BitConverter.ToSingle(big_bytes,0);
	}
	// Update is called once per frame
	void Update () {
		// A quaternion consists of 4 floats which is 16 bytes
		byte[] read_bytes = new byte[16];
		
		// This counter is used for trying to fix a mono bug for reading from a serial port
		// The user may need to adjust the value to run properly on there system
		int counter = 50;
		int byte_idx = 0;
		while (counter > 0){
			byte_idx += sp.Read(read_bytes, byte_idx, 1);
			if (byte_idx == 16){
				break;
			}
			--counter;
		}
		// Convert bytes to floats
		float x = bytesToFloat(read_bytes,0);
		float y = bytesToFloat(read_bytes,4);
		float z = bytesToFloat(read_bytes,8);
		float w = bytesToFloat(read_bytes,12);
		
		// Create a quaternion
		Quaternion quat = new Quaternion(x,y,z,w);
		
		// Perform rotation
		transform.rotation = quat;
		
		// Send command
		sp.Write(send_bytes,0,3);
	}
}

Communicating with a Wireless 3-Space Sensor via a 3-Space Sensor Dongle

using UnityEngine;
using System;
using System.Collections;
using System.IO.Ports;

public class cube_rotator_wireless : MonoBehaviour {
	// Connect to the serial port the 3-Space Dongle is connected to
	public static SerialPort sp = new SerialPort("\\\\.\\COM3");
	
	// Wireless Command packet for getting the filtered tared orientation as a quaternion
	// {header byte,  logical id byte + command byte, [data bytes], checksum byte}
	// checksum = (logical id byte + command byte + data bytes) % 256
	public static byte[] send_bytes = {0xf8,0x00,0x00,0x00};
	
	// Use this for initialization
	void Start () {
		// Allow the user to set the appropriate properties.
		sp.BaudRate = 115200;
		sp.Parity = Parity.None;
		sp.DataBits = 8;
		sp.StopBits = StopBits.One;
		
		// Set the read/write timeouts
		sp.WriteTimeout = 500;
		sp.ReadTimeout = 500;
		
		sp.Open();
		
		sp.Write(send_bytes,0,4);
		
	}
	// Helper function for taking the bytes read from the 3-Space Sensor and converting them into a float
	float bytesToFloat(byte[] raw_bytes, int offset){
		byte[] big_bytes = new byte[4];
		big_bytes[0] = raw_bytes[offset+3];
		big_bytes[1] = raw_bytes[offset+2];
		big_bytes[2] = raw_bytes[offset+1];
		big_bytes[3] = raw_bytes[offset+0];
		return BitConverter.ToSingle(big_bytes,0);
	}
	
	// Update is called once per frame
	void Update () {
		// Wireless packets always have a confirm and logical id byte pre-appened to the data
		byte[] confirm_byte = new byte[1];
		byte[] id_byte = new byte[1];
		try{
			sp.Read(confirm_byte,0,1);
			sp.Read(id_byte,0,1);
		}
		catch{
			confirm_byte[0] = 1;
		}
		if (confirm_byte[0] == 0){
			// Read data length of packet
			byte[] length_byte = new byte[1];
			try{
				sp.Read(length_byte, 0, 1);
			}
			catch{
				return;
			}
			byte length = length_byte[0];
			
			// Read the length of the data in the packet
			byte[] read_bytes = new byte[length];
			try{
				sp.Read(read_bytes, 0, length);
			}
			catch{
				return;
			}
			
			// Convert bytes to floats
			float x = bytesToFloat(read_bytes, 0);
			float y = bytesToFloat(read_bytes, 4);
			float z = bytesToFloat(read_bytes, 8);
			float w = bytesToFloat(read_bytes, 12);
			
			// Create a quaternion
			Quaternion quat = new Quaternion(x, y, z, w);
			
			// Perform rotation
			transform.rotation = quat;
		}
		
		// Send command
		sp.Write(send_bytes,0,4);
	}
}

It seems that I somehow deleted the old answer, but this one is better suited anyways. Good to hear that your sensor is working. To do other commands, follow the comment block about the command packets located right before the declaration of send_bytes. So for wireless communication the button command it would look like this:

byte[] button_bytes = {0xf8, 0x00, 0xfa, 0xfa};

And for the tare command:

byte[] tare_bytes = {0xf8, 0x00, 0x60, 0x60};

You can find the list of commands and some more detail about the commands in the Wireless Manual.

Here are the scripts we have edited to have the button states and taring in them. They are timed based, so if you wanted to use keyboard commands you will have to look up how to capture keyboard events in Unity. There is also some bug fixes that were not apparent before that Mono has when reading from a serial port.


Communicating via USB or Bluetooth

using UnityEngine;
using System;
using System.Collections;
using System.IO.Ports;

public class cube_rotator : MonoBehaviour{
	// Connect to the serial port the 3-Space Sensor is connected to
	public static SerialPort sp = new SerialPort("\\\\.\\COM4");
	
	// Command packet for getting the filtered tared orientation as a quaternion
	// {header byte, command byte, [data bytes], checksum byte}
	// checksum = (command byte + data bytes) % 256
    public static byte[] send_bytes = {0xf7, 0x00, 0x00};
    public static byte[] button_bytes = {0xf7, 0xfa, 0xfa};
    public static byte[] tare_bytes = {0xf7, 0x60, 0x60};

    public int counter = 0;

	// Use this for initialization
	void Start(){
		// Allow the user to set the appropriate properties.
		sp.BaudRate = 115200;
		sp.Parity = Parity.None;
		sp.DataBits = 8;
		sp.StopBits = StopBits.One;
		
		// Set the read/write timeouts
		sp.WriteTimeout = 500;
		sp.ReadTimeout = 500;
		
		sp.Open();
		
		sp.Write(send_bytes,0,3);
	}
	
    // Helper function for taking the bytes read from the 3-Space Sensor and converting them into a float
	float bytesToFloat(byte[] raw_bytes, int offset){
		byte[] big_bytes = new byte[4];
		big_bytes[0] = raw_bytes[offset+3];
		big_bytes[1] = raw_bytes[offset+2];
		big_bytes[2] = raw_bytes[offset+1];
		big_bytes[3] = raw_bytes[offset+0];
		return BitConverter.ToSingle(big_bytes,0);
	}

    void getButtonState(){
        sp.Write(button_bytes, 0, 3);

        byte[] read_bytes = new byte[1];
        int fail_counter = 0;

        // Mono, for some reason, seems to randomly fail on the first read after a wirte so we must loop
        // through to make sure the bytes are read
        while (true){
            try{
                sp.Read(read_bytes, 0, 1);
                break;
            }
            catch{
                ++fail_counter;
                // Failed to read from serial port
            }
            if (fail_counter > 100){
                throw new System.Exception("Failed to read from port too many times." +
                    " This could mean the port is not open or the Mono serial read is not responding.");
            }
        }

        if (read_bytes[0] == 0){
            print("No buttons are pressed.");
        }
        else if (read_bytes[0] == 1){
            print("Right button is pressed.");
        }
        else if (read_bytes[0] == 2){
            print("Left button is pressed.");
        }
        else if (read_bytes[0] == 3){
            print("Left and Right buttons are pressed.");
        }
    }

    void tareSensor(){
        sp.Write(tare_bytes, 0, 3);
    }

	// Update is called once per frame
	void Update(){
        counter++;
		// A quaternion consists of 4 floats which is 16 bytes
        byte[] read_bytes = new byte[16];

        // Mono, for some reason, seems to randomly fail on the first read after a wirte so we must loop
        // through to make sure the bytes are read and Mono also seems not to always read the amount asked
        // so we must also read one byte at a time
        int read_counter = 100;
        int byte_idx = 0;
        while (read_counter > 0){
            try{
                byte_idx += sp.Read(read_bytes, byte_idx, 1);
            }
            catch{
                // Failed to read from serial port
            }
            if (byte_idx == 16){
                break;
            }
            if (read_counter <= 0){
                throw new System.Exception("Failed to read quaternion from port too many times." +
                    " This could mean the port is not open or the Mono serial read is not responding.");
            }
            --read_counter;
        }

		// Convert bytes to floats
		float x = bytesToFloat(read_bytes,0);
		float y = bytesToFloat(read_bytes,4);
		float z = bytesToFloat(read_bytes,8);
		float w = bytesToFloat(read_bytes,12);
		
		// Create a quaternion
		Quaternion quat = new Quaternion(x,y,z,w);
		
		// Perform rotation
        transform.rotation = quat;

        if ((counter % 25) == 0){
            getButtonState();
        }
        else if (counter > 150){
            tareSensor();
            counter = 1;
        }
		
		// Send command
		sp.Write(send_bytes,0,3);
	}
}

Communicating via Wireless

using UnityEngine;
using System;
using System.Collections;
using System.IO.Ports;

public class cube_rotator_wireless : MonoBehaviour{
    // Connect to the serial port the 3-Space Dongle is connected to
    public static SerialPort sp = new SerialPort("\\\\.\\COM3");

    // Wireless Command packet for getting the filtered tared orientation as
    // a quaternion
    // {header byte, id byte + command byte, [data bytes], checksum byte}
    // checksum = (id byte + command byte + data bytes) % 256
    public static byte[] send_bytes = {0xf8, 0x00, 0x00, 0x00};
    public static byte[] button_bytes = {0xf8, 0x00, 0xfa, 0xfa};
    public static byte[] tare_bytes = {0xf8, 0x00, 0x60, 0x60};

    public int counter = 0;

    // Use this for initialization
    void Start(){
        // Allow the user to set the appropriate properties.
        sp.BaudRate = 115200;
        sp.Parity = Parity.None;
        sp.DataBits = 8;
        sp.StopBits = StopBits.One;

        // Set the read/write timeouts
        sp.WriteTimeout = 500;
        sp.ReadTimeout = 500;

        sp.Open();

        sp.Write(send_bytes, 0, 4);
    }

    // Helper function for taking the bytes read from the 3-Space Sensor and
    // converting them into a float
    float bytesToFloat(byte[] raw_bytes, int offset){
        byte[] big_bytes = new byte[4];
        big_bytes[0] = raw_bytes[offset + 3];
        big_bytes[1] = raw_bytes[offset + 2];
        big_bytes[2] = raw_bytes[offset + 1];
        big_bytes[3] = raw_bytes[offset + 0];
        return BitConverter.ToSingle(big_bytes, 0);
    }

    void getButtonState(){
        sp.Write(button_bytes, 0, 4);

        // Wireless packets always have a confirm and id byte pre-appened to the data
        byte[] confirm_byte = new byte[1];
        byte[] id_byte = new byte[1];
        int fail_counter = 0;

        // Mono, for some reason, seems to randomly fail on the first read after a wirte so we must loop
        // through to make sure the bytes are read
        while (true){
            try{
                sp.Read(confirm_byte, 0, 1);
                sp.Read(id_byte, 0, 1);
                break;
            }
            catch{
                ++fail_counter;
                // Failed to read from serial port
            }
            if (fail_counter > 100){
                throw new System.Exception("Failed to read from port too many times." +
                    " This could mean the port is not open or the Mono serial read is not responding.");
            }
        }

        if (confirm_byte[0] == 0){
            // Read data length of packet
            byte[] length_byte = new byte[1];
            sp.Read(length_byte, 0, 1);

            byte length = length_byte[0];

            // Read the length of the data in the packet
            byte[] read_bytes = new byte[length];

            sp.Read(read_bytes, 0, length);


            if (read_bytes[0] == 0){
                print("No buttons are pressed.");
            }
            else if (read_bytes[0] == 1){
                print("Right button is pressed.");
            }
            else if (read_bytes[0] == 2){
                print("Left button is pressed.");
            }
            else if (read_bytes[0] == 3){
                print("Left and Right buttons are pressed.");
            }
        }
    }

    void tareSensor(){
        sp.Write(tare_bytes, 0, 4);

        // Wireless packets always have a confirm and id byte pre-appened to the data
        byte[] confirm_byte = new byte[1];
        byte[] id_byte = new byte[1];
        int fail_counter = 0;

        // Mono, for some reason, seems to randomly fail on the first read after a wirte so we must loop
        // through to make sure the bytes are read
        while (true){
            try{
                sp.Read(confirm_byte, 0, 1);
                sp.Read(id_byte, 0, 1);
                break;
            }
            catch{
                ++fail_counter;
                // Failed to read from serial port
            }
            if (fail_counter > 100){
                throw new System.Exception("Failed to read from port too many times." +
                    " This could mean the port is not open or the Mono serial read is not responding.");
            }
        }

        if (confirm_byte[0] == 0){
            // Read data length of packet
            byte[] length_byte = new byte[1];
            sp.Read(length_byte, 0, 1);

            byte length = length_byte[0];

            // Read the length of the data in the packet
            byte[] read_bytes = new byte[length];

            sp.Read(read_bytes, 0, length);
        }
    }

    // Update is called once per frame
    void Update(){
        counter++;

        // Wireless packets always have a confirm and id byte pre-appened to the data
        byte[] confirm_byte = new byte[1];
        byte[] id_byte = new byte[1];
        int fail_counter = 0;

        // Mono, for some reason, seems to randomly fail on the first read after a wirte so we must loop
        // through to make sure the bytes are read
        while (true){
            try{
                sp.Read(confirm_byte, 0, 1);
                sp.Read(id_byte, 0, 1);
                break;
            }
            catch{
                ++fail_counter;
                // Failed to read from serial port
            }
            if (fail_counter > 100){
                throw new System.Exception("Failed to read from port too many times." +
                    " This could mean the port is not open or the Mono serial read is not responding.");
            }
        }

        if (confirm_byte[0] == 0){
            // Read data length of packet
            byte[] length_byte = new byte[1];
            sp.Read(length_byte, 0, 1);

            byte length = length_byte[0];

            // Read the length of the data in the packet
            byte[] read_bytes = new byte[length];

            sp.Read(read_bytes, 0, length);
            
            // Convert bytes to floats
            float x = bytesToFloat(read_bytes, 0);
            float y = bytesToFloat(read_bytes, 4);
            float z = bytesToFloat(read_bytes, 8);
            float w = bytesToFloat(read_bytes, 12);

            // Create a quaternion
            Quaternion quat = new Quaternion(x, y, z, w);

            // Perform rotation
            transform.rotation = quat;
        }

        if ((counter % 25) == 0){
            getButtonState();
        }
        else if (counter > 150){
            tareSensor();
            counter = 1;
        }

        sp.Write(send_bytes, 0, 4);
    }
}

We have used Unity in some of our demos like this bird flight demo in this video YEI 3-Space Family IMU/AHRS Sensors in Action

There are a few ways to get the orientation from the 3-Space sensor into Unity. The 3-Space sensors are assessable to any application that can open a comport. So its possible to write a simple interface in C# to get the orientations into Unity. The other option is to make a socket server where you have another application read the data from the 3-Space Sensor and then stream the bytes to Unity or any application that can open a socket.

If you are planning on using multiple sensors, the YEI 3-Space Sensor™ Wireless is the preferred option because it has lower latency and better throughput than the Bluetooth model.