This Processing sketch takes data from the serial port, graphs it, and writes it to a text file with a time stamp if there’s a significant change in any of the incoming values. It expects five values between 0-255 in ASCII, separated by tabs, and ended by a carriage return and newline.
The text file it generates is tab-delimited, and can be read easily in a spreadsheet.
A Wiring/Arduino program to send data to this sketch follows at the end.
/* Grapher Pro! by Tom Igoe This program takes raw bytes from the serial port at 9600 baud and graphs them. It expects five ASCII-encoded decimal values from 0-255, tab-delimited, ended by a newline and carriage return. To change which of the five channels is being shown in the graph, type 0 through 5. When any of the values changes by more than a set threshold, the program writes the data to a text file called dataFile.txt, which can be found in the sketch's directory. Created 20 April 2005 Updated 15 October 2007 */ import processing.serial.*; Serial myPort; // The serial port int arrayLength = 5; // number of values to expect int [] sensorValues = new int [arrayLength]; // array to hold the incoming values int hPosition = 0; // horizontal position on the graph int displayChannel = 0; // which of the five readings is being displayed String dataSet; // string holding the incoming data int threshold = 50; // threshold for whether or not to write // data to a file void setup () { size (400, 300); // window size // List all the available serial ports println (Serial. list ()); // I know that the third port in the serial list on my mac // is always my Keyspan adaptor, so I open Serial.list()[2]. // Open whatever port is the one you're using. myPort = new Serial( this , Serial. list ()[0], 9600); // clear the serial buffer: myPort.clear(); // don't generate a serialEvent() until you get a carriage return myPort.bufferUntil( '\r' ); // ASCII 13 // create a font with the second font available to the system: PFont myFont = createFont ( PFont . list ()[1], 24 ); textFont (myFont); // make the graphics smooth: smooth (); // set inital background: background (0); } void draw () { // if the value for the given channel is valid, graph it: if (sensorValues[displayChannel] > 0 ) { // print the name of the channel being graphed: text ( "Channel: " + displayChannel, 30, 30); // draw the graph: graph(sensorValues[displayChannel]); } } void serialEvent(Serial myPort) { // read incoming data until you get a newline: String serialString = myPort.readStringUntil( '\n' ); // if the read data is a real string, parse it: if (serialString != null ) { // save the string in case you need to write it to a file: dataSet = serialString; // split it into substrings on the tab character: String [] numbers = split (serialString, "\t" ); // convert each subastring into an int for ( int i = 0; i < numbers. length ; i++) { // make sure you're only reading as many numbers as // you can fit in the array: if (numbers. length <= arrayLength) { // trim off any whitespace from the substring: numbers[i] = trim (numbers[i]); // find the difference between the current value and the incoming value: int diff = abs (sensorValues[i] - int (numbers[i])); // if the difference exceeds the threshold, write the data to a file: if (diff > threshold) { writeToFile(); } // save the new values in the array for use in graphing: sensorValues[i] = int (numbers[i]); } } } } void graph ( int numberToGraph) { // draw the line: stroke (0,255,0); line (hPosition, height , hPosition, height - numberToGraph); // at the edge of the screen, go back to the beginning: if (hPosition >= width ) { hPosition = 0; // wipe the screen clean: background (0); } else { // advance the horizontal position on the graph: hPosition++; } } void keyPressed () { // if the key pressed is "0" through "4" if ((48 <= key ) && ( key <= 52)) { // set the display channel accordingly displayChannel = key - 48; // wipe the screen: background (0); } } void writeToFile() { // string for the new data you'll write to the file: String [] newData = new String [1]; // add a time stamp: newData[0] = timeStamp(); // add a tab: newData[0] += "\t" ; // add the latest data from the serial port: newData[0] += trim (dataSet); // get the existing data from the file: String [] dataSoFar = loadStrings ( "dataFile.txt" ); // if there's something there, dump it into an array // and add the new data to it: if (dataSoFar != null ) { // array needs to accommodate the old data and the new: String [] dataToWrite = new String [dataSoFar. length + newData. length ]; // dump the existing data from the file back in: for ( int s = 0; s < dataSoFar. length ; s++) { dataToWrite[s] = dataSoFar[s]; } // append the new data: for ( int s = dataSoFar. length ; s < dataToWrite. length ; s++) { dataToWrite[s] = newData[s-dataSoFar. length ]; } // dump the result back to the file: saveStrings ( "dataFile.txt" , dataToWrite); } // if there's no existing data: else { // add a header to the file: newData[0] = "Time:\tSensor 1\tSensor 2\tSensor 3\tSensor 4\tSensor 5" ; saveStrings ( "dataFile.txt" , newData); } } // make up a timeStamp string for writing data to the file: String timeStamp() { String now = hour ()+ ":" + minute ()+ ":" + second ()+ " " + month () + "/" + day () + "/" + year (); return now; } |
Here's a Wiring/Arduino program to send some serial data that the grapher can read:
/* Read in five analog sensor values, send them out as tab-delimited ASCCI-encoded decimal numbers, ended by a carriage return and newline created 15 Oct. 2007 */ void setup() { // iniaialize the serial port: Serial.begin(9600); } void loop() { // count from 0 to 4 (five channels): for (int channel=0; channel <=4; channel++) { // read an analog input, divide the result by 4 // to limit it to 0-255: int sensorValue = analogRead(channel)/4; // print it: Serial.print(sensorValue, DEC); // if it's not the last channel to be read, // print a tab character: if (channel < 4) { Serial.print("\t"); } // delay to let the analog-to-digital converter settle: delay(8); } // once you've printed all the values, // print a newline and carriage return: Serial.println(); }