Use gyroscope pitch/roll data to control circle in OpenFrameworks app


#1

Hi there,

I’m kinda new to OpenFrameworks and I was wondering how can I use the pitch/roll data I’m reading from my gyroscope (Particle photon) via WiFi in order to control the movements of a white circle I have drawn in OpenFrameworks.
I have the gyroscope and OpenFrameworks app connected together and I’m getting the pitch/roll data already.

What I’m looking for is to know how can I feed this pitch/roll as movements to the WhiteBall in my code?

Thanks in advance for your help!

#include "ofApp.h"

#define RECONNECT_TIME 400

//--------------------------------------------------------------
void ofApp::setup() {

	//HERE WE ARE SETTING THE RATE AT WHICH WE THE PROGRAM WILL RUN (AND HOW MANY TIMES PER SECOND THE UPDATE AND DRAW METHODS ARE CALLED IT IN FRAMES PER SECOND)
	ofSetFrameRate(60);
	ofSetBackgroundColor(230);

	msgTx = "";
	msgRx = "";
	
	roll = "";
	pitch = "";

	ofxTCPSettings settings("192.168.1.103", 23);

	tcpClient.setup(settings);
	tcpClient.setMessageDelimiter("\n");

	connectTime = 0;
	deltaTime = 0;

	//NOW WE TELL THE FIRST BALL HOW IT LOOKS LIKE:

	//FIRST WE GIVE IT A RANDOM POSITION
	WhiteBall.position.x = ofGetWidth() / 2;
	WhiteBall.position.y = ofGetHeight() / 2;
	GreenBall.position.x = ofRandom(0, ofGetWidth() / 2);
	GreenBall.position.y = ofRandom(0, ofGetHeight() / 2);
	RedBall.position.x = ofRandom(0, ofGetWidth() / 2);
	RedBall.position.y = ofRandom(0, ofGetHeight() / 2);

	//AND A RADIUS
	WhiteBall.radius = 30;
	GreenBall.radius = 10;
	RedBall.radius = 10;

	//AND THE COLOR
	WhiteBall.color = ofColor(255, 255, 255);
	GreenBall.color = ofColor(0, 255, 0);
	RedBall.color = ofColor(255, 0, 0);

	//AND A DIRECTION/SPEED
	//WhiteBall.direction.x = 3, 10;
	//WhiteBall.direction.y = 3, 10;
}

//--------------------------------------------------------------
void ofApp::update() {

	if (tcpClient.isConnected()) {
		// we are connected - lets try to receive from the server
		//cout << "Connected to photon." << endl;
		string str = tcpClient.receiveRaw();
		if (str.length() > 0) {
			if (str != "^") {
				vector<string> splitString = ofSplitString(str, ";");
				roll = splitString[0].c_str();
				pitch = splitString[1].c_str();
				//cout << str << endl;
				cout << roll << endl;
				cout << pitch << endl;
				msgRx = str;
			}
		}
	}
	else {
		msgTx = "";
		// if we are not connected lets try and reconnect every 5 seconds
		deltaTime = ofGetElapsedTimeMillis() - connectTime;

		if (deltaTime > 5000) {
			tcpClient.setup("192.168.1.103", 23);
			tcpClient.setMessageDelimiter("\n");
			connectTime = ofGetElapsedTimeMillis();
		}

	}



	//HERE WE CHECK IF WE REACHED THE BORDERS OF THE WINDOW AND BOUNCE IT BACK
	if (WhiteBall.position.x < 0) {
	WhiteBall.direction.x = abs(WhiteBall.direction.x);
	}
	if (WhiteBall.position.y < 0) {
	WhiteBall.direction.y = abs(WhiteBall.direction.y);
	}

	if (WhiteBall.position.x > ofGetWidth()) {
	WhiteBall.direction.x = -abs(WhiteBall.direction.x);
	}

	if (WhiteBall.position.y > ofGetHeight()) {
	WhiteBall.direction.y = -abs(WhiteBall.direction.y);
	}




	//HERE WE UPDATE THE POSITION OF THE BALL BASED ON THE SPEED/DIRECTION
	WhiteBall.position.x = WhiteBall.position.x + WhiteBall.direction.x;
	WhiteBall.position.y = WhiteBall.position.y + WhiteBall.direction.y;
	GreenBall.position = GreenBall.position;
	RedBall.position = RedBall.position;

}

//--------------------------------------------------------------
void ofApp::draw() {

	ofSetColor(20);
	ofDrawBitmapString("openFrameworks Project", 15, 30);

	if (tcpClient.isConnected()) {
		ofDrawBitmapString("IMU=Roll;Pitch " + msgRx, 15, 700);
	}
	else {
		ofDrawBitmapString("status: server not found. launch server app and check ports!\n\nreconnecting in " + ofToString((5000 - deltaTime) / 1000) + " seconds", 15, 55);
	}

	//AND NOW WE CAN DRAW A CIRCLE AT THE POSITION OF THE BALL WITH ITS RADIUS
	ofSetColor(WhiteBall.color);
	ofDrawCircle(WhiteBall.position, WhiteBall.radius);
	ofSetColor(GreenBall.color);
	ofDrawCircle(GreenBall.position, GreenBall.radius);
	ofSetColor(RedBall.color);
	ofDrawCircle(RedBall.position, RedBall.radius);

}

//--------------------------------------------------------------
void ofApp::keyPressed(ofKeyEventArgs & key) {

}

//--------------------------------------------------------------
void ofApp::keyReleased(int key) {
	
}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y) {

}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button) {

}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button) {

}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button) {

}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y) {

}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y) {

}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h) {

}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg) {

}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo) {

}

#2

You have all of the pieces right here, and you just need to decide the relationship between the roll or pitch to the to how you want the white ball to move. For instance, do you want it to feel like you are tilting the table under a marble and the roll/pitch is how much that tilt is? Or do you want it to be more like using a joystick?

For the latter you want to remap the values coming in for the roll (might be -9.8 to 9.8?) to the extent of motion you want for the white ball. That might look something like this:

//here the position is directly mapped to the roll, moving the ball across the entire screen. be sure to substitute the actual lowest and highest values for -9.8 and 9.8…

Whiteball.position.x = ofMap(roll, -9.8, 9.8, 0, ofGetWidth());


#3

Hey thanks a lot for looking through this for me.

I had to convert the strg values received from the tcpserver (roll/pitch) into float in order to use it in the ofMap line you posted.
Thing is ofMap is now positioning the WhiteBall at x and y coordinates accordingly to roll and pitch value.
How would I be able to make it act as if it were a joystick moving the ball around? so that it would move left only when tilting left and not go back in the middle when not touching the joystick?

Once again thanks for the help!

Here is the current code:

#include "ofApp.h"

#define RECONNECT_TIME 400

//--------------------------------------------------------------
void ofApp::setup() {

	//HERE WE ARE SETTING THE RATE AT WHICH WE THE PROGRAM WILL RUN (AND HOW MANY TIMES PER SECOND THE UPDATE AND DRAW METHODS ARE CALLED IT IN FRAMES PER SECOND)
	ofSetFrameRate(60);
	ofSetBackgroundColor(230);

	msgTx = "";
	msgRx = "";

	roll = "";
	pitch = "";

	ofxTCPSettings settings("192.168.1.103", 23);

	tcpClient.setup(settings);
	tcpClient.setMessageDelimiter("\n");

	connectTime = 0;
	deltaTime = 0;

	//NOW WE TELL THE FIRST BALL HOW IT LOOKS LIKE:

	//FIRST WE GIVE IT A RANDOM POSITION
	WhiteBall.position.x = ofGetWidth() / 2;
	WhiteBall.position.y = ofGetHeight() / 2;
	GreenBall.position.x = ofRandom(0, ofGetWidth() / 2);
	GreenBall.position.y = ofRandom(0, ofGetHeight() / 2);
	RedBall.position.x = ofRandom(0, ofGetWidth() / 2);
	RedBall.position.y = ofRandom(0, ofGetHeight() / 2);

	//AND A RADIUS
	WhiteBall.radius = 30;
	GreenBall.radius = 15;
	RedBall.radius = 15;

	//AND THE COLOR
	WhiteBall.color = ofColor(255, 255, 255);
	GreenBall.color = ofColor(0, 255, 0);
	RedBall.color = ofColor(255, 0, 0);

	//AND A DIRECTION/SPEED
	//WhiteBall.direction.x = 1, (ofRandom(-6, 6));
	//WhiteBall.direction.y = (ofRandom(-3, 3)), (ofRandom(-6, 6));
	GreenBall.direction.x = 1, (ofRandom(-6, 6));
	GreenBall.direction.y = (ofRandom(-3, 3)), (ofRandom(-6, 6));
	RedBall.direction.x = 1, (ofRandom(-6, 6));
	RedBall.direction.y = (ofRandom(-3, 3)), (ofRandom(-6, 6));
}

//--------------------------------------------------------------
void ofApp::update() {

	const char *tempRoll;
	const char *tempPitch;

	if (tcpClient.isConnected()) {
		// we are connected - lets try to receive from the server
		//cout << "Connected to photon." << endl;
		string str = tcpClient.receiveRaw();
		if (str.length() > 0) {
			if (str != "^") {
				vector<string> splitString = ofSplitString(str, ";");
				roll = splitString[0].c_str();
				tempRoll = splitString[0].c_str();
				convertedRoll = atof(tempRoll);
				pitch = splitString[1].c_str();
				tempPitch = splitString[1].c_str();
				convertedPitch = atof(tempPitch);
				//cout << str << endl;
				cout << roll << endl;
				cout << pitch << endl;
				msgRx = str;
			}
		}
	}
	else {
		msgTx = "";
		// if we are not connected lets try and reconnect every 5 seconds
		deltaTime = ofGetElapsedTimeMillis() - connectTime;

		if (deltaTime > 5000) {
			tcpClient.setup("192.168.1.103", 23);
			tcpClient.setMessageDelimiter("\n");
			connectTime = ofGetElapsedTimeMillis();
		}

	}



	//HERE WE CHECK IF WE REACHED THE BORDERS OF THE WINDOW AND BOUNCE IT BACK
	if (WhiteBall.position.x < 0) {
		WhiteBall.direction.x = abs(WhiteBall.direction.x);
	}
	if (WhiteBall.position.y < 0) {
		WhiteBall.direction.y = abs(WhiteBall.direction.y);
	}

	if (WhiteBall.position.x > ofGetWidth()) {
		WhiteBall.direction.x = -abs(WhiteBall.direction.x);
	}

	if (WhiteBall.position.y > ofGetHeight()) {
		WhiteBall.direction.y = -abs(WhiteBall.direction.y);
	}

	//GREENBALL
	if (GreenBall.position.x < 0) {
		GreenBall.direction.x = abs(GreenBall.direction.x);
	}
	if (GreenBall.position.y < 0) {
		GreenBall.direction.y = abs(GreenBall.direction.y);
	}

	if (GreenBall.position.x > ofGetWidth()) {
		GreenBall.direction.x = -abs(GreenBall.direction.x);
	}

	if (GreenBall.position.y > ofGetHeight()) {
		GreenBall.direction.y = -abs(GreenBall.direction.y);
	}

	//REDBALL
	if (RedBall.position.x < 0) {
		RedBall.direction.x = abs(RedBall.direction.x);
	}
	if (RedBall.position.y < 0) {
		RedBall.direction.y = abs(RedBall.direction.y);
	}

	if (RedBall.position.x > ofGetWidth()) {
		RedBall.direction.x = -abs(RedBall.direction.x);
	}

	if (RedBall.position.y > ofGetHeight()) {
		RedBall.direction.y = -abs(RedBall.direction.y);
	}

	//HERE WE UPDATE THE POSITION OF THE BALL BASED ON THE SPEED/DIRECTION
	WhiteBall.position.x = ofMap(convertedRoll, -179.0, 179.0, 0, ofGetWidth(), true);
	WhiteBall.position.y = ofMap(convertedPitch, -90.0, 90.0, 0, ofGetHeight(), true);
	GreenBall.position.x = GreenBall.position.x + GreenBall.direction.x;
	GreenBall.position.y = GreenBall.position.y + GreenBall.direction.y;
	RedBall.position.x = RedBall.position.x + RedBall.direction.x;
	RedBall.position.y = RedBall.position.y + RedBall.direction.y;

}

//--------------------------------------------------------------
void ofApp::draw() {

	ofSetColor(20);
	ofDrawBitmapString("openFrameworks Project", 15, 30);

	if (tcpClient.isConnected()) {
		ofDrawBitmapString("IMU=Roll;Pitch " + msgRx, 15, 700);
	}
	else {
		ofDrawBitmapString("status: server not found. launch server app and check ports!\n\nreconnecting in " + ofToString((5000 - deltaTime) / 1000) + " seconds", 15, 55);
	}

	//AND NOW WE CAN DRAW A CIRCLE AT THE POSITION OF THE BALL WITH ITS RADIUS
	ofSetColor(WhiteBall.color);
	ofDrawCircle(WhiteBall.position, WhiteBall.radius);
	ofSetColor(GreenBall.color);
	ofDrawCircle(GreenBall.position, GreenBall.radius);
	ofSetColor(RedBall.color);
	ofDrawCircle(RedBall.position, RedBall.radius);

}

//--------------------------------------------------------------
void ofApp::keyPressed(ofKeyEventArgs & key) {

}

//--------------------------------------------------------------
void ofApp::keyReleased(int key) {

}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y) {

}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button) {

}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button) {

}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button) {

}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y) {

}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y) {

}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h) {

}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg) {

}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo) {

}

#4

Ok, if that is the case, you need to treat the results of the ofMap as your direction instead of position.

In this code you are changing the position by adding the old position plus the direction (other people would call this the velocity or speed). So instead of setting the WhiteBall to have a random direction in the setup, you want to have direction.x changing according to the roll. Note that if you just use the ofMap as is, it will move the white ball way too fast! you will need to adjust the target range (right now it is set to 0, ofGetWidth() but you might want something more like -6,6 ?).

See where that gets you. Tuning your ofMap can help you get the kind of control that feels right. You are really close!