Recording Realtime Graph for reviewing

I am currently using an Arduino and Processing Interface in order to view readings of 5 sensors via Realtime Graph in processing. I just wondered if it is possible to record my readings somehow to review later and possibly pause the graph at certain time intervals. Does Anyone have any good Ideas how this could be done?

I just wondered if it is possible to record my readings somehow to review later

Well, Processing runs on a PC, right? There is a file allocation table on your PC, right? This means that you can create files, right? Of course you can. The Processing application is a file. So, look into creating and writing to files using Processing.

possibly pause the graph at certain time intervals

Pause as in stop collecting and graphing the data? Or, pause as in continue collecting the data, but stop updating the screen for a period of time? Both are possible.

ok, With Pause I mean, Pause as in stop collecting and graphing the data. Let me put it this way, I can at the moment see the graph live of all the sensors which is great.

So I would like to collect data for a minute or two, stop collecting data then have a playback option to review the data, except this time be able to pause the graph at certain intervals of time in order to document sensor readings at specific times. :-/

Pausing is easy.

Data is being collected in each pass through the draw function. Add a boolean flag, initialized to false. Draw a “Wait, wait, hold on a minute…” “button”. Detect mouse clicks in that “button”. If a mouse click occurs in that area, toggle the boolean (flag = !flag).

If the boolean (flag) is true, collect, record, and draw the next batch of data. If not, don’t.

The playback option is really a separate program. You can define that in any way you like.

Thanks, that makes some sense and seems simple, although it seems that the playback option would be the main control I need for what I want to create.

With "another program" do you mean one of the programs like a video program loaded in processing/libraries? Any suggestions on which program would be suitable to use for the playback feature and how I will be able to combine it with my existing program in order to 'playback' my Realtime graph?

This is the first time I am using processing or Arduino.

Thanks for all the help so far and having patience with the newbe ::slight_smile:

You have copied or written a Processing application to collect, record, and graph data.

The "another program" I was referring to is the new program you will write to replay that recorded data, in whatever was seems appropriate to you. There is nothing existing that will replay your data the way that you want.

Thank you. I understand. At the moment I am using this code below in order to visualise the realtime graph. Any Ideas for a code for the playback option? All I need really is a recording of the values of the individual sensors at certain times. If I am not able to program a playback code to actually see a visual graph, cant I somehow just review the values of the sensors numerically at certain times?

/*
  Serial Graphing Sketch
  by Tom Igoe
  Language: Processing
 
  This sketch takes ASCII values from the serial port 
  at 9600 bps and graphs them.
  The values should be comma-delimited, with a newline 
  at the end of every set of values.
  The expected range of the values is between 0 and 1023.
 
  Created 20 April 2005
  Updated 27 June 2008
*/
 
import processing.serial.*;
 
int maxNumberOfSensors = 6;       // Arduino has 6 analog inputs, so I chose 6
boolean fontInitialized = false;  // whether the font's been initialized
Serial myPort;                    // The serial port
 
float[] previousValue = new float[maxNumberOfSensors];  // array of previous values
int xpos = 0;                     // x position of the graph
PFont myFont;                     // font for writing text to the window
 
void setup () {
  // set up the window to whatever size you want:
  size(800, 600);        
  // List all the available serial ports:
  println(Serial.list());
  // I know that the first port in the serial list on my mac
  // is always my  Arduino or Wiring module, so I open Serial.list()[0].
  // Open whatever port is the one you're using.
  String portName = Serial.list()[0];
  myPort = new Serial(this, portName, 9600);
  myPort.clear();
  // don't generate a serialEvent() until you get a newline (\n) byte:
  myPort.bufferUntil('\n');
  // create a font with the fourth font available to the system:
  myFont = createFont(PFont.list()[3], 14);
  textFont(myFont);
  fontInitialized = true;
  // set inital background:
  background(0);
  // turn on antialiasing:
  smooth();
}
 
void draw () {
  // nothing happens in the draw loop, 
  // but it's needed to keep the program running
}
 
void serialEvent (Serial myPort) {
  // get the ASCII string:
  String inString = myPort.readStringUntil('\n');
 
  // if it's not empty:
  if (inString != null) {
    // trim off any whitespace:
    inString = trim(inString);
 
    // convert to an array of ints:
    int incomingValues[] = int(split(inString, ","));
 
    // print out the values
    //  print("length: " + incomingValues.length + " values.\t");
    if (incomingValues.length <= maxNumberOfSensors && incomingValues.length > 0) {
      for (int i = 0; i < incomingValues.length; i++) {
 
        // map the incoming values (0 to  1023) to an appropriate
        // graphing range (0 to window height/number of values):
        float ypos = map(incomingValues[i], 0, 1023, 0, height/incomingValues.length);
 
        // figure out the y position for this particular graph:
        float graphBottom = i * height/incomingValues.length;
        ypos = ypos + graphBottom;
 
        // make a black block to erase the previous text:
        noStroke();
        fill(0);
        rect(10, graphBottom+1, 110, 20);
 
        // print the sensor numbers to the screen:
        fill(255);
        int textPos = int(graphBottom) + 14;
        // sometimes serialEvent() can happen before setup() is done.
        // so you need to make sure the font is initialized before
        // you text():
        if (fontInitialized) {
          text("Sensor " + i + ":" + incomingValues[i], 10, textPos);
        }
        // draw a line at the bottom of each graph:
        stroke(127);
        line(0, graphBottom, width, graphBottom);
        // change colors to draw the graph line:
        stroke(64*i, 32*i, 255);
        line(xpos, previousValue[i], xpos+1, ypos);
        // save the current value to be the next time's previous value:
        previousValue[i] = ypos;
      }
    }
    // if you've drawn to the edge of the window, start at the beginning again:
    if (xpos >= width) {
      xpos = 0;
      background(0);
    } 
    else {
      xpos++;
    }
  }
}

The serialEvent method is called every time there is serial data to read. In that method, add code to write the time and the values to a file (that is opened in setup()).

If the file is a .csv file (comma separated values), and the fields (time and sensor values) are separated by some separator (there's a hint in the name), the file can be opened in Excel. Then, you can just scroll down and see the values. If you are fancy with Excel (I am not), you can even make it graph values for you. Or find the minimum and maximum.

That sounds like a wonderfull idea and I understand the concept but for the last two days I have been struggeling with understanding the code. I got some examples from the libraries on saving Strings, frames, bytes, Streams etc. It seems that I should add the code right at the end of my existing processing code, with some of them the program runs but makes the graph in the sketch very slow. It just seems like I have no clue what's going on. Could It be possible to give me an example of what the code should look like and where to add it? Please :cry:

For starters try putting

PrintWriter output;

at the start of your Processing code (after "Serial myPort;" would be a good place.

Then put

output = createWriter("positions.txt");

in setup() at the end of the function, and put

output.println(ypos);

in the serialEvent() function, just after these two lines

// save the current value to be the next time's previous value:
previousValue[i] = ypos;

This should get you a file called "positions.txt" containing your graph's y values. You can then alter it to do what you want.

I'm basing this on the example at the processing.org website - http://processing.org/learning/topics/savefile2.html - did you look at these examples?

Andrew

Thanks, At the moment I am getting an error ( Cannot find anything named “ypos”). Any ideas?

import processing.serial.*;
 
int maxNumberOfSensors = 3;       // Arduino has 6 analog inputs, so I chose 6
boolean fontInitialized = false;  // whether the font's been initialized
Serial myPort;   // The serial port
PrintWriter output;
 
float[] previousValue = new float[maxNumberOfSensors];  // array of previous values
int xpos = 0;                     // x position of the graph
PFont myFont;                     // font for writing text to the window
 
void setup () {
  // set up the window to whatever size you want:
  size(800, 600);        
  // List all the available serial ports:
  println(Serial.list());
  // I know that the first port in the serial list on my mac
  // is always my  Arduino or Wiring module, so I open Serial.list()[0].
  // Open whatever port is the one you're using.
  String portName = Serial.list()[4];
  myPort = new Serial(this, portName, 9600);
  myPort.clear();
  // don't generate a serialEvent() until you get a newline (\n) byte:
  myPort.bufferUntil('\n');
  // create a font with the fourth font available to the system:
  myFont = createFont(PFont.list()[3], 14);
  textFont(myFont);
  fontInitialized = true;
  // set inital background:
  background(0);
  // turn on antialiasing:
  smooth();
  output.printIn(ypos);
}
 
void draw () {
  // nothing happens in the draw loop, 
  // but it's needed to keep the program running
}
 
void serialEvent (Serial myPort) {
  // get the ASCII string:
  String inString = myPort.readStringUntil('\n');
 
  // if it's not empty:
  if (inString != null) {
    // trim off any whitespace:
    inString = trim(inString);
 
    // convert to an array of ints:
    int incomingValues[] = int(split(inString, ","));
 
    // print out the values
    //  print("length: " + incomingValues.length + " values.\t");
    if (incomingValues.length <= maxNumberOfSensors && incomingValues.length > 0) {
      for (int i = 0; i < incomingValues.length; i++) {
 
        // map the incoming values (0 to  1023) to an appropriate
        // graphing range (0 to window height/number of values):
        float ypos = map(incomingValues[i], 0, 1023, 0, height/incomingValues.length);
 
        // figure out the y position for this particular graph:
        float graphBottom = i * height/incomingValues.length;
        ypos = ypos + graphBottom;
 
        // make a black block to erase the previous text:
        noStroke();
        fill(0);
        rect(10, graphBottom+1, 110, 20);
 
        // print the sensor numbers to the screen:
        fill(255);
        int textPos = int(graphBottom) + 14;
        // sometimes serialEvent() can happen before setup() is done.
        // so you need to make sure the font is initialized before
        // you text():
        if (fontInitialized) {
          text("Sensor " + i + ":" + incomingValues[i], 10, textPos);
        }
        // draw a line at the bottom of each graph:
        stroke(127);
        line(0, graphBottom, width, graphBottom);
        // change colors to draw the graph line:
        stroke(64*i, 32*i, 255);
        line(xpos, previousValue[i], xpos+1, ypos);
        // save the current value to be the next time's previous value:
        previousValue[i] = ypos;
      }
    }
    // if you've drawn to the edge of the window, start at the beginning again:
    if (xpos >= width) {
      xpos = 0;
      background(0);
    } 
    else {
      xpos++;
      // save the current value to be the next time's previous value:
previousValue[i] = ypos;
    }
  }
}

Yes, you've put the output.println(ypos) in the wrong place. Like I said, it goes in the serialEvent() function towards the end, just after previousValue*=ypos;*
Andrew

Oops, ok its working fine now and I have changed it to a csv file like PaulS said so that it is simpler to convert into a graph.

Thanks a million you guys!!! :slight_smile: