Go Down

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



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()
 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");

 if (outputFileName!="") output = createWriter(outputFileName);

String  PUMPmode = "ON";
void Toggle_PUMP() {
   PUMPmode = "OFF";
   PUMPmode = "ON";

// 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;

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++)
   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.



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();

 // 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))
   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.




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,




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?



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.


Go Up