1

I am working on an artistic project that includes an ADXL345 sensor (accelerometer), Arduino Uno R3 Board, Arduino IDE 2.0.3 and Processing 4.1.2. I want Processing to display images randomly and continuously every time the values of the sensor that are received from the serial communication with the Arduino sketch, go x>5, x<-5, y.5, y.-5, z>1, z<-1.

UPDATE: A friend helped me with some lines of code and now the image being displayed when I move the sensor.

CHALLENGE: What I want to be able to do now is run the processing sketch ONCE and let the windows containing the images pop up, close down, open new windows and display new random images from my folder. For this process to repeat on itself so I don't have to run the sketch manually every time.

These are the codes that I am using in Arudino and Processing.

ARDUINO

void setup() {
  // initialize serial communication at 9600 baud rate
  Serial.begin(9600);
}

void loop() {
  // send x, y, and z values over serial
  int x = analogRead(A0);
  int y = analogRead(A1);
  int z = digitalRead(2);
  Serial.print(x);
  Serial.print(",");
  Serial.print(y);
  Serial.print(",");
  Serial.println(z);
  delay(1000);
}

& PROCESSING

import processing.serial.*;
Serial mySerial;
PImage fragment;
int rand;

void setup() {
  size(1000, 500);
  rand = int(random(0,133)); 
  takerandomimage("C:/Users/user/Documents/Processing/Trial_300123/frag_" + nf(rand, 3) + ".jpg");
  String portName = Serial.list()[0];
  mySerial = new Serial(this, portName, 9600);
  println("Serial port connected: " + portName);
  loop();
}

void takerandomimage(String fn) {
   fragment = loadImage(fn); 
   println(fragment);
}

void draw() {
  background(255); //clears the screen
  if (fragment.width>0 && fragment.height > 0){ //check if image has been loaded
    String data = mySerial.readStringUntil('\n');
    if (data != null && data != "\n" && data != " " && data != "\r" && data != "\t") {
      println("Data received: " + data);
      String[] values = data.split(" ",0);
      int counter = 0;
      int x = 0;
      int y = 0;
      int z = 0;
      for(String w :values){
         System.out.println(w); 
         if (counter == 1)
         x = int(w);
        if ( counter == 4)
         y = int(w);
        if ( counter == 7)
         z = int(w);
        counter++;
        }
        println(x);
        println(y);
        println(z);
      if (x < 0 || y > 0 || z > 0) {
          takerandomimage("C:/Users/user/Documents/Processing/Trial_300123/frag_" + nf(rand, 3) + ".jpg");
          image(fragment, 0,0);
          delay(1000);
        }
      }
    }
  }

Thank you!!

Livia Tice
  • 11
  • 2

1 Answers1

0

So many things are right with your code (and question)(+1):

  • your question mentions version of the Arduino/Processing used, Arduino board, sensor and also includes minimal code focused on the issue
  • checking if data is not null
  • checking if the split array is right length, etc.

You are so close !

There is one issue throwing off parsing:

  • your Processing code assumes you have the x, y, z values one line: String data = mySerial.readStringUntil('\n');
  • your Arduino code is printling mulitple lines instead of one(e.g. Serial.println(x); instead of Serial.print(x))
  • to double check, you would see x, y, z values (as well as the two "," symbols) in Serial Monitor on separate lines each (instead of all on one line)

I suspect the intention was to use println() only on the last line:

void setup() {
  // initialize serial communication at 9600 baud rate
  Serial.begin(9600);
}

void loop() {
  // send x, y, and z values over serial
  int x = analogRead(A0);
  int y = analogRead(A1);
  int z = digitalRead(2);
  Serial.print(x);
  Serial.print(",");
  Serial.print(y);
  Serial.print(",");
  Serial.println(z);
  delay(1000);
}

(The above should print 3 values separated by "," on a single line. Double check if z should be a digitalRead(2) (returning 1 or 0) reading on pin 2 on your Arduino or analogRead(A2) (returning 0 to 1023) reading analog pin A2.)

Update

Another idea is move the accelerometer conditions in the Arduino code and simply output a single byte/character ('i' for example) when you want to trigger loading a random image in Processing.

Here's a rough / untested example:


void setup() {
  // initialize serial communication at 9600 baud rate
  Serial.begin(9600);
}

void loop() {
  // send x, y, and z values over serial
  int x = analogRead(A0);
  int y = analogRead(A1);
  int z = digitalRead(2);
  // if the right motion is picked up, send 'i' to Processing to load a random image
  if (x > 5 || y > 5 || z > 0) {
    Serial.print('i');
  }

  delay(1000);
}

Note that with the 1000 ms (1s) delay means you can't trigger an image change faster than that. Adjust the delay as needed. Additionally you say you want to trigger the change when accelerometer values meet these conditions:

x>5, x<-5, y.5, y.-5, z>1, z<-1.

If so, you might want to change:

if (x > 5 || y > 5 || z > 0) 

to

if (abs(x) > 5 || abs(y) > 5 || abs(z) > 0)

(where abs() returns the absolute value (e.g. 5 remains 5, but -5 becomes 5))

Hopefully the values you selected make sense for the motion you're trying to capture with the accelerometer (otherwise the Serial Plot tool in Arduino can be helpful).

If you move the condition to Arduino it simplifies the Processing code as you don't need to wait for a full string to parse into an array: you can read just one character.

import processing.serial.*;
Serial mySerial;
PImage fragment;

void setup() {
  size(1000, 500);
  loadRandomImage(); 
  String portName = Serial.list()[0];
  mySerial = new Serial(this, portName, 9600);
}

void loadRandomImage() {
  int rand = int(random(0,133)); 
  String filename = "frag_" + nf(rand, 3) + ".jpg"
  fragment = loadImage(fn); 
}

void draw() {
  // if there is a serial data
  if(mySerial.available > 0){
    // read one byte/char (mask (&) because byte range is -127 to 127 in java, but 0 to 255 in c++)
    char data = mySerial.read() & 255;
    // optional: print serial to debug
    println("received from Arduino", data);
    // if the expect char (i) was received, load random image
    if(data == 'i'){
      loadRandomImage();
    }
  }
    
  background(255); //clears the screen
  if (fragment.width>0){ //check if image has been loaded
    image(fragment, 0, 0);
  }
}

Notice a few other minor changes:

  • I've renamed takerandomimage to loadRandomImage() and changed it so each time the function gets called it generates the random number and loads the random image
  • whenever the 'i' character is received from Ardiuno a new random is loaded
  • whatever random image has been set gets displayed continuously (decoupling image display from reading serial data). Before you only loaded and displayed an image if the right serial data came through).
George Profenza
  • 50,687
  • 19
  • 144
  • 218
  • Thank you so much for responding! I tried to implement these changes. I used println only on the z value in Arduino, however nothing changed. I think the digital read is correct. Do you perhaps have any other ideas? Bless you! :) – Livia Tice Jan 29 '23 at 08:26
  • I haven't used the ADXL345 myself, but having a quick look on [Adafruit](https://learn.adafruit.com/adxl345-digital-accelerometer/assembly-and-wiring) I see the few differences. It appears the sensor is used with the I2C protocol using pins A4 (SDA (data)) and A5 (SCL (clock)); Additionally I see the examples use the [Adafruit_Sensor](https://github.com/adafruit/Adafruit_Sensor) and [Adafruit_ADXL345](https://github.com/adafruit/Adafruit_ADXL345) libraries which your code doesn't use. Maybe I'm looking at the wrong thing ? – George Profenza Jan 29 '23 at 22:39
  • @LiviaTice Could you please update your question with a few lines of the output from Serial Monitor before and after the code change above to compare ? Also, just to check I understood what you're trying to achieve, you want to swap to a different image each time there is motion (e.g. someone shakes the accelerometer) ? Is that correct or am I missing some nuance ? – George Profenza Jan 29 '23 at 22:41
  • Hello again! Thank you for responding. I worked on some lines with a friend and now it is working. I updated the situation and the code. What I want to ask you is whether you know how to make a window pop up, close and open a new one, repeat the process without having to run the sketch every time? – Livia Tice Jan 30 '23 at 12:25
  • Hi @LiviaTice ! I've just read your update: so it seems using println() at the end was part of the solution. It seems [triming whitespace](https://processing.org/reference/trim_.html) is the other part. (You can try calling `data = data.trim()` after the `null` check to potentially simplify your code: ```if (data != null) { data = data.trim();String[] values = split(data, ",");//etc...```). Regarding your second question I recommend posting it on the site with clarifications (and a snippet of code you've tried so far?). Can you clarify there what you mean by window (containing what) ? – George Profenza Jan 30 '23 at 14:11
  • What I mean by window is the setup, so the window where the image is being displayed. What I want to achieve is for the window to close and open by itself, revealing new images everytime. I tried using the functions loop() and redraw() but they didnt work, or maybe i didn't use it correctly. Any thoughts? – Livia Tice Jan 30 '23 at 14:52
  • @LiviaTice To double check I understood: by window you mean the window of you running sketch. Do you need to close and open the window to display a new image ? Would it be sufficient to simply swap the previously displayed image with the new random image whenever there's a new movement event(shake/etc) from the accelerometer ? (without closing/opening the window). Can you please post a new question ? – George Profenza Jan 30 '23 at 22:11