Maxbotix with Arduino: Inaccurate readings

edited May 2017 in Arduino

Hello,

I am try to control image fade and pixel sorting with a MaxBotix sensor using an Arduino. The readings are correct in the Serial Monitor, but I am getting wildly inaccurate numbers in Processing. Code Below:

Arduino:

 //Feel free to use this code.
//Please be respectful by acknowledging the author in the code if you use or modify it.
//Author: Bruce Allen
//Date: 23/07/09

const int anPin = 1;

//variables needed to store values
long anVolt, inches, cm;
int sum = 0; //Create sum variable so it can be averaged
int avgrange = 60; //Quantity of values to average (sample size)

void setup()
{
  Serial.begin(9600);
}

void loop()
{

  for (int i = 0; i < avgrange ; i++)
  {
    anVolt = analogRead(anPin) / 2;
    sum += anVolt;
    delay(10);
  }

  inches = sum / avgrange;
  cm = inches * 2.54;
  Serial.write(inches);
  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();

  //reset sample total
  sum = 0;

  delay(500);
}

Processing

     import processing.video.*;
        import processing.sound.*;
        import processing.serial.*;

//Media variables
Movie YW;
SoundFile DS1, DS2;
PImage bg, mom, sorted, sonja, test;

//Port
Serial port;

//Sound/Image
float picAlpha = 0;
float vol = 0;

//Glitch
int passes = 64;
int square = 8;
boolean select = false;
int selX = 0;
int selY = 0;
int glitchX = 0;
int glitchY = 0;
boolean vertical = false;
boolean fullSort = false;

//Sensor
int val;

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

  //image
  bg = loadImage("Sonja.jpg");
  sonja = loadImage("Sonja.png");
  mom = loadImage("Mom.png");
  sorted = mom;

  //video
  YW = new Movie(this, "YW_1sm.mp4");
  YW.loop();

  //sound
  DS1 = new SoundFile(this, "DigitalStorge_1.mp3");
  DS1.loop();
  DS1.amp(.75);
  DS2 = new SoundFile(this, "DigitalStorge_2.mp3");
  DS2.loop();

  //sensor
  String portName = Serial.list()[1];
  port = new Serial(this, portName, 9600);
}

void draw() {
  noCursor();

  //sensor
  if (port.available() > 0) {  
   //val = port.readStringUntil('\n');
   val = port.read();
   }
   print(val);
   print("in");
   println();

  //background
  blendMode(BLEND);
  tint(255, 255);
  image(bg, 0, 0);

  //video
  blendMode(SUBTRACT);
  tint(255, 255); 
  image(YW, 0, 0, 1050, 700);

  //Sonja
  blendMode(BLEND);
  tint(255, 255);
  image(sonja, 0, 0);


  //Interaction
  picAlpha = map(val, 48, 0, 0, 225);
  vol = map(val, 48, 0, 0, 1.0);
  glitchX = round(map(val, 24, 0, 300, 650));
  glitchY = round(map(val, 24, 0, 100, 550));

  //Mom
  blendMode(HARD_LIGHT);  
  tint(255, picAlpha);
  image(mom, 0, 0, 1050, 700);
  image(sorted, 0, 0);

  //Glitch 
  if (val < 24) {
    if (select) {
      sorted = mom;
      if (vertical) {
        if (fullSort) {
          fullSortV(selX, selY, glitchX, glitchY);
        } else {
          sortV(selX, selY, glitchX, glitchY);
        }
      } else {
        if (fullSort) {
          fullSortH(selX, selY, glitchX, glitchY);
        } else {
          sortH(selX, selY, glitchX, glitchY);
        }
      }

      select = false;
    } else {
      select=true;
      selX = glitchX;
      selY = glitchY;
    }
  }

  DS2.amp(vol);
}

void movieEvent(Movie m) {
  m.read();
}



//Glitch
void sortH(int startx, int starty, int endx, int endy) {
  int index;
  int index2;
  int swap;

  for (int y = starty; y<endy; y++) {
    for (int i =0; i<passes; i++) {
      for (int x = startx+1; x<endx; x++) {
        index = x+y*mom.width;
        index2 = index-1;
        if (mom.pixels[index] < mom.pixels[index2]) {
          swap = mom.pixels[index];
          mom.pixels[index] = mom.pixels[index2];
          mom.pixels[index2] = swap;
        }
      }
    }
  }
}

void fullSortH(int startx, int starty, int endx, int endy) {
  int index;
  int index2;
  int swap;

  boolean swapped = false;
  for (int y = starty; y<endy; y++) {
    do {
      swapped=false;
      for (int x = startx+1; x<endx; x++) {
        index = x+y*mom.width;
        index2 = index-1;
        if (mom.pixels[index] < mom.pixels[index2]) {
          swap = mom.pixels[index];
          mom.pixels[index] = mom.pixels[index2];
          mom.pixels[index2] = swap;
          swapped = true;
        }
      }
    } while (swapped);
  }
}

void sortV(int startx, int starty, int endx, int endy) {
  int index;
  int index2;
  int swap;


  for (int x = startx; x<endx; x++) {
    for (int i =0; i<passes; i++) {
      for (int y = starty+1; y<endy; y++) {
        index = x+y*mom.width;
        index2 = index-mom.width;
        if (mom.pixels[index] < mom.pixels[index2]) {
          swap = mom.pixels[index];
          mom.pixels[index] = mom.pixels[index2];
          mom.pixels[index2] = swap;
        }
      }
    }
  }
}

void fullSortV(int startx, int starty, int endx, int endy) {
  int index;
  int index2;
  int swap;
  boolean swapped = false;
  for (int x = startx; x<endx; x++) {
    do {
      swapped=false;
      for (int y = starty+1; y<endy; y++) {
        index = x+y*mom.width;
        index2 = index-mom.width;
        if (mom.pixels[index] < mom.pixels[index2]) {
          swap = mom.pixels[index];
          mom.pixels[index] = mom.pixels[index2];
          mom.pixels[index2] = swap;
          swapped = true;
        }
      }
    } while (swapped);
  }
}

Answers

  • I incorporated the suggested changes, but now it doesn't map. Error message: "The method map(float, float, float, float, float) in the type PApplet is not applicable for the arguments (int[], int, int, int, int)."

  • ... arguments (int[], int, int, int, int) ...

    1st argument int[] means you're passing a whole array.
    You need to access them 1 by 1 via the operator []:
    https://Processing.org/reference/arrayaccess.html

  • edited May 2017

    https://www.arduino.cc/en/Serial/Print

    https://www.arduino.cc/en/Serial/write

    In your arduino code:

    Serial.write(inches); Serial.print(inches);

    Do you intend to do this? You are sending a byte value (truncated if inches is out of the byte range) followed by the ascii representation of your number (plus other string info). I will suggest you remove the write reference as you won't need it.

    Now your Processing code. You have two issues:

    #1: You have this in your code:

     blendMode(BLEND);
      tint(255, 255);
      image(bg, 0, 0);
    
      //video
      blendMode(SUBTRACT);
      tint(255, 255); 
      image(YW, 0, 0, 1050, 700);
    
      //Sonja
      blendMode(BLEND);
      tint(255, 255);
      image(sonja, 0, 0);
    

    The draw() function in processing works like this: https://processing.org/reference/draw_.html

    The point to keep in mind in your case is that draw executes 60 fps (or it tries to). You are applying these operation on the image 60fps. Why not to do it once in setup and accumulate all these operation is a second image buffer and use it in draw() instead of performing these operations 60fps?

    #2: What GoToLoop mention in his last post. If you check your arduino code, you are doing this (I am assuming you are not using the write() function:

      Serial.print(inches);
      Serial.print("in, ");
      Serial.print(cm);
      Serial.print("cm");
      Serial.println();
    

    So your data stream looks like this: ##in, ##cm\n....

    However, when you read the data in Processing, you are reading one byte at the time:

    ////val = port.readStringUntil('\n');
       val = port.read();
    

    You should be using your commented out code instead: val = port.readStringUntil('\n');

    Then, it is here where you follow GoToLoop's post: int[] input = int(split(val)); and your line 64 above should look like this instead:

       print(input[0]);  //input[0] contains "##in" so a mix of numbers and letters
       print("in");
       println();
    

    This mix of numbers and letters is undesirable. Check the following code by itself (you can run it in Processing in a new separate window):

    String in="50in,48cm,500in";
    
    println("====Printing the strings without any conversion:");
    println(split(in,','));
    
    println("====Printing the strings converted to integers:");
    println(int(split(in.trim(),',')));   //FAILS: it prints 0
    
    println("====Printing the strings converted to integers, second attempt:");   //REFER to this post: https://forum.processing.org/two/discussion/5517/numberformatexception-with-serial-communication
    println(Integer.parseInt((split(in.trim(),',')[0])));  //FAILS: Throws an error
    exit();
    

    As you see, neither int() nor Integer.parseInt() will succeed in this operation. This can be solved by doing this in your arduino code:

      Serial.print(inches);
      Serial.print(" in, ");   // ## CHANGE: space added
      Serial.print(cm);
      Serial.print(" cm");   // ## CHANGE: space added
      Serial.println(); 
    

    This might not solve all your issues, but it should make your code operational for the next debugging phase if you encounter any other problems.

    Kf

  • Can you clarify what you mean you state: "Why not to do it once in setup and accumulate all these operation is a second image buffer and use it in draw() instead of performing these operations 60fps?" Specifically, how do I create the second image buffer?

  • edited May 2017

    @kinesthtiaSonja --

    Use PGraphics pg and createGraphics() to make the image buffer. Then in setup act on the PGraphics object rather than the canvas with your with your commands: pg.blendMode() pg.image() etc. Finally, display your buffer in draw w eg image(pg, 0, 0)

    https://processing.org/reference/PGraphics.html

  • The PGraphics implementation should improve performance but it is not mandatory. I believe applying blendingModes is expensive and your sketch can easily be optimized. However, if you don't have any issues related to lagging, then you can leave it as it is and focus in the arduino part for now.

    Using a second PGraphics is not difficult. If you check jeremydouglass'es post you can see an example of its usage. If you have problems implementing it, just ask.

    Kf

  • I changed the code as follows to connect with Arduino using Firmata, and it is working now.

    import cc.arduino.*;
    import org.firmata.*;
    
    import processing.video.*;
    import processing.sound.*;
    import processing.serial.*;
    
    //Media variables
    SoundFile DS1, DS2;
    Arduino arduino;
    
    //Sound/Image
    float picAlpha = 0;
    float vol = 0;
    int val;
    
    void setup() {
      fullScreen();
    
      //image
      bg = loadImage("Sonja.jpg");
      sonja = loadImage("Sonja_2.png");
      mom = loadImage("Mom.png");
      sorted = mom;
    
      //video
      YW = new Movie(this, "YW_1sm.mp4");
      YW.loop();
    
      //sound
      DS1 = new SoundFile(this, "DigitalStorge_1a.mp3");
      DS1.loop();
      DS1.amp(.75);
      DS2 = new SoundFile(this, "DigitalStorge_2a.mp3");
      DS2.loop();
    
      //sensor
      println(Arduino.list());
      arduino = new Arduino(this, "/dev/cu.usbmodem1421", 57600);
      arduino.pinMode(0, Arduino.INPUT);
    }
    
    void draw() {
      noCursor();
    
      //sensor
      //val = port.readStringUntil('\n');
      val = arduino.analogRead(0);
      print(val);
      print("in");
      println(); 
    
      //Interaction
      picAlpha = map(val, 80, 12, 0, 225);
      vol = map(val, 80, 0, 0, 1.0);
      glitchX = round(map(val, 24, 0, 410, 900));
      glitchY = round(map(val, 24, 0, 150, 750));
    
      //Sound
      DS2.amp(vol);
    

    However, the volume of one of the soundtracks is supposed to change in relation to movement sensor (DS2), but instead it just plays at the same volume the whole time. Also, I receive this message: "ERROR: /node/free: Cannot free root node 0." I changed the files from stereo to mono, but I still receive this message.

  • Are you calling DS2.stop() in your code by any chance?

    Kf

  • I am not calling DS2.stop(). I added an if, else statement that resolved the issues with volume.

    At the moment, when I leave the images as is, it works fine. However, if I want to set it up so the images fit the resolution of the screen, it lags. The blending modes are needed because the images are layered a particular way, and if I remove them they don't appear properly. I am trying to figure out the PGraphics method to create a buffer, but I am not clear on how to go about this: do I create one for each image or just one overarching buffer?

  • @kinesthtiaSonja

    Can you explain your code, what it suppose to do? Base on functionality, one could come up with an optimized code. Notice that doing resizing and applying blending (or any image operations) in draw could create program overloading aka. lagging issues as you might know by now. You could experience some improvement in your results if you use P2D renderer and if your computers supports accessing Video card resources. However, the key point is to optimize your code but reducing unnecessary operations in draw. If you provide this info, you will get more feedback about how to use PGraphics to alleviate overloading. Notice the suggestion of PGraphics was an idea based on a quick review of your code but without fully understanding it. Also I notice you are working with an arduino so it will limit the number of responses you get. People could always provide extra details to your application but without to be able to run the code, one cannot easily test the layer added by the arduino data. My suggestion: Create a second sketch that does not use arduino. Work on this sketch to get the effect you want (apply proper optimizations) and then migrate it to use your Arduino data.

    Kf

Sign In or Register to comment.