Homebrew PID Development

Hello,

I am working on developing a PID for my homebrew setup. At this point, I just want it to do two things: control my mash temp and have manual control of the electric heating element during the boil. I have been putting this together in stages and I think I am now ready to add the PID portion, but I have a few questions.

I lifted part of Brett Beauregard’s “PID_RelayOutput” example code for the manual “PWM” with a frequency of .2 HZ. and modified it to blink the onboard LED to test that the code was working. When I thought the light should be on, it was off, and when I thought it should be off, it was on. Here is the code that I borrowed mine from:

void loop()
{
  Input = analogRead(0);
  myPID.Compute();   
  /************************************************
   * turn the output pin on/off based on pid output
   ************************************************/
  if(millis() - windowStartTime>WindowSize)
  { //time to shift the Relay Window
    windowStartTime += WindowSize;
  }
  if(Output < millis() - windowStartTime) digitalWrite(RelayPin,HIGH);
  else digitalWrite(RelayPin,LOW);
}

I will use a DPDT switch to choose between case A and B. A will be the PID control loop, B will be manual control loop. I plan on using the above code with whatever needed mods, to implement my PID. But when I used it for the manual control, the on/off time seemed to be inverted. No problem, just switch the LOW and HIGH right? It seems ok to do for case B, but for case A, I am not sure. Since I do not understand the code very well, I am not sure how it will work when implementing the PID. If it runs backwards, then I would think it would make the PID function act strangely.

Here is what I have so far:

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <Quadrature.h>
#include <PID_v1.h>
//#define RelayPin 6 
int Setpoint, Input, Output;
//PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT);
LiquidCrystal_I2C lcd(0x27,20,4);
Quadrature quad1(4, 3); 
int WindowSize = 5000;
unsigned long windowStartTime; 

void setup()
{
  windowStartTime = millis();
  pinMode(2, INPUT_PULLUP);
  pinMode(7, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
  lcd.init();                      // initialize the lcd 
  lcd.backlight();
  pinMode(13, OUTPUT);//for test code to LCD
}

void loop()
{
  int sensorValue = digitalRead(2);
  int aValue = digitalRead(7);
  int bValue = digitalRead(8);
  //Part A
    if (aValue==LOW){
      quad1.minimum(115);
      quad1.maximum(170);
      lcd.clear();
      lcd.setCursor(9,1);
      lcd.print(quad1.position()); //Don't understand this yet, but it works
      lcd.setCursor(4,2);
       if (sensorValue == LOW) {Setpoint = quad1.position();}
      lcd.print("Setpoint = ");
      lcd.print(Setpoint,DEC);
    }
   //Part B
   else if (bValue==LOW){
      //Manual mode is set
      quad1.minimum(0);
      quad1.maximum(100);
      lcd.clear();
      lcd.setCursor(1,1);
      lcd.print("DutyRatio = ");
      lcd.print(quad1.position());
      lcd.print("%");
      lcd.setCursor(4,2);
      Output = 50*quad1.position(); //Translate encoder position to output for 
                                    //manual mode.
      lcd.print("Output = ");
      lcd.print(Output,DEC);
      //Code here to blink onboard LED, later to control a relay
        if(millis() - windowStartTime>WindowSize){           
          windowStartTime += WindowSize;                      
          }
      if(Output < millis() - windowStartTime) digitalWrite(13,LOW);
      else digitalWrite(13,HIGH);
      //The previous two lines are where the flip-flop was.  When the duty cycle was 80%, the light was off for 4 sec and on for 1/
      //As it is written now, it is on/off in sync with the duty cycle because I flip-flopped the HIGH and LOW.
       }
  delay(100);
}

There was a third part to the structure that I think I will do without, so I might just need a SPST switch (that’s why it says if else for now). So I am wondering, if I use the code in Brett’s example in part A of the sketch without flipping the HIGH/LOW values, will the PID function work as it should, or will it be backwards as when I put it in part B?

Also, I changed the variable declarations to ints, (that’s why the PID declaration is commented out of the code). I saw a post where Brett said the PID would work with integers, but I would need to change the variable types in the supporting files because the functions are looking for doubles . Is there anything at all in the process that would require floating point calculations or numbers? That is assuming that I do not want decimal point precision. Would the PID control be too course for a mash tun if I just used integers?

On the other hand, the reason I am thinking of using int variable types are that some of my code relies on integers to run properly (the rotary encoder), integers are easier for me to display on my LCD, and they take less space in memory. But as I compile my code when I add new parts, I don’t think I am in danger of running out of space anywhere in the program. If I lose the idea of using integers for most of my code except for where I need them, can I just mix integers and doubles with impunity?

Any feedback is much appreciated!

ok im confussed. I think you have a problem with the idea more than the code. if the heater is relay controlled loss the pwm idea just turn the relay on when its low temp and turn the relay off when its high temp. add a little timing depending on the sensors ability to update.

if you are running a mosfet or other hardware to control the heater then pwm at full speed and use a pid.

as for problems displaying a float or long on a lcd just add ,1 so it only displays to one digit after the point.

“If I lose the idea of using integers for most of my code except for where I need them, can I just mix integers and doubles with impunity?”

will it let you …yep

what will happen when it does is a different answer. spent a hour searching for a problem in the code last week trying to work out why a heater failed to respond to a 0.1 degree difference but reacted to a 1 degree change. found out I moved a float into a int which doesn’t support decimal so it just ignores them.

Actually, the manual control is needed for the boil because you need to control the intensity of the boil. Water boils at whatever temperature around 212 degrees F it boils at, varying slightly by atmospheric conditions. The boil kettle needs manual control because you have to babysit the kettle during the boil time and vary the intensity of the heating coil from time to time to prevent boil over. If you walk away it will either stop boiling or it will boil over--and that is very messy. The PID is for controlling the temperature of the mash before the boil, most likely, I will be using a single infusion mash for starters. I found a simple work around for my variable question. I just created other variables to rename the doubles to integer types for the parts of the code where integers are more convenient. I would still be interested in finding out if someone knows about those lines of code I borrowed from the PID with Relay (displayed in my first post). Will they appear to me to work backwards in the PID like they appeared to do when I put them in my manual control loop? I think I just have three more steps: 1) Get the temperature sensors written into the code. 2) Enter the PID code. 3) Figure out how to tune it. I have heard some suggestions that PI control would be just as good for temperature control. There is one fewer tuning parameter. Anyone want to share their experiences with it if they used PI control for mash temperature?