Standalone Controller for kegerator and measuring how much beer is left!

Hey I am working on a temperature controller and have come up with the following code. I have one problem and that is I am controlling a compressor for the cooling and do not want to short cycle the equipment. How can i set it up so that the relay output is high for say at least 10 minutes?

Here is the PID code:

void doPID(){

	myPID.Compute();
	unsigned long now = millis();  //get now time
	if(now - windowStartTime > WindowSize)
    { //time to shift the Relay Window
    	windowStartTime += WindowSize;
    } 
	if(output > now - windowStartTime) {
		digitalWrite(relayOut,HIGH);   //ON
	}
    else {
    	digitalWrite(relayOut,LOW);    //OFF
    }
    Serial.print(relayOut);
}

Also here is the full thing:

//  This Arduino sketch reads DS18B20 "1-Wire" digital
//  temperature sensors and displays it on a 16x2 LCD

//  Include the libraries below
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>
#include <PID_v1.h>

// initialize the lcd library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

// Data wire for one wire sensors is on pin 3
#define ONE_WIRE_BUS 4
//Define encoder pins
#define encoderPinA  2
#define encoderPinB  3

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);
// Assign the addresses of your 1-Wire temp sensors.
// See the tutorial on how to obtain these addresses:
// http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html
DeviceAddress Temp1 = { 0x28, 0xA8, 0x4B, 0x22, 0x05, 0x00, 0x00, 0x38 };

const int displaySwitch = 5;
const int displayOnOff = 6;
const int relayOut = 14;
int onCount;
double tempSP = 33;
float maxTemp =-100;
float minTemp=100;
int resetCount = 0;
double inputTemp, output;
int WindowSize = 5000;          //5 sec
unsigned long windowStartTime;

PID myPID(&inputTemp, &output, &tempSP,2,5,1, DIRECT);

void setup(void)
{
  // start serial port
  Serial.begin(9600);
  // Start up the library
  sensors.begin();
  // set the resolution to 10 bit
  sensors.setResolution(Temp1, 10);
  //Setup LCD
  lcd.begin(16, 2);
  //Setup Button pins
  pinMode(displaySwitch, INPUT);
  pinMode(displayOnOff, OUTPUT);
  //setup encoder
  pinMode(encoderPinA, INPUT_PULLUP); 
  digitalWrite(encoderPinA, HIGH);       // turn on pullup resistor
  pinMode(encoderPinB, INPUT_PULLUP); 
  digitalWrite(encoderPinB, HIGH);       // turn on pullup resistor
  //Setup interupt 
  attachInterrupt(0, doEncoder, CHANGE);  // encoder pin on interrupt 0 - pin 2
  attachInterrupt(1, doEncoder, CHANGE);  // encoder pin on interrupt 1 - pin 3
  myPID.SetMode(AUTOMATIC);
  pinMode(relayOut,OUTPUT);
  windowStartTime = millis();
  myPID.SetOutputLimits(0, WindowSize);
}

void doEncoder() {
  /* If pinA and pinB are both high or both low, it is spinning
   * forward. If they're different, it's going backward.
   * For more information on speeding up this process, see
   * [Reference/PortManipulation], specifically the PIND register.
   */
  if (digitalRead(encoderPinA) == digitalRead(encoderPinB)) {
    tempSP = tempSP + 0.5;
  } else {
    tempSP = tempSP - 0.5;
  }
  lcd.clear();
  lcd.print("SetPoint:");
  lcd.print(tempSP,2);
  lcd.print(" F");
}

void doPID(){

	myPID.Compute();
	unsigned long now = millis();  //get now time
	if(now - windowStartTime > WindowSize)
    { //time to shift the Relay Window
    	windowStartTime += WindowSize;
    } 
	if(output > now - windowStartTime) {
		digitalWrite(relayOut,HIGH);   //ON
	}
    else {
    	digitalWrite(relayOut,LOW);    //OFF
    }
    Serial.print(relayOut);
}

void printTemperature(DeviceAddress deviceAddress)
{
  // This is the Serial Print function to see temperature if the diplay is not working
  Serial.print("Getting temperatures...\n\r");
  sensors.requestTemperatures();
  Serial.print("Current temperature is: ");
  float tempC = sensors.getTempC(deviceAddress);
  if (tempC == -127.00) {
    Serial.print("Error getting temperature");
  } else {
    Serial.print("C: ");
    Serial.print(tempC);
    Serial.print(" F: ");
    Serial.print(DallasTemperature::toFahrenheit(tempC));
    Serial.print("\n\r");
  }
}

void displayTemperature(DeviceAddress deviceAddress)
{
	delay(2000);

float tempC = sensors.getTempC(deviceAddress);
float tempF = sensors.getTempF(deviceAddress);

   //calculate min and max
   if (tempF>maxTemp){maxTemp = tempF;}
   if (tempF<minTemp){minTemp = tempF;}
   if (tempC == -127.00) // Measurement failed or no device found
   {
    lcd.clear();
    lcd.print("Temperature Error");
   } 
   else
   {
   lcd.clear();
   lcd.print("SetPoint=");
   lcd.print(tempSP,2); //Display Setpoint
   lcd.print(" F");
   lcd.setCursor(0,1);  //Start at character 0 on line 1
   lcd.print("Act.Temp=");
   lcd.print(tempF,2); //Display temperature reading with 2 decimal places
   lcd.print(" F");
   tempF = 0;
   inputTemp = tempF;
   }
}

void displayMinMaxTemperature(DeviceAddress deviceAddress)
{
   delay(2000);
   lcd.clear();
   lcd.print("Min Temp=");
   lcd.print(minTemp,2); //Display min temp
   lcd.print(" F");
   lcd.setCursor(0,1);  //Start at character 0 on line 1
   lcd.print("Max Temp=");
   lcd.print(maxTemp,2); //Display max temp
   lcd.print(" F");
}

void backLightReset()
{
  //Now turn on the backlight if the button is pressesd
  //Now rest min and max temps if button is held down
  if (digitalRead(displaySwitch) == HIGH){
   onCount = 5; // This determines time that it will stay on 
  // Remember delay also affects the time it is on
   resetCount = resetCount + 1;}
  else{
  	resetCount = 0;
   if(onCount > 0){
    onCount = onCount - 1;
   }
   else{
    onCount = 0;
   }
  }
  if(onCount > 0){
   digitalWrite(displayOnOff, HIGH);
  }
  else{
   digitalWrite(displayOnOff, LOW);
  }
  if(resetCount > 3){
   maxTemp = -100;
   minTemp = 100;
  }
}

void loop(void)
{ 
  // Thiw will print the temp reading so it can be read
  // through serial connection when a display is not attached
  // Remove this to speed things up once the display is working
  //printTemperature(Temp1);

  // This will display temp and setpoint on the LCD
  displayTemperature(Temp1);
  
  doPID();
  
  // Now turn on the backlight if the button is pressesd
  // Now rest min and max temps if button is held down
  backLightReset();
  
  // Display Min and Max Temperatures
  displayMinMaxTemperature(Temp1);
  
  // Now turn on the backlight if the button is pressesd
  // Now rest min and max temps if button is held down
  backLightReset();

}

I would appreciate any help I'm sure this has been done but I have not been able to find it.

Also the only untested code is the PID loop I am waiting on my relay to arrive in the mail so I have not tried it at all yet.

So I have completed a wiring diagram and updated the programming with a few more features.

The controller no longer stores the invalid temperature readings as min an max in the case that the temperature sensor is unplugged or not working it will also not use an invalid number for control.

I am still waiting on my relay for the control of the compressor but if anyone has any insight into PID control and preventing shortcycling please let me know my PID programming is rapidly turning on or off the relay output.

//  This Arduino sketch reads DS18B20 "1-Wire" digital
//  temperature sensors and displays it on a 16x2 LCD

//  Include the libraries below
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>
#include <PID_v1.h>

// initialize the lcd library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

// Data wire for one wire sensors is on pin 4
#define ONE_WIRE_BUS 4
//Define encoder pins
#define encoderPinA  2
#define encoderPinB  3
//define relay on analog pin 0 which will be used as digital
#define relayOut  14

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);
// Assign the addresses of your 1-Wire temp sensors.
// See the tutorial on how to obtain these addresses:
// http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html
DeviceAddress Temp1 = { 0x28, 0xA8, 0x4B, 0x22, 0x05, 0x00, 0x00, 0x38 };


//Define other variable
const int displaySwitch = 5;
const int displayOnOff = 6;
int onCount;//used to delay the backlight from turning off
double tempSP = 33; // control point
float maxTemp =-100; //stores max
float minTemp=100; //stores min
int resetCount = 0; //used to reset min and max temps when the rotary button is held down
double inputTemp, output; // input and output for PID
int WindowSize = 5000;          //5 sec
unsigned long windowStartTime;

//Define PID parameters
PID myPID(&inputTemp, &output, &tempSP,2,5,1, DIRECT);

void setup(void)
{
  // start serial port
  Serial.begin(9600);
  // Start up the sensor library
  sensors.begin();
  // set the sensor resolution to 10 bit
  sensors.setResolution(Temp1, 10);
  //Setup LCD
  lcd.begin(16, 2);
  //Setup Button pins
  pinMode(displaySwitch, INPUT);
  pinMode(displayOnOff, OUTPUT);
  //setup encoder
  pinMode(encoderPinA, INPUT_PULLUP); 
  pinMode(encoderPinB, INPUT_PULLUP);
  //Setup interupt 
  attachInterrupt(0, doEncoder, CHANGE);  // encoder pin on interrupt 0 - pin 2
  // Settup PID
  myPID.SetMode(AUTOMATIC);
  pinMode(relayOut,OUTPUT);
  windowStartTime = millis();
  myPID.SetOutputLimits(0, WindowSize);
}

void doEncoder() {
  /* If pinA and pinB are both high or both low, it is spinning
   * forward. If they're different, it's going backward.
   * For more information on speeding up this process, see
   * [Reference/PortManipulation], specifically the PIND register.
   */
  if (digitalRead(encoderPinA) == digitalRead(encoderPinB)) {
    tempSP = tempSP + 0.5;
  } else {
    tempSP = tempSP - 0.5;
  }
  digitalWrite(displayOnOff, HIGH);
  lcd.clear();
  lcd.print("SetPoint:");
  lcd.print(tempSP,0);
  lcd.print(" F");
  onCount = 5;
}

void doPID(){

	myPID.Compute();
	unsigned long now = millis();  //get now time
	if(now - windowStartTime > WindowSize)
    { //time to shift the Relay Window
    	windowStartTime += WindowSize;
    } 
	if(output > now - windowStartTime) {
		digitalWrite(relayOut,HIGH);   //ON
	}
    else {
    	digitalWrite(relayOut,LOW);    //OFF
    }
    Serial.print(relayOut);
}

void printTemperature(DeviceAddress deviceAddress)
{
  // This is the Serial Print function to see temperature if the diplay is not working
  Serial.print("Getting temperatures...\n\r");
  sensors.requestTemperatures();
  Serial.print("Current temperature is: ");
  float tempC = sensors.getTempC(deviceAddress);
  if (tempC == -127.00) {
    Serial.print("Error getting temperature");
  } else {
    Serial.print("C: ");
    Serial.print(tempC);
    Serial.print(" F: ");
    Serial.print(DallasTemperature::toFahrenheit(tempC));
    Serial.print("\n\r");
  }
}

void displayTemperature(DeviceAddress deviceAddress)
{
	delay(2000);
 sensors.requestTemperatures();
float tempC = sensors.getTempC(deviceAddress);
float tempF = sensors.getTempF(deviceAddress);

   //calculate min and max
   if (tempF>maxTemp){if(tempF<100){maxTemp = tempF;}}
   if (tempF<minTemp){if(tempF>-10){minTemp = tempF;}}
   if (tempC == -127.00) // Measurement failed or no device found
   {
    lcd.clear();
    lcd.print("Temperature Error");
   } 
   else
   {
   lcd.clear();
   lcd.print("SetPoint=");
   lcd.print(tempSP,0); //Display Setpoint
   lcd.print(" F");
   lcd.setCursor(0,1);  //Start at character 0 on line 1
   lcd.print("Act.Temp=");
   lcd.print(tempF,2); //Display temperature reading with 2 decimal places
   lcd.print(" F");
   if(tempF>-20){if(tempF<80){inputTemp = tempF;}}
   }
}

void displayMinMaxTemperature(DeviceAddress deviceAddress)
{
   delay(2000);
   lcd.clear();
   lcd.print("Min Temp=");
   lcd.print(minTemp,2); //Display min temp
   lcd.print(" F");
   lcd.setCursor(0,1);  //Start at character 0 on line 1
   lcd.print("Max Temp=");
   lcd.print(maxTemp,2); //Display max temp
   lcd.print(" F");
}

void backLightReset()
{
  //Now turn on the backlight if the button is pressesd
  //Now rest min and max temps if button is held down
  if (digitalRead(displaySwitch) == HIGH){
   onCount = 5; // This determines time that it will stay on 
  // Remember delay also affects the time it is on
   resetCount = resetCount + 1;}
  else{
  	resetCount = 0;
   if(onCount > 0){
    onCount = onCount - 1;
   }
   else{
    onCount = 0;
   }
  }
  if(onCount > 0){
   digitalWrite(displayOnOff, HIGH);
  }
  else{
   digitalWrite(displayOnOff, LOW);
  }
  if(resetCount > 3){
   maxTemp = -100;
   minTemp = 100;
  }
}

void loop(void)
{ 
  // Thiw will print the temp reading so it can be read
  // through serial connection when a display is not attached
  // Remove this to speed things up once the display is working
  //printTemperature(Temp1);

  // This will display temp and setpoint on the LCD
  displayTemperature(Temp1);
  
  doPID();
  
  // Now turn on the backlight if the button is pressesd
  // Now rest min and max temps if button is held down
  backLightReset();
  
  // Display Min and Max Temperatures
  displayMinMaxTemperature(Temp1);
  
  // Now turn on the backlight if the button is pressesd
  // Now rest min and max temps if button is held down
  backLightReset();

}

Wiring Diagram

Not sure if any one is looking but here is an update...

I dropped the PID loop as it was unnecessary controlling a binary output when I needed a minimum run time on about 15 minutes. This seems to work well as the average temp is within .5 degrees of the setpoint

Added average using the statistics library

Used button to turn on back light but also switch between different screen displaying different data

Next I will be adding load cells under the kegs to measure how many beers are left using the hx711 library!!

Also have mounted the LCD, Arduino and Rotary Encoder to plexiglass and embedded it into the lid of the Kegerator.

Pic 1
Pic2

//  Include the libraries below
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>
#include <Statistic.h>


// initialize the lcd library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

// Data wire for one wire sensors is on pin 4
#define ONE_WIRE_BUS 4
//Define encoder pins
#define encoderPinA  2
#define encoderPinB  3
//define relay on analog pin 0 which will be used as digital
#define relayOut  A0

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// Assign the addresses of your 1-Wire temp sensors.
// See the tutorial on how to obtain these addresses:
// http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html
DeviceAddress Temp1 = { 0x28, 0xA8, 0x4B, 0x22, 0x05, 0x00, 0x00, 0x38 };


const int displaySwitch = 5;
const int displayOnOff = 6;
int onCount;//used to delay the backlight from turning off
double tempSP = 33; // control point
float maxTemp = -100; //stores max
float minTemp = 100; //stores min
int resetCount = 0; //used to reset min and max temps when the rotary button is held down
int compressorTimer = 0;//Compressor Min on/off time
//on off timer keep the compressor for staying on or off for to long
//they circumvent the compressortimer
int onTimer = 0;
int offTimer = 0;
Statistic myStats;//setup for average temp calculation
float tempF;//temp in F
float tempC;//temp in C
int screenSwitch = 0;//Controls which screen is displayed at which time
long time;//stores current time for sensor retrival delay
int timeBetweenReadings = 5000;//time between readings
int lastScreen;//the last screen displayed
int lightFirst = 0; //keeps the screen from switching when the button is first pressed

void setup(void)
{
	// start serial port for print screen not used once lcd is setup
	//Serial.begin(9600);
	// Start up the sensor library
	sensors.begin();
	// set the sensor resolution to 10 bit
	sensors.setResolution(Temp1, 10);
	//Setup LCD
	lcd.begin(16, 2);
	//Setup Button pins
	pinMode(displaySwitch, INPUT);
	pinMode(displayOnOff, OUTPUT);
	//setup encoder
	pinMode(encoderPinA, INPUT_PULLUP);
	pinMode(encoderPinB, INPUT_PULLUP);
	//Setup interupt
	attachInterrupt(0, doEncoder, CHANGE);  // encoder pin on interrupt 0 - pin 2
	// Setup Relay pin
	pinMode(relayOut, OUTPUT);
	//clear stats to ensure it starts clean
	myStats.clear();

}

void doEncoder()
{
	/* If pinA and pinB are both high or both low, it is spinning
	 * forward. If they're different, it's going backward.
	 * For more information on speeding up this process, see
	 * [Reference/PortManipulation], specifically the PIND register.
	 */
	if (digitalRead(encoderPinA) == digitalRead(encoderPinB))
	{
		tempSP = tempSP + 0.5;
	}
	else
	{
		tempSP = tempSP - 0.5;
	}
	digitalWrite(displayOnOff, HIGH);
	lcd.clear();
	lcd.print("SetPoint:");
	lcd.print(tempSP, 0);
	lcd.print(" F");
	onCount = 200;
}

void printTemperature(DeviceAddress deviceAddress)
{
	// This is the Serial Print function to see temperature if the diplay is not working
	Serial.print("Getting temperatures...\n\r");
	sensors.requestTemperatures();
	Serial.print("Current temperature is: ");
	float tempC = sensors.getTempC(deviceAddress);
	if (tempC == -127.00)
	{
		Serial.print("Error getting temperature");
	}
	else
	{
		Serial.print("C: ");
		Serial.print(tempC);
		Serial.print(" F: ");
		Serial.print(DallasTemperature::toFahrenheit(tempC));
		Serial.print("\n\r");
	}
}

void displayTemperature()
{
	lcd.clear();
	lcd.print("SetPoint=");
	lcd.print(tempSP, 0); //Display Setpoint
	lcd.print(" F");
	lcd.setCursor(0, 1); //Start at character 0 on line 1
	lcd.print("Act.Temp=");
	lcd.print(tempF, 2); //Display temperature reading with 2 decimal places
	lcd.print(" F");
}

void displayAverage()
{
	lcd.clear();
	lcd.print("Avg.Temp=");
	lcd.print(myStats.average(), 2); //Display avg temp
	lcd.print(" F");
	lcd.setCursor(0, 1); //Start at character 0 on line 1
	lcd.print("Act.Temp=");
	lcd.print(tempF, 2); //Display actual temp
	lcd.print(" F");
}

void displayMinMaxTemperature()
{
	lcd.clear();
	lcd.print("Min Temp=");
	lcd.print(minTemp, 2); //Display min temp
	lcd.print(" F");
	lcd.setCursor(0, 1); //Start at character 0 on line 1
	lcd.print("Max Temp=");
	lcd.print(maxTemp, 2); //Display max temp
	lcd.print(" F");
}

void displayBeerLeft()
{
	lcd.clear();
	lcd.print("Measuring is not");
	lcd.setCursor(0, 1); //Start at character 0 on line 1
	lcd.print("setup yet");
}

void loop(void)
{
	//only read sensor if 5sec has passed
	if(millis() > time + timeBetweenReadings)
	{
		//get the temp from the sensor
		sensors.requestTemperatures();
		tempC = sensors.getTempC(Temp1);
		tempF = sensors.getTempF(Temp1);

		//test for sensor errors
		if (tempC == -127.00) // Measurement failed or no device found
		{
			lcd.clear();
			lcd.print("Temperature Error");
		}
		else
		{
			//calculate min and max
			if (tempF > maxTemp)
			{
				if(tempF < 100)
				{
					maxTemp = tempF;
				}
			}
			if (tempF < minTemp)
			{
				if(tempF > -50)
				{
					minTemp = tempF;
				}
			}
			//Control the Compressor relay
			if (tempF - 4 > tempSP && compressorTimer == 0)
			{
				if (onTimer < 100)
				{
					compressorTimer = 150;
				}
				else
				{
					compressorTimer = 5;
				}
			}
			if (tempF <= tempSP && compressorTimer == 0)
			{
				if (offTimer < 100)
				{
					compressorTimer = -150;
				}
				else
				{
					compressorTimer = -5;
				}
			}
			if (compressorTimer > 0)
			{
				digitalWrite(relayOut, HIGH);
				compressorTimer = compressorTimer - 1;
				onTimer++;
				offTimer = 0;
			}
			if (compressorTimer < 0)
			{
				digitalWrite(relayOut, LOW);
				compressorTimer = compressorTimer + 1;
				onTimer = 0;
				offTimer++;
			}
			//Now add reading to the average or reset average if a day has passed
			myStats.add(tempF);
			if (myStats.count() == 17280)
			{
				myStats.clear();
			}
		}
		time = millis();// set time to current so that delay works
		lastScreen = 5;// set last screen to random rumber so dispaly will refresh
	}


	//Now turn on the backlight if the button is pressesd
	//Now rest min and max temps if button is held down
	if (digitalRead(displaySwitch) == HIGH)
	{
		onCount = 200; // This determines time that it will stay on
		// Remember delay also affects the time it is on
		resetCount = resetCount + 1;
		if(screenSwitch < 3 && lightFirst == 1)
		{
			screenSwitch++;
		}
		if(screenSwitch == 4 && lightFirst == 1)
		{
			screenSwitch = 0;
		}
		lightFirst = 1;
	}
	else
	{
		resetCount = 0;
		if(onCount > 0)
		{
			onCount = onCount - 1;
		}
	}
	if(onCount > 0)
	{
		digitalWrite(displayOnOff, HIGH);
	}
	else
	{
		digitalWrite(displayOnOff, LOW);
		lightFirst = 0;
	}
	if(resetCount > 20)
	{
		maxTemp = -100;
		minTemp = 100;
		myStats.clear();
	}
	//Choose which screen to display
	//First Check for errors
	if(resetCount > 1 && resetCount < 20)
	{
		lcd.clear();
		lcd.print("Hold to Reset");
	}
	else
	{
		if(resetCount >= 20)
		{
			lcd.clear();
			lcd.print("Reset Complete");
		}
		else
		{
			if(lastScreen != screenSwitch)
			{
				if (tempC == -127.00) // Measurement failed or no device found
				{
					lcd.clear();
					lcd.print("Temperature Error");
				}
				else
				{
					if(screenSwitch == 0)
					{
						displayTemperature();
					}
					if(screenSwitch == 1)
					{
						displayAverage();
					}
					if(screenSwitch == 2)
					{
						displayMinMaxTemperature();
					}
					if(screenSwitch == 3)
					{
						displayBeerLeft();
					}
				}
				lastScreen = screenSwitch;
			}
		}
	}
	delay(200);

}
if(millis() > time + timeBetweenReadings)

Don't do that.
(The reason you don't do that is a problem called "millis() rollover". Search the forums for more info.)

Try this instead:

if(millis() - time > timeBetweenReadings)

I am not following this.

First you cannot control a relay with PID. end of story.
bad application.

If you have any intention of trying to control a compressor in any way but run and stop and for no less than 10 minutes, you really need to re-think your scheme. or, keep a couple spare compressors on-hand. note how long it takes to get and make sure you order them as they fail so you always have a couple on the shelf while waiting for shipping.

dave-in-nj:
I am not following this.

First you cannot control a relay with PID. end of story.
bad application.

If you have any intention of trying to control a compressor in any way but run and stop and for no less than 10 minutes, you really need to re-think your scheme. or, keep a couple spare compressors on-hand. note how long it takes to get and make sure you order them as they fail so you always have a couple on the shelf while waiting for shipping.

That's why I am no longer using a PID and the compressor has a minimum run time of 15 minutes!

odometer:

if(millis() > time + timeBetweenReadings)

Don't do that.
(The reason you don't do that is a problem called "millis() rollover". Search the forums for more info.)

Try this instead:

if(millis() - time > timeBetweenReadings)

Awesome I was worried about that Thanks! But are you sure this will solve the issue? I was planning on just having the whole thing reset say each week.

I think this will work to prevent a rollover problem, but I find it hard to read.
I prefer to not do the math each time, but just a[ IF mili>nextRunTime ]. and then reset before/at the rollover. I seem to be the only one on here partial to that tho.

I'm not sure how you would not do the math each time or what you mean? If you have a way to reset millis() without resetting the Arduino please let me know I have not been able to find that anywhere and it would solve the problem.

My concern is that when millis() automatically resets to zero after about 50 days both my original equation and the one Odometer suggested will fail to work as the time variable will not also reset.

void loop() 
    {

          nowMilli=millis();  // used to simulate multi-tasking

          if( nowMilli>nextMilliUpdateLcd) {         // half second processes
              displayTime();

If you don't need to do two or more processes (simulate multi tasking), then just use delay().

If you do need multi tasking, and after 50 days you get rollover, (Millis() < lastMillis ), then just reset, if you are not saving an important log of data.

You don't have to do the math each time: the Arduino does.

Let's look at my code again.

if(millis() - time > timeBetweenReadings)

This part

millis() - time

calculates how long it has been since the last reading.

Once you have that, then this part

> timeBetweenReadings

checks to see if it is time for the next reading.

And resetting "before or at the rollover" is just one more thing that you have to keep track of.

if( nowMilli>nextMilliUpdateLcd) {

How about:

if(((long)(nowMilli-nextMilliUpdateLcd)) > 0) {
void loop() 
    {

          nowMilli=millis();  // used to simulate multi-tasking

          if( nowMilli>nextMilliUpdateLcd) {         // half second processes
              displayTime();

If you don't need to do two or more processes (simulate multi tasking), then just use delay().

If you do need multi tasking, and after 50 days you get rollover, (Millis() < lastMillis ), then just reset, if you are not saving an important log of data.
Just make sure the setup() sets the compressor off for the right length of time (15min maybe).

Thanks Odometer and Jack wp.

I am doing two processes one to read the sensor every 5 seconds and one to read a button to turn on a backlight/reset/switch display screens So...

Could I also simply do this to reset the time variable as described below?

if (time>millis()){time=millis();}

Under normal operation millis() will always be greater than the previously recorded time but in the case that it resets to zero only then will time be greater and the code above will correct this.

Here is the full code again with the update and just to clarify the Compressor already has a min. on off time of 15 min I put this in there when i got rid of the pointless PID.

//  Include the libraries below
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>
#include <Statistic.h>


// initialize the lcd library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

// Data wire for one wire sensors is on pin 4
#define ONE_WIRE_BUS 4
//Define encoder pins
#define encoderPinA  2
#define encoderPinB  3
//define relay on analog pin 0 which will be used as digital
#define relayOut  A0

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// Assign the addresses of your 1-Wire temp sensors.
// See the tutorial on how to obtain these addresses:
// http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html
DeviceAddress Temp1 = { 0x28, 0xA8, 0x4B, 0x22, 0x05, 0x00, 0x00, 0x38 };


const int displaySwitch = 5;
const int displayOnOff = 6;
int onCount;//used to delay the backlight from turning off
double tempSP = 33; // control point
float maxTemp = -100; //stores max
float minTemp = 100; //stores min
int resetCount = 0; //used to reset min and max temps when the rotary button is held down
int compressorTimer = 0;//Compressor Min on/off time
//on off timer keep the compressor for staying on or off for to long
//they circumvent the compressortimer
int onTimer = 0;
int offTimer = 0;
Statistic myStats;//setup for average temp calculation
float tempF;//temp in F
float tempC;//temp in C
int screenSwitch = 0;//Controls which screen is displayed at which time
long time;//stores current time for sensor retrival delay
int timeBetweenReadings = 5000;//time between readings
int lastScreen;//the last screen displayed
int lightFirst = 0; //keeps the screen from switching when the button is first pressed

void setup(void)
{
	// start serial port for print screen not used once lcd is setup
	//Serial.begin(9600);
	// Start up the sensor library
	sensors.begin();
	// set the sensor resolution to 10 bit
	sensors.setResolution(Temp1, 10);
	//Setup LCD
	lcd.begin(16, 2);
	//Setup Button pins
	pinMode(displaySwitch, INPUT);
	pinMode(displayOnOff, OUTPUT);
	//setup encoder
	pinMode(encoderPinA, INPUT_PULLUP);
	pinMode(encoderPinB, INPUT_PULLUP);
	//Setup interupt
	attachInterrupt(0, doEncoder, CHANGE);  // encoder pin on interrupt 0 - pin 2
	// Setup Relay pin
	pinMode(relayOut, OUTPUT);
	//clear stats to ensure it starts clean
	myStats.clear();

}

void doEncoder()
{
	/* If pinA and pinB are both high or both low, it is spinning
	 * forward. If they're different, it's going backward.
	 * For more information on speeding up this process, see
	 * [Reference/PortManipulation], specifically the PIND register.
	 */
	if (digitalRead(encoderPinA) == digitalRead(encoderPinB))
	{
		tempSP = tempSP + 0.5;
	}
	else
	{
		tempSP = tempSP - 0.5;
	}
	digitalWrite(displayOnOff, HIGH);
	lcd.clear();
	lcd.print("SetPoint:");
	lcd.print(tempSP, 0);
	lcd.print(" F");
	onCount = 200;
}

void printTemperature(DeviceAddress deviceAddress)
{
	// This is the Serial Print function to see temperature if the diplay is not working
	Serial.print("Getting temperatures...\n\r");
	sensors.requestTemperatures();
	Serial.print("Current temperature is: ");
	float tempC = sensors.getTempC(deviceAddress);
	if (tempC == -127.00)
	{
		Serial.print("Error getting temperature");
	}
	else
	{
		Serial.print("C: ");
		Serial.print(tempC);
		Serial.print(" F: ");
		Serial.print(DallasTemperature::toFahrenheit(tempC));
		Serial.print("\n\r");
	}
}

void displayTemperature()
{
	lcd.clear();
	lcd.print("SetPoint=");
	lcd.print(tempSP, 0); //Display Setpoint
	lcd.print(" F");
	lcd.setCursor(0, 1); //Start at character 0 on line 1
	lcd.print("Act.Temp=");
	lcd.print(tempF, 2); //Display temperature reading with 2 decimal places
	lcd.print(" F");
}

void displayAverage()
{
	lcd.clear();
	lcd.print("Avg.Temp=");
	lcd.print(myStats.average(), 2); //Display avg temp
	lcd.print(" F");
	lcd.setCursor(0, 1); //Start at character 0 on line 1
	lcd.print("Act.Temp=");
	lcd.print(tempF, 2); //Display actual temp
	lcd.print(" F");
}

void displayMinMaxTemperature()
{
	lcd.clear();
	lcd.print("Min Temp=");
	lcd.print(minTemp, 2); //Display min temp
	lcd.print(" F");
	lcd.setCursor(0, 1); //Start at character 0 on line 1
	lcd.print("Max Temp=");
	lcd.print(maxTemp, 2); //Display max temp
	lcd.print(" F");
}

void displayBeerLeft()
{
	lcd.clear();
	lcd.print("Measuring is not");
	lcd.setCursor(0, 1); //Start at character 0 on line 1
	lcd.print("setup yet");
}

void loop(void)
{
	//if millis() has been reset this will correct the error
	if (time > millis())
	{
		time = millis();
	}
	
	//only read sensor if 5sec has passed
	if(millis() > time + timeBetweenReadings)
	{
		//get the temp from the sensor
		sensors.requestTemperatures();
		tempC = sensors.getTempC(Temp1);
		tempF = sensors.getTempF(Temp1);

		//test for sensor errors
		if (tempC == -127.00) // Measurement failed or no device found
		{
			lcd.clear();
			lcd.print("Temperature Error");
		}
		else
		{
			//calculate min and max
			if (tempF > maxTemp)
			{
				if(tempF < 100)
				{
					maxTemp = tempF;
				}
			}
			if (tempF < minTemp)
			{
				if(tempF > -50)
				{
					minTemp = tempF;
				}
			}
			//Control the Compressor relay
			if (tempF - 4 > tempSP && compressorTimer == 0)
			{
				if (onTimer < 100)
				{
					compressorTimer = 150;
				}
				else
				{
					compressorTimer = 5;
				}
			}
			if (tempF <= tempSP && compressorTimer == 0)
			{
				if (offTimer < 100)
				{
					compressorTimer = -150;
				}
				else
				{
					compressorTimer = -5;
				}
			}
			if (compressorTimer > 0)
			{
				digitalWrite(relayOut, HIGH);
				compressorTimer = compressorTimer - 1;
				onTimer++;
				offTimer = 0;
			}
			if (compressorTimer < 0)
			{
				digitalWrite(relayOut, LOW);
				compressorTimer = compressorTimer + 1;
				onTimer = 0;
				offTimer++;
			}
			//Now add reading to the average or reset average if a day has passed
			myStats.add(tempF);
			if (myStats.count() == 17280)
			{
				myStats.clear();
			}
		}
		time = millis();// set time to current so that delay works
		lastScreen = 5;// set last screen to random rumber so dispaly will refresh
	}


	//Now turn on the backlight if the button is pressesd
	//Now rest min and max temps if button is held down
	if (digitalRead(displaySwitch) == HIGH)
	{
		onCount = 200; // This determines time that it will stay on
		// Remember delay also affects the time it is on
		resetCount = resetCount + 1;
		if(screenSwitch < 3 && lightFirst == 1)
		{
			screenSwitch++;
		}
		if(screenSwitch == 4 && lightFirst == 1)
		{
			screenSwitch = 0;
		}
		lightFirst = 1;
	}
	else
	{
		resetCount = 0;
		if(onCount > 0)
		{
			onCount = onCount - 1;
		}
	}
	if(onCount > 0)
	{
		digitalWrite(displayOnOff, HIGH);
	}
	else
	{
		digitalWrite(displayOnOff, LOW);
		lightFirst = 0;
	}
	if(resetCount > 20)
	{
		maxTemp = -100;
		minTemp = 100;
		myStats.clear();
	}
	//Choose which screen to display
	//First Check for errors
	if(resetCount > 1 && resetCount < 20)
	{
		lcd.clear();
		lcd.print("Hold to Reset");
	}
	else
	{
		if(resetCount >= 20)
		{
			lcd.clear();
			lcd.print("Reset Complete");
		}
		else
		{
			if(lastScreen != screenSwitch)
			{
				if (tempC == -127.00) // Measurement failed or no device found
				{
					lcd.clear();
					lcd.print("Temperature Error");
				}
				else
				{
					if(screenSwitch == 0)
					{
						displayTemperature();
					}
					if(screenSwitch == 1)
					{
						displayAverage();
					}
					if(screenSwitch == 2)
					{
						displayMinMaxTemperature();
					}
					if(screenSwitch == 3)
					{
						displayBeerLeft();
					}
				}
				lastScreen = screenSwitch;
			}
		}
	}
	delay(200);// debounce switch

}
if (time>millis()){time=millis();}

I think we need a little more.

if (lastTime>millis() { lastTime=millis();}

But, then you need to clear all the other variables that are testing against nowMilli(). They will still be high numbers else. Eg.
if nowMilli() > nextdisplayMilli ... of course, at rollover nowMilli() will not be !

What I'm saying is:

Don't compare anything to millis().

Instead, you should subtract and then do a comparison.
To see that I mean, read my other posts in this thread.

@odometer. I understand what you are saying (same thing most everyone on here says). I respectfully have a different way I do it, since I normally don't have to worry about 50 day rollover. But, If I have an application that may run that long, I code for it.

I hope we are not confusing the OP to much.

I guess I'll find out in 50 days if it works :slight_smile: