Peltier (TEC) based Temperature controller

I have made a Peltier based Temperature controller. I have used L298 to control the current.

PWM signal from arduino is fed to the enable pin of the L298. I have written the following code to control the temperature of a box which is connected to the petlier. There is a LM35 temperature sensor inside the box.

When I test this code on my hardware without the load (peltier), everything seems to work as it is expected to, but when I connect the peltier to the L298, the arduino seems to freeze or reset after 10 seconds. I can't seem to figure out what could be wrong.

/*

    Thermo Electric Cooler (TEC)
   ````````````````````````````````
  
   
Circuit Description:
  Serial Communication  - Pin 0,1
  Input Buttons         - Pins 2,3,4
  TEC Module (H Bridge) - Pins 5,6,7
  LCD                   - Pins 8,9,10,11,12,13
  LED                   - Pin A0
  Current Sensor        - Pin A1
  LM35                  - Pin A3
  
  
H Bridge Description (L298)

  Enable Pin - Controls the votlage (indirectly current) applied across load.
  
  Digital Inputs:
      For positive voltage (cooling)
            Pin 6 - HIGH
            Pin 7 - LOW
      For negative voltage (heating)
            Pin 6 - LOW
            Pin 7 - HIGH      
  
_____________________________________________________________*/


#include <math.h>
#include <Button.h>
Button start_stop_button = Button(4,PULLUP);
Button dec_button = Button(3,PULLUP);
Button inc_button = Button(2,PULLUP);

#include <LiquidCrystal.h>
LiquidCrystal lcd(13,12,11,10,9,8);
char blank[] = "    ";                 // to earase 4 characters

double Tref = 20, Tmeas = 30;
int button_time = 0;
unsigned long lcd_time = 0, I_tym = 0;
float Imeas = 0;


//----------------------------Variables-----------------

float step_size = 1;         // Inc/Dec Button step size
float I_calib = 1;             // Current sensor calibration
float T_delta = 2;             // +- 3 degree Celsius
int Imax_dutyCycle = 120;      // 100 -> ~1.8 Amps of current

//------------------------------------------------------


void setup()
{
  Serial.begin(9600);
  
  pinMode(5,OUTPUT);    digitalWrite(5,LOW);
  pinMode(6,OUTPUT);    digitalWrite(6,LOW);
  pinMode(7,OUTPUT);    digitalWrite(7,LOW);
  
  lcd.begin(16, 2);
  lcd.print("Enter Tref: 20");
 
a:
 if(millis() - lcd_time > 2000)  //update lcd every 2 sec
    {
      float Tmeas = analogRead(3); 
      for(int i=1;i<10;i++)
          Tmeas = (Tmeas + analogRead(3) ) / 2;   // take 10 samples avg value
      Tmeas = Tmeas * 500 / 1024;   
  
      lcd.setCursor(0,1);
      lcd.print("Tmeas : ");
      lcd.print(Tmeas);
      lcd_time = millis();
    }  

 if(inc_button.isPressed())
      {
        Tref += step_size;
        lcd.setCursor(12,0);
        lcd.print(Tref); 
        delay(200);
      }
      
  if(dec_button.isPressed())
       {
        Tref -= step_size;
        lcd.setCursor(12,0);
        lcd.print(Tref); 
        delay(200);
      }
  
  if(start_stop_button.isPressed())
      {
          lcd.clear();
          lcd.print("Starting...");
          goto b;
          delay(100);   
      }
  
  goto a;      
b: 
   delay(1000);
}


void loop()
{
 //_____________________Temp_______________________  
 
  float Tmeas = analogRead(3); 
  for(int i=1;i<10;i++)
        Tmeas = (Tmeas + analogRead(3) ) / 2;   // take 10 samples avg value

  Tmeas = Tmeas * 500 / 1024;                     // convert to temp
 
//_______________________Current____________________
/*
  if(millis() - I_tym > 100)       // every 100 ms
  {
    calc_current();
    I_tym = millis();
  }
 */ 
 
//______________________LCD______________________
  
  if(millis() - lcd_time > 1000)  //update lcd every 1 sec
    {
      lcd.clear();
      lcd.print("Tr:");
      lcd.print(Tref);
//      lcd.setCursor(9,0);
    //  lcd.print("I:");
     // lcd.print(Imeas);
      lcd.setCursor(0,1);
      lcd.print("Tm:");
      lcd.print(Tmeas);
      lcd_time = millis();
    }

// ________________________Buttons________________________    
    
  if(inc_button.isPressed())
      {
        Tref += step_size;
        lcd.setCursor(3,0);
        lcd.print(Tref); 
        lcd.print(blank);
        delay(200);
      }
      
  if(dec_button.isPressed())
       {
        Tref -= step_size;
        lcd.setCursor(3,0);
        lcd.print(Tref); 
        lcd.print(blank);
        delay(200);
      } 
       
  if(start_stop_button.isPressed())    // Used only to stop the controller
      {
        
        digitalWrite(5,LOW);        
        digitalWrite(6,LOW);
        digitalWrite(7,LOW); 
        lcd.clear(); 
        delay(1000);
        setup();            // start all over again
        } 
        
    
// ________________________Controller________________________

 
   float E = Tmeas - float(Tref);
   
   if( E > T_delta)    // cooling at max  - positive voltage
       {
            //positive voltage   
          digitalWrite(6,HIGH);
          digitalWrite(7,LOW);
          
          analogWrite(5,Imax_dutyCycle); 
          lcd.setCursor(9,1);
          lcd.print("Max-C");
          lcd.setCursor(9,0);
          lcd.print("D:");
          lcd.print(Imax_dutyCycle *100/255);  
          lcd.print("%   ");
       }
  
    if( E < -T_delta)
        {
          //negative voltage
          digitalWrite(6,LOW);
          digitalWrite(7,HIGH);
          analogWrite(5,Imax_dutyCycle);
          lcd.setCursor(9,1);
          lcd.print("Max-H");
          lcd.setCursor(9,0);
          lcd.print("D:");
          lcd.print(Imax_dutyCycle *100/255);  
          lcd.print("%   ");
        }
        
     if( (E > - T_delta)  &&  (E < T_delta))
        {
          // PID or other control
          
          if( E >= 0   )  // cooling
            {
              digitalWrite(6,HIGH);
              digitalWrite(7,LOW);
              
              int d =  fscale( 0, T_delta, 0, Imax_dutyCycle, E, 0);            
              constrain(d,0,Imax_dutyCycle);
              analogWrite(5,d);
              
              lcd.setCursor(9,1);
              lcd.print("PID-C");
              lcd.setCursor(0,1);
              lcd.print("Tm:");
              lcd.print(Tmeas);
              lcd.setCursor(9,0);
              lcd.print("D:");
              lcd.print(d*100/255);
              lcd.print("%   ");            
            }
            
           else if(E < 0)
            {  // heating
              digitalWrite(6,LOW);
              digitalWrite(7,HIGH);
              
              int d =  fscale( (-1* T_delta), 0, Imax_dutyCycle, 0, E, 0);            
              constrain(d,0,Imax_dutyCycle);
              analogWrite(5,d);
              
              lcd.setCursor(9,1);
              lcd.print("PID-H"); 
              lcd.setCursor(0,1);
              lcd.print("Tm:");
              lcd.print(Tmeas);
              lcd.setCursor(9,0);
              lcd.print("D:");
              lcd.print(d*100/255);
              lcd.print("%   ");
            }
           
       }      
     
  delay(50);    
 
 Serial.print("Error ");
 Serial.print(E);
 Serial.print("   T : ");
 Serial.println(Tmeas);

 delay(500); 
}


float fscale( float originalMin, float originalMax, float newBegin, float
newEnd, float inputValue, float curve){

  float OriginalRange = 0;
  float NewRange = 0;
  float zeroRefCurVal = 0;
  float normalizedCurVal = 0;
  float rangedValue = 0;
  boolean invFlag = 0;


  // condition curve parameter
  // limit range

  if (curve > 10) curve = 10;
  if (curve < -10) curve = -10;

  curve = (curve * -.1) ; // - invert and scale - this seems more intuitive - postive numbers give more weight to high end on output 
  curve = pow(10, curve); // convert linear scale into lograthimic exponent for other pow function

  /*
   Serial.println(curve * 100, DEC);   // multply by 100 to preserve resolution  
   Serial.println(); 
   */

  // Check for out of range inputValues
  if (inputValue < originalMin) {
    inputValue = originalMin;
  }
  if (inputValue > originalMax) {
    inputValue = originalMax;
  }

  // Zero Refference the values
  OriginalRange = originalMax - originalMin;

  if (newEnd > newBegin){ 
    NewRange = newEnd - newBegin;
  }
  else
  {
    NewRange = newBegin - newEnd; 
    invFlag = 1;
  }

  zeroRefCurVal = inputValue - originalMin;
  normalizedCurVal  =  zeroRefCurVal / OriginalRange;   // normalize to 0 - 1 float

  /*
  Serial.print(OriginalRange, DEC);  
   Serial.print("   ");  
   Serial.print(NewRange, DEC);  
   Serial.print("   ");  
   Serial.println(zeroRefCurVal, DEC);  
   Serial.println();  
   */

  // Check for originalMin > originalMax  - the math for all other cases i.e. negative numbers seems to work out fine 
  if (originalMin > originalMax ) {
    return 0;
  }

  if (invFlag == 0){
    rangedValue =  (pow(normalizedCurVal, curve) * NewRange) + newBegin;

  }
  else     // invert the ranges
  {   
    rangedValue =  newBegin - (pow(normalizedCurVal, curve) * NewRange); 
  }

  return rangedValue;
}

Do you have a schematic of how it's all wired up and maybe a link to the specific Peltier plate(s) your using or datasheets for them. Code wise I can see no specific problems apart from the call back to setup() when the device is turned off, I'm not sure if this will eventually cause stack overflow and memory problems or what effect it will have re-defining devices etc. Better to re-code to move the code between a: & b: to the loop(). Another suggestion would be to give the pins names to make the code more readable.
#define Enable 5
#define Cool 6
#define Heat 7

Thanks a lot Riva for going through my code.

I am using this peltier http://in.element14.com/multicomp/mchpe-127-10-08-e/peltier-cooler-55w/dp/1639737

I din't refer any circuit, just connected the elements in the usual way. For L298, I referred the datasheet. I have given the circuit description in the beginning of the code.

I agree, the call to setup() could create problems, but arduino freezes even before I press the stop button, so setup() is actually never called in the execution. I'll try to remove it anyways and incorporate your suggestions but I doubt this will correct the arduino from freezing/reseting.

I did not think the setup() was the cause of your problem but think it will be later on once you get it all working.

What voltage are you driving the L298 (Vs) with and what power supply/rating is it fed from?
How is the arduino powered?
What value of sense resistor are you using?
The HPE-127-10-08 can sink up to 6 amps but each output on the L298 can only handle 2 amps (4A total for both outputs) so maybe your drawing to much current for the PSU and this is causing the arduino to brownout. It could also be power spikes when the heater/cooler is turned on/off causing the arduino to crash.

Using goto: is a bit ugly but shouldn't cause any problems. Calling setup() again from within loop() is also pretty unconventional but I don't see you doing anything in setup that would cause any problems if you ran it again.

Since it works OK until you connect the load up I'd suspect a voltage supply issue, most likely that the Peltier is taking so much current that the Arduino's voltage regulator is browning out. Can you describe how the Arduino logic, L298 logic and L298 load power supplies are arranged?

I am supplying power through a DC power adapter rated for 12V, 5Amp. This supplies power to both arduino & L298 individually.

The arduino's vcc (5V) is connected to the logic supply of the L298. The power supply of L298 is 12V from the adapter. They all share a common ground. I've connected both the channels of L298 together, so that it can supply 4A max. Although I have never exceeded the current more than 2.5Amp.

Even I thought it could be power issues, but the strange part is the arduino freezes/resets after roughly 10 secs after the TEC module is turned on. It starts the TEC and then displays the temp for a couple of seconds and then out of nowhere it freezes leaving the TEC turned on. I don't change anything, no button press, no turning TEC on/off.

Really can't seem to figure out what could be wrong here.

Put a voltmeter on the Arduino's 5V supply and see what voltage you're actually getting there in the scenario where the problem happens.

To eliminate the possibility of the issue being with the power supply, consider running two power supplies, one for the Arduino, the other for the Peltier. If the Peltier is bringing the 5A PS to its knees, the voltmeter will tell the story.

Next, I'd consider investigating whether the sketch works on a very light PWM basis - maybe 20% of max L298 capacity. Only later ramp up the power to see if the response is as expected. Frequently, solid state devices offer nowhere near the advertised sinking / sourcing capacity unless they're sporting gargantuan heat sinks.

Measured the voltage across the arduino, it stays at 5.13V throughout. The voltage doesn't drop below this value irrespective of whether the peltier is on or off.

Tried reducing the PWM and for some odd reason, it seems to work without any problems whenever the current is below 0.7Amps. As soon as the current is more than 0.7 amps, arduino freezes in a couple of seconds, even though the arduino voltage is still ~5.13V.

A DC current is injected into the TEC through the two terminals, and this generates a temperature difference between the two plates: One plate becomes cooler, while the other side becomes warmer.
When the cold side cools down, the TEC terminal connecting the positive lead of the power supply is designated as the plus (+) terminal, or the positive lead of the TEC; the other is designated as the minus (-) terminal, or the negative lead. As the DC current reverses direction – i.e., flows out from the plus terminal – the cold side will heat up and the hot side cool down. Therefore, TECs can be used both for cooling down and heating up the thermal load.

TECs can’t “generate” cold; they only move heat from one side to the other. They “produce” heat more efficiently than cold under the same size or current conditions because not all of the electric energy of the DC current is used for removing the heat; some energy is consumed by the Peltier elements and converted into heat, which is conducted to the two sides of the TEC at the same time. This heat boosts the heating-up power of the TEC but makes the cooling-down function less efficient.
The more information you can see: TEC Controllers/Temperature Controllers/Peltier Controller |Analogtechnologies.com

You can come here and you will get many TEC so it is good to you link here http://www.analogti.com/thermo-system-components.html