import processing.core.*; import processing.serial.*; import java.applet.*; import java.awt.*; import java.awt.image.*; import java.awt.event.*; import java.io.*; import java.net.*; import java.text.*; import java.util.*; import java.util.zip.*; import javax.sound.midi.*; import javax.sound.midi.spi.*; import javax.sound.sampled.*; import javax.sound.sampled.spi.*; import java.util.regex.*; import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.dom.*; import javax.xml.transform.sax.*; import javax.xml.transform.stream.*; import org.xml.sax.*; import org.xml.sax.ext.*; import org.xml.sax.helpers.*; public class BBC_Grapher extends PApplet {/* BBCC_Grapher by Tim Hirzel, Feb 2008 With gratitude, base on: Grapher Pro! by Tom Igoe (Created 20 April 2005 Updated 15 October 2007) 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. */ char DELIM = ','; char INIT_MARKER = '!'; //Look for this character before sending init message String INIT_MESSAGE = "gu"; //Send these characters to the arduino after init to start up messages int windowWidth = 1000; int windowHeight = 600; int xBoundary = 60; int yBoundary = 60; int graphHeight, graphWidth; int legendWidth = 200; int legendHeight = 100; int legendX = windowWidth - 220; int legendY = 20; int gridSpace = 50; String title = "Bare Bones Coffee Controller Tuning"; String names = "Goal Curr P I D"; boolean[] graphThisName = {true,true, false, false, false}; 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 = 1; // 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 boolean newData = false; int [] lastSet = new int[arrayLength]; int[][] colors = new int[arrayLength][3]; public void setupColors() { colors[0][0] = 102; colors[0][1] =194; colors[0][2] = 165; colors[1][0] = 252; colors[1][1] = 141; colors[1][2]= 98; colors[2][0] = 141; colors[2][1] = 160; colors[2][2]= 203; colors[3][0] = 231; colors[3][1] = 138; colors[3][2]= 195; colors[4][0] = 166; colors[4][1] = 216; colors[4][2]= 84; //colors[5][0] = 255; //colors[5][1] = 217; //colors[5][2]= 47; } public void setup () { lastSet[0] = -1; size(windowWidth, windowHeight); // window size setupColors(); smooth(); println(PFont.list()); PFont myFont = createFont("Courier", 14 ); textFont(myFont); clearGraph(); // 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('\n'); // ASCII 13 // create a font with the second font available to the system: // make the graphics smooth: } public void draw () { // if the value for the given channel is valid, graph it: if (newData) { // print the name of the channel being graphed: // draw the graph: graph(sensorValues); newData = false; } } public void clearGraph() { graphHeight = windowHeight - yBoundary*2; graphWidth =windowWidth-2*xBoundary; background(10); strokeWeight(1.5f); stroke(10); fill(30); // draw boundary rect(xBoundary,yBoundary,graphWidth, graphHeight); textAlign(RIGHT); text(title, windowWidth/2); stroke(10); //draw grid fill(40); textAlign(RIGHT); for (int i = graphHeight; i > 0; i-= gridSpace) { line(xBoundary - 3, yBoundary + i, windowWidth - xBoundary - 1, yBoundary + i); text(str(graphHeight - i), xBoundary - 10, yBoundary + i); } textAlign(LEFT); for (int i = 0; i < graphWidth ; i+= gridSpace) { line(xBoundary + i, yBoundary+1, xBoundary + i, windowHeight - yBoundary + 3); text(str(i), xBoundary + i, windowHeight - yBoundary + 20); } // DRAW LEGEND fill(255,255,255,50); rect(legendX, legendY, legendWidth, legendHeight); // print the name of the channel being graphed: for (int i = 0; i < arrayLength; i++) { fill(colors[i][0], colors[i][1],colors[i][2]); text(split(names,' ')[i] , windowWidth-xBoundary+5, yBoundary + (i+1) * 20); } } public void graph (int [] numbersToGraph) { // draw the line: for (int i = 0; i < numbersToGraph.length; i++) { if (graphThisName[i]) { stroke(colors[i][0], colors[i][1],colors[i][2]); // point(hPosition, height - numbersToGraph[i]); if (lastSet[0] != -1) { line(xBoundary + hPosition-1, windowHeight - yBoundary - lastSet[i] - 1, xBoundary + hPosition, windowHeight - yBoundary - numbersToGraph[i] - 1); } // at the edge of the screen, go back to the beginning: lastSet[i] = numbersToGraph[i]; } } if (hPosition >= graphWidth) { hPosition = 1; // wipe the screen clean: clearGraph(); } else { hPosition += 1; } } public 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); } else { myPort.write(key); } } public 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] += DELIM; // 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 1tSensor 2tSensor 3tSensor 4tSensor 5"; saveStrings("dataFile.txt", newData); } } // make up a timeStamp string for writing data to the file: public String timeStamp() { String now = hour()+ ":" + minute()+ ":" + second()+ " " + month() + "/" + day() + "/" + year(); return now; } public 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) { //println(serialString.charAt(serialString.length()-3)); // println(serialString.charAt(serialString.length()-2)); if (serialString.length() > 2) { if (serialString.charAt(serialString.length()-3) == INIT_MARKER) { println("sent"); myPort.write(INIT_MESSAGE); } } // save the string in case you need to write it to a file: dataSet = serialString; println(dataSet); // split it into substrings on the tab character: String[] numbers = split(serialString, DELIM); // 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 incomgin value: int diff = abs(sensorValues[i] - PApplet.parseInt(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] = PApplet.parseInt(numbers[i]); } } newData = true; } } static public void main(String args[]) { PApplet.main(new String[] { "BBC_Grapher" }); }}