Unity Arduino
Unity Arduino
Fall 2013
September 30
The workshop has been created and tested with the following versions of the software:
Unity and Arduino will be connected via Processing. Unity and Processing will talk to each other via
TCP/IP and Processing and Arduino will talk to each other via firmata.
TCP/IP Firmata
Since we are using TCP/IP, Processing and Unity do not need to be in the same computer. They talk
through internet.
We will create a simple example with simple scripts, which means the system we will create will work.
However, it will not be the most efficient or robust system. The system will work such that, we will use a
potentiometer to rotate an object Unity and we will use a trigger in Unity to switch on and off the built
in LED in Arduino UNO. The codes provided with this document will have multiple lines of debug code
(println() in Processing and Debug.Log() in Unity) that can be enabled to debug and understand the
signals that is being transmitted.
A server!
An object to rotate.
A way to create a trigger in the game.
The server will be a script that will be attached to an object. Make sure that the object is passed through
all the scenes. The Main Camera or the character controller can be used.
For the rotation, any object is sufficient. However, for this workshop, I will use a light source with a
cookie (world map) attached to a textured (world map) sphere.
For the trigger, I will use OnTriggerEnter and OnTriggerExit functions with a Box Collider.
Before starting to write scripts, we need to create our project and a scene. Go to
On the Project Wizard window, choose the location of the project and from the packages click on
‘Character Controller.unityPackage . Once the project is ready, press CTRL+S or APPLE+S to save your
scene1.
1
A consistent folder structure is always recommended. For example: create a folder named Scenes and save the
scene inside that folder.
The plane will be our extremely simple level. Now, we will create a First Person controller by using a
prefab2. Drag and drop ‘First Person Controller’ from Project Window to Hierarchy window. Put it
somewhere on the plane. On the top of the Inspector Window, change the tag of the object to Player.
2
Prefabs are template objects that are packaged and ready to use.
When you have time, feel free to improve your scene in any way you like. Below image shows the scene
from the First Person view.
External Libraries
using UnityEngine;
using System.Collections;
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
Variables
The first set of variables is required for the server and the second set is required for the game
mechanics. The ‘incoming_client’ object of class TcpClient will be the Processing that connects to the
server, the ‘netStream’ object of class NetworkStream will be the stream or the connection between the
server and the ‘server’ object of class TcpListener will be the server.
As for the game, we need a game object, which we will rotate. This object is referenced as
‘targetObjcet’. We need a Boolean variable to control the LED at pin 13 on Arduino. And we need
another Boolean for a simple improvement of the system, so that we only send data when the LED13
value changes.
3
In the case, where your C# script needs to reach a variable from a JavaScript/UnityScript script and vice versa,
check Unity’s documentation about the order in which codes are compiled:
http://docs.unity3d.com/Documentation/Manual/ScriptCompileOrderFolders.html
4
C# scripts name must have the same name as the class name.
In the ‘Start()’, we initialize and start the server. For more details of the TcpListener object see the
reference: http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.aspx
The Parse Function, processes the String received from client (Processing) and does the necessary
actions. Here I have used a simple handshaking signal: Blank space is defined as the end of a message.
Thus, the received data package is split from the blank spaces. The server uses the first part and
disregards the rest.
//you can use multiple commands and use string.Split() to split them
//Using and an end signal is always good, here we are using blank space (" ")
string[] values = toParse.Split(' ');
//Debug.Log("first is " + values[0]);
Update Function
The ‘Update’ is the heart of the server: It checks whether the server is busy or not. If it is not, the code
looks for the next client and reads the data if it is available. After a successful read attempt, LED13
activation data is sent to the Processing patch. It is important to note that the server employs error
caching in order to sustain functioning after any corrupted data or connection problems.
The data s first suffixed with the end signal (single blank space) and then the new String is converted
into an array of bytes. The array sent to the Processing patch through the ‘netstream’ object.
s = Encoding.ASCII.GetString(tmpbuf, 0, numread);
s = s.Replace("\n","");
values = s.Split(';');
//print ("values"+values.ToString()); //for debugging
if (values.Length > 1) {
for (int i = 0; i < (values.Length-1); i++) {
Parse2(values[i]);
}
}
else Parse2(values[0]);
//sending data
//Test
//Byte[] data =
System.Text.Encoding.ASCII.GetBytes("HelloThere");
//netStream.Write(data, 0, data.Length);
Byte[] data =
System.Text.Encoding.ASCII.GetBytes(TempSend);
netStream.Write(data, 0, data.Length);
IsLED13Changed = LED13;
}
}
//Called when netStream fails to read from the stream.
catch (IOException e) {
waiting = false;
netStream.Close();
incoming_client.Close();
}
//Called when netStream has been closed already.
catch (ObjectDisposedException e) {
waiting = false;
incoming_client.Close();
}
} Merging Physical and Virtual - 9
}
2.2. Object to Rotate
Here we need an object. We will create a cube similar to creating a plane. To create a ‘Cube’ go to:
Rename the ‘Cube’ to something more specific and recognizable. Place the object somewhere. We need
to attach a script to the object. The script will be used to link the data that the server received to the
object. Let us create C# script and call the script ‘Rotator’:
using UnityEngine;
using System.Collections;
//Debug.Log("v3y = "+V3.y.ToString());
transform.Rotate(V3);
}
}
The main idea of the script is to map the Arduino input to the rotation of the object. In Unity, the
rotation of an object can be accessed from transform.Rotate5. The rotation is a vector with three
elements that maps to the X, Y and Z-Axes. We will only rotate the Y-Axis. The data that is received from
Arduino ranges from 0 to 1023 since the analog-to-digital converters have 10-bits. We need to convert
this range to 0-360 degrees before applying. In the ‘Start’ function this conversion is done. In the
‘Update’, the script does not only scale the value with the degree-conversion rate but it also scales the
value with ‘Time.deltaTime’. Scaling with the unit time-change allows us to have numerically correct
change that is related with the real time rather than the number of frames. Thus, once scaled with
‘Time.deltaTime’, our object rotates X degrees per second rather than X degree per frame.
5
http://docs.unity3d.com/Documentation/ScriptReference/Transform.Rotate.html
Rename the object to something definitive. Scale the object so that it is easy to trigger. In the ‘Inspector
Window’, find ’Box Collider’ and click on ‘Is Trigger’. This turns the object to a trigger zone such that
objects that collide with the trigger zone can go through without reflection; however, the game engine
sends a signal when the collision happens.
Now create a script and label it TriggerActivation. When another game object enters or exits the trigger
zone. Our script will look at that object’s tag. If it is “Player”, it will make the Boolean variable, ‘IsInside’
true when the player enters and false when the players exits. The Boolean will be sent to the TCPHost
script via the variable ‘TCPSendReference’.
1. Server
First, attach the ‘TCPHost’ script to the ‘First Person Controller’ object by dragging and dropping it from
Project View on to the object in Hierarchy View. In the Inspector View, set variable as follows:
Here, the name of the object in my scene that I will rotate is ‘World’. Thus for the ‘Target Object’,
choose the object that you created.
2. Rotating Object
Attach the script, ‘Rotator’, to the object you have created. You do not need to change any of the
variables.
3. Trigger Zone
Attach the script, ‘ActivationTrigger’, to the trigger zone object that you have created. For the
‘TCPSendReference’ variable drag and drop ‘First Person Controller’ from Hierarchy View.
Now, our scene should be ready and we will move on to Processing. Do not forget to save your scene!
Arduino Library
Arduino.list(): returns a list of the available serial devices. If your Arduino board is connected to
the computer when you call this function, its device will be in the list.
Arduino(parent, name, rate): create an Arduino object. Parent should be "this" (without the
quotes); name is the name of the serial device (i.e. one of the names returned by Arduino.list());
rate is the speed of the connection (115200 for the v2 version of the firmware, 57600 for v1).
Note that in the v2 library, the rate parameter is optional.
pinMode(pin, mode): set a digital pin to input or output mode (Arduino.INPUT or
Arduino.OUTPUT).
digitalWrite(pin, value): writes Arduino.LOW or Arduino.HIGH to a digital pin.
analogRead(pin): returns the value of an analog input (from 0 to 1023).
6
To install a library copy the extracted ZIP package, "arduino" folder, into the "libraries" sub-folder of your
Processing Sketchbook. (You can find the location of your Sketchbook by opening the Processing Preferences. If
you haven't made a "libraries" sub-folder, create one.)
//Libraries
import cc.arduino.*; //this imports arduino as a class in processing
import processing.serial.*; //processing will talk to arduino through serial port
//Custom classes
Arduino arduino; //let's have an object named arduino from the class, Arduino
//Default classes
int dataOut; //from Arduino to Unity
String dataIn = "False"; //from Unity to Arduino, initialized to False
void setup()
{
// Setup Arduino connection
println(Arduino.list()); //list the Arduinos connected to the computer
arduino = new Arduino(this, Arduino.list()[0], 57600); //choose the first for
Arduino
arduino.pinMode(13, Arduino.OUTPUT); //set Arduino pin 13 as ouput
}
void draw()
{
if(dataIn.equals("True"))
{
arduino.digitalWrite(13, Arduino.HIGH);
}
if(dataIn.equals("False"))
{
arduino.digitalWrite(13, Arduino.LOW);
}
//Libraries
import processing.net.*; //TCP/IP libraries
void setup()
{
// Setup Unity connection
// Local host at port 8090.
// Change IP if Unity is on a remote machine
myClient = new Client(this, "127.0.0.1", 8090);
myClient.write("Connected to Unity!");
}
void draw()
{
7
127.0.0.1 is the local host (same computer), if you need to reach another computer use that computer’s IP.
8
Make sure that the port is open.
void setup()
{
// Setup Unity connection
// Local host at port 8090.
// Change IP if Unity is on a remote machine
myClient = new Client(this, "127.0.0.1", 8090);
myClient.write("Connected to Unity!");
void draw()
{
4.1. Software
We will only load Standard Firmata. Firmata allows us to talk to the Processing in real time through the
serial port. See the image below to load Firmata.
File -> Examples -> Firmata -> Standard Firmata
9
Potentiometer is a variable resistor. For this workshop any element that provides an analog input to Arduino can
be used.
Once the patch is up and running, return to Unity window. Unity Game Window works only when it is in
focus. If another software (Arduino or Processing) is on the foreground, the game (thus the server) is
paused. If the Processing patch is started before Unity, it will give errors; however, this can be improved.
Make sure there is no mistake between the manual and your implementation.
Make sure the port is open and the local host is accessible.
Make sure that correct port is chosen for Arduino.
Use the debug lines to see if the signals are correct and are received correctly.
https://dl.dropboxusercontent.com/u/265455/MergingPhysicalandVirtual.rar