Out of Control y Axis- ROLLING GRAPH

edited August 2016 in Library Questions

Noobie here, I'm trying to take an input from an arduino over a usb and plot onto a graph. Everything is fine except I cant seem to map where the y begins and ends on my graph. I tried so many things but its stuck between 0 and about 400px. You'll see the box where the graph is suppose to occupy. I can't move/control the y axis. I drew out the issue in red on the attached image, please help!!

You'll see reference to multiple sensors, I am only trying to plot the sensor "moisture" for now.

/This sketch is frankensteined from the SerialCall Response example with arduino and 
//this guy's sketch(code in description of video) - http://pastebin.com/kSrU3nVH

import processing.serial.*;

int moisture;  //the only sensor I'm currently trying to plot 
float inByte;
int second; //sensors that ar![]()e not yet plotted but printed in the Console
int third;  // " "
int fourth; // " "

Serial myPort;                       // The serial port
int[] serialInArray = new int[4];    // Where we'll put what we receive
int serialCount = 0;                 // A count of how many bytes we receive
PFont  g_font;
boolean firstContact = false;        // Whether we've heard from the microcontroller

int[] yValues;
int w;

//SETUP
void setup() {
  size(1280, 720);  // Stage size

  g_font = loadFont("ArialMT-20.vlw");
  textFont(g_font, 20);

  w = 640; //width of our plot
  strokeWeight(1);
  smooth(); // or noSmooth();
  yValues = new int[w];

  println(Serial.list());
  String portName = Serial.list()[1];
  myPort = new Serial(this, portName, 9600);
}

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

  if (inString != null) {
    inString = trim(inString);
    inByte = float(inString);

    moisture = int(map(inByte, 0, 255, 360, 25));
    //setting the boundry for the graph line, inside box EXCEPT IT DOESNT WORK
  }

  //This should be down here, but works inside the if statement.
  //moisture = int(map(inByte, 0, 255, 360, 25));

  //Background and box for graph
  background(55);
  rect(25, 25, 615, 335);
  fill(255);
  //LEGEND
  strokeWeight(1.5);
  stroke(255, 0, 0);     
  line(20, 420, 35, 420);
  stroke(0, 255, 0);     
  line(20, 440, 35, 440);
  stroke(0, 0, 255);     
  line(20, 460, 35, 460);
  stroke(255, 255, 0);   
  line(20, 480, 35, 480);

  fill(0, 0, 0);
  text("Moisture", 40, 430);
  text("Analog A1", 40, 450);
  text("Analog A2", 40, 470);
  text("Analog A3", 40, 490);

  //ACTUAL PLOTTED LINE
  for (int i = 1; i < w; i++) {
    yValues[i-1] = yValues[i];
  }
  yValues[w-1] = moisture; 
  //}

  stroke(120, 200, 0);
  line(w, moisture, 0, moisture);
  strokeWeight(3);


  //This controls the horizontal "TICKER LINE"
  for (int i=1; i<w; i++) {
    //stroke (rgb)
    stroke(220, 75, yValues[i]);
    //point folar x, float y
    point(i, yValues[i]);
    rect(0, 25, 50, 335);
    fill(255);
  }
}

void serialEvent(Serial myPort) {
  // read a byte from the serial port:
  int inByte = myPort.read();

  if (firstContact == false) {
    if (inByte == 'A') {
      myPort.clear();          // clear the serial port buffer
      firstContact = true;     // you've had first contact from the microcontroller
      myPort.write('A');       // ask for more
    }
  } else {
    // Add the latest byte from the serial port to array:
    serialInArray[serialCount] = inByte;
    serialCount++;

    // If we have 3 bytes:
    if (serialCount > 3) {
      moisture = serialInArray[0];
      second = serialInArray[1];
      third = serialInArray[2];
      fourth = serialInArray[3];

      // print the values (for debugging purposes only):
      println(moisture + "\t" + second + "\t" + third + "\t" + fourth);

      // Send a capital A to request new sensor readings:
      myPort.write('A');
      // Reset serialCount:
      serialCount = 0;
    }
  }
}

/*ARDUINO UNO CODE
 int firstSensor = 0;    // first analog sensor
 int secondSensor = 0;   // second analog sensor
 int thirdSensor = 0;    // digital sensor
 int fourthSensor = 0; //turning this into a fluxuating value
 int inByte = 0;         // incoming serial byte

 void setup()
 {
 // start serial port at 9600 bps:
 Serial.begin(9600);


 pinMode(2, INPUT);   // digital sensor is on digital pin 2
 establishContact();  // send a byte to establish contact until receiver responds
 }

 void loop()

 {

 // if we get a valid byte, read analog ins:
 if (Serial.available() > 0) {
 // get incoming byte:
 inByte = Serial.read();
 // read first analog input, divide by 4 to make the range 0-255:
 firstSensor = analogRead(A0);
 // delay 10ms to let the ADC recover:
 delay(10);
 // read second analog input, divide by 4 to make the range 0-255:
 secondSensor = analogRead(A1) / 4;
 // read  switch, map it to 0 or 255L
 thirdSensor = map(digitalRead(A2), 0, 1, 0, 255);
 // send sensor values:
 fourthSensor = analogRead(A3) / 4; //new sensor I added
 Serial.write(firstSensor);
 Serial.write(secondSensor);
 Serial.write(thirdSensor);
 Serial.write(fourthSensor);

 }
 }

 void establishContact() {
 while (Serial.available() <= 0) {
 Serial.print('A');   // send a capital A
 delay(300);

 }
 }
 */![Screen Shot 2016-08-18 at 11.45.51 PM](https://forum.processing.org/two/uploads/imageupload/608/2KIEZIROU05C.jpg "Screen Shot 2016-08-18 at 11.45.51 PM")

Answers

  • edited August 2016

    Maybe it's better starting small? O:-)

    /**
     * Efficient Serial ReadBytes (v1.0)
     * GoToLoop (2016-Aug-19)
     *
     * forum.processing.org/two/discussion/17900/
     * out-of-control-y-axis-rolling-graph#Item_1
     */
    
    import processing.serial.Serial;
    
    static final int BYTES = 4, PORT_IDX = 1, BAUDS = 9600;
    final byte[] receivedBytes = new byte[BYTES];
    
    void setup() {
      noLoop();
      final String[] ports = Serial.list();
      printArray(ports);
      new Serial(this, ports[PORT_IDX], BAUDS).buffer(BYTES);
    }
    
    void draw() {
      println(receivedBytes);
    }
    
    void serialEvent(final Serial s) {
      s.readBytes(receivedBytes);
      redraw = true;
    }
    
  • I'll try it whenI get home from work.

    Maybe the serial handshake is f***ing it up, I get the arduino to send A everytime it has new points to plot, where then the processing sketch sends another A to accept them.

    A has a DEC value of 65, maybe that's controlling the sketch somehow.

    Maybe it is better to start small

  • I'm confused, i'm using Serial.println() to print analog values form my arduino, shows up fine in the serial monitor. But I get a pattern of 10, 48, 13 MEaning line feed, o, Carraige return

    I'm certainprinting the serial numbers my way was f***ing up the graph, I have a good serial plotter not but can't view info and can only do one line, ideally want 4.

    Can you explain how I can use your sketch to print analog values from my arduino. I tried all morning and cant do anything with your answer.

  • edited August 2016

    There are mainly 2 ways to send bytes: via write() & via print().
    The example code I've posted above is ready to accept 4 write() bytes for 1 serialEvent().

    If you prefer print(), it's another approach. For the corresponding Arduino code, go here:
    https://forum.Processing.org/two/discussion/14988/drawing-of-graphs-from-i2c-imu

    Gonna repost the corresponding Processing sketch below:

    /**
     * Efficient Serial Multi-Value Reading (v1.1.1)
     * GoToLoop (2015-Feb-18)
     *
     * forum.Processing.org/two/discussion/16618/
     * processing-with-arduino-void-serialevent#Item_1
     *
     * forum.Processing.org/two/discussion/14988/
     * drawing-of-graphs-from-i2c-imu#Item_3
     */
    
    import processing.serial.Serial;
    
    static final int PORT_INDEX = 1, BAUDS = 9600;
    
    int[] vals;
    //float[] vals;
    
    void setup() {
      noLoop();
      final String[] ports = Serial.list();
      printArray(ports);
      new Serial(this, ports[PORT_INDEX], BAUDS).bufferUntil(ENTER);
    }
    
    void draw() {
      println(vals);
    }
    
    void serialEvent(final Serial s) {
      vals = int(splitTokens(s.readString()));
      //vals = float(splitTokens(s.readString()));
      redraw = true;
    }
    
  • Okay, I'll look through this thread you posted.

    I can get the code you posted to respond to sensor changes a little, but If a sensor is reading 1023 on the arduino side, your code will only output around -50 or 60.

  • edited August 2016 Answer ✓

    1 byte is 8 bits. Value 1023 needs 10 bits though. It is 2 bits more than a byte can fit in! :-SS
    Simplest solution is right-shift the value by 2 bits via >>> operator: int val = analogRead(A1) >>> 2;
    That's gonna constrain the value within 0 - 255 range rather than 0 - 1023.
    However, only do that if you don't mind losing those 2 bits of precision. :-@

  • edited August 2016

    Another issue is that in Java, the datatype byte varies from -128 to 127 range, rather than 0 to 255!
    In order to convert the signed range byte to unsigned range, we're gonna need to & 0xff that:

    byte b = -50;
    int  i = b & 0xff;
    
    println(b, i); // -50 206
    exit();
    
  • Yes that's a good answer, I'll keep it 0-255, thanks.

    So pretty much I a want to serial.println a sensor reasing in my arduino code I replace

    Serial.print(moisture);

    with

    byte b = -50;
    int  i = b & 0xff;
    
    println(b, i); // -50 206
    exit();
    
  • edited August 2016 Answer ✓

    That conversion is needed for the receiving Java, not the sender C language code.
    And only if using the 1st approach w/ write().

    The print() approach doesn't need either the >>> operator nor the unsigned to signed byte range conversion! $-)

    Only disadvantage is the number of bytes sent w/ print() (varying # of bytes) is gr8 than w/ write() (fixed # of bytes). 8-|

  • Thanks! This is perfect; I can expand the amount of sensors and data I send over to processing and have an easy graph setup.

    Do you think I can use your method to sent 8 sensor values and yse the same method to graph each one?

    import processing.serial.Serial; float[] vals = new float[3];

    int[] yValues;
    int w;
    
    int xn = 0;
    float yn1 = 250;
    int xk = 0; 
    
    void setup() {
      size(300, 200);
      w = width;
      noLoop();
      new Serial(this, "/dev/cu.wchusbserial1420", 9600).bufferUntil(ENTER);
    
      background(77);
    
    }
    void draw() {
      print(vals[0]);
      print ("\t");
      print (vals[1]);
      print ("\t");
      println(vals[2]);
    
      // A0
      float m = map((vals[0]), 0, 255, 200, 0);
      stroke(255);
      //Plotted Line
      line (xn, yn1, xk, m); 
    
    
      //endstop
      if (xk > 300) {  
        background(77); 
        noStroke();
        rect(0, 200, 300, 200);
        fill(55);
        xk=0;
      }      
      xn=xk; 
      yn1 = m;
      xk++;
    }
    
    void serialEvent(final Serial s) {
      vals = float(splitTokens(s.readString()));
      redraw();
    }
    
    /*//Arduino Code - Work1.2.1
     float firstSen;   
     float secondSen;
     float thirdSen;
    
     boolean DR;
     unsigned long t;
     unsigned long tt;
    
     void setup() {
     Serial.begin (9600);
    
     DR = true;
    
     }
     void loop() {
    
     firstSen = analogRead(A0) / 4;
     secondSen = analogRead(A1) / 4;
     thirdSen = analogRead(A2) / 4;
    
     Serial.print(firstSen);
     Serial.print("\t");
     Serial.print(secondSen);
    
     Serial.print("\t");
     Serial.println(thirdSen);
     t = tt;
     delay(50);
     }
     */
    
  • Not particular good at graphing. Given it's constantly receiving data from Arduino, once it reaches canvas' width, you're gonna need to start from the left side again. Like this:

    http://studio.SketchPad.cc/sp/pad/view/XH7YUVNwqZ/latest

    Just choose 8 distinct colors for each of those 8 sensors and you're done. :D

  • Yeah I know, the refresh and beginning at the leftside isn't the most sexy solution, but for now I'm satisfied. Later I'll implement a rolling element. I'll check out what you posted though.

    Thanks again for the first bit of code you posted, this method of serial is great because I can add more sensors easily.

Sign In or Register to comment.