How to use data from accelerometer to control camera and perspective in Processing

no worries!
Actually I gotta sleep hahaha…maybe we can discuss later

1 Like

I put just an arbitrary number. Remember, the more negative Z is, the further the object is away from the camera / screen.

In my Sketch, it’s initially on the middle left.

works here…

too bad…

Are you using a Windows PC?

see Trouble with multiple COM ports -import processing.serial.*;

Sorry my another account have reply restraints… this is my another account
I’m using Mac
if I comment out this part

  //xA = data[0];
  //yA = data[1];
  //zA = data[2];

  //xV = xA*0.1;
  //yV = yA*0.1;
  //zV = zA*0.1;

  //xS = 1/2*xA*0.01+xV*0.1;
  //yS = 1/2*yA*0.01+yV*0.1;
  //zS = 1/2*zA*0.01+zV*0.1;

the solar system and serial data came back (still on right lower)
I’m not sure if I’m writing the Serial part right:

void serialEvent(Serial myPort) {
  //inString = myPort.readSrting();
  //try {
  //  String[]xyz = data.trim().spilt(",");
  //  print("xyz")
  //    if ("xyz".length == 3) {
  //xA = data[0];
  //yA = data[1];
  //zA = data[2];
  //    }
  //  }
  //  catch(Exception e) {
  //    e.printStacktrace();
  //  }
  //}
  String inString = myPort.readStringUntil('\n');
  //xA = data[0];
  //yA = data[1];
  //zA = data[2];

  //xV = xA*0.1;
  //yV = yA*0.1;
  //zV = zA*0.1;

  //xS = 1/2*xA*0.01+xV*0.1;
  //yS = 1/2*yA*0.01+yV*0.1;
  //zS = 1/2*zA*0.01+zV*0.1;

  if (inString != null) {

    inString = trim(inString);
    println(inString);
    data = float (split(inString, ','));
    //println(data[0]);
  }
}

Remove the // here and
place the entire section after the code line
with split()

you mean like this?

import processing.serial.*;
Serial myPort;
String inString ;
float[] data = new float [3];
float xA = data[0];
float yA = data[1];
float zA = data[2];

float xV = xA*0.1;
float yV = yA*0.1;
float zV = zA*0.1;

//float xS = 1/2*xA*0.01+xV*0.1;
//float yS = 1/2*yA*0.01+yV*0.1;
//float zS = 1/2*zA*0.01+zV*0.1;
float xS =width/2;
float yS =height/2;
float zS =333;

Planet sun;

PImage sunTexture;
PImage[] textures = new PImage[10];


float fov = PI/3.0;
float cameraZ = (height/2.0) / tan(fov/2.0);

void setup() {
  size(600, 375, P3D);
  //fullScreen(P3D);

  myPort = new Serial(this, Serial.list()[0], 9600);
  println(Serial.list()[1]);
  myPort.bufferUntil('\n');

  //perspective(fov, float(width)/float(height), cameraZ/10.0, cameraZ*10.0);
  //translate(width/2, height/2, 0);


  sunTexture = loadImage("sun.jpg");
  textures[0] = loadImage("dirt.jpg");
  textures[1] = loadImage("eris.jpg");
  textures[2] = loadImage("golden.jpg");
  textures[3] = loadImage("green.jpg");
  textures[4] = loadImage("ice.jpg");
  textures[5] = loadImage("lava.jpg");
  textures[6] = loadImage("north.jpg");
  textures[7] = loadImage("ocean.jpg");
  textures[8] = loadImage("pink.jpg");
  textures[9] = loadImage("purple.jpg");


  sun = new Planet(50, 0, 0, sunTexture);
  sun.spawnMoons(10, 1);
}

void draw() {
 
  background(0);
  camera(xS, yS, zS, (width/2.0)-300, (height/2.0)-187, 0, 0, 1, 0);
  translate(width/2, height/2, -width);
  sun.show();
  sun.orbit();
  //perspective(fov, float(width)/float(height), cameraZ/10.0, cameraZ*10.0);


  //camera(width/2.0, height/2.0, (height/2.0) / tan(PI*30.0 / 180.0), (width/2.0)-300, (height/2.0)-187, 0, 0, 1, 0);
  //camera(xA/100, yA/100, zA/100, (width/2.0)-800, (height/2.0)-500, 0, 0, 1, 0);
  //camera(0, 0, (height/2) / tan(PI*30.0 / 180.0), width/2.0, height/2.0, cameraZ, 0, 1, 0);
  //translate(width/2,height/2,mouseX);
  //lights();
  //pointLight(255,255,255,0,0,0);
}


void serialEvent(Serial myPort) {
  String inString = myPort.readStringUntil('\n');


  if (inString != null) {

    inString = trim(inString);
    println(inString);
    data = float (split(inString, ','));
    xA = data[0];
    yA = data[1];
    zA = data[2];

    xV = xA*0.1;
    yV = yA*0.1;
    zV = zA*0.1;

    xS = 1/2*xA*0.01+xV*0.1;
    yS = 1/2*yA*0.01+yV*0.1;
    zS = 1/2*zA*0.01+zV*0.1;
    //println(data[0]);
  }
}

Because I wrote this:

 println(Serial.list()[1]);

My console have the right port name as output. However, it’s not printing the xyz accelerations.
If I change the 0 to 1 in the line below, it reads:“Error, disabling serialEvent() for /dev/cu.usbmodem142301
null” This is so weird

myPort = new Serial(this, Serial.list()[0], 9600);
1 Like

First off, remove the 300 here in the code line with camera() please

Also, code looks much better now

For the port trouble
I am no expert

See LED CONTROL Serial myPort vs. Serial port - #2 by noahbuddy

Finally, I fixed the Serial and successfully transported the acceleration data on xyz to Processing. Also, I smoothed the data and not the console prints something like:
2.54,8.49,0.00
2.54,8.52,0.00
2.49,8.49,0.00
2.49,8.61,0.00
2.40,8.58,0.00
2.49,8.46,0.00
2.49,8.52,0.00
2.51,8.58,0.00
2.54,8.49,0.00
This is because I changed the measurement of acceleration to acceleration of gravity. Now the problems left are: the perspective is not focusing at the center, and the planets are not moving according to the xS, yS, zS…

import processing.serial.*;
Serial myPort;
String inString ;
float[] data = new float [3];
float xA = data[0];
float yA = data[1];
float zA = data[2];

float xV = xA*0.1;
float yV = yA*0.1;
float zV = zA*0.1;

//float xS = 1/2*xA*0.01+xV*0.1;
//float yS = 1/2*yA*0.01+yV*0.1;
//float zS = 1/2*zA*0.01+zV*0.1;
float xS =width/2;
float yS =height/2;
float zS =333;

Planet sun;

PImage sunTexture;
PImage[] textures = new PImage[10];


float fov = PI/3.0;
float cameraZ = (height/2.0) / tan(fov/2.0);

void setup() {
  size(600, 375, P3D);


  myPort = new Serial(this, Serial.list()[1], 9600);
  println(Serial.list()[1]);
  myPort.bufferUntil('\n');



  sunTexture = loadImage("sun.jpg");
  textures[0] = loadImage("dirt.jpg");
  textures[1] = loadImage("eris.jpg");
  textures[2] = loadImage("golden.jpg");
  textures[3] = loadImage("green.jpg");
  textures[4] = loadImage("ice.jpg");
  textures[5] = loadImage("lava.jpg");
  textures[6] = loadImage("north.jpg");
  textures[7] = loadImage("ocean.jpg");
  textures[8] = loadImage("pink.jpg");
  textures[9] = loadImage("purple.jpg");


  sun = new Planet(50, 0, 0, sunTexture);
  sun.spawnMoons(10, 1);
}

void draw() {

  background(0);
  camera(xS, yS, zS, (width/2.0), (height/2.0)-187, 0, 0, 1, 0);
  translate(width/2, height/2, -width);
  sun.show();
  sun.orbit();

}


void serialEvent(Serial myPort) {
  
  String inString = myPort.readStringUntil('\n');
  if (inString != null) {

    inString = trim(inString);
    println(inString);
    data = float (split(inString, ','));
    //println(data);
    //xA = data[0];
    //yA = data[1];
    //zA = data[2];

    //xV = xA*0.1;
    //yV = yA*0.1;
    //zV = zA*0.1;

    //xS = 1/2*xA*0.01+xV*0.1;
    //yS = 1/2*yA*0.01+yV*0.1;
    //zS = 1/2*zA*0.01+zV*0.1;

  }
}

If I change xS, yS, zS in this way and uncomment this part in SerialEvent, the screen become absolute black.

float xS = 1/2*xA*0.01+xV*0.1;
float yS = 1/2*yA*0.01+yV*0.1;
float zS = 1/2*zA*0.01+zV*0.1;
//float xS =width/2;
//float yS =height/2;
//float zS =333;
    xA = data[0];
    yA = data[1];
    zA = data[2];

    xV = xA*0.1;
    yV = yA*0.1;
    zV = zA*0.1;

    xS = 1/2*xA*0.01+xV*0.1;
    yS = 1/2*yA*0.01+yV*0.1;
    zS = 1/2*zA*0.01+zV*0.1;

You have to test what is the best solution.

When xS, yS, zS have reasonable start values as in my examples,
you can just change those by using the acceleration data:


    data = float (split(inString, ','));

    xA = data[0];
    yA = data[1];
    zA = data[2];

    xS += xA*10;
    yS += yA*10;
    zS += zA*10;


I had this in my code.

That won’t work when it is before setup().

(my apologies for my mistake)

Instead before setup()
float xS,yS,zS;

and inside setup() AND after size() command

xS = width/2;
yS = height/2;
zS = 333;

Here is an example


float xS;
float yS;
float zS;

void setup() {
  size(700, 700, P3D);

  xS = width/2;
  yS = height/2;
  zS = 333;
}

void draw() {
  camera(xS, yS, zS, 
    width/2.0, height/2.0, 0, 
    0, 1, 0);

  translate(width/2, height/2); 
  sphere (33);
}

I would guess that the planets are off

This works well, the solar system is displayed nicely in the center! But how might it link to the accelerations? I think I’m getting real close to success!

1 Like

we have the camera() command

  camera(xS, yS, zS, 

and xS, yS, zS are global variables that we change (= eye = camera position).

I suggest:

    data = float (split(inString, ','));

    xA = data[0];
    yA = data[1];
    zA = data[2];

    xS += xA*10;
    yS += yA*10;
    zS += zA*10;

Chrisir

here is an example for a changing camera position by mouse movement

Since the “Look At” of the camera stays the same, the change is not big. You just look from left and right to the center.

float xS;
float yS;
float zS;

void setup() {
  size(700, 700, P3D);

  xS = width/2;
  yS = height/2;
  zS = 333;
}

void draw() {
  background(0); 
  lights();

  xS=map(mouseX, 0, width, 0, width); 
  yS=map(mouseY, 0, width, 0, width);

  camera(xS, yS, zS, 
    width/2.0, height/2.0, 0, 
    0, 1, 0);

  translate(width/2, height/2); 
  noStroke(); 
  sphere (33);

  translate(0, -44); 
  box(16);
}

Thanks! Now it seems a bit unstable. It worked twice during my testings, this is when my xA = 0, zA = 0, and yA equals to a relatively small number. The solar system first appears, then it went further and further to the window(because the yA always exists)
The majority times it read:
Error, disabling serialEvent() for /dev/cu.usbmodem142301
null
How might I fix it? (I’m trying to use AP-Sync library but I don’t know how to write my Arduino part to adopt the AP-Sync library
Here is the whole codes right now, I didn’t make much changes:
Processing:

import processing.serial.*;
Serial myPort;
String inString ;
float[] data = new float [3];
float xA = data[0];
float yA = data[1];
float zA = data[2];

float xV = xA*0.1;
float yV = yA*0.1;
float zV = zA*0.1;

//float xS = 1/2*xA*0.01+xV*0.1;
//float yS = 1/2*yA*0.01+yV*0.1;
//float zS = 1/2*zA*0.01+zV*0.1;
//float xS =width/2;
//float yS =height/2;
//float zS =333;
float xS;
float yS;
float zS;


Planet sun;

PImage sunTexture;
PImage[] textures = new PImage[10];


float fov = PI/3.0;
float cameraZ = (height/2.0) / tan(fov/2.0);

void setup() {
  size(700, 700, P3D);
  xS = width/2;
  yS = height/2;
  zS = 333;
  

  myPort = new Serial(this, Serial.list()[1], 9600);
  //println(Serial.list()[1]);
  myPort.bufferUntil('\n');



  sunTexture = loadImage("sun.jpg");
  textures[0] = loadImage("dirt.jpg");
  textures[1] = loadImage("eris.jpg");
  textures[2] = loadImage("golden.jpg");
  textures[3] = loadImage("green.jpg");
  textures[4] = loadImage("ice.jpg");
  textures[5] = loadImage("lava.jpg");
  textures[6] = loadImage("north.jpg");
  textures[7] = loadImage("ocean.jpg");
  textures[8] = loadImage("pink.jpg");
  textures[9] = loadImage("purple.jpg");


  sun = new Planet(50, 0, 0, sunTexture);
  sun.spawnMoons(10, 1);
}

void draw() {

  background(0);
  camera(xS, yS, zS, (width/2.0), (height/2.0), 0, 0, 1, 0);
  translate(width/2, height/2);
  sun.show();
  sun.orbit();

}


void serialEvent(Serial myPort) {
  
  String inString = myPort.readStringUntil('\n');
  if (inString != null) {

    inString = trim(inString);
    println(inString);
    data = float (split(inString, ','));
    //println(data);
    xA = data[0];
    yA = data[1];
    zA = data[2];
    
    xS += xA*10;
    yS += yA*10;
    zS += zA*10;
    
    
    
    
    //xV = xA*0.1;
    //yV = yA*0.1;
    //zV = zA*0.1;

    //xS = 1/2*xA*0.01+xV*0.1;
    //yS = 1/2*yA*0.01+yV*0.1;
    //zS = 1/2*zA*0.01+zV*0.1;
class Planet {
  float radius;
  float angle;
  float distance;
  Planet[] planets;
  float orbitspeed;
  PVector v;
  
  PShape globe;
  

  Planet(float r, float d, float o, PImage img) {
    v = PVector.random3D();
    radius = r;
    distance = d;
    v.mult(distance);
    angle = random(TWO_PI);
    orbitspeed = o;
    
    noStroke();
    noFill();
    globe = createShape(SPHERE,radius);
    //globe.setTexture(img);
    globe.setTexture(img);
  }
  void orbit() {
    angle = angle + orbitspeed;
    if (planets != null) {
      for (int i=0; i<planets.length; i++) {
        planets[i].orbit();
      }
    }
  }
  void spawnMoons(int total, int level) {
    planets = new Planet[total];
    for (int i=0; i<planets.length; i++) {
      float r = radius/(level*4);
      float d = i*6 + random((radius + r), (radius+r)*6);
    
      float o = random(-0.005, 0.005);
      
      planets[i] = new Planet(r, d, o, textures[i]);
      if (level < 2) {
        int num = int(random(0,2));
        planets[i].spawnMoons(num, level+1);
      }
    }
  }


  void show() {
    pushMatrix();
    noStroke();
    fill(255);
    //rotate(angle);
    PVector v2 = new PVector(1,0,1);
    PVector p = v.cross(v2);
    rotate(angle,p.x,p.y,p.z);
    //stroke(255);
    //line(0,0,0,v.x,v.y,v.z);
    //line(0,0,0,p.x,p.y,p.z);
    
    translate(v.x,v.y,v.z);
    
    
    shape(globe);
    if(distance ==0){
      pointLight(255,255,255,300,187,0);
    }
    

    //sphere(radius);
    //ellipse(0, 0, radius*2, radius*2);

    if (planets != null) {
      //println(planets.length);
      for (int i=0; i<planets.length; i++) {
        planets[i].show();
      }
    }
    popMatrix();
  }
}
  }
}

Arduino:

#include <AP_Sync.h>

/**!
 * @file getAcceleration.ino
 * @brief Get the acceleration in the three directions of xyz, the range can be ±6g, ±12g or ±24g
 * @n When using SPI, chip select pin can be modified by changing the value of LIS331HH_CS
 * @copyright  Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
 * @license     The MIT License (MIT)
 * @author [fengli](li.feng@dfrobot.com)
 * @version  V1.0
 * @date  2021-01-16
 * @url https://github.com/DFRobot/DFRobot_LIS
 */

#include <DFRobot_LIS.h>

//When using I2C communication, use the following program to construct an object by DFRobot_LIS331HH_I2C
/*!
 * @brief Constructor 
 * @param pWire I2c controller
 * @param addr  I2C address(0x18/0x19)
 */
//DFRobot_LIS331HH_I2C acce(&Wire,0x18);
DFRobot_LIS331HH_I2C acce;

//When using SPI communication, use the following program to construct an object by DFRobot_LIS331HH_SPI
#if defined(ESP32) || defined(ESP8266)
#define LIS331HH_CS  D3
#elif defined(__AVR__) || defined(ARDUINO_SAM_ZERO)
#define LIS331HH_CS 3
#elif (defined NRF5)
#define LIS331HH_CS 2  //The pin on the development board with the corresponding silkscreen printed as P2
#endif
/*!
 * @brief Constructor 
 * @param cs : Chip selection pinChip selection pin
 * @param spi :SPI controller
 */
//DFRobot_LIS331HH_SPI acce(/*cs = */LIS331HH_CS,&SPI);
//DFRobot_LIS331HH_SPI acce(/*cs = */LIS331HH_CS);

AP_Sync myPort(Serial);


void setup(void){

  Serial.begin(9600);
  //Chip initialization
  while(!acce.begin()){
     delay(1000);
     Serial.println("Initialization failed, please check the connection and I2C address setting");
  }
  //Get chip id
  //Serial.print("chip id : ");
  //Serial.println(acce.getID(),HEX);
  
  /**
    set range:Range(g)
              eLis331hh_6g = 6,/<±6g>/
              eLis331hh_12g = 12,/<±12g>/
              eLis331hh_24g = 24/<±24g>/
  */
  acce.setRange(/*range = */DFRobot_LIS::eLis331hh_6g);

  /**
    Set data measurement rate:
      ePowerDown_0HZ = 0,
      eLowPower_halfHZ,
      eLowPower_1HZ,
      eLowPower_2HZ,
      eLowPower_5HZ,
      eLowPower_10HZ,
      eNormal_50HZ,
      eNormal_100HZ,
      eNormal_400HZ,
      eNormal_1000HZ,
  */
  acce.setAcquireRate(/*rate = */DFRobot_LIS::eNormal_50HZ);
  //Serial.print("Acceleration:\n");
  delay(1000);
}

void loop(void){

    //Get the acceleration in the three directions of xyz
    //The mearsurement range can be ±6g, ±12g or ±24g, set by the setRange() function
    float ax,ay,az;
    
    ax = acce.readAccX();//Get the acceleration in the x direction
    ax = ax/100;
    if(ax>1000){
      ax = 0;
    }
    ay = acce.readAccY();//Get the acceleration in the y direction
    ay = ay/100;
    if(ay>1000){
      ay = 0;
    }
    az = acce.readAccZ();//Get the acceleration in the z direction
    az = az/100;
    if(az>1000){
      az = 0;
    }
    //Serial.print("x: "); //print acceleration
    
    myPort.print(ax);
    myPort.print(",");
    myPort.print(ay);
    myPort.print(",");
    myPort.println(az);
    //Serial.println("\n");
    //Serial.println(" mg");
    delay(100);
}

yA is vertical and z is going into the screen, away from you

Can’t help you with the port problem.

As for the incoming data / xA, yA:

When you hold the accelerometer absolutely still, it sends 0,0,0? And when you move it, a number/ force, and when you stop it’s 0 again? Because then the camera shouldn’t change when you hold it still.

You can change the factor which is now 10, maybe 0.1 or 1 is better?

Yes, you are absolutely right. When I hold it still, it sends: 0.00, 0.00, 0.00. And it’s 0 again when a force stop.
When the factor is 10, the screen is black and when I change it into 1 or 0.1, the solar system appears (the two cases look same)

Do negative values appear when you move in the other direction?

No. The numbers varied from 0.00-10.00

It’s obvious that the numbers get VERY high here.

When you add this to your camera position it’s too far to see anything

You could use constrain on your camera position

I also wonder why we don’t have negative values.

Because when you move the accelerometer left it’s different than move to the right…?