PID T-Stat. Switching from analog temp sensing to IR Digital Sensor - mlx90614

Hello, I hope you're all having a fantastic holiday.

I am digging a little project out of the storage shed and am shining some new light on it... It's basically a fancy thermostat.

Basically, what I had all working nicely before is:

-1 Seeeduino v1.0 (like the arduino Duemilanove)
-1 Serial LCD (2x16)
-1 Thermocouple and TC amp, wired into the microcontroller.

This along with some PID and liquid crystal code worked very nicely.... Ohh I also have two pushbutton switches to change the SETPOINT in steps of 10 degrees. One to increase and one to decrease.

Now what I have is the "MLX90614 IR Thermometer" and I want to switch the info the PID code gets about the temperature.

I want to switch that from the ANALOG information it was receiving from the TC amplifier over to the DIGITAL temperature reading I will be able to get with this new sensor.

Here's what my old code looked like that worked at one point:

#include <EEPROM.h>
#include <LiquidCrystal.h>
#include <PIDv1.h> 

double Setpoint, Input, Output;
double aggKp=0.5, aggKi=15, aggKd=0.01 , LoLIM=50, HiLIM=255; 
double consKp=.25, consKi=30, consKd=0;
double SampleTime=120;
LiquidCrystal lcd(12, 11, 8, 4, 3, 2);

const int onPin = 5;     //pin to trigger heater
const int upPin = 6;     //  pin to increase temp
const int downPin = 7;   //pin to decrease temp
int buttonState = 0;     // variable for reading the pin status
int DownPushed = 0;
int UpPushed = 0;
int OnPushed = 0;
int PID_ON = 0;
int UpdScr = 0;
int inpt;
int gap;
int S_gap;
int count;
int EEPROM_ADDR;

void UpdateEEPROM() {

  int val, wr;
  val = (int) Setpoint;
  wr = val>>8;
  EEPROM.write(EEPROM_ADDR, wr);
  wr = val & 0XFF;
  EEPROM.write(EEPROM_ADDR+1, wr);

};

void LoadEEPROM() {
  int val;
  val = EEPROM.read(EEPROM_ADDR);
  val = val <<8;
  val +=EEPROM.read(EEPROM_ADDR+1);
  Setpoint = val;

};
unsigned long lastTime, lShowTime; 

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, aggKp, aggKi, aggKd, DIRECT);


void setup()
{
  Serial.begin( 19200 );
  //initialize the variables we're linked to
  Input = analogRead(0);
  LoadEEPROM();
  if( (Setpoint +1.) < 0.001)
    Setpoint = 570;
  //    Setpoint = xxx;   

  // Declare inputs
  pinMode(onPin, INPUT);    // declare pushbutton as input
  pinMode(upPin, INPUT);    // declare pushbutton as input
  pinMode(downPin, INPUT);    // declare pushbutton as input

  //turn the PID on STANDBY, but start the output at its max and let the PID adjust it from there
  myPID.SetOutputLimits(LoLIM, HiLIM);
  myPID.SetMode(AUTOMATIC);
  myPID.SetSampleTime(SampleTime);
  myPID.SetTunings(aggKp, aggKi, aggKd);

  lShowTime = lastTime = millis();

  // set up the LCD's number of columns and rows:
  lcd.begin(16,2);

  count = 0;
  EEPROM_ADDR = 30;
  //lcd.print ("HEat");
  //delay(1000);
  // lcd.clear();
  //lcd.print(" on ");
  //delay(1000);
  //lcd.clear();
  //lcd.print("its way :D");
  // delay(1000);
  //lcd.clear();
  lcd.print("hot");
  lcd.setCursor( 0,1);
  lcd.print(aggKp);
  lcd.print("-");
  lcd.print((int)(aggKi) );
  lcd.print("-");
  lcd.print(aggKd);
  delay(2000);
  lcd.clear();
  lcd.print("S_Rate = ");
  lcd.print((int)(SampleTime));
  lcd.print(" ms");
  delay(1000);
  lcd.clear();
  lcd.print("Limits ");
  lcd.print((int)(LoLIM));
  lcd.print(",");
  lcd.print((int)(HiLIM));
  delay(1000);
  lcd.clear();
}

void loop()
{
  Input = analogRead(0);



  buttonState = digitalRead(onPin);

  if (buttonState == HIGH) {
    myPID.SetMode(AUTOMATIC);
    PID_ON =1;                
  } 
  else {
    myPID.SetMode(MANUAL);
    Output = 0;
    PID_ON =0;
  }

  myPID.Compute();
  analogWrite(10,Output);
  // I would not change these lines, because you are expecting 250 ms for a "push"
  // that is if you hold button for more then 1/4 second, 
  if(digitalRead(upPin)==HIGH) {
    if (millis()-lastTime >= 250) {
      Setpoint+=10;
      UpdateEEPROM();
      lastTime=millis();
      UpdScr = 1;
    }
  }

  if(digitalRead(downPin)==HIGH) {
    if (millis()-lastTime >= 250) {
      Setpoint-=10;
      UpdateEEPROM();
      lastTime=millis();
      UpdScr = 1;
    }
  }

  //and output to LCD

  if( (millis() - lShowTime > 100 ) || UpdScr ) {
    UpdScr = 0;
    lcd.setCursor(0,0);
    //if heater is on - show *
    //if not - empty
    if( PID_ON ==1 ) {
      lcd.print("*");
    }
    else {
      lcd.print(" ");
    }
    lcd.print("SET.TEMP-> ");
    lcd.print((int)(Setpoint) );
    lcd.setCursor( 0,1);
    lcd.print(" AIR.TEMP-> ");
    inpt = (inpt *5 + Input)/6;
    count = (count +1)%6;

    if(count == 0) {
      if( inpt < 100) lcd.print( " ");
      lcd.print( inpt );
    }


    lShowTime = millis();  
  }

}

according to the guys on this site http://bildr.org/2011/02/mlx90614-arduino/ the sensor is pretty easy to hook up. My main question is what is the first thing i need to do to make the PID code take those digital readings?

P.S. Here's what the PID control code looks like:

#ifndef PID_v1_h
#define PID_v1_h
#define LIBRARY_VERSION	1.0.0

class PID
{


  public:

  //Constants used in some of the functions below
  #define AUTOMATIC	1
  #define MANUAL	0
  #define DIRECT  0
  #define REVERSE  1

  //commonly used functions **************************************************************************
    PID(double*, double*, double*,        // * constructor.  links the PID to the Input, Output, and 
        double, double, double, int);     //   Setpoint.  Initial tuning parameters are also set here
	
    void SetMode(int Mode);               // * sets PID to either Manual (0) or Auto (non-0)

    void Compute();                       // * performs the PID calculation.  it should be
                                          //   called every time loop() cycles. ON/OFF and
                                          //   calculation frequency can be set using SetMode
                                          //   SetSampleTime respectively

    void SetOutputLimits(double, double); //clamps the output to a specific range. 0-255 by default, but
										  //it's likely the user will want to change this depending on
										  //the application
	


  //available but not commonly used functions ********************************************************
    void SetTunings(double, double,       // * While most users will set the tunings once in the 
                    double);         	  //   constructor, this function gives the user the option
                                          //   of changing tunings during runtime for Adaptive control
	void SetControllerDirection(int);	  // * Sets the Direction, or "Action" of the controller. DIRECT
										  //   means the output will increase when error is positive. REVERSE
										  //   means the opposite.  it's very unlikely that this will be needed
										  //   once it is set in the constructor.
    void SetSampleTime(int);              // * sets the frequency, in Milliseconds, with which 
                                          //   the PID calculation is performed.  default is 100
										  
										  
										  
  //Display functions ****************************************************************
	double GetKp();						  // These functions query the pid for interal values.
	double GetKi();						  //  they were created mainly for the pid front-end,
	double GetKd();						  // where it's important to know what is actually 
	int GetMode();						  //  inside the PID.
	int GetDirection();					  //

  private:
	void Initialize();
	
	double dispKp;				// * we'll hold on to the tuning parameters in user-entered 
	double dispKi;				//   format for display purposes
	double dispKd;				//
    
	double kp;                  // * (P)roportional Tuning Parameter
    double ki;                  // * (I)ntegral Tuning Parameter
    double kd;                  // * (D)erivative Tuning Parameter

	int controllerDirection;

    double *myInput;              // * Pointers to the Input, Output, and Setpoint variables
    double *myOutput;             //   This creates a hard link between the variables and the 
    double *mySetpoint;           //   PID, freeing the user from having to constantly tell us
                                  //   what these values are.  with pointers we'll just know.
			  
	unsigned long lastTime;
	double ITerm, lastInput;

	int SampleTime;
	double outMin, outMax;
	bool inAuto;
};
#endif

Jetski:
Hello, I hope you're all having a fantastic holiday.

Back at ya.

Now what I have is the "MLX90614 IR Thermometer" and I want to switch the info the PID code gets about the temperature. I want to switch that from the ANALOG information it was receiving from the TC amplifier over to the DIGITAL temperature reading I will be able to get with this new sensor.

The MLX90614 provides two methods of output: PWM and SMBus (i.e. TWI, I2C).

Why don't you just read the temperature over the I2C bus?

Why would you bother with "the digital temperature reading"? (I assume you are referring to the PWM output.)

whats the most accurate?

Precision is my aim. From what I've been reading it seems that the I2C method is the best.

Another rendom question... I have the serial rate at "Serial.begin( 19200 );"

Can i change that to 9600 without any drawbacks?

Also.... another problem I run into now when I try and compile my code written a couple years ago on the newer arduino software is this:

See attached photo.

error on code.tiff (151 KB)

Jetski:
whats the most accurate? Precision is my aim. From what I've been reading it seems that the I2C method is the best.

According to SparkFun...

The 10-bit PWM output provides a resolution of 0.14°C, while the TWI interface has a resolution of 0.02°C.

I2C is the better choice.

Another rendom question... I have the serial rate at "Serial.begin( 19200 );" Can i change that to 9600 without any drawbacks?

Well, one drawback is that communications will be much slower. The other drawback is that, if the other side is not also using 9600 baud, the Arduino will not be able to communicate with whatever is on the other side.

See attached photo.

Using the mouse, it is possible to drag-and-select the text in the status window. Ctrl+C works to copy the text.

Jetski:
Also.... another problem I run into now when I try and compile my code written a couple years ago on the newer arduino software is this:

First check for an update to the LiquidCrystal library (or is that something you developed?).

Okay well... the reason I ask about the serial rate is that I was using 19200 with my old code.

This new example code they have working with their IR thermometer seems so be running at 9600.

Can I change their suggested baud rate up from 9600 to what I was running at .... 19200???

I have the latest LiquidCrystal library file... from arduino.cc

and I think I have it placed correctly on my mac

Located: /Users/user/Documents/Arduino/libraries/LiquidCrystal ???

So maybe here is where I need change the input fed to the PID code?

void setup()
{
  Serial.begin( 19200 );
  //initialize the variables we're linked to
  Input = analogRead(0);
  LoadEEPROM();

Jetski:
Can I change their suggested baud rate up from 9600 to what I was running at .... 19200???

What's on the other side? Your computer? If "yes" then you can use whatever baud rate you'd like.

I have the latest LiquidCrystal library file... from arduino.cc
and I think I have it placed correctly on my mac

Looks like it's placed with your sketch. Which is not the correct place.

Located: /Users/user/Documents/Arduino/libraries/LiquidCrystal ???

Then you may want to delete the copy in the same directory as your sketch.

So maybe here is where I need change the input fed to the PID code?

void setup()
{
Serial.begin( 19200 );
//initialize the variables we're linked to
Input = analogRead(0); <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
LoadEEPROM();

So what would be the best way to melt both of these codes together? Should I copy the new sensor portion of the code and stick that in where the old sensor was previously providing feedback (analog(0) in) ??

Or... Should I start with a blank new page and just start pasting selected portions of what I need into that?

I know I'm just a barrel full of questions :slight_smile:

Like are there a few lines of code here that I can replace that "analog (0)" with :

void loop(){
  int dev = 0x5A<<1;
  int data_low = 0;
  int data_high = 0;
  int pec = 0;
  
  i2c_start_wait(dev+I2C_WRITE);
  i2c_write(0x07);
  
  // read
  i2c_rep_start(dev+I2C_READ);
  data_low = i2c_readAck(); //Read 1 byte and then send ack
  data_high = i2c_readAck(); //Read 1 byte and then send ack
  pec = i2c_readNak();
  i2c_stop();
  
  //This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
  double tempFactor = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
  double tempData = 0x0000; // zero out the data
  int frac; // data past the decimal point
  
  // This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
  tempData = (double)(((data_high & 0x007F) << 8) + data_low);
  tempData = (tempData * tempFactor)-0.01;
  
  float celcius = tempData - 273.15;
  float fahrenheit = (celcius*1.8) + 32;

  Serial.print("Celcius: ");
  Serial.println(celcius);

like maybe this.....:

void setup()
{
  Serial.begin( 19200 );
  //initialize the variables we're linked to

  Input = celcius  ; <<< WILL THIS UNDERSTAND THE I2C feedback/input?

  LoadEEPROM();

Jetski:
So what would be the best way to melt both of these codes together? Should I copy the new sensor portion of the code and stick that in where the old sensor was previously providing feedback (analog(0) in) ??

I would "refactor" the code you have: create and use a function that returns the input reading. That allows you to easily change how the input is obtained. It also allows you to easily test reading and pre-processing the input.

static double GetInput( void )
{
  return( analogRead( 0 ) );
}

Replace this (appears twice in your sketch)...

  Input = analogRead(0);

...with this...

  Input = GetInput();

At this point, you can replace GetInput with what you want: read from the I2C bus, scale the value, clamp the value, save the value, get the value from a human, whatever.

Does this look a little more like what I should be working with? Or at least look like it might work?

I read up a bit on refactoring and that's cool stuff..

/**
 * Infrared Thermometer MLX90614
 * by Jaime Patarroyo
 * based on 'Is it hot? Arduino + MLX90614 IR Thermometer' by bildr.blog
 * 
 * Returns the temperature in Celcius and Fahrenheit from a MLX90614 
 * Infrared Thermometer, connected to the TWI/I²C pins.
 */


#include <i2cmaster.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>
#include <PID_v1.h> 
#include <SPI.h>
  
double Setpoint, Input, Output;
double aggKp=1, aggKi=3, aggKd=0; 
double consKp=0.5, consKi=5, consKd=3;
double LoLIM=5, HiLIM=255; 
double SampleTime=250;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, aggKp, aggKi, aggKd, DIRECT);



LiquidCrystal lcd(12, 11, 8, 3, 2, 1);

const int onPin = 9;     //pin to trigger heater
const int upPin = 6;     //  pin to increase temp
const int downPin = 7;   //pin to decrease temp
int buttonState = 0;     // variable for reading the pin status
int DownPushed = 0;
int UpPushed = 0;
int OnPushed = 0;
int PID_ON = 0;
int UpdScr = 0;
int inpt;
int gap;
int S_gap;
int count;
int EEPROM_ADDR;

void UpdateEEPROM() {

  int val, wr;
  val = (int) Setpoint;
  wr = val>>8;
  EEPROM.write(EEPROM_ADDR, wr);
  wr = val & 0XFF;
  EEPROM.write(EEPROM_ADDR+1, wr);

};

void LoadEEPROM() {
  int val;
  val = EEPROM.read(EEPROM_ADDR);
  val = val << 8;
  val +=EEPROM.read(EEPROM_ADDR+1);
  Setpoint = val;

};
unsigned long lastTime, lShowTime; 


  int deviceAddress = 0x5A<<1;    // From MLX906114 datasheet's, 0x5A is 
                                // the default address for I²C communication.
                                // Shift the address 1 bit right, the 
                                // I²Cmaster library only needs the 7 most 
                                // significant bits for the address.

float celcius = 0;              // Variable to hold temperature in Celcius.
float fahrenheit = 0;           // Variable to hold temperature in Fahrenheit.

 static double GetInput( void )
{
  return( celcius );
}

void setup() {
  Serial.begin(19200);           // Start serial communication at 19200bps.

  i2c_init();                               // Initialise the i2c bus.
  PORTC = (1 << PORTC4) | (1 << PORTC5);    // Enable pullups.
  
  Input = GetInput();
  LoadEEPROM();
  if( (Setpoint +1.) < 0.001)
    Setpoint = 300;
  //    Setpoint = xxx;   

  // Declare inputs
  pinMode(onPin, INPUT);    // declare pushbutton as input
  pinMode(upPin, INPUT);    // declare pushbutton as input
  pinMode(downPin, INPUT);    // declare pushbutton as input
  
  //turn the PID on STANDBY.   When triggered, IDEALLY start the output at its max and let the PID adjust it from there.. This needs to be added to the code.
  myPID.SetOutputLimits(0, 255);
  myPID.SetMode(AUTOMATIC);
  myPID.SetSampleTime(SampleTime);
  myPID.SetTunings(aggKp, aggKi, aggKd);

  lShowTime = lastTime = millis();

  // set up the LCD's number of columns and rows:
  lcd.begin(16,2);

  count = 0;
  EEPROM_ADDR = 30;
 
  lcd.print("Settings");
  lcd.setCursor( 0,1);
  lcd.print(aggKp);
  lcd.print("-");
  lcd.print((int)(aggKi) );
  lcd.print("-");
  lcd.print(aggKd);
  delay(3000);
  lcd.clear();
  lcd.print("S_Rate = ");
  lcd.print((int)(SampleTime));
  lcd.print(" ms");
    delay(2000);
    lcd.clear();
}

void loop() {
 
  celcius = temperatureCelcius(deviceAddress);  // Read's data from MLX90614
                                                // with the given address,
                                                // transform's it into
                                                // temperature in Celcius and
                                                // store's it in the Celcius
                                                // variable.
  
  fahrenheit = (celcius*1.8) + 32;     // Converts celcius into Fahrenheit 
                                       // and stores in Fahrenheit variable.

  Input = GetInput();


  buttonState = digitalRead(onPin);

  if (buttonState == HIGH) {
    myPID.SetMode(AUTOMATIC); //Turn on heat.  Adjust output acordinglly via myPIDs computations.  
    PID_ON =1;                
  } 
  else {
    myPID.SetMode(MANUAL); // Turn off heat. 
    Output = 0;
    PID_ON =0;
  }

  myPID.Compute();
  analogWrite(10,Output);
  
  
  
  // Setpoint control section.
  // I would not change these lines, because you are expecting 250 ms for a "push"
  // that is if you hold button for more then 1/4 second, 
  if(digitalRead(upPin)==HIGH) {
    if (millis()-lastTime >= 250) {
      Setpoint+=5;
      UpdateEEPROM();
      lastTime=millis();
      UpdScr = 1;
    }
  }

  if(digitalRead(downPin)==HIGH) {
    if (millis()-lastTime >= 250) {
      Setpoint-=5;
      UpdateEEPROM();
      lastTime=millis();
      UpdScr = 1;
    }
  }

  //and output to LCD

  if( (millis() - lShowTime > 100 ) || UpdScr ) {
    UpdScr = 0;
    lcd.setCursor(0,0);
    //if heater is on - show *
    //if not - empty
    if( PID_ON ==1 ) {
      lcd.print("*");
    }
    else {
      lcd.print(" ");
    }
    lcd.print("SET.TEMP-> ");
    lcd.print((int)(Setpoint) );
    lcd.setCursor( 0,1);
    lcd.print("SENS.TEMP-> ");
    inpt = (inpt *5 + Input)/6;
    count = (count +1)%6;

    if(count == 0) {
      if( inpt < 100) lcd.print( " ");
      lcd.print( inpt );
    }


    lShowTime = millis();  
  }

}

float temperatureCelcius(int address) {
  int dev = address;
  int data_low = 0;
  int data_high = 0;
  int pec = 0;

  // Write
  i2c_start_wait(dev+I2C_WRITE);
  i2c_write(0x07);

  // Read
  i2c_rep_start(dev+I2C_READ);
  data_low = i2c_readAck();       // Read 1 byte and then send ack.
  data_high = i2c_readAck();      // Read 1 byte and then send ack.
  pec = i2c_readNak();
  i2c_stop();

  // This converts high and low bytes together and processes temperature, 
  // MSB is a error bit and is ignored for temps.
  double tempFactor = 0.02;       // 0.02 degrees per LSB (measurement 
                                  // resolution of the MLX90614).
  double tempData = 0x0000;       // Zero out the data
  int frac;                       // Data past the decimal point

  // This masks off the error bit of the high byte, then moves it left 
  // 8 bits and adds the low byte.
  tempData = (double)(((data_high & 0x007F) << 8) + data_low);
  tempData = (tempData * tempFactor)-0.01;
  float celcius = tempData - 273.15;
  
  // Returns temperature un Celcius.
  return celcius;
}

It seems to compile okay. I am going to try and upload it to the board and see what happens :smiley:

None of the following is used (dead code). I suggest removing it...

float fahrenheit = 0;           // Variable to hold temperature in Fahrenheit.

  fahrenheit = (celcius*1.8) + 32;     // Converts celcius into Fahrenheit 
                                       // and stores in Fahrenheit variable.

The variable celcius serves no useful purpose and can be eliminated.

Technically, either will work but a bitwise-or makes it more clear what you are doing...

val |=EEPROM.read(EEPROM_ADDR+1);

deviceAddress is a constant...

const int deviceAddress = 0x5A<<1;

After making the changes listed above, this is what I ended up with...

/**
 * Infrared Thermometer MLX90614
 * by Jaime Patarroyo
 * based on 'Is it hot? Arduino + MLX90614 IR Thermometer' by bildr.blog
 * 
 * Returns the temperature in Celcius and Fahrenheit from a MLX90614 
 * Infrared Thermometer, connected to the TWI/I²C pins.
 */


#include <i2cmaster.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>
#include <PID_v1.h> 
#include <SPI.h>
  
double Setpoint, Input, Output;
double aggKp=1, aggKi=3, aggKd=0; 
double consKp=0.5, consKi=5, consKd=3;
double LoLIM=5, HiLIM=255; 
double SampleTime=250;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, aggKp, aggKi, aggKd, DIRECT);



LiquidCrystal lcd(12, 11, 8, 3, 2, 1);

const int onPin = 9;     //pin to trigger heater
const int upPin = 6;     //  pin to increase temp
const int downPin = 7;   //pin to decrease temp
int buttonState = 0;     // variable for reading the pin status
int DownPushed = 0;
int UpPushed = 0;
int OnPushed = 0;
int PID_ON = 0;
int UpdScr = 0;
int inpt;
int gap;
int S_gap;
int count;
int EEPROM_ADDR;

void UpdateEEPROM() {

  int val, wr;
  val = (int) Setpoint;
  wr = val>>8;
  EEPROM.write(EEPROM_ADDR, wr);
  wr = val & 0XFF;
  EEPROM.write(EEPROM_ADDR+1, wr);

};

void LoadEEPROM() {
  int val;
  val = EEPROM.read(EEPROM_ADDR);
  val = val << 8;
  val |=EEPROM.read(EEPROM_ADDR+1);
  Setpoint = val;

};
unsigned long lastTime, lShowTime; 


  int deviceAddress = 0x5A<<1;    // From MLX906114 datasheet's, 0x5A is 
                                // the default address for I²C communication.
                                // Shift the address 1 bit right, the 
                                // I²Cmaster library only needs the 7 most 
                                // significant bits for the address.

void setup() {
  Serial.begin(19200);           // Start serial communication at 19200bps.

  i2c_init();                               // Initialise the i2c bus.
  PORTC = (1 << PORTC4) | (1 << PORTC5);    // Enable pullups.
  
  Input = GetInput();
  LoadEEPROM();
  if( (Setpoint +1.) < 0.001)
    Setpoint = 300;
  //    Setpoint = xxx;   

  // Declare inputs
  pinMode(onPin, INPUT);    // declare pushbutton as input
  pinMode(upPin, INPUT);    // declare pushbutton as input
  pinMode(downPin, INPUT);    // declare pushbutton as input
  
  //turn the PID on STANDBY.   When triggered, IDEALLY start the output at its max and let the PID adjust it from there.. This needs to be added to the code.
  myPID.SetOutputLimits(0, 255);
  myPID.SetMode(AUTOMATIC);
  myPID.SetSampleTime(SampleTime);
  myPID.SetTunings(aggKp, aggKi, aggKd);

  lShowTime = lastTime = millis();

  // set up the LCD's number of columns and rows:
  lcd.begin(16,2);

  count = 0;
  EEPROM_ADDR = 30;
 
  lcd.print("Settings");
  lcd.setCursor( 0,1);
  lcd.print(aggKp);
  lcd.print("-");
  lcd.print((int)(aggKi) );
  lcd.print("-");
  lcd.print(aggKd);
  delay(3000);
  lcd.clear();
  lcd.print("S_Rate = ");
  lcd.print((int)(SampleTime));
  lcd.print(" ms");
    delay(2000);
    lcd.clear();
}

void loop() {
 
  /* Read's data from MLX90614 with the given address, transform's it into 
    temperature in Celcius and store's it in the Celcius variable. */
  Input = GetInput( deviceAddress );

  buttonState = digitalRead(onPin);

  if (buttonState == HIGH) {
    myPID.SetMode(AUTOMATIC); //Turn on heat.  Adjust output acordinglly via myPIDs computations.  
    PID_ON =1;                
  } 
  else {
    myPID.SetMode(MANUAL); // Turn off heat. 
    Output = 0;
    PID_ON =0;
  }

  myPID.Compute();
  analogWrite(10,Output);
  
  
  
  // Setpoint control section.
  // I would not change these lines, because you are expecting 250 ms for a "push"
  // that is if you hold button for more then 1/4 second, 
  if(digitalRead(upPin)==HIGH) {
    if (millis()-lastTime >= 250) {
      Setpoint+=5;
      UpdateEEPROM();
      lastTime=millis();
      UpdScr = 1;
    }
  }

  if(digitalRead(downPin)==HIGH) {
    if (millis()-lastTime >= 250) {
      Setpoint-=5;
      UpdateEEPROM();
      lastTime=millis();
      UpdScr = 1;
    }
  }

  //and output to LCD

  if( (millis() - lShowTime > 100 ) || UpdScr ) {
    UpdScr = 0;
    lcd.setCursor(0,0);
    //if heater is on - show *
    //if not - empty
    if( PID_ON ==1 ) {
      lcd.print("*");
    }
    else {
      lcd.print(" ");
    }
    lcd.print("SET.TEMP-> ");
    lcd.print((int)(Setpoint) );
    lcd.setCursor( 0,1);
    lcd.print("SENS.TEMP-> ");
    inpt = (inpt *5 + Input)/6;
    count = (count +1)%6;

    if(count == 0) {
      if( inpt < 100) lcd.print( " ");
      lcd.print( inpt );
    }


    lShowTime = millis();  
  }

}

static float GetInput(int address) {
  int dev = address;
  int data_low = 0;
  int data_high = 0;
  int pec = 0;

  // Write
  i2c_start_wait(dev+I2C_WRITE);
  i2c_write(0x07);

  // Read
  i2c_rep_start(dev+I2C_READ);
  data_low = i2c_readAck();       // Read 1 byte and then send ack.
  data_high = i2c_readAck();      // Read 1 byte and then send ack.
  pec = i2c_readNak();
  i2c_stop();

  // This converts high and low bytes together and processes temperature, 
  // MSB is a error bit and is ignored for temps.
  double tempFactor = 0.02;       // 0.02 degrees per LSB (measurement 
                                  // resolution of the MLX90614).
  double tempData = 0x0000;       // Zero out the data
  int frac;                       // Data past the decimal point

  // This masks off the error bit of the high byte, then moves it left 
  // 8 bits and adds the low byte.
  tempData = (double)(((data_high & 0x007F) << 8) + data_low);
  tempData = (tempData * tempFactor)-0.01;
  float celcius = tempData - 273.15;
  
  // Returns temperature un Celcius.
  return celcius;
}

Here's what a friend suggested but I can't seem to get it to work.... Most of the modifications are at the end.

#include <PID_v1.h>

#include <OneWire.h>

#include <Wire.h>

/**
 * Infrared Thermometer MLX90614
*/


//#include <i2cmaster.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>
#include <PID_v1.h> 
#include <SPI.h>
  
double Setpoint, Input, Output;
double aggKp=1, aggKi=3, aggKd=0; 
double consKp=0.5, consKi=5, consKd=3;
double LoLIM=5, HiLIM=255; 
double SampleTime=250;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, aggKp, aggKi, aggKd, DIRECT);



LiquidCrystal lcd(12, 11, 8, 4, 3, 2);

const int onPin = 5;     //pin to trigger heater
const int upPin = 6;     //  pin to increase temp
const int downPin = 7;   //pin to decrease temp
int buttonState = 0;     // variable for reading the pin status
int DownPushed = 0;
int UpPushed = 0;
int OnPushed = 0;
int PID_ON = 0;
int UpdScr = 0;
int inpt;
int gap;
int S_gap;


int count;
int EEPROM_ADDR;

void UpdateEEPROM() {

  int val, wr;
  val = (int) Setpoint;
  wr = val>>8;
  EEPROM.write(EEPROM_ADDR, wr);
  wr = val & 0XFF;
  EEPROM.write(EEPROM_ADDR+1, wr);

};

void LoadEEPROM() {
  int val;
  val = EEPROM.read(EEPROM_ADDR);
  val = val << 8;
  val |=EEPROM.read(EEPROM_ADDR+1);
  Setpoint = val;

};
unsigned long lastTime, lShowTime; 


  int deviceAddress = 0x5A<<1;    // From MLX906114 datasheet's, 0x5A is 
                                // the default address for I²C communication.
                                // Shift the address 1 bit right, the 
                                // I²Cmaster library only needs the 7 most 
                                // significant bits for the address.

float celcius = 0;              // Variable to hold temperature in Celcius.
float fahrenheit = 0;           // Variable to hold temperature in Fahrenheit.

 static double GetInput( void )
{
  return( celcius );
}

void setup() {
  Serial.begin(9600);           // Start serial communication at 19200bps.

  //i2c_init();                               // Initialise the i2c bus.
  PORTC = (1 << PORTC4) | (1 << PORTC5);    // Enable pullups.
  
  Input = GetInput();
  LoadEEPROM();
  if( (Setpoint +1.) < 0.001)
    Setpoint = 400;
  //    Setpoint = xxx;   

  // Declare inputs
  pinMode(onPin, INPUT);    // declare pushbutton as input
  pinMode(upPin, INPUT);    // declare pushbutton as input
  pinMode(downPin, INPUT);    // declare pushbutton as input
  
  //turn the PID on STANDBY, but start the output at its max and let the PID adjust it from there
  myPID.SetOutputLimits(5, 220);
  myPID.SetMode(AUTOMATIC);
  myPID.SetSampleTime(SampleTime);
  myPID.SetTunings(aggKp, aggKi, aggKd);

  lShowTime = lastTime = millis();

  // set up the LCD's number of columns and rows:
  lcd.begin(16,2);

  count = 0;
  EEPROM_ADDR = 30;
 
  lcd.print("Settings");
  lcd.setCursor( 0,1);
  lcd.print(aggKp);
  lcd.print("-");
  lcd.print((int)(aggKi) );
  lcd.print("-");
  lcd.print(aggKd);
  delay(3000);
  lcd.clear();
  lcd.print("S_Rate = ");
  lcd.print((int)(SampleTime));
  lcd.print(" ms");
    delay(2000);
    lcd.clear();
}

void loop()
{
 
  celcius = temperatureCelcius(deviceAddress);  // Read's data from MLX90614
                                                // with the given address,
                                                // transform's it into
                                                // temperature in Celcius and
                                                // store's it in the Celcius
                                                // variable.
  
  fahrenheit = (celcius*1.8) + 32;     // Converts celcius into Fahrenheit 
                                       // and stores in Fahrenheit variable.

  Input = GetInput();


  buttonState = digitalRead(onPin);

  if (buttonState == HIGH) {
    myPID.SetMode(AUTOMATIC); //Turn on heat.  Adjust output acordinglly via myPIDs computations.  
    PID_ON =1;                
  } 
  else {
    myPID.SetMode(MANUAL); // Turn off heat. 
    Output = 0;
    PID_ON =0;
  }

  myPID.Compute();
  analogWrite(10,Output);
  
  
  
  // Setpoint control section.
  // I would not change these lines, because you are expecting 250 ms for a "push"
  // that is if you hold button for more then 1/4 second, 
  if(digitalRead(upPin)==HIGH) {
    if (millis()-lastTime >= 250) {
      Setpoint+=5;
      UpdateEEPROM();
      lastTime=millis();
      UpdScr = 1;
    }
  }

  if(digitalRead(downPin)==HIGH) {
    if (millis()-lastTime >= 250) {
      Setpoint-=5;
      UpdateEEPROM();
      lastTime=millis();
      UpdScr = 1;
    }
  }

  //and output to LCD

  if( (millis() - lShowTime > 100 ) || UpdScr ) {
    UpdScr = 0;
    lcd.setCursor(0,0);
    //if heater is on - show *
    //if not - empty
    if( PID_ON ==1 ) {
      lcd.print("*");
    }
    else {
      lcd.print(" ");
    }
    lcd.print("SET.TEMP-> ");
    lcd.print((int)(Setpoint) );
    lcd.setCursor( 0,1);
    lcd.print(" AIR.TEMP-> ");
    lcd.print((int)(fahrenheit));
    


    lShowTime = millis();  
  }

}

float temperatureCelcius(int address) {
  int dev = address;
  int data_low = 0;
  int data_high = 0;
  int pec = 0;
/* DS18B20 Temperature chip i/o

 */
byte sensor1[8]= {0x28,0xB5,0x4E,0x8A,0x01,0x00,0x00,0xD0};
OneWire  ds(13);  // on pin 12
  // initialize inputs/outputs
  // start serial port
  Serial.begin(9600);
  pinMode(13,OUTPUT);
  digitalWrite(13,LOW);
}

char* tempToAscii(double temp)
{
  char ascii[32];
  int frac;
  frac=(unsigned int)(temp*1000)%1000;  //get three numbers to the right of the deciaml point
 itoa((int)temp,ascii,10);
  strcat(ascii,".");
  itoa(frac,&ascii[strlen(ascii)],10); //put the frac after the deciaml

  return ascii;
//  // Write
//  i2c_start_wait(dev+I2C_WRITE);
//  i2c_write(0x07);
//
//  // Read
//  i2c_rep_start(dev+I2C_READ);
//  data_low = i2c_readAck();       // Read 1 byte and then send ack.
//  data_high = i2c_readAck();      // Read 1 byte and then send ack.
//  pec = i2c_readNak();
//  i2c_stop();
//
//  // This converts high and low bytes together and processes temperature, 
//  // MSB is a error bit and is ignored for temps.
//  double tempFactor = 0.02;       // 0.02 degrees per LSB (measurement 
//                                  // resolution of the MLX90614).
//  double tempData = 0x0000;       // Zero out the data
//  int frac;                       // Data past the decimal point
//
//  // This masks off the error bit of the high byte, then moves it left 
//  // 8 bits and adds the low byte.
//  tempData = (double)(((data_high & 0x007F) << 8) + data_low);
//  tempData = (tempData * tempFactor)-0.01;
//  float celcius = tempData - 273.15;
//  
//  // Returns temperature un Celcius.
//  return celcius;
}