# DIY Arduino PID Controller



## mahcann

Hey guys thought I would share my home made arduino PID controller. It uses a DS18b20 temperature sensor connected to an arduino uno which in turn controls a relay connected to my keg king element. I use it mainly to heat my strike water and to maintain my mash temps, but sometimes to control the boil level as well. I know you are able to just buy a PID controller off eBay for ~$40 but if you are anything like me then making your own and discovering more about how these things work is much more fun! The code is adapted from the sous vide controller by adafruit. It uses an excellent PID library made by Brett Beauregard. Some features include manual and automatic tuning of PID variables, temperature readings with about 0.5°c accuracy, real time temperature readings and set point adjustment (no going back to change the code) using a LCD shield and a timer with alarm! If you are interested I would definitely recommend having a go at making your own because its a great way to learn more about electronics and its a lot of fun. I'll post a tutorial and code if anyone is interested in making their own!

Here are some pics, its not perfect (yet  ) but still a work in progress plan on brewing with it tomorrow! It connected up to my laptop so I can record and plot temperatures.


----------



## dblunn

Nice work!
I'm doing a similar thing based on Matho's code. I have built a Braumiser controller but the rest of the build is taking a while. In the interim I am resurrecting my old setup based on a 20l got cooler and an old S/S "laundry copper". The controller will control the HLT which is a bucket fermenter with kettle element.
Regards, Dave


----------



## mahcann

Sounds good Dave, what language is matho's code written in? Are you just going to upload it to a microprocessor?

-Michael


----------



## mr_wibble

Cool!

I made one of these a few weeks ago.
Although it has a bug where the temp measurement locks up after half an hour (I think it's something to do with painting the little oled screen).

just can't seem to find some time to work on it.


----------



## mahcann

Mr Wibble said:


> Although it has a bug where the temp measurement locks up after half an hour (I think it's something to do with painting the little oled screen).


Strange that it only locks up after half an hour. Possibly your sensor decoding is lagging up the system? 



Mr Wibble said:


> just can't seem to find some time to work on it.


The benefits of being a uni student on holidays


----------



## dblunn

mahcann said:


> Sounds good Dave, what language is matho's code written in? Are you just going to upload it to a microprocessor?
> 
> -Michael


I'm using the Arduino development environment (like Matho's, C++-ish, supports function overloading etc) and a Arduino pro mini (these are $4-ish). I originally thought of using MPS430 series MCUs ($2) but the Arduino has so many libraries, already on a pcb with a voltage regulator and they are 5V so interfaces to LCD modules ok. How could I resist? Anyway, I'm just adding a second temp sensor (PID control off primary and just display secondary) so I can monitor input temp to HEX so I can tell when the entire mash has reached the set temp. 
Regards, Dave


----------



## marksy

Looks good mate. How is it all going now after a few weeks? How much of the code did you change from the adafruit? Can you put it up so we can have a look!


----------



## mahcann

Progress has slowed up considerably Marksy as I have been forced to turn my attention away to less important things (namely uni). I have have distracted myself with other silly projects such as aqauponics and cider. But I really like where the controller is going. I have a few more ideas on features to add but they are in the planning phase.

Here is my code so far: 


//-------------------------------------------------------------------
//
// Brewing Kettle Controller adapated from Sous Vide Controller (Bill Earl - for Adafruit Industries)
// Michael Ah-Cann
//
// Based on the Arduino PID and PID AutoTune Libraries 
// by Brett Beauregard
//------------------------------------------------------------------

// PID Library
#include <PID_v1.h>
#include <PID_AutoTune_v0.h>


// Libraries for the LCD Sheild
#include <Wire.h> //is this required for project??
#include <LiquidCrystal.h>

// Libraries for the DS18B20 Temperature Sensor
#include <OneWire.h>
#include <DallasTemperature.h>

// So we can save and retrieve settings
#include <EEPROM.h>

// ************************************************
// Pin definitions
// ************************************************

// Output Relay (PowerSwitch Tail)
#define RelayPin 3

// One-Wire Temperature Sensor
// (Use GPIO (General Purpose In Out) pins for power (and ground) to simplify the wiring)
#define ONE_WIRE_BUS 11
#define ONE_WIRE_PWR 12
//#define ONE_WIRE_GND 13 //COMMENTED OUT TO ACCOMONDATE SPEAKER 

// ************************************************
// Timer Variables
// ************************************************

//Define the variables used for the timer
int sound_pin = 13;
int set_time = 0;
float time_hold;
int buttons;
int time_remain;
float elapsed = 0.00;
boolean alarm = false;
boolean sound_state = true;
boolean time_state = false;

// ************************************************
// PID Variables and constants
// ************************************************

//Define Variables we'll be connecting to
double Setpoint;
double Input;
double Output;

volatile long onTime = 0;

// pid tuning parameters
double Kp;
double Ki;
double Kd;

// EEPROM addresses for persisted data
const int SpAddress = 0;
const int KpAddress = 8;
const int KiAddress = 16;
const int KdAddress = 24;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

// 10 second Time Proportional Output window
int WindowSize = 10000; 
unsigned long windowStartTime;

// ************************************************
// Auto Tune Variables and constants
// ************************************************
byte ATuneModeRemember=2;

double aTuneStep=500;
double aTuneNoise=1;
unsigned int aTuneLookBack=20;

boolean tuning = false;

PID_ATune aTune(&Input, &Output);

// ************************************************
// DiSplay Variables and constants
// ************************************************
LiquidCrystal lcd(8,9,4,5,6,7);

unsigned long lastInput = 0; // last button press

byte degree[8] = // define the degree symbol 
{ 
B00110, 
B01001, 
B01001, 
B00110, 
B00000,
B00000, 
B00000, 
B00000 
}; 

const int logInterval = 10000; // log every 10 seconds
long lastLogTime = 0;

// ************************************************
// States for state machine
// ************************************************
enum operatingState { OFF = 0, SETP, RUN, TUNE_P, TUNE_I, TUNE_D, TUNE_T, AUTO};
operatingState opState = OFF;

// ************************************************
// Sensor Variables and constants
// Data wire is plugged into port 2 on the Arduino

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

// arrays to hold device address
DeviceAddress tempSensor;

// ************************************************
// Setup and diSplay initial screen
// ************************************************
void setup()
{
Serial.begin(9600);

// Initialize Relay Control:

pinMode(RelayPin, OUTPUT); // Output mode to drive relay
digitalWrite(RelayPin, LOW); // make sure it is off to start

// Set up Ground & Power for the sensor from GPIO pins

//pinMode(ONE_WIRE_GND, OUTPUT); // COMMENTED OUT TO ACCOMONDATE SPEAKER
//digitalWrite(ONE_WIRE_GND, LOW);

pinMode(ONE_WIRE_PWR, OUTPUT);
digitalWrite(ONE_WIRE_PWR, HIGH);

// Initialize LCD DiSplay 

lcd.begin(16, 2);
lcd.createChar(1, degree); // create degree symbol from the binary
lcd.setCursor(0,0);
lcd.print("Welcome Michael");
lcd.setCursor(0, 1);
lcd.print("Initializing..");
delay(1000); //Splash Screen

// Start up the DS18B20 One Wire Temperature Sensor

sensors.begin();
if (!sensors.getAddress(tempSensor, 0)) 
{
lcd.setCursor(0, 1);
lcd.print("Sensor Error");
}
sensors.setResolution(tempSensor, 12);
sensors.setWaitForConversion(false);

delay(3000); // Splash screen

// Initialize the PID and related variables
LoadParameters();
myPID.SetTunings(Kp,Ki,Kd);

myPID.SetSampleTime(1000);
myPID.SetOutputLimits(0, WindowSize);

// Run timer2 interrupt every 15 ms 
TCCR2A = 0;
TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20;

//Timer2 Overflow Interrupt Enable
TIMSK2 |= 1<<TOIE2;

}

// ************************************************
// Timer Interrupt Handler
// ************************************************
SIGNAL(TIMER2_OVF_vect) 
{
if (opState == OFF)
{
digitalWrite(RelayPin, LOW); // make sure relay is off
}
else
{
DriveOutput();
}
}

// ************************************************
// Main Control Loop
//
// All state changes pass through here
// ************************************************
void loop()
{
// wait for button release before changing state
while(analogRead(0) < 1000) {}

lcd.clear();

switch (opState)
{
case OFF:
Off();
break;
case SETP:
Tune_Sp();
break;
case RUN:
Run();
break;
case TUNE_P:
TuneP();
break;
case TUNE_I:
TuneI();
break;
case TUNE_D:
TuneD();
break;
case TUNE_T:
Timer();
break;
}
}

// ************************************************
// Initial State - press RIGHT to enter setpoint
// ************************************************
void Off()
{
myPID.SetMode(MANUAL);
digitalWrite(RelayPin, LOW); // make sure it is off
lcd.print("Brew Kettle");
lcd.setCursor(0, 1);
lcd.print("Controller");
int buttons = analogRead(0);

while(buttons != 0)
{
buttons = analogRead(0);
}

// Prepare to transition to the RUN state
sensors.requestTemperatures(); // Start an asynchronous temperature reading

//turn the PID on
myPID.SetMode(AUTOMATIC);
windowStartTime = millis();
opState = RUN; // start control
}

// ************************************************
// Set Timer
// UP/DOWN to change timing length
// LEFT for OFF
// SELECT to BEGIN
// ************************************************
void Timer()
{
if (time_state == false)
{
lcd.clear();
lcd.print("Timer Set:");
lcd.setCursor(0,1);
lcd.print("Select to begin");
elapsed = 0.00;
time_hold = 0.00;
while (true)
{
buttons = analogRead(0);
if (buttons == 480) //left button
{
opState = RUN;
return;
}
if (buttons == 721 && set_time != 0) // begin timer
{
time_hold = millis()/60000.00; // Time stamp decimal minutes
time_state = true; // Enter in to timer state
opState = TUNE_T;
return; 
}
if (buttons == 131) // Increase time
{
set_time += 1;
delay(200);
}
if (buttons == 306 && set_time != 0) // Decrease time but not below 0
{
set_time -= 1;
delay(200);
}
lcd.setCursor(10,0);
lcd.print(set_time);
lcd.print("min ");
DoControl();
}
}
else if (time_state == true)
{
if (alarm == true)
{
lcd.clear();
lcd.print("Timer Completed");
lcd.setCursor(0,1);
lcd.print("Select to Reset");
while (true)
{
buttons = analogRead(0);
if (buttons == 721)
{
time_state = false;
alarm = false; 
opState = TUNE_T; 
return;
}
DoControl();
delay(100);
}
}
else
{
lcd.clear();
lcd.print("Time Remaining:");
while (true)
{
buttons = analogRead(0);
if (buttons == 480) //left button
{
opState = RUN;
return;
}
if (buttons == 721) //Press Select to reset timer
{
time_state = false;
opState = TUNE_T;
return;
}
if (alarm == true)
{
opState = TUNE_T;
return;
} 
time_remain = set_time - elapsed;
lcd.setCursor(0,1);
lcd.print(time_remain);
lcd.print("min ");
DoControl();
}
}
}
}

// ************************************************
// Setpoint Entry State
// UP/DOWN to change setpoint
// RIGHT for tuning parameters
// LEFT for OFF
// SELECT for toggling between 0.1 and 1 increments
// ************************************************
void Tune_Sp()
{
//Serial.print("Tuning Setpoint"); //debugging checkpoint
lcd.print("Set Temperature:");
int buttons = analogRead(0);
boolean increment_toggle = LOW; 
float increment = 0.1;

while(true)
{
buttons = analogRead(0);

if (buttons == 721) //select button
{
if (increment_toggle == LOW)
{
increment = 1;
increment_toggle = HIGH;
delay(200);
}
else if (increment_toggle == HIGH)
{
increment = 0.1;
increment_toggle = LOW;
delay(200);
}
}
if (buttons == 480) //left button
{
opState = RUN;
return;
}
if (buttons == 0) //right button
{
opState = TUNE_P;
return;
}
if (buttons == 131)
{
Setpoint += increment;
delay(200);
}
if (buttons == 306)
{
Setpoint -= increment;
delay(200);
}
/* ************************************************************************************ Timer to be debugged
if ((millis() - lastInput) > 3000) // return to RUN after 3 seconds idle
{
opState = RUN;
return;
}
*/
lcd.setCursor(0,1);
lcd.print(Setpoint);
lcd.print(" ");
DoControl();
}
}

// ************************************************
// Proportional Tuning State
// UP/DOWN to change Kp
// RIGHT for Ki
// LEFT for setpoint
// SHIFT for 10x tuning
// ************************************************
void TuneP()
{
lcd.print("Set Kp");

int buttons = analogRead(0);
float increment = 1.0;
boolean increment_toggle = LOW;

while(true)
{
buttons = analogRead(0);

if (buttons == 721) //select button
{
if (increment_toggle == LOW)
{
increment = 10.0;
increment_toggle = HIGH;
delay(200);
}
else if (increment_toggle == HIGH)
{
increment = 1.0;
increment_toggle = LOW;
delay(200);
}
}
if (buttons == 480)
{
opState = SETP;
return;
}
if (buttons == 0)
{
opState = TUNE_I;
return;
}
if (buttons == 131)
{
Kp += increment;
delay(200);
}
if (buttons == 306)
{
Kp -= increment;
delay(200);
}

/* ************************************************************************************ Timer to be debugged
if ((millis() - lastInput) > 3000) // return to RUN after 3 seconds idle
{
opState = RUN;
return;
}
*/
lcd.setCursor(0,1);
lcd.print(Kp);
lcd.print(" ");
DoControl();
}
}

// ************************************************
// Integral Tuning State
// UP/DOWN to change Ki
// RIGHT for Kd
// LEFT for Kp
// SHIFT for 10x tuning
// ************************************************
void TuneI()
{
lcd.print("Set Ki");
int buttons = analogRead(0);
float increment = 0.01;
boolean increment_toggle = LOW;

while(true)
{
buttons = analogRead(0);

if (buttons == 721) //select button
{
if (increment_toggle == LOW)
{
increment = 0.10;
increment_toggle = HIGH;
delay(200);
}
else if (increment_toggle == HIGH)
{
increment = 0.01;
increment_toggle = LOW;
delay(200);
}
}
if (buttons == 480)
{
opState = TUNE_P;
return;
}
if (buttons == 0)
{
opState = TUNE_D;
return;
}
if (buttons == 131)
{
Ki += increment;
delay(200);
}
if (buttons ==306)
{
Ki -= increment;
delay(200);
}

/* ************************************************************************************ Timer to be debugged
if ((millis() - lastInput) > 3000) // return to RUN after 3 seconds idle
{
opState = RUN;
return;
}
*/
lcd.setCursor(0,1);
lcd.print(Ki);
lcd.print(" ");
DoControl();
}

}

// ************************************************
// Derivative Tuning State
// UP/DOWN to change Kd
// RIGHT for setpoint
// LEFT for Ki
// SHIFT for 10x tuning
// ************************************************
void TuneD()
{
lcd.print(F("Set Kd"));
int buttons = analogRead(0);
boolean increment_toggle = LOW;
float increment = 0.01;

while(true)
{
buttons = analogRead(0);

if (buttons == 721) //select button
{
if (increment_toggle == LOW)
{
increment = 0.10;
increment_toggle = HIGH;
delay(200);
}
else if (increment_toggle == HIGH)
{
increment = 0.01;
increment_toggle = LOW;
delay(200);
}
}
if (buttons == 480)
{
opState = TUNE_I;
return;
}
if (buttons == 0)
{
opState = RUN;
return;
}
if (buttons == 131)
{
Kd += increment;
delay(200);
}
if (buttons == 306)
{
Kd -= increment;
delay(200);
}

/* *****************************************************************************************************
if ((millis() - lastInput) > 3000) // return to RUN after 3 seconds idle
{
opState = RUN;
return;
}
*/

lcd.setCursor(0,1);
lcd.print(Kd);
lcd.print(" ");
DoControl();
}
}

// ************************************************
// PID COntrol State
// SELECT for autotune
// RIGHT - Setpoint
// LEFT - OFF
// ************************************************
void Run()
{
//Serial.print("Checkpoint Run"); //debugging checkpoint
// set up the LCD's number of rows and columns: 
lcd.print("Sp: ");
lcd.print(Setpoint);
lcd.write(1);
lcd.print("C : ");
SaveParameters();
myPID.SetTunings(Kp,Ki,Kd);
int buttons = analogRead(0);

while(true)
{ 
buttons = analogRead(0);
if ((buttons == 721) 
&& (abs(Input - Setpoint) < 0.5)) // Should be at steady-state
{
StartAutoTune();
}
else if (buttons == 0)
{
opState = SETP;
//Serial.println("Run Return to SETP"); //debugging checkpoint
return;
}
else if (buttons == 480)
{
opState = OFF;
//Serial.println("Run Return to Off"); //debugging checkpoint
return;
}
else if (buttons == 131)
{
opState = TUNE_T;
return;
}

DoControl();

lcd.setCursor(0,1);
lcd.print(Input);
lcd.write(1);
lcd.print("C : ");

float pct = map(Output, 0, WindowSize, 0, 1000);
lcd.setCursor(10,1);
lcd.print((" "));
lcd.setCursor(10,1);
lcd.print(pct/10);
//lcd.print(Output);
lcd.print("%");

lcd.setCursor(15,0);
if (tuning)
{
lcd.print("T");
//Serial.print("Run - Tuning Active"); debugging active
}
else
{
lcd.print(" ");
}

// periodically log to serial port in csv format
if (millis() - lastLogTime > logInterval) 
{
Serial.print(Input);
Serial.print(",");
Serial.print(Output);
Serial.print(",");
Serial.println(millis());
}

delay(100);
}
}

// ************************************************
// Execute the control loop
// ************************************************
void DoControl()
{
// Read the input:
if (sensors.isConversionAvailable(0))
{
Input = sensors.getTempC(tempSensor);
sensors.requestTemperatures(); // prime the pump for the next one - but don't wait
}

if (tuning) // run the auto-tuner
{
if (aTune.Runtime()) // returns 'true' when done
{
FinishAutoTune();
}
}
else // Execute control algorithm
{
myPID.Compute();
}

// Time Proportional relay state is updated regularly via timer interrupt.
onTime = Output; 

if (time_state == true)
elapsed = (millis()/60000.00) - time_hold;
{
if (elapsed > set_time)
{
alarm = true;
if (sound_state = true)
{
tone(sound_pin, 800, 200);
delay(200);
sound_state = false;
}
else
{
sound_state = true;
delay(200);
}
}
}
}

// ************************************************
// Called by ISR every 15ms to drive the output
// ************************************************
void DriveOutput()
{ 
long now = millis();
// Set the output
// "on time" is proportional to the PID output
if(now - windowStartTime>WindowSize)
{ //time to shift the Relay Window
windowStartTime += WindowSize;
}
if((onTime > 100) && (onTime > (now - windowStartTime)))
{
digitalWrite(RelayPin,HIGH);
}
else
{
digitalWrite(RelayPin,LOW);
}
}


// ************************************************
// Start the Auto-Tuning cycle
// ************************************************

void StartAutoTune()
{
// REmember the mode we were in
ATuneModeRemember = myPID.GetMode();

// set up the auto-tune parameters
aTune.SetNoiseBand(aTuneNoise);
aTune.SetOutputStep(aTuneStep);
aTune.SetLookbackSec((int)aTuneLookBack);
tuning = true;
}

// ************************************************
// Return to normal control
// ************************************************
void FinishAutoTune()
{
tuning = false;

// Extract the auto-tune calculated parameters
Kp = aTune.GetKp();
Ki = aTune.GetKi();
Kd = aTune.GetKd();

// Re-tune the PID and revert to normal control mode
myPID.SetTunings(Kp,Ki,Kd);
myPID.SetMode(ATuneModeRemember);

// Persist any changed parameters to EEPROM
SaveParameters();
}


// ************************************************
// Save any parameter changes to EEPROM
// ************************************************
void SaveParameters()
{
if (Setpoint != EEPROM_readDouble(SpAddress))
{
EEPROM_writeDouble(SpAddress, Setpoint);
}
if (Kp != EEPROM_readDouble(KpAddress))
{
EEPROM_writeDouble(KpAddress, Kp);
}
if (Ki != EEPROM_readDouble(KiAddress))
{
EEPROM_writeDouble(KiAddress, Ki);
}
if (Kd != EEPROM_readDouble(KdAddress))
{
EEPROM_writeDouble(KdAddress, Kd);
}
}

// ************************************************
// Load parameters from EEPROM
// ************************************************
void LoadParameters()
{
// Load from EEPROM
Setpoint = EEPROM_readDouble(SpAddress);
Kp = EEPROM_readDouble(KpAddress);
Ki = EEPROM_readDouble(KiAddress);
Kd = EEPROM_readDouble(KdAddress);

// Use defaults if EEPROM values are invalid
if (isnan(Setpoint))
{
Setpoint = 60;
}
if (isnan(Kp))
{
Kp = 850;
}
if (isnan(Ki))
{
Ki = 0.5;
}
if (isnan(Kd))
{
Kd = 0.1;
} 
}


// ************************************************
// Write floating point values to EEPROM
// ************************************************
void EEPROM_writeDouble(int address, double value)
{
byte* p = (byte*)(void*)&value;
for (int i = 0; i < sizeof(value); i++)
{
EEPROM.write(address++, *p++);
}
}

// ************************************************
// Read floating point values from EEPROM
// ************************************************
double EEPROM_readDouble(int address)
{
double value = 0.0;
byte* p = (byte*)(void*)&value;
for (int i = 0; i < sizeof(value); i++)
{
*p++ = EEPROM.read(address++);
}
return value;
} 



And thats it! Changes mostly related to the hardware (LCD, relay, etc) I had, and the changes to pin allocations. If youre still here I might just describe how it works just a little. So it begins with a splash screen in which leads to the the "off" screen. To go to the next screen you press the right button to go to the "on screen". This activates relay control, and temperature monitoring. From here you have three choices, left to go back to "off", up to set the timer, and right again to go into "tuning SP". Tuning SP allows you to set the set point temperature which the arduino will aim to get to and stablize at. You can press up or down to adjust set point up or down, and pressing select increases the up and down adjustment by a factor of ten. Once the setpoint is adjusted to your liking you may either go back by pressing left, or you can tune the PID control variables by pressing right. Pressing right once you go into a screen to tune Kd in the same way you adjusted setpoint, by pressing twice you can adjust Ti, and by pressing right a third time you may adjust Td. From this last screen, if you press right again you will end back up in the "run section". Other features that i wont bother to describe are the alarm and the autotuning function for the PID control variables. The timer works well but the autotuner is still requiring some adjustment. 

If anyone was thinking of trying this here are some of the parts i used:

Arduino Uno (or similar rip-off on ebay): - i think its good to support the original though
http://www.australianrobotics.com.au/products/arduino-uno-r3

LCD Screen Shield with Pushbuttons: 
http://www.dx.com/p/2-6-lcd-keypad-shield-for-arduino-green-black-161359#.UzGWhfmSyek

DS18B20 Temperature Sensor: - this one has a teflon coat is safer to use in food at higher temperatures
https://core-electronics.com.au/store/index.php/sensors-modules/temperature/high-temp-waterproof-ds18b20-digital-temperature-sensor-extras.html 

The relay to control mains power to the element: (requires soldering and caution!!)
https://www.sparkfun.com/products/11042

Protoscrew Shield: - If using arduino I would highly recommend this, makes life a lot easier
https://www.sparkfun.com/products/9729

Junction box to hold the relay:
https://www.masters.com.au/product/900029131/tripac-adaptable-weatherproof-junction-box-abko332

My element is from Keg keg and I wired up the relay using an extension cable I had lying around. To add a buzzer, a piezo electric buzzer that runs on 5 V will do. 
The prices can add up but over time the parts can be re-used (eg: my arduino is begin used to monitor ambient air temp for my aquaponics) but the main benefits is that putting it all together is fun, educational, and extremely satisfying.

Im always making revisions to the code, so if you see any changes that need to be made be sure to let me know! Sorry for the extreme post.. :super:


----------



## marksy

Yeah sweet as thanks machann, I am looking to do this sometime this year. I almost have collected all the parts. I think I am like your self and just lack the time factor, haha, kids, work and study 

Keep us updated.


----------



## dehaasguus

Hi everybody
[SIZE=10.5pt]I am a student from Netherlands, and for college we need to make a temperature control with pid and we have the same hardware parts as your alignment. 

We have copied your code into the arduino software, only we get some errors, we can already measure the temperature with another program and the DS18B20 Temperature Sensor. When I verify your code in arduino software I got the following error: PID_van_forum:914: error: unterminated argument list invoking macro "F". 
I thought, that when I download al the libraries and so on it have to work? At least the verifying has to work? If the verifying is oké without any faults, I can upload the code to the Arduino.[/SIZE]

I hope you can help me further and give me some tips?

[SIZE=10.5pt]Thanks in advance![/SIZE]

[SIZE=10.5pt]Guus de Haas. [/SIZE]


----------



## Dingerb

Hi Guys,

New member here.

I know this is an older thread, but fingers crossed I get a reply.

Stumbled across this via a Google search.

Anyway on to my point, could this PID be easily used to control fermentation temperature?

Would a couple of PC fans drawing in colder air from outside the fridge, sorry, fermentation chamber, reduce the temp enough?

Could I alter the code to use the pc fans OR the fridge compressor depending on how high the temp got?

I'm working on making my 3rd, (or is it 4th?) version of a fridge converted to fermentation chamber.

My best results so far have been with a ST1000, but this ended up screwing the fridge compressor due to it being switched off and on too quickly.

I'm now looking towards a PID controlled system with arduino (and the added boost of lcd display).

Very steep learning curve in regards to PID.

Brewing I've sussed. I'm now just trying to perfect my setup to get those consistent results.

Thanks in advance gents


----------



## Kenf

Dingerb said:


> Hi Guys,
> 
> New member here.
> 
> I know this is an older thread, but fingers crossed I get a reply.
> 
> Stumbled across this via a Google search.
> 
> Anyway on to my point, could this PID be easily used to control fermentation temperature?
> 
> Would a couple of PC fans drawing in colder air from outside the fridge, sorry, fermentation chamber, reduce the temp enough?
> 
> Could I alter the code to use the pc fans OR the fridge compressor depending on how high the temp got?
> 
> I'm working on making my 3rd, (or is it 4th?) version of a fridge converted to fermentation chamber.
> 
> My best results so far have been with a ST1000, but this ended up screwing the fridge compressor due to it being switched off and on too quickly.
> 
> I'm now looking towards a PID controlled system with arduino (and the added boost of lcd display).
> 
> Very steep learning curve in regards to PID.
> 
> Brewing I've sussed. I'm now just trying to perfect my setup to get those consistent results.
> 
> Thanks in advance gents


I just use a SmartPID - no coding required and all you need is three SSR’s


----------



## gap

You have not set up the STC1000 correctly if you have your fridges switching on and off too quickly.
There will be no difference using a PID controller.
You need to set a fridge delay to stop this happening.
I have used STC1000 controllers on fridges for 10 years or so.


----------



## Dingerb

Hi Gap,

Understand where you are coming from, but where in the settings do I do that?

Regards

Dave


----------



## gap

Please do a search on google for STC1000 manual . you will find videos and written instructions.
F3 is the compressor delay - stops the fridge from turning on again until a set time from when it turned off. You set this time, at least 3 minutes and preferably longer.


----------



## Dingerb

Hello again Gap,

Cheers for that bit of info.

Regards

Dave


----------

