We are about to switch to a new forum software. Until then we have removed the registration on this forum.
/******************************************************** * Arduino PID Tuning Front-End, Version 0.3 * by Brett Beauregard * License: Creative-Commons Attribution Share-Alike * April 2011 * * This application is designed to interface with an * arduino running the PID Library. From this Control * Panel you can observe & adjust PID performance in * real time * * The ControlP5 library is required to run this sketch. * files and install instructions can be found at * http://www.sojamo.de/libraries/controlP5/ * ********************************************************/ /********************** * user\Documents\Processing\libraries\PID_FrontEnd_v03\PID_FrontEnd_v03\PID_FrontEnd_v03USER20 ********/
import java.nio.ByteBuffer; import processing.serial.*; import controlP5.*;
/*********************************************** * User specification section **********************************************/ int windowWidth = 900; // set the size of the int windowHeight = 600; // form
float InScaleMin = 0; // set the Y-Axis Min float InScaleMax = 1024; // and Max for both float OutScaleMin = 0; // the top and float OutScaleMax = 255; // bottom trends
int windowSpan = 300000; // number of mS into the past you want to display int refreshRate = 100; // how often you want the graph to be reDrawn;
//float displayFactor = 1; //display Time as Milliseconds //float displayFactor = 1000; //display Time as Seconds float displayFactor = 60000; //display Time as Minutes
String outputFileName = "C:/A1Trash holder/ProcessorOutputFileFolder/USERProcOut.txt"; // if you'd like to output data to // a file, specify the path here
/*********************************************** * end user spec **********************************************/
int nextRefresh; int arrayLength = windowSpan / refreshRate+1; int[] InputData = new int[arrayLength]; //we might not need them this big, but int[] SetpointData = new int[arrayLength]; // this is worst case int[] OutputData = new int[arrayLength];
float inputTop = 25; float inputHeight = (windowHeight-70)2/3; float outputTop = inputHeight+50; float outputHeight = (windowHeight-70)1/3;
float ioLeft = 150, ioWidth = windowWidth-ioLeft-50; float ioRight = ioLeft+ioWidth; float pointWidth= (ioWidth)/float(arrayLength-1);
int vertCount = 10;
int nPoints = 0;
float Input, Setpoint, Output;
boolean madeContact =false; boolean justSent = true;
Serial myPort;
// USER adds a test to print the String String inString; // Input string from serial port USER added
ControlP5 controlP5; controlP5.Button AMButton, DRButton; controlP5.Textlabel AMLabel, AMCurrent, InLabel, OutLabel, SPLabel, PLabel, ILabel, DLabel,DRLabel, DRCurrent; controlP5.Textfield SPField, InField, OutField, PField, IField, DField;
PrintWriter output; PFont AxisFont, TitleFont;
void setup() { frameRate(30); size(900, 600);
println(Serial.list()); // * Initialize Serial myPort = new Serial(this, Serial.list()[0], 9600); //USER changed 1 to 0for comport 3 //USER Communication with arduino via comport this is because it is the first listed of the serial list and 1st one is number 0 // myPort.bufferUntil(10); // the Arduino
controlP5 = new ControlP5(this); // * Initialize the various SPField= controlP5.addTextfield("Setpoint",10,100,60,20); // Buttons, Labels, and InField = controlP5.addTextfield("Input",10,150,60,20); // Text Fields we'll be OutField = controlP5.addTextfield("Output",10,200,60,20); // using PField = controlP5.addTextfield("Kp (Proportional)",10,275,60,20); // IField = controlP5.addTextfield("Ki (Integral)",10,325,60,20); // DField = controlP5.addTextfield("Kd (Derivative)",10,375,60,20); // AMButton = controlP5.addButton("Toggle_AM",0.0,10,50,60,20); // AMLabel = controlP5.addTextlabel("AM","Manual",12,72); // AMCurrent = controlP5.addTextlabel("AMCurrent","Manual",80,65); // controlP5.addButton("Send_To_Arduino",0.0,10,475,120,20); // SPLabel=controlP5.addTextlabel("SP","3",80,103); // InLabel=controlP5.addTextlabel("In","1",80,153); // OutLabel=controlP5.addTextlabel("Out","2",80,203); // PLabel=controlP5.addTextlabel("P","4",80,278); // ILabel=controlP5.addTextlabel("I","5",80,328); // DLabel=controlP5.addTextlabel("D","6",80,378); // DRButton = controlP5.addButton("Toggle_DR",0.0,10,425,60,20); // DRLabel = controlP5.addTextlabel("DR","Direct",12,447); // DRCurrent = controlP5.addTextlabel("DRCurrent","Direct",80,440); //
AxisFont = loadFont("axis.vlw"); TitleFont = loadFont("Titles.vlw");
nextRefresh=millis(); if (outputFileName!="") output = createWriter(outputFileName); }
void draw() { background(200); drawGraph(); drawButtonArea();
}
void drawGraph() { //draw Base, gridlines stroke(0); fill(230); rect(ioLeft, inputTop,ioWidth-1 , inputHeight); rect(ioLeft, outputTop, ioWidth-1, outputHeight); stroke(210);
//Section Titles textFont(TitleFont); fill(255); text("PID Input / Setpoint",(int)ioLeft+10,(int)inputTop-5); text("PID Output",(int)ioLeft+10,(int)outputTop-5);
//GridLines and Titles textFont(AxisFont);
//horizontal grid lines int interval = (int)inputHeight/5; for(int i=0;i<6;i++) { if(i>0&&i<5) line(ioLeft+1,inputTop+iinterval,ioRight-2,inputTop+iinterval); text(str((InScaleMax-InScaleMin)/5(float)(5-i)+InScaleMin),ioRight+5,inputTop+iinterval+4);
} interval = (int)outputHeight/5; for(int i=0;i<6;i++) { if(i>0&&i<5) line(ioLeft+1,outputTop+iinterval,ioRight-2,outputTop+iinterval); text(str((OutScaleMax-OutScaleMin)/5(float)(5-i)+OutScaleMin),ioRight+5,outputTop+iinterval+4); }
//vertical grid lines and TimeStamps int elapsedTime = millis(); interval = (int)ioWidth/vertCount; int shift = elapsedTime*(int)ioWidth / windowSpan; shift %=interval;
int iTimeInterval = windowSpan/vertCount; float firstDisplay = (float)(iTimeInterval(elapsedTime/iTimeInterval))/displayFactor; float timeInterval = (float)(iTimeInterval)/displayFactor; for(int i=0;i<vertCount;i++) { int x = (int)ioRight-shift-2-iinterval;
line(x,inputTop+1,x,inputTop+inputHeight-1);
line(x,outputTop+1,x,outputTop+outputHeight-1);
float t = firstDisplay-(float)i*timeInterval;
if(t>=0) text(str(t),x,outputTop+outputHeight+10);
}
// add the latest data to the data Arrays. the values need // to be massaged to get them to graph correctly. they // need to be scaled to fit where they're going, and // because 0, 0 is the top left, we need to flip the values. // this is easier than having the user stand on their head // to read the graph. if(millis() > nextRefresh && madeContact) { nextRefresh += refreshRate;
for(int i=nPoints-1;i>0;i--)
{
InputData[i]=InputData[i-1];
SetpointData[i]=SetpointData[i-1];
OutputData[i]=OutputData[i-1];
}
if (nPoints < arrayLength) nPoints++;
InputData[0] = int(inputHeight)-int(inputHeight*(Input-InScaleMin)/(InScaleMax-InScaleMin));
SetpointData[0] =int( inputHeight)-int(inputHeight*(Setpoint-InScaleMin)/(InScaleMax-InScaleMin));
OutputData[0] = int(outputHeight)-int(outputHeight*(Output-OutScaleMin)/(OutScaleMax-OutScaleMin));
} //draw lines for the input, setpoint, and output strokeWeight(2); for(int i=0; i<nPoints-2; i++) { int X1 = int(ioRight-2-float(i)pointWidth); int X2 = int(ioRight-2-float(i+1)pointWidth); boolean y1Above, y1Below, y2Above, y2Below;
//DRAW THE INPUT
boolean drawLine=true;
stroke(255,0,0);
int Y1 = InputData[i];
int Y2 = InputData[i+1];
y1Above = (Y1>inputHeight); // if both points are outside
y1Below = (Y1<0); // the min or max, don't draw the
y2Above = (Y2>inputHeight); // line. if only one point is
y2Below = (Y2<0); // outside constrain it to the limit,
if(y1Above) // and leave the other one untouched.
{ //
if(y2Above) drawLine=false; //
else if(y2Below) { //
Y1 = (int)inputHeight; //
Y2 = 0; //
} //
else Y1 = (int)inputHeight; //
} //
else if(y1Below) //
{ //
if(y2Below) drawLine=false; //
else if(y2Above) { //
Y1 = 0; //
Y2 = (int)inputHeight; //
} //
else Y1 = 0; //
} //
else //
{ //
if(y2Below) Y2 = 0; //
else if(y2Above) Y2 = (int)inputHeight; //
} //
if(drawLine)
{
line(X1,Y1+inputTop, X2, Y2+inputTop);
}
//DRAW THE SETPOINT
drawLine=true;
stroke(0,255,0);
Y1 = SetpointData[i];
Y2 = SetpointData[i+1];
y1Above = (Y1>(int)inputHeight); // if both points are outside
y1Below = (Y1<0); // the min or max, don't draw the
y2Above = (Y2>(int)inputHeight); // line. if only one point is
y2Below = (Y2<0); // outside constrain it to the limit,
if(y1Above) // and leave the other one untouched.
{ //
if(y2Above) drawLine=false; //
else if(y2Below) { //
Y1 = (int)(inputHeight); //
Y2 = 0; //
} //
else Y1 = (int)(inputHeight); //
} //
else if(y1Below) //
{ //
if(y2Below) drawLine=false; //
else if(y2Above) { //
Y1 = 0; //
Y2 = (int)(inputHeight); //
} //
else Y1 = 0; //
} //
else //
{ //
if(y2Below) Y2 = 0; //
else if(y2Above) Y2 = (int)(inputHeight); //
} //
if(drawLine)
{
line(X1, Y1+inputTop, X2, Y2+inputTop);
}
//DRAW THE OUTPUT
drawLine=true;
stroke(0,0,255);
Y1 = OutputData[i];
Y2 = OutputData[i+1];
y1Above = (Y1>outputHeight); // if both points are outside
y1Below = (Y1<0); // the min or max, don't draw the
y2Above = (Y2>outputHeight); // line. if only one point is
y2Below = (Y2<0); // outside constrain it to the limit,
if(y1Above) // and leave the other one untouched.
{ //
if(y2Above) drawLine=false; //
else if(y2Below) { //
Y1 = (int)outputHeight; //
Y2 = 0; //
} //
else Y1 = (int)outputHeight; //
} //
else if(y1Below) //
{ //
if(y2Below) drawLine=false; //
else if(y2Above) { //
Y1 = 0; //
Y2 = (int)outputHeight; //
} //
else Y1 = 0; //
} //
else //
{ //
if(y2Below) Y2 = 0; //
else if(y2Above) Y2 = (int)outputHeight; //
} //
if(drawLine)
{
line(X1, outputTop + Y1, X2, outputTop + Y2);
}
} strokeWeight(1); }
void drawButtonArea() { stroke(0); fill(100); rect(0, 0, ioLeft, windowHeight); }
void Toggle_AM() {
if(AMLabel.valueLabel().getText()=="Manual")
{
AMLabel.setValue("Automatic");
}
else
{
AMLabel.setValue("Manual");
}
}
void Toggle_DR() {
if(DRLabel.valueLabel().getText()=="Direct")
{
DRLabel.setValue("Reverse");
}
else
{
DRLabel.setValue("Direct");
}
}
// Sending Floating point values to the arduino // is a huge pain. if anyone knows an easier // way please let know. the way I'm doing it: // - Take the 6 floats we need to send and // put them in a 6 member float array. // - using the java ByteBuffer class, convert // that array to a 24 member byte array // - send those bytes to the arduino void Send_To_Arduino() { float[] toSend = new float[6];
toSend[0] = float(SPField.getText()); toSend[1] = float(InField.getText()); toSend[2] = float(OutField.getText()); toSend[3] = float(PField.getText()); toSend[4] = float(IField.getText()); toSend[5] = float(DField.getText()); Byte a = (AMLabel.valueLabel().getText()=="Manual")?(byte)0:(byte)1; Byte d = (DRLabel.valueLabel().getText()=="Direct")?(byte)0:(byte)1; myPort.write(a); myPort.write(d); myPort.write(floatArrayToByteArray(toSend)); justSent=true; }
byte[] floatArrayToByteArray(float[] input) { int len = 4input.length; int index=0; byte[] b = new byte[4]; byte[] out = new byte[len]; ByteBuffer buf = ByteBuffer.wrap(b); for(int i=0;i<input.length;i++) { buf.position(0); buf.putFloat(input[i]); for(int j=0;j<4;j++) out[j+i4]=b[3-j]; } return out; }
//take the string the arduino sends us and parse it void serialEvent(Serial myPort) { // String read = myPort.readStringUntil(10); String read = myPort.readStringUntil(10); //USER changed from 10 to // inString = myPort.readString(); //USER added this //try the next one String inString = myPort.readStringUntil(10); //USER added this if(outputFileName!="") output.print(str(millis())+ " "+read); String[] s = split(read, " ");
if (s.length ==9) { Setpoint = float(s[1]); // * pull the information println("setpoint:",Setpoint); //TBGFE Input = float(s[2]); // we need out of the println("Inpute:", Input); //TGBFE Output = float(s[3]); // string and put it println("Output: ",Output); //TGBFE
//USER adds test print
text(" text received: " + inString, 10,50); //USER added this
println ("println read received: " ,read); //USER added to see what is in string
println ("println inString received: " ,inString); //USER added to see what is in string
SPLabel.setValue(s[1]); // where it's needed
InLabel.setValue(s[2]); //
OutLabel.setValue(trim(s[3])); //
PLabel.setValue(trim(s[4])); //
ILabel.setValue(trim(s[5])); //
DLabel.setValue(trim(s[6])); //
AMCurrent.setValue(trim(s[7])); //
DRCurrent.setValue(trim(s[8]));
if(justSent) // * if this is the first read
{ // since we sent values to
SPField.setText(trim(s[1])); // the arduino, take the
InField.setText(trim(s[2])); // current values and put
OutField.setText(trim(s[3])); // them into the input fields
PField.setText(trim(s[4])); //
IField.setText(trim(s[5])); //
DField.setText(trim(s[6])); //
// mode = trim(s[7]); //
AMLabel.setValue(trim(s[7])); //
//dr = trim(s[8]); //
DRCurrent.setValue(trim(s[8])); //
justSent=false; //
} //
if(!madeContact) madeContact=true;
} }
Answers
"Does not work"
In what way does it not work?
Read the sticky post about formatting code, the above is unreadable.
Version 3 is optional and still in beta, you don't have to use it.
it errors at the line: void Toggle_AM() { if(AMLabel.valueLabel().getText()=="Manual")
it makes me think it did not do this line: import java.nio.ByteBuffer; I failed to say I am using Windows, 64, The processing sketch is run after starting an Arduino sketch that reads temperatures that I want visibility of using the processing sketch. I have hope for 3.0 as the issues it says it resolves are the exact problems I was having using 2.2. Thank you for responding. This is my first time and I appreciate you looking at this. (I don't see a sticky note)
http://forum.processing.org/two/discussion/8045/how-to-format-code-and-text
Highlight the code, press ctrl-o. You might have difficulty because of the size of the code - there is a limit.
You still don't say what the error is. Null pointer? Syntax error? Logical error?
Here is the error message: The function "valueLabel()" does not exist. and it highlights line 359 that says : if (AMLabel.valueLabel().getText()=="Manual") I will work on putting the code in the message properly...THANK YOU SO MUCH
don't bother posting the whole thing, 99% of it is unnecessary.
this: http://www.sojamo.de/libraries/controlP5/reference/ says that Textlabel.valueLabel() is deprecated.
try AMLabel.get().getText() instead.
[mangled code deleted]
oh my, I probably didn't do that right (looked good in the preview). I am suspicious that somehow the three import commands are not being properly executed. Does Processing 3.0 still work with: import java.nio.ByteBuffer and import processing.serial.* and import controlP5.* ?
here is the code that the error highlights:
void Toggle_AM() { if(AMLabel.valueLabel().getText()=="Manual") { AMLabel.setValue("Automatic"); } else { AMLabel.setValue("Manual"); } }
koogs...thank you, that did the trick and got me to the next error which said "could not load font axis.vlw. Make sure that the font has been copied to the data folder of your sketch. " So I will double check that I have all in the right folders
Also, sorry I posted the code at the same time you did and didn't see your great answer. THANK YOU
koogs...thank you again, but how did you figure it out that I need to replace valueLabel with get? I read through the link .... obviously I am a newbie and I did not write the original sketch, but I want to get it to work with the new Processing 3.0.
koogs....thanks...I now have the graph running...I am still impressed and curious how you dug "get" out of the link.... thank you very much! :\"> It made all the difference! (I had to be sure the data file was in the right folder which solved my last error message)
oh, that link i posted was to the frame, not the page i wanted
http://www.sojamo.de/libraries/controlP5/reference/controlP5/Textlabel.html
the object was a Textlabel and we wanted a method that returned a Label like the previous method did. get() was the only fit, badly named though it is.
I will look at the link so I can learn more. I am very grateful for your help...now I will work on the functionality of my project.