-
Notifications
You must be signed in to change notification settings - Fork 6
Home
Here's how to get started with developing using the singularity!
These steps are dropdowns that contain images. Just click the little triangles on the left.
Head to the releases section of this repo and find the latest one.
Click on this link to go to the releases page. Or find it on the sidebar of the repository.
Download the .unitypackage file from Assets section of the release version you want.
You do not need the source code to run the Singularity in Unity.
Import the .unitypackage file into your Unity Editor project by going to Assets > Import Package > Custom Package..
Do not try to import the git repository directly into your Unity project.
Create a new empty GameObject in your Hierarchy.
Name this GameObject "SingularityManager".
Add the "Singularity Manager" component into your SingularityManager GameObject.
v0.04 and greater supports wifi communication, which is the communication method to use for MIT Reality Hack 2025, as the ESPs do not support the bluetooth version implemented in Singularity.
WiFi Setup
You'll need to find the IP address of the ESP you're using. To do this, you can print out the IP address that has been assigned on the ESP by uploading the following code to it:#include <WiFi.h>
const char* ssid = "SingularityTesting";
const char* password = "12345678";
WiFiServer server(80);
int loops = 0;
void setup() {
Serial.begin(115200);
// Create Access Point
WiFi.softAP(ssid, password);
Serial.println("Access Point Started2");
Serial.print("IP Address: ");
Serial.println(WiFi.softAPIP());
server.begin();
}
void loop() {
WiFiClient client = server.available();
}
The IP address to enter into unity will be listed after IP Address
in the serial monitor after plugging the ESP in.
In the Unity script, make sure that Communication Type is set to "Wifi" and add the IP and the port (script defaults it to 80) of the ESP:
You can then set up callbacks in the inspector to be called when certain events happen. These are:
- On connection
- On message
- On error
If you want to call a method on an object in your scene on a certain event, set the callback to "Runtime only", select the object from the list:
And then select the desired function:
The following method signatures are assumed:
- On connection:
void()
- On message:
void(string)
- On error:
void(string)
Sending messages from the ESP
The script by default assumes that all messages start with the character `S` and end with `E`. Once a message with these two characters is received, the on message callback is triggered with these two characters removed from the string. Below is a full example of a program that sends two messages from the ESP:#include <WiFi.h>
const char* ssid = "Lucas";
const char* password = "12345678";
WiFiServer server(80);
int loops = 0;
void setup() {
Serial.begin(115200);
// Create Access Point
WiFi.softAP(ssid, password);
Serial.println("Access Point Started2");
Serial.print("IP Address: ");
Serial.println(WiFi.softAPIP());
server.begin();
}
void loop() {
WiFiClient client = server.available();
loops++;
if (client) {
Serial.println("Client connected!"); // Add this
while (client.connected()) {
client.println("SHello World1E " + String(loops));
client.println("SHello World2E " + String(loops));
Serial.println("Printed to client " + String(loops));
delay(100);
}
Serial.println("Client disconnected"); // Add this
client.stop();
}
}
Recieving messages from the ESP
Arduino side:#include <WiFi.h>
const char* ssid = "Dan";
const char* password = "12345678";
WiFiServer server(80);
int loops = 0;
void setup() {
Serial.begin(115200);
// Create Access Point
WiFi.softAP(ssid, password);
Serial.println("Access Point Started2");
Serial.print("IP Address: ");
Serial.println(WiFi.softAPIP());
server.begin();
}
void loop() {
WiFiClient client = server.available();
loops++;
if (client) {
Serial.println("Client connected!");
char buffer[1024];
while (client.connected()) {
String packet = client.readStringUntil('\n');
client.println("SHello World1E " + String(loops));
client.println("SHello World2E " + String(loops));
Serial.println("Printed to client " + packet);
delay(100);
}
Serial.println("Client disconnected");
client.stop();
}
}
NOTE THAT THIS SECTION AND BELOW ARE NOT REQUIRED FOR MIT REALITY HACK 2025.
Create a new empty GameObject in your hierarchy to put the script in. I'm going to call mine ExampleCommunicator.
Under this GameObject, add a component and make it a new script. And start editing it.
Now, at the top of your MonoBehavior script, add the line:
using Sngty;
At the top of your file, to use the Sngty
(singularity) namespace. Alternatively you can use Syngty.SingularityManager
etc if you don't want to conflict with another namespace.
In the class definition for your script, add the line
public SingularityManager mySingularityManager;
This will add a field to your script under the inspector by which you can set the value by dragging your SingularityManager gameObject into that field.
Now to call the methods of the SingularityManager, you just have to reference that mySingularityManager
variable that you declared. (Or call it whatever you want, I'm not your boss.)
See the reference page for the SingularityManager
component to look at what its methods are if you'd like.
Before connecting to a device, we have to know what device we want to connect to, via it's DeviceSignature
.
First, in your Start()
method, get all the paired devices.
List<DeviceSignature> pairedDevices = mySingularityManager.GetPairedDevices();
If you already know the name of the device you want to connect to and it's paired, you can iterate through the list to find it.
List<DeviceSignature> pairedDevices = mySingularityManager.GetPairedDevices();
DeviceSignature myDevice = new DeviceSignature();
//If you are looking for a device with a specific name (in this case exampleDeviceName):
for (int i = 0; i < pairedDevices.Count; i++)
{
if ("exampleDeviceName".Equals(pairedDevices[i].name))
{
myDevice = pairedDevices[i];
break;
}
}
if (!myDevice.Equals(default(DeviceSignature)))
{
//Do stuff to connect to the device here
}
Note that if you want to use pairedDevices
and myDevice
outside of the Start()
method, you should declare it at the class level instead of within the Start()
method.
Also, if the bluetooth device is paired with the Quest headset but the device is not actually available (turned off, out of range, etc.), it can still show up in pairedDevices
. You can verify that the device is actually connected using the onConnected()
method.
Once you have the DeviceSignature
for the device you want to connect to, attempting the connection is as easy as calling:
mySingularityManager.ConnectToDevice(myDevice);
If the connection succeeds, the manager will invoke the onConnected()
UnityEvent. If it fails, it will invoke the onError(string errorMessage)
event.
This SingularityManager
Component has three UnityEvents that you can connect to corresponding methods in your own Unity scripts, for onConnected()
, onMessageRecieved(string message)
, and onError(string errorMessage)
.
Make a public method to be called in your script for each of the events, with the same arguments.
//KEEP IN MIND IF YOU ARE USING THIS WITH AN ANDROID DEVICE YOU WON'T BE ABLE TO READ LOGS FROM UNITY.
//You'll have to read the logcat logs through USB debugging. Or just display your messages on a GUI instead of Debug.Log.
public void onConnected()
{
Debug.Log("Connected to device!");
}
public void onMessageRecieved(string message)
{
Debug.Log("Message recieved from device: " + message);
}
public void onError(string errorMessage)
{
Debug.LogError("Error with Singularity: " + errorMessage);
}
Now in the SingularityManager, connect the methods to their corresponding events by clicking the plus icon under the event, dragging your gameObject with the script into the slot, and then select the correct method.
For onMessageRecieved() and onError() which have a string parameter, make sure you select the dynamic parameter version of the method instead of the static parameter one.
Once you're connected to the device, sending messages/data over Bluetooth to the device is easy! Just call the sendMessage(string message, DeviceSignature sig)
method of the SingularityManager
.
//Make sure you're connected to the device before you do this or it won't work
mySingularityManager.sendMessage("Hello, world!", myDevice);
This is good to do when you're done with the device. You can call DisconnectDevice
to disconnect just one, or DisconnectAll
to disconnect all of them.
//When program is closing or whatever
mySingularityManager.DisconnectDevice(myDevice);
//OR
mySingularityManager.DisconnectAll();
The Singularity requires Bluetooth permissions from Android. The first time you run your app in the headset, you should get a window asking for the permissions. After granting the permissions, you may need to quit and restart your app one or two times until the Bluetooth fully works. If you are still having trouble after restarting the app, verify that your app was granted the necessary permissions using adb: ./adb shell pm dump <package.name> | less
and verify that Bluetooth is toggled on in your headset settings.
As of Dec. 4, 2023, the Singularity has been tested and verified to work on Unity version 2022.3.9f1 on an M1 Mac using the following Android APIs:
- min API: 26, target API 31
- min API: 26, target API 32
Currently (Dec. 4, 2023), the Singularity only supports classic Bluetooth, not BLE. Support for BLE is planned for the future. Check that the device you are trying to connect to is using classic Bluetooth.
For some reason, the Singularity does not work if Developer Build is on in Unity. If you're having trouble connecting to the devices, try turning Developer Build off.
If you have multiple Bluetooth devices with the same name, make sure your headset is paired to the correct device and the correct device is powered on.
There can be a time delay between when OnConnected()
is called and when the bluetooth is actually ready for writing to the device.