We are about to switch to a new forum software. Until then we have removed the registration on this forum.
I've been working on a graphing sketch to visualise data coming over the serial port, especially with the Arduino in mind. It is a step up from the basic one you find on the Arduino website (on which this is very loosely based) as it constantly scrolls across in real time, automatically zooms to the current range of values of the screen and saves the data as a .csv file so you can open it in Excel and graph it. I'd just like to say thanks to the great Processing developers and the Processing community which was really helpful in taking the quality of this up a notch (or two :P ). I know at least if no one else uses it, it's really helpful to me!
Here it is:
/* Dynamic Grapher
Graphs data coming from the serial port.
In slowGraph mode, the range is automatically adjusted so it zooms to the highest
and lowest values, but can't sample as fast so you have to leave about a 40ms gap between
successive values over the Serial port, or the program will flush out extra values
it can't keep up with to keep the graph in real time (it will print out "Too fast!"
in the console when this happens. It's probably best that you set it fast and let it
eliminate the extra values so switching to fastGraph is easier, and the speed in slowGraph
is optimised (or at least I think that's how it would work). You can press the '+' button
on your keyboard to zoom in and centre on on the current values coming through, and zoom
back out by pressing '-'. The number at the top left is the highest value being displayed
on the screen, the bottom left is the lowest, and the middle number is what's currently
coming through. Pressing 's' switches between slow and fastGraph
In fast graph, there will be no zoom available but it will be able to sample information
much faster. The points will simply be displayed from rangeMin to rangeMax. The number
in the bottom left is the most recent/current value coming through.
The window is resizable so there's no need to worry about setting width and height,
unless you want to change the size that the sketch starts as. If you want full screen,
set fullScreen to true and it will set the sketch to full width and height and
use present mode.
All the points received will be recorded (raw) along with their time since the end of
setup() so the data can be transferred to Excel or other applications. It is saved as
"data.csv" in the sketch's data folder. The sketch can also open this file for you when
it closes using the default application set to open .csv files. You can turn the automatic
opening off by setting autoOpen to false. In Excel, click on one of the points in a cell
and select all (on Mac: command a, on Windows: ctrl a, maybe) or highlight them in your
preferred way and go to charts -> smooth lined scatter (or any scatter you want).
Pressing 'p' will pause/play the graph, but you can still zoom in and change the size of the
sketch, and it will autozoom, acting as normal.
Feel free to ask me anything relevant and worth asking over the Arduino or
Processing forums, which is where I presume you got this from.
This has only been tested on Mac, but should work without problems on any other OS
that Processing supports.
MAKE SURE YOU LOOK AT AND READ THE VARIABLES BELOW TO SUIT YOUR COMPUTER!
*/
import processing.serial.*;
//-------------------------------------- CHANGE THESE ----------------------------------------//
boolean autoOpen = true; // whether or not to auto open data file (data.csv) at end of sketch
// If false, can graph faster but no zoom. If true, Arduino must send info with at least about a 40ms gap
//Can change by pressing 's' while running
boolean slowGraph = true;
int myWidth = 1280, myHeight = 720; // Starting screen size, just here for convenience
//What number your desired port is in the serialList()[] of serial ports which you can see in
byte serialListNum = 0; // the console after starting. Once again here for convenience
int baudRate = 9600; //Match up with your device. Once more, for convenience
//Location of the processing folder. The data file is saved inside the data folder of this sketch
String p5Location ="/Users/JoeBloggs/Documents/Processing";
//Expected minimum and maximum values (range) to come over the serial port
//set to a larger, more extreme range if unsure and the auto zoom will adjust
float rangeMin = 0, rangeMax = 1023;
boolean fullScreen = false; //set to true to run in full screen / present mode
color backgroundColor = color(0);
color pointColor = color(255); //colour of the line
float thickness = 2; //how many pixels thick the line should be (using strokeWeight())
//--------------------------------------- LEAVE THESE ----------------------------------------//
Serial myPort; // The serial port
PrintWriter dataFile;
int initTime;
boolean play = true; //pause/play when you press p
long lastClick; //last time mouse was clicked
float inByte; //inString converted to a float
String inString;//the most recent value over the serial port as a string
float[] points;//where all the values are stored
int arrayPos; //Where we are in the array cycle
float currentVal; //shortcut for graphing
float lowerBound; //used for auto zoom
float higherBound;
float zoom = 1; //for manual zoom when pressing '-' and '=' (plus)keys
float zoomCount; //how many times zoomed in
//------------------------------------------------- SETUP ------------------------------------------------//
void setup() {
if (fullScreen) size(displayWidth, displayHeight);
else size(myWidth, myHeight);
println(Serial.list());
myPort = new Serial(this, Serial.list()[serialListNum], baudRate); //************************ Serial stuff *****************//
// don't generate a serialEvent() unless you get a newline character:
myPort.bufferUntil('\n');
// set inital background:
if (frame != null) {
frame.setResizable(true);
}
points = new float[displayWidth];
dataFile = createWriter("data/data.csv"); //for excel output
dataFile.println("Time,Reading"); //titles for the columns in excel
prepareExitHandler();
// List all the available serial ports
//colour the points
stroke(pointColor);
strokeWeight(thickness);
delay(1500);//stop spike in beginning of serial
initTime = millis();
}
//------------------------------------------------ DRAW ------------------------------------------------//
void draw () {
if (play) {
if (myPort.available() > 0) {
if (myPort.available() > 10) {
println("Too fast!");
myPort.clear();
}
// get the ASCII string:
inString = myPort.readStringUntil('\n');
if (inString != null) {
// trim off any whitespace:
inString = trim(inString);
// convert to an int and map to the screen height:
//println(points[arrayPos]);
inByte = float(inString);
dataFile.println(millis() - initTime + "," + inByte);
dataFile.flush();
if (slowGraph) {
points[arrayPos] = inByte;
slowGraph();
} else {
points[arrayPos] = map(inByte, rangeMin, rangeMax, 1, height);
fastGraph();
}
arrayPos++; //increment the array position
if (arrayPos >= points.length) arrayPos = 0;//reset the array
}
}
} else slowGraph(); //allow zooming/adjusting while paused
}
//------------------------------------------------ FUNCTIONS ------------------------------------//
void slowGraph(){
lowerBound = rangeMax; //set to opposite ends of range so it works
higherBound = rangeMin;
//graph the points
beginShape(LINES);
for (int i = 0; i < width; i++) {
if (arrayPos + 1 + i < width) {
//determine highest + lowest boundaries of points on screen for autozoom
if (points[arrayPos + 1 + i] < lowerBound) lowerBound = points[arrayPos + 1 + i];
else if (points[arrayPos + 1 + i] > higherBound) higherBound = points[arrayPos + 1 + i];
} else {//continue around cycle
if (points[arrayPos + 1 + i - width] < lowerBound) lowerBound = points[arrayPos + 1 + i - width];
else if (points[arrayPos + 1 + i - width] > higherBound) higherBound = points[arrayPos + 1 + i - width];
}
}
background(backgroundColor);
text(inByte, 5, height / 2); //display the incoming value
text(lowerBound, 5, height - 4);
text(higherBound, 5, 9);
for (int i = 0; i < width; i++) {
if (arrayPos + 1 + i < width) currentVal = points[arrayPos + 1 + i];
else currentVal = points[arrayPos + 1 + i - width];
if (zoomCount == 0)
vertex(i, height - map(currentVal, lowerBound, higherBound, 1, height)); // 1 so 0 and 1023 are visible
else
vertex(i, height - map(currentVal, points[arrayPos] - (points[arrayPos] / (zoomCount * zoomCount)), points[arrayPos] + (points[arrayPos] / (zoomCount * zoomCount)), 1, height));
}
endShape();
//println(zoom);
}
void fastGraph() {
background(backgroundColor);
//println(frameRate);
text(inByte, 5, height - 4); //display the incoming value
//graph the points
beginShape(LINES);
for (int i = 0; i < width; i++) {
if (arrayPos + 1 + i < width) {
vertex(i, height - points[arrayPos + 1 + i] - 1); // -1 so 0 and 1023 are visible
} else //continue around cycle
vertex(i, height - points[arrayPos + 1 + i - width]);
}
endShape();
}
void keyPressed() {
if (key == 's') {
slowGraph = !slowGraph;
if(!slowGraph) {
for(int i = 0; i < points.length; i++)
map(points[i], rangeMin, rangeMax, 1, height); //convert all values to fastGraph() range
} else {
for(int i = 0; i < points.length; i++)
map(points[i], 1, height, rangeMin, rangeMax); //convert all values back to slowGraph() range
}
}
if (key == '=') zoomCount++;
if (key == '-' && (zoomCount != 0)) zoomCount--; //stop it inverting
if (key == 'p') play = !play;
}
// When exiting sketch, run what's in run() here:
private void prepareExitHandler () {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run () { //run this when stopping
println("Shutdown Procedure");
// application exit code here
dataFile.close();
//println("dataFile closed");
//Open the data file in its location, trim() just in case
if (autoOpen) open(trim(p5Location) + "/Dynamic_Grapher/data/data.csv");
}
}
));
}
boolean sketchFullScreen() {
return fullScreen;
}