Go Down

Topic: Processing Front-End for the PID Library (Read 18924 times) previous topic - next topic

br3ttb

May 30, 2009, 10:07 pm Last Edit: May 30, 2009, 10:10 pm by br3ttb Reason: 1
I've just posted a new bit of code to the Arduino PID Library page (http://www.arduino.cc/playground/Code/PIDLibrary)

It's a processing user-interface designed to connect with the PID Library.  I originally wrote it as a way to quickly adjust the tuning parameters, but it turns out it's also a nice tool during normal operation.  you can turn the pid on and off, adjust the setpoint, and graphically see how the controller is doing.

here's a clean screen capture:


...and here's one showing features / what everything is:


This is a work in progress, so any feedback you guys can give will be helpful.

Brett

that_chap

#1
Jun 04, 2009, 09:49 am Last Edit: Jun 04, 2009, 09:50 am by wdh Reason: 1
Absent any other comment as yet, can I just say that that all looks pretty awesome.
About the only thing I'm not seeing (that I'd maybe expect to see) would be a display of the current scales on the axes. But hey, I'm nit-picking!
This is something I'm looking forward to playing with, sometime 'real soon now'.  [smiley=rolleyes.gif]

Anyway, right now, thank you!

br3ttb

Quote
About the only thing I'm not seeing (that I'd maybe expect to see) would be a display of the current scales on the axes. But hey, I'm nit-picking!

no worries about "nit-picking".  I did ask for feedback after all  ;).  I think that's a really good point . it'd also probably be a good idea have some corresponding grid lines.    my first new feature for v0.2!

Brett

goose2283

Wow! I've been fascinated by PID controllers ever since I found out what they were, but have been scared off by the math. This looks like a really useful piece of work. I'll have to find a project to use it in!

br3ttb

Frontend v0.2 is now available for download: http://arduino-pid-library.googlecode.com/files/PID_FrontEnd_v02.zip

In this version:
  • There are grid lines displayed with corresponding values. The time axis scrolls, and can be expressed in milliseconds, seconds, or minutes from program start.
  • The trend lines are a little thicker to make them easier to read.
  • Some whitespace and section labels have been added.

I hope this works for you guys, and as always, suggestions are greatly appreciated.

Brett.





Flotec

Hi,

The front-end that you made looks really nice and is something that I would really benefit from using. Since I'm fairly new at programming for Arduino and it's the first time that I have used pde files then it would be nice to have some more instructions on how to run the program. As far as I can see the front-end is written I java and should be run separately from the terminal (is this correct?). Should I add code similar to the code in the example to my project so that is sends out specific info on the serial buss?

Your help is much appreciated.  
Regards,
Flotec

br3ttb

Flotec:  the program was designed in a java language known as processing.  it can be downloaded here: http://processing.org/
you are correct.  it is run separate from the arduino terminal.  if you want to interface your arduino sketch with this program, use the comm code in sample arduino sketch, included in the download.

general:  I made a mistake.  in an effort to clean the code a little before sending it out, I wound up crippling the program.  whoops.  many of you have already figured this out I'm sure, but AxisFont & TitleFont, while being declared in the correct place, cannot be initialized there.  the initialization has to occur in void setup().
a fixed version has been put up here: http://arduino-pid-library.googlecode.com/files/PID_FrondEnd_v02-fixed.zip

sorry for the mess-up.

Brett


Flotec

Okey, I think I need the "dummies guide" for this. What you are saying is that I should add the printouts that can be seen in the example program to my own program. Run the program on the Aruino board and then run the Front-End from the terminal. How do I run the program? I'm not that familiar with the pde format. Should I compile it with the normal java compiler (javac)?

And is it only the SerialSend and SerialRecieve functions that I need to add (including calls to them at loop)?

Thanks for all your help.
Regards Flotec

br3ttb

#9
Jul 09, 2009, 03:44 pm Last Edit: Jul 09, 2009, 03:50 pm by br3ttb Reason: 1
sorry for the confusion.  the front-end is written in a program completely separate from the arduino IDE.  so after you load your arduino code to the board, the arduino IDE can be completely shut down.

to run the frontend, you need a completely different development environment, which you can download here: http://processing.org/download/

after downloading and running this program (known as "processing")  you can use it to open, edit, and run the FrontEnd pde file.

As far as what you need to add to your Arduino program, I'm unfamiliar with your particular needs, so I don't know exactly which portion of the arduino sample program you're going to need.  I'd say for starters, load the full arduino sample onto your Arduino and get it talking with the front end.  once that's working you can build from there.

Brett

EVAN

Brett,

I really love this program. It works great for my home brewery automation project that I have been working on. In my project I have a pump recirculating fluid through a copper tubing array immersed in a larger pipe containing water and an electrical heater. I have gotten the pid working on heating the water in larger pipe thus heating the fluid inside the copper array, but the pump is running continuously from the code on the arduino, which makes it a little hard to stop if I need to stop it for some reason. I tried adding a button on the front end with success, but I am having trouble returning it to the arduino. I got stuck at the point when you change the floats to characters and throw them in the array that passes between both programs. I was wondering if I could get some expert advice on how to proceed. I've gotten to this point. I tried to post all of the code, but since it was too long I just posted the parts that I've made changes to. Hopefully you will be able to make sense of this,

Code: [Select]

Serial myPort;

ControlP5 controlP5;
controlP5.Button AMButton;
controlP5.Button PUMPButton;
controlP5.Textlabel AMLabel, AMCurrent, InLabel,
OutLabel, SPLabel, PLabel, PUMPLabel, PUMPCurrent,
ILabel, DLabel;
controlP5.Textfield SPField, InField, OutField,
PField, IField, DField;

PrintWriter output;
PFont AxisFont, TitleFont;

void setup()
{
 frameRate(30);
 size(windowWidth , windowHeight);

 println(Serial.list());                                           // * Initialize Serial
 myPort = new Serial(this, Serial.list()[1], 9600);                //   Communication with
 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("P_Param",10,275,60,20);          //
 IField = controlP5.addTextfield("I_Param",10,325,60,20);          //
 DField = controlP5.addTextfield("D_Param",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,425,120,20);         //
 PUMPButton = controlP5.addButton("Toggle_Pump",0.0,10,475,60,20); //
 PUMPLabel = controlP5.addTextlabel("ON","OFF",12,497);             //
 PUMPCurrent =  controlP5.addTextlabel("ONCURRENT","OFF",80,485);  //
 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);                    //

 AxisFont = loadFont("axis.vlw");
 TitleFont = loadFont("Titles.vlw");

 nextRefresh=millis();
 if (outputFileName!="") output = createWriter(outputFileName);
}

String  PUMPmode = "ON";
void Toggle_PUMP() {
 if(PUMPmode=="ON")
 {
   PUMPmode = "OFF";
   PUMPLabel.setValue(PUMPmode);
 }
 else
 {
   PUMPmode = "ON";
   PUMPLabel.setValue(PUMPmode);  
 }
}

// 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 b = (mode=="Manual")?(byte)0:(byte)1;
 Byte c = (mode=="ON")?(byte)0:(byte)1;
 myPort.write(b);
 myPort.write(c);
 myPort.write(floatArrayToByteArray(toSend));
 justSent=true;
}


byte[] floatArrayToByteArray(float[] input)
{
 int len = 4*input.length;
 int index=0;
 byte[] b = new byte[4];
 byte[] c = new byte[4];
 byte[] out = new byte[len];
 ByteBuffer buf = ByteBuffer.wrap(b);
 ByteBuffer buf = ByteBuffer.wrap(c);
 for(int i=0;i<input.length;i++)
 {
   buf.position(0);
   buf.putFloat(input[i]);
   for(int j=0;j<4;j++) out[j+i*4]=b[3-j];
 }
 return out;
}


//take the string the arduino sends us and parse it
void serialEvent(Serial myPort)
{
 String read = myPort.readStringUntil(10);
 if(outputFileName!="") output.print(str(millis())+ " "+read);
 String[] s = split(read, " ");

 if (s.length ==8)
 {
   Setpoint = float(s[1]);           // * pull the information
   Input = float(s[2]);              //   we need out of the
   Output = float(s[3]);             //   string and put it
   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]));   //

   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(mode);         //
     justSent=false;                 //
   }                                 //

   if(!madeContact) madeContact=true;
 }
}


I would really appreciate any help you can provide or anyone else that has had luck with doing something similar.

Thanks,
Evan

br3ttb

Hi Evan,

for future reference, the phrase "home brewery automation" illicits my immediate attention and desire to help.

on to it.  I need to apologize to you.  when I'm coding quickly, I use short variable names (easier to type.)   At times I even use the same name in different functions for different things.  That's what I did here.  you correctly added your "c" to Send_To_Arduino(), mimicking the "b" I used there.  That's not the same "b" as the one in floatArrayToByteArray, which you did not have to copy.

Now to the arduino side!  We're sending something different, so we need to catch differently. based on your Processing Code, you would need to modify the arduino Receive code as follows:

Code: [Select]
void SerialReceive()
{

 // read the bytes sent from Processing
 int index=0;
 byte Auto_Man = -1;
 while(Serial.available()&&index<26/* CHANGED: upped from 25, since a byte was added*/)
 {
   if(index==0) Auto_Man = Serial.read();
   else if(index==1) pump_OnOff = Serial.read(); //CHANGED: pull the on/off byte from the stream
   else foo.asBytes[index-2/*CHANGED from 1 to account for the extra byte*/] = Serial.read();
   index++;
 }

 // if the information we got was in the correct format,
 // read it into the system
 if(index==26/*CHANGED from 25*/  && (Auto_Man==0 || Auto_Man==1))
 {
   Setpoint=int(foo.asLong[0]);
   Input=int(foo.asLong[1]);       // * the user has the ability to send the
                                         //   value of "Input"  in most cases (as
                                         //   in this one) this is not needed.
   if(Auto_Man==0)                       // * only change the output if we are in
   {                                     //   manual mode.  otherwise we'll get an
     Output=double(foo.asLong[2]);      //   output blip, then the controller will
   }                                     //   overwrite.

   int p, i, d;                       // * read in and set the controller tunings
   p = int(foo.asLong[3]);           //
   i = int(foo.asLong[4]);           //
   d = int(foo.asLong[5]);           //
   myPID.SetTunings(p, i, d);            //

   if(Auto_Man==0) myPID.SetMode(MANUAL);// * set the controller mode
   else myPID.SetMode(AUTO);             //
 }
 Serial.flush();                         // * clear any random data from the serial buffer
}



I should mention that I recently fried my Arduino (dangling wire contacted where it shouldn't have,)  so I was unable to verify the above code.  Please let me know if it works or not, and I'll edit accordingly.

Brett

EVAN

Brett,

I'm glad your as motivated by beer as I am! I really appreciate the help. Your code worked beautifully, but I made a minor mistake with some letter cases that was causing the button not to reference the function, but never the less, everything is working. There's on little nuance though that I didn't really have time to figure out, but if you click any of the buttons, say the toggle_am and then hit send to arduino it changes the automatic/manual mode, which is what it's supposed to do. Now, if I'm in manual mode and now try to change the output and send to the arduino again, the mode changes back to automatic again. It's easy to work around, you just have to click the Toggle_AM button first before you send to arduino, but it's a little cumbersome. Do you know if there is an easy way to fix that? If not, no worries.

Thanks again,

Evan

EVAN

Brett,

So, I think I've got it pretty much working, but I tried to add a few more temp sensors. I've successfully got 2 sensors, but when I try to get the 3rd in there it doesn't communicate any of the info to  the front panel. I think there is a timing issue and converting to strings. I think the string is too long. Does this make sense?

Evan

br3ttb

That could certainly be the case.  as you said, the arduino->processing communication is in the form of a string, which is not the most efficient way to send numbers :)

the first thing to try would be upping the communication speed and see if that fixes the problem.  I think I set it at 9600, but the arduino will support up to 115200.  

if you wanted to keep the comm rate the same, another option would be to send Arduino->processing information less often.  Currently it's every 500ms (serialTime+=500;)

Of course, this all assumes that the issue is too much information being sent.  Hopefully that's it, and one of the above solutions takes care of the problem.

-Brett

Go Up