Hello,
I try to build temperature control system using TEC (thermo-electric cooler or Peltier element) as heating-cooling body, thermocouple as feedback and Arduino as brain. I'm new to arduino and microcontrollers in general. I watched some DIY videos where people build similar systems, but once I tried build it myself, it didn't work as expected. I created discussion here with hope, that you can point out on ideas that I might have missed.
Now I'll try to provide more details. Once again because of my lack of experience some details might be not relevant or I can miss something. Please do not hesitate asking me if you need something else.
General purpose of the system:
Change and maintain temperature of working body in temperature range of 25 - 35 C. Woking body is small plastic container with water (~1cm X 1cm X 0.3cm).
The Device allows to set temperature. Then it adjusts voltage supplied to TEC to reach this temperature. After that the device maintains temperature constant.
If the set temperature is higher than actual one, voltage is applied in one direction to heat the top surface of TEC. If the set temperature is lower, voltage should be applied in different direction to cool down top surface.
Here are some requirements (or good to have) for the system:
- Fast heating-cooling. In temperature range 25-35 C it must do its job within seconds.
- Linear change of temperature.
- Precise maintanence of temperature if desired temperature is reached. Fluctuation no more then 0.2 will be ok. Lower better =)
Now, I'll try to explain how I thought to reach this goal before. Maybe it helps you to guide me better.
I used the scheme proposed in the following video
I did some changes in the code though. Instead of using LED i used Arduino serial terminal to read current state. So, LED relating part of code I successfuly removed.
I used Elegoo UNO R3 (is it still arduino?).
For feedback system I used the same things as in the video: K-type thermocouple, ADC - MAX 6675 powered by 5V from controller.
I used simple rotary encoder to change set temperature. Power: 5V from controller.
For voltage control part I used NPN S8050 transistor, MOSFET IRFZ44, 10k resistor. Power: 12V from external power supply.
As heater-cooler I used TEC-12706.
I've managed to read actual temperature from thermocouple and change set temperature via rotary encoder. But I can't apply needed voltage with respect to changes of temperature. Here I attached some pictures of how I did connections.
Thank you for reading to the end. I highly appreciate any help with my problem. I understand that this might be quite simple task but once again I'm not experienced in this field, so will be happy to all reasonable advices and references.
Code is shown below
/* Max6675 Module ==> Arduino
* CS ==> D10
* SO ==> D12
* SCK ==> D13
* Vcc ==> Vcc (5v)
* Gnd ==> Gnd */
#include <SPI.h>
//LCD config
#include <Wire.h>
//I/O
int PWM_pin = 3; //Pin for PWM signal to the MOSFET driver (the BJT npn with pullup)
int clk = 8; //Pin 1 from rotary encoder
int data = 9; //Pin 2 from rotary encoder
//Variables
float set_temperature = 40; //Default temperature setpoint. Leave it 0 and control it with rotary encoder
float temperature_read = 0.0;
float PID_error = 0;
float previous_error = 0;
float elapsedTime, Time, timePrev;
float PID_value = 0;
int button_pressed = 0;
int menu_activated=0;
float last_set_temperature = 0;
//Vraiables for rotary encoder state detection
int clk_State;
int Last_State;
bool dt_State;
//PID constants
//////////////////////////////////////////////////////////
int kp = 90; int ki = 30; int kd = 80;
//////////////////////////////////////////////////////////
int PID_p = 0; int PID_i = 0; int PID_d = 0;
float last_kp = 0;
float last_ki = 0;
float last_kd = 0;
int PID_values_fixed =0;
//Pins for the SPI with MAX6675
#define MAX6675_CS 10
#define MAX6675_SO 12
#define MAX6675_SCK 13
void setup() {
// Setup Serial Monitor
Serial.begin (9600);
pinMode(PWM_pin,OUTPUT);
TCCR2B = TCCR2B & B11111000 | 0x03; // pin 3 and 11 PWM frequency of 928.5 Hz
Time = millis();
Last_State = (PINB & B00000001); //Detect first state of the encoder
PCICR |= (1 << PCIE0); //enable PCMSK0 scan
PCMSK0 |= (1 << PCINT0); //Set pin D8 trigger an interrupt on state change.
PCMSK0 |= (1 << PCINT1); //Set pin D9 trigger an interrupt on state change.
PCMSK0 |= (1 << PCINT3); //Set pin D11 trigger an interrupt on state change.
pinMode(11,INPUT);
pinMode(9,INPUT);
pinMode(8,INPUT);
}
void loop() {
// First we read the real value of temperature
temperature_read = readThermocouple();
//Next we calculate the error between the setpoint and the real value
PID_error = set_temperature - temperature_read + 3;
//Calculate the P value
PID_p = 0.01*kp * PID_error;
//Calculate the I value in a range on +-3
PID_i = 0.01*PID_i + (ki * PID_error);
//For derivative we need real time to calculate speed change rate
timePrev = Time; // the previous time is stored before the actual time read
Time = millis(); // actual time read
elapsedTime = (Time - timePrev) / 1000;
//Now we can calculate the D calue
PID_d = 0.01*kd*((PID_error - previous_error)/elapsedTime);
//Final total PID value is the sum of P + I + D
PID_value = PID_p + PID_i + PID_d;
//We define PWM range between 0 and 255
if(PID_value < 0)
{ PID_value = 0; }
if(PID_value > 255)
{ PID_value = 255; }
//Now we can write the PWM signal to the mosfet on digital pin D3
//Since we activate the MOSFET with a 0 to the base of the BJT, we write 255-PID value (inverted)
analogWrite(PWM_pin,255-PID_value);
previous_error = PID_error; //Remember to store the previous error for next loop.
Serial.print("Set temperature: ");
Serial.println(set_temperature);
Serial.print("Current temperature: ");
Serial.println(temperature_read);
Serial.print("Output signal: ");
Serial.println(255-PID_value);
delay(1000); //Refresh rate + delay of LCD print
}//Loop end
//The function that reads the SPI data from MAX6675
double readThermocouple() {
uint16_t v;
pinMode(MAX6675_CS, OUTPUT);
pinMode(MAX6675_SO, INPUT);
pinMode(MAX6675_SCK, OUTPUT);
digitalWrite(MAX6675_CS, LOW);
delay(1);
// Read in 16 bits,
// 15 = 0 always
// 14..2 = 0.25 degree counts MSB First
// 2 = 1 if thermocouple is open circuit
// 1..0 = uninteresting status
v = shiftIn(MAX6675_SO, MAX6675_SCK, MSBFIRST);
v <<= 8;
v |= shiftIn(MAX6675_SO, MAX6675_SCK, MSBFIRST);
digitalWrite(MAX6675_CS, HIGH);
if (v & 0x4)
{
// Bit 2 indicates if the thermocouple is disconnected
return NAN;
}
// The lower three bits (0,1,2) are discarded status bits
v >>= 3;
// The remaining bits are the number of 0.25 degree (C) counts
return v*0.25;
}
//The interruption vector for push button and rotary encoder
ISR(PCINT0_vect)
{
clk_State = (PINB & B00000001); //pin 8 state? It is HIGH?
dt_State = (PINB & B00000010);
if (clk_State != Last_State)
{
// If the data state is different to the clock state, that means the encoder is rotating clockwise
if (dt_State != clk_State) {
set_temperature = set_temperature+0.5 ;
}
else {
set_temperature = set_temperature-0.5;
}
}
Last_State = clk_State; // Updates the previous state of the clock with the current state
}