-
Notifications
You must be signed in to change notification settings - Fork 11
Connecting a motorized slide pot to Unity via Spacebrew
This tutorial will demonstrate how to create a simple mixed reality game using a motorized slide pot, Arduino and Raspberry Pi connected to Spacebrew and Unity.
Motorized slider pot is a standard slide pot belt-driven by a small DC motor. The slide contains two separate 10k linear taper potentiometers that can be used as servo-feedback and control. There is also a touch sense line which is electrically connected directly to the metal slider tab that can be interfaced with capacitive touch circuitry. Inspired by these properties, we use the motorized slide pot to control the movement of our virtual avatar.
To connect the physical world with the virtual one we are going to use the Raspberry Pi. We need to read the analog value from the potentiometer, however, the Raspberry Pi is not able to read analog values directly. Thus, we chose to put an Arduino Uno in between the slider and the Raspberry Pi. This is just one of the applicable solutions.
Hardware needed:
- 1x Motorized Slide Pot
- 1x Arduino Uno
- 1x Raspberry Pi
- 1x L293D H-bridge
- 4x 10m ohm resistors
- 1x DC Power Supply HY3003D
Software needed:
- Python
- Spacebrew
- Unity
There are three things that connect from the slide pot to the Arduino board:
- the motor to move the slider;
- the potentiometer to read the position of the slider;
- the capacitive touch sensor to interact with the slider.
To control the movement of the slider we used an H-bridge driver (L293D) hooked up as in the following picture:
Then to hook up everything we used the following scheme but without the motor shield:
We faced a problem in setting the speed of the motor using the analogWrite function so we achieved this using the digitalWrite function switching the motor on and off very quickly. The resulting movement of course is less smooth than what is achievable with the PWM. A probable solution to this issue could be the use of a better motor driver such as a motor shield.
Now that we set up the Arduino to read the values from the slider we need to send this values to the Raspberry Pi. To do this we can connect the Arduino board to the Raspberry with the USB cable and send the data via Serial port. The two data that we are sending are the position of the slider and if someone is touching it or not. To read these values we run a python script into the Raspberry.
After reading the two values sent from Arduino we need to upload them from Raspberry Pi to Spacebrew. The position of the slider is of course a range type but the second value that we are sending from the Arduino is true or false that should be a boolean type, but because we are sending this value through the serial port the string is converted in numbers: 0 and 1. So for this reason we are going to send also this data as range type.
Arduino code:
#include <CapacitiveSensor.h> //Simple library to read values from capacitive sensors
const int controlPin1 = 3; //first direction pin - connected to pin 7 on the H-bridge
const int controlPin2 = 4; //second direction pin - connected to pin 2 on the H-bridge
const int enablePin = 9; //on/off pin
const int potPin = A0; //potentiometer pin
int motorEnabled = 0; // Turns the motor on/off
int motorDirection = 1; // current direction of the motor
int potVal;
int i;
unsigned long timeTo;
int TOUCH_SENSE_THRESH = 35; //threshold for the touch sensor
boolean touch;
CapacitiveSensor cs = CapacitiveSensor(6, 7); //initialize the capacitive sensor on pins 6 and 7
void setup() {
cs.set_CS_AutocaL_Millis(0xFFFFFFFF); // turn off autocalibrate for the touch sensor
Serial.begin(9600);
pinMode(controlPin1, OUTPUT);
pinMode(controlPin2, OUTPUT);
pinMode(enablePin, OUTPUT);
digitalWrite(controlPin1, HIGH); //start the motor
digitalWrite(controlPin2, LOW); //with one direction
timeTo = millis();
}
void loop() {
potVal = analogRead(potPin);
if (cs.capacitiveSensor(30) > TOUCH_SENSE_THRESH) { //if someone is touching the sensor
touch = true;
if (potVal > 1010 || potVal < 15) { //when the slider reach one of the extremes change the direction
turnOff();
motorDirection = !motorDirection; //change direction
if (motorDirection == 1) {
digitalWrite(controlPin1, HIGH);
digitalWrite(controlPin2, LOW);
} else {
digitalWrite(controlPin1, LOW);
digitalWrite(controlPin2, HIGH);
}
while (i < 100) { //this loop is used to allow the slider to exit from the range where the code will change the direction, otherwise it could stuck.
i++;
turnOn();
}
i = 0;
}
turnOn(); //these four lines are used to control the speed, switching on and off quicly.
delay(5);
turnOff();
delay(40);
} else {
touch = false;
}
if (millis() - timeTo > 200) { //sending values through the serial every 200 milliseconds
timeTo = millis();
potVal = analogRead(potPin);
potVal = map(potVal, 0, 1023, 0, 100); //mapping the values of the potentiometer to the values needed to move the object in Unity
Serial.println(potVal); //sending the values through the serial port
Serial.println(touch);
}
}
void turnOff() { //turn off the motor
digitalWrite(enablePin, LOW);
}
void turnOn() { //turn on the motor
digitalWrite(enablePin, HIGH);
}
Raspberry code:
#!/usr/bin/env python
import sys
import time
import subprocess
import serial
from pySpacebrew.spacebrew import Spacebrew
brew = Spacebrew("Raspberry", description="Animate some wifi networks", server="192.168.1.165", port=9000) #initialize spacebrew
#first argument is the name visualized in spacebrew, then change the server according to the address of your spacebrew server
brew.addPublisher("touch", "range");
brew.addPublisher("potVal", "range")
CHECK_FREQ = 0.2 #check mail every 200 milliseconds
ser = serial.Serial('/dev/ttyACM0',9600) #initialize the serial port
try:
brew.start()
print("Press Ctrl-C to quit.")
while True:
time.sleep(CHECK_FREQ)
s = float(ser.readline()) #read the first value and convert from string to float
val = int(s) #convert from float to int
print val
f = ser.readline()
print f
brew.publish('touch', f); #publish the two values
brew.publish('potVal', val)
finally:
brew.stop()
To map the slider’s movement to the virtual world, we need to connect Unity to Spacebrew. For this project, we used the example MRHW_Proto001 in the Mixed Reality Hardware Toolkit as the basis and downloaded a cat model from the asset store. The movement of the slider will change the position of the cat. More specificly, slider's position controls the cat's x position, and the sense of touch controls the cat's z position, making it move forward. In doing so, we need to add subscribers in the SpacebrewEvents.cs script:
Define the cat position:
float catpos;
float forward = 0.0f;
Add event listeners in the setup function:
sbClient.addEventListener (this.gameObject, "catlistener");
sbClient.addEventListener (this.gameObject, "catcatcher");
Update the OnSpacebrewEvent:
if (_msg.name == "catlistener")
{
GameObject go = GameObject.Find("BaseBoARd/YourObjectsGoHere/cat_Eat");
print("String "+_msg.value);
int x = int.Parse(_msg.value);
print("Int " + x);
catpos = -(float)x;
print(catpos);
go.gameObject.transform.position = new Vector3(catpos , 0.0f, forward);
}
if (_msg.name == "catcatcher"){
print("our value: " + _msg.value);
int x = int.Parse(_msg.value);
print("Int " + x);
if (x == 0)
{
print("stop");
forward = 200.0f;
} else {
print("play");
forward = 0.0f;
}
print(forward);
}
The overall SpacebrewEvents.cs code:
using UnityEngine;
using System.Collections;
public class SpacebrewEvents : MonoBehaviour {
SpacebrewClient sbClient;
bool lightState = false;
float catpos;
float forward = 0.0f;
void Start () {
GameObject go = GameObject.Find ("SpacebrewObject"); // the name of your client object
sbClient = go.GetComponent <SpacebrewClient> ();
// register an event with the client and a callback function here.
// COMMON GOTCHA: THIS MUST MATCH THE NAME VALUE YOU TYPED IN THE EDITOR!!
sbClient.addEventListener (this.gameObject, "catlistener");
sbClient.addEventListener (this.gameObject, "catcatcher");
}
void Update () {
}
public void OnSpacebrewEvent(SpacebrewClient.SpacebrewMessage _msg) {
print ("Received Spacebrew Message");
print (_msg);
if (_msg.name == "catlistener")
{
GameObject go = GameObject.Find("BaseBoARd/YourObjectsGoHere/cat_Eat");
print("String "+_msg.value);
int x = int.Parse(_msg.value);
print("Int " + x);
catpos = -(float)x;
print(catpos);
go.gameObject.transform.position = new Vector3(catpos , 0.0f, forward);
}
if (_msg.name == "catcatcher"){
print("our value: " + _msg.value);
int x = int.Parse(_msg.value);
print("Int " + x);
if (x == 0)
{
print("stop");
forward = 200.0f;
} else {
print("play");
forward = 0.0f;
}
print(forward);
}
}
}
Now you can run Unity and play with the cat: