Temperature controlled Dry block heater for cuvette

Introduction
A cuvette is a small tube-like container with straight sides and a circular or square cross section. It is sealed at one end, and made of a clear material such as plastic, glass, or fused quartz. Cuvettes are designed to hold samples for spectroscopic measurement, where a beam of light is passed through the sample within the cuvette to measure the absorbance. This measurement is done with a colorimeter.

Colorimeters are highly sensitive devices that can measure the concentration and intensity of a particular color that is used in a product. The color photometers are used for measuring the reflectance of a color as well as the transmission.

The working of colorimeters is mainly based on the Beer-Lambert’s Law. This law states that the light absorption when passes through a medium are directly proportional to the concentration of the medium. When a colorimeter is used, there is a ray of light with a certain wavelength is directed towards a solution. The colorimeter analyzes the reflected/transmitted light and compares with a predetermined standard. A processing circuit is used for calculation of the absorbance of the light by the solution. If the absorption of the solution is higher, then there will be more light absorbed by the solution and if the concentration of the solution is low then more lights will be transmitted through the solution.

Test samples are prepared and kept in a cuvette.

Colorimeters can be used for measuring the color concentration and intensity of a variety of materials such as: Food ingredients, Building materials, Textile products, Biological samples and so on.

Our new project is a colorimeter for the use in a biological clinical lab where the concentration of sodium, glucose etc. in blood and urine.

Sample preparation includes serum extraction, treating the serum with reagents, keeping the treated samples at constant temperature for a predetermined time before actual test using the colorimeter.

For temperature control of samples kept in cuvette a heated Aluminum block is used where there are cavities to place a number of Cuvettes at the same time. Two methods of heating available; wet bath heater and dry block heater. In wet bath the heater block is heated using hot oil or water. In dry block heater the aluminum block is directly heated using electrical heating element.

This part of our project is to design a dry block heater controller. The heated block is made of solid aluminum (see Figure 1). This block is designed for heating 12 Cuvettes at a time as shown in the figure and the thermal simulation study reveals that a maximum of 15 watts of electrical power is sufficient. The Cuvettes are to be kept for predetermined period at a constant temperate say 37.1 degree C, otherwise accuracy will be affected. Since the temperature is to be kept accurately and precisely, an accurate heater control circuit is needed. The block diagram of dry block heater control is shown in the figure 5.

Accurate temperature control of cuvette with samples is achieved by maintaining the temperature of dry heater Aluminum block. Cuvettes are inserted to slots in the aluminum block and this block is electrically heated and temperature is set by the desired value by closed loop temperature control. Precise measurement of temperature demands a temperature transducer and signal processing. Since the temperature control is to be achieved by a microcontroller, digital output from the temperature transducer is needed. MAX31856 is an integrated circuit that contains all the functionality Precision thermo couple amplifier, like automatic Linearization correction for different types of thermocouples, minimum error %, higher resolution, and input protection against ±45V, minimum component count, built in fault detection and management, noise rejection, with digital output easily communicated to the host microcontroller using SPI communication.

BOM
BOM IS UPLOADED


Schematics
SCHEMATICS ARE UPLOADED
Instructions
Simulation study reveals that 15 watts of heater power is sufficient for the cuvette heater to maintain temperature up to 45 degree C. Since the calorimeter is for biomedical application, 37 degree C is the standard temperature to be set and maintain. Please refer the results of the simulation study results attached.
Heating the aluminum block: In conventional design resistive wires are used as heating element. An alternate method of heating with POWER MOSFETs is tried in this design. The advantages being reduced cost compared to the cost of resistive wire heating element. I purchased a 25 W heater (the one shown in the figure of the Al. block) costs around 4 USD where as the two MOSFETS used for the same purpose was purchased for less than 2 USD! MOSFETs are driven with a low gate voltage so as to bias it to operate in the linear region (ohmic region). So a considerable amount of power is dissipated across the MOSFET which causes heating that will be effectively “conducted” to the aluminum block by suitable heat conducting mounting.
It has been experimetnaly determined that a voltage in between 0 to 4 volts as VGs, the MOSFETS will be operating in the linear region, producing heat.
Temperature measurement: The most important part of this project is to sense the temperature precisely an accurately. By referring to the datasheet of MAX31856, it is highly suitable for this application. The chip contains a total solution to all temperature sensing problems that is commonly encountered by the designer. It uses thermo couple as the sensor that has very high accuracy. Since thermocouples output voltage/ degree C is of the order of few micro volts, a high gain amplifier is essential that is available in the chip.

The MAX31856 performs cold-junction compensation and digitizes the signal from any type of thermocouple. The output data is formatted in degrees Celsius. This converter resolves temperatures to 0.0078125°C, with an accuracy of ±0.15%. Also no need to worry about the stray voltages picked up by the thermo couple element. It can accept different types of thermo couples (K, J, N, R, S, T, E, and B) and also provides good rejection of power supply hum. The chip can easily interface into a low cost Micro controller having SPI Bus. Built in cold junction compensation, fault detection are additional features of this chip.All these features are used in the proposed design.

Microcontroller: PIC Microcontroller 16F15344 is selected for this application as the chip is very familiar to me. Also it has a built in DAC; the output from this DAC is used to bias the MOSFETs for controlled heating. Details about the controller can be had from the microchip web site (www.microchip.com).

Schematic Diagram: The entire schematic is attached. The hardware design is straight forward except the MOSFET based heater. 12V DC is used to power the heater so as to facilitate operation of the heater from 12 V batteries and at the same time avoids AC power supply interference. 16ch X 2 line LCD display is used to display the temperature reading and statuses. It is also easy to set and the read the temperature using 4 push buttons provided. Some I/O lines of the MCU are multiplexed for display and push button keys.

K type thermo couple is firmly inserted into the aluminum block y drilling a hole into the block from one face of the block. The thermo couple was positioned various locations on the block to find an optimum place where it can be attached to get correct reading and came to know that there is not much effect on the sensing point on the temperature settled.
PID Control: PID ( proportional, integral and derivative) control algorithm is used for temperature control. This control loop will calculate the PID parameters and the control value to control the heater is executed every 100 milli seconds that is sufficient due to the bulk volume of the heater block.
Fine tuning of PID values: Due to time limitations tuning of the PID parameters and the Loop tine has not yet completed.

Programming: Program is written in C and compiled by the CCS C compiler. For details about the Compiler refer www.csinfo.com The source code written is attached. PID control is used to control the temperature of the cuvette. The source code is self explanatory.
Major portion of the program code is written for user interface. 4 Keys are designed for the purpose namely ( MENU key, UP key Down key and ENTER key ) LCD display shows the the various menus based on the Key Press.
By using these for keys, navigation through the menus and setting of temperature are possible. 37 degree C is the normal setting for the Menu.

Video
Project is nearing completion. Video will be updated after final testing.

Source Code
Code:
// Temperature controller code written for PIC 16F15344 with MAX31856
#include <heater.h>
#use spi (MASTER, CLK=PIN_B4, DI=PIN_B5, DO=PIN_C0, MODE=0, BITS=8, STREAM=SPI_1)
#use spi (MASTER, MODE=0, BITS=8, STREAM=SPI_2)
#use spi (MASTER, MODE=0, BITS=8, STREAM=SPI_3)

#include <LCD.C>
#include <float.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <MAX31856.h>

uint8_t switchTime = 0;
uint8_t pidTime = 0;

#INT_TIMER1
void  TIMER1_isr(void)
{
   switchTime ^= 1;    pidTime = 1;}

//LCD Pin Defenitions
#define LCD_ENABLE_PIN     PIN_A4
#define LCD_RS_PIN         PIN_A5
#define LCD_DATA4          PIN_C4
#define LCD_DATA5          PIN_C5
#define LCD_DATA6          PIN_C6
#define LCD_DATA7          PIN_C7

// Keyboard pin definition

#define MENU               PIN_C7
#define UP                 PIN_C6
#define DOWN               PIN_C5
#define ENTER              PIN_C4
#define top_limit          600
#define middle_val         300
uint8_t menuSelect = 1;
uint8_t select = 1;
uint8_t heater_status = 0;

// PID paraiables
int16 _fixed(1) kp = 5;
int16 _fixed(1) ki = 1;
int16 _fixed(1) kd = 0;

int16 _fixed(1) setpoint = 35.0;
int16 _fixed(1) CurrentTemp;
signed int16 _fixed(1) error;
signed int16 _fixed(1) integral = 0;
signed int16 _fixed(1) derivative = 0;
signed int16 control_var;
signed int16 _fixed(1) last_error;
float currentValue;

// Prototype defentions
void welcome_message(uint16_t delay);
void Buttons_Init(void);
void Output_Pin_Init(void);
void Alert(uint8_t time);
void menu_1(void);
void menu_2(void);
void menu_3(void);
void menu_4(void);
void menu_temp_edit(void);
void MAX31856_Init(void);
void WriteRegister(uint8_t reg_address, uint8_t data);
float ReadThermocouple(void);
void PID(void);
void dac_heater_on(int16 _fixed(1) controlData);
void check_error();

void eeprom_read_object(unsigned int ee_addr, void *obj_p, size_t obj_size);
void eeprom_write_object(unsigned int ee_addr, void *obj_p, size_t obj_size);

// Main program starts from here
void main() {
 
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);      //104 ms overflow
   setup_dac(DAC_VSS_VDD|DAC_OUTPUT);
   setup_spi(SPI_MASTER|SPI_CLK_DIV_4|SPI_XMIT_L_TO_H|SPI_SAMPLE_AT_END);

   lcd_init();
   Output_Pin_Init();
   welcome_message(2000);
 
   eeprom_read_object(0x0ff0, &setpoint, sizeof setpoint);
   if(setpoint >45 || setpoint < 35) {
      setpoint= 40;
   }
   enable_interrupts(INT_TIMER1);
   enable_interrupts(GLOBAL);
 
   while(TRUE) {
 
      if(pidTime == 1 && heater_status == 1) {          PID();         dac_heater_on(control_var);      }
   
      if(switchTime == 1) {
         Buttons_Init();
         if(input_state(MENU) == 0 && switchTime == 1) {
         
               Alert(100);
               switch(menuSelect) {
                  case 1:
                     menuSelect = 2;
                     select = 1;
                     menu_1();
                     break;
                  case 2:
                     menuSelect = 3;
                     select = 2;
                     menu_2();
                     break;
                  case 3:
                     menuSelect = 4;
                     select = 3;
                     menu_3();
                     break;
                  case 4:
                     menuSelect = 1;
                     select = 4;
                     menu_4();
                     break;
             
            }
       }
         else if(input_state(ENTER) == 0 && switchTime == 1) {
       
            Alert(100);
            switch(select) {
               case 1:
                  heater_status = 1;
                  break;
               case 2:
                  menu_temp_edit();
                  break;
               case 3:
                  break;
               case 4:
                  heater_status ^= 1;
                 if (heater_status==0) dac_write(0);
                  break;
               case 5:
                  heater_status = 1;
                  eeprom_write_object(0x0ff0, &setpoint, sizeof setpoint);
            }
       
         }
         else if(input_state(UP) == 0 && switchTime == 1) {
       
            Alert(100);
            menu_temp_edit();
            setpoint++;
            check_error();
            select = 5;
       
         }
         else if(input_state(DOWN) == 0 && switchTime == 1) {
       
            Alert(100);
            menu_temp_edit();
            setpoint--;
            check_error();
            select = 5;
       
         }
      }
   }
}

void Buttons_Init(void) {
   set_tris_c(0b11110000);
   port_c_pullups(0xF0);
}
// Initialize Pin C2 and Pin c3 as outputs
// Pin c2: Buzzer
// Pin c3: Switches_pin

void Output_Pin_Init(void) {
   set_tris_c(0b00000000);
}

void welcome_message(uint16_t delay) {
   lcd_send_byte(0,0x01);
   lcd_send_byte(0,0x28);
   lcd_send_byte(0,0x80);
   lcd_putc("   Welcome to   ");
   lcd_send_byte(0,0xC0);
   lcd_putc(" Heater Control ");
   delay_ms(delay);
   lcd_send_byte(0,0x01);
}

void menu_1(void) {
   lcd_init();
   lcd_send_byte(0,0x01);
   lcd_send_byte(0,0x80);
   lcd_putc(" Set Temperature");
   delay_ms(100);
   lcd_gotoxy(6,2);
   //if(Temp_eeprom != )
   printf(lcd_putc,"%w",setpoint);
   delay_ms(500);
}

void menu_2(void) {
   lcd_init();
   lcd_send_byte(0,0x01);
   lcd_send_byte(0,0x80);
   lcd_putc(" Enter new Temp ");
   lcd_gotoxy(6,2);
   printf(lcd_putc,"%w",setpoint);
   delay_ms(500);
}

void menu_3(void) {
 
   lcd_init();
   uint8_t status = 1;
   if(heater_status == 0) {
      lcd_send_byte(0,0x01);
      lcd_send_byte(0,0x80);
      lcd_putc("     PID off    ");
      lcd_send_byte(0,0xC0);
      lcd_putc("   Heater Off   ");
   }
 
   while(heater_status == 1 && status == 1) {
      lcd_send_byte(0,0x01);
      lcd_send_byte(0,0x80);
      lcd_putc("Set point: ");
      lcd_gotoxy(12,1);
      printf(lcd_putc,"%w",setpoint);
      lcd_send_byte(0,0xC0);
      lcd_putc("Reading : ");
      lcd_gotoxy(11,2);
      CurrentTemp = (int16 _fixed(1))currentValue;
      printf(lcd_putc,"%w",CurrentTemp);
   
      if(input_state(MENU) == 0) {
         delay_ms(10);
         if(input_state(MENU) == 0) {
         status = 0;
         }
      }
   }
   delay_ms(500);
}

void menu_4(void) {
 
   lcd_init();
   lcd_send_byte(0,0x01);
   lcd_send_byte(0,0x80);
   if(heater_status == 1) {
      lcd_putc("  Heater is On  ");
      lcd_send_byte(0,0xC0);
      lcd_putc("Press Enter: OFF");
   }
   else {
      lcd_putc(" Heater is  Off ");
      lcd_send_byte(0,0xC0);
      lcd_putc("Press Enter: ON ");
   }
   delay_ms(50);
}

void menu_temp_edit(void) {
   lcd_init();
   lcd_send_byte(0,0x01);
   lcd_send_byte(0,0x80);
   lcd_putc("Use UP/DOWN Key");
   lcd_gotoxy(7,2);
   printf(lcd_putc,"%w",setpoint);
   delay_ms(50);
}

// Buzzer output

void Alert(uint8_t time) {
   output_high(PIN_C2);
   delay_ms(time);
   output_low(PIN_C2);
   delay_ms(time);
}

// Initialization of MAX31856(Thermocouple amplifier to digital converter
// with linearization IC)
// input: none
// output: none

void MAX31856_Init(void) {
   WriteRegister(REG_CR0_W,CR0_INIT);
   WriteRegister(REG_CR1_W,CR1_INIT);
}

// Communication between uC and Max31856
// inputs: Max31856 register address and curresponding bits
// output: none

void WriteRegister(uint8_t reg_address, uint8_t data) {
   spi_write(reg_address);
   spi_write(data);
}

// Thermocouple sensor to celcius converter
// It will read linearized thermocouple temperature equavalent 24 bit digital
// number and convert it into degree celcius
// Input :  none
// output:  degree celcius value(signed)

float ReadThermocouple(void) {   float temperature;   int32_t temp=0;   uint8_t byte_number = 3; 
while(byte_number) {
      temp = temp | spi_read();
      temp <<= 8;
      byte_number--;
   }
   temp >>= 13;
   temperature = (float)temp* 0.0078125;
   return temperature;
}

// PID Function
// Evaluates error and produce a control variable
// Control variable will controlls the voltage that gives to the heater
// This Function will work on every 100ms


void PID(void) { 
   pidTime = 0;
   currentValue = ReadThermocouple();
   error = setpoint - currentValue;
   integral = integral + ki*error;
   derivative = error - last_error;
   control_var = (kp * error) + integral + (derivative * kd);
   last_error = error;
}
void check_error() {
   if(setpoint > 45) {
      setpoint = 45;
      lcd_init();
      lcd_send_byte(0,0x01);
      lcd_send_byte(0,0x80);
      lcd_putc("     WARNING    ");
      lcd_send_byte(0,0xC0);
      lcd_putc(" MAX TEM REACHED");
      delay_ms(1000);
      Alert(200);
      Alert(200);
   }
   else if(setpoint< 35) {
      setpoint = 35;
      lcd_init();
      lcd_send_byte(0,0x01);
      lcd_send_byte(0,0x80);
      lcd_putc("     WARNING    ");
      lcd_send_byte(0,0xC0);
      lcd_putc(" MIN TEM REACHED");
      delay_ms(1000);
      Alert(200);
      Alert(200);
   }
 

}
// Heater control ======================
void dac_heater_on(unsigned int16 control_var) {

if (heater_status ==1)
   {control_var = (control_var*10+middle_val);
   if (control_var >Top_limit) control_var=top_limit;
   dac_write(control_var);
   }
else dac_write(0);  // heater switched off in case of error

}
void eeprom_read_object(unsigned int ee_addr, void *obj_p, size_t obj_size)
{
     unsigned char *p = obj_p;
     while (obj_size--) {         *p++ = read_program_eeprom(ee_addr++);     }
}

void eeprom_write_object(unsigned int ee_addr, void *obj_p, size_t obj_size)
{
     unsigned char *p = obj_p;

     while (obj_size--) {         write_program_eeprom(ee_addr++, *p++);     }
}
==============================================================================================
max31856.h
/-------------------------------------------------------------------------------------------------------------
// MAX31856 header file
// Created 20/1/2018
// Created by Muneer KM

//------------------Registers-----------------------------------------------------------------------------------
    
#define REG_CR0_R               0x00       // CR0 Configuration register 0 Read Address
#define REG_CR0_W               0x80      // CR0 Configuration register 0 Write Address

#define REG_CR1_R               0x01      // CR1 Configuration register 1 Read Address
#define REG_CR1_W               0x81      // CR1 Configuration register 1 Write Address

#define REG_FAULTMASK_R         0x02      // FAULT MASK Register Read
#define REG_FAULTMASK_W         0x82      // FAULT MASK Register write

#define REG_CJHF_THRESHOLD_R    0x03      // Cold Junction High Fault Threshold Register Read
#define REG_CJHF_THRESHOLD_W    0x83      // Cold Junction High Fault Threshold Register Write

#define REG_CJLF_THRESHOLD_R    0x04h      // Cold Junction Low Fault Threshold Register Read
#define REG_CJLF_THRESHOLD_W    0x84h      // Cold Junction Low Fault Threshold Register Write

#define REG_LTHFT_MSB_R         0x05      // Linearized Temperature High Fault Threshold MSB Register Read
#define REG_LTHFT_MSB_W         0x85      // Linearized Temperature High Fault Threshold MSB Register write

#define REG_LTHFT_LSB_R         0x06      // Linearized Temperature High Fault Threshold LSB Register Read
#define REG_LTHFT_LSB_W         0x86      // Linearized Temperature High Fault Threshold LSB Register Write

#define REG_LTLFT_MSB_R         0x07      // Linearized Temperature Low Fault Threshold MSB Register Read
#define REG_LTLFT_MSB_W         0x87      // Linearized Temperature Low Fault Threshold MSB Register Write

#define REG_LTLFT_LSB_R         0x08      // Linearized Temperature Low Fault Threshold lSB Register Read
#define REG_LTLFT_LSB_W         0x88      // Linearized Temperature Low Fault Threshold lSB Register Write

#define REG_CJT_OFFSET_R        0x09      // Cold Junction Temperature Offset Register Read
#define REG_CJT_OFFSET_W        0x89      // Cold Junction Temperature Offset Register Write

#define   REG_CJ_TEMP_MSB_R     0x0A      // Cold junction Temperature Register MSB Read
#define   REG_CJ_TEMP_MSB_W     0x8A      // Cold junction Temperature Register MSB Write

#define   REG_CJ_TEMP_LSB_R     0x0B      // Cold junction Temperature Register lSB Read
#define   REG_CJ_TEMP_LSB_W     0x8B      // Cold junction Temperature Register lSB Write

#define REG_LT_THERMOCOUPLE_B2  0x0C      // Linearized Thermocouple Temperature Byte 2
#define REG_LT_THERMOCOUPLE_B1  0x0D      // Linearized Thermocouple Temperature Byte 1
#define REG_LT_THERMOCOUPLE_B0  0x0E      // Linearized Thermocouple Temperature Byte 0

#define REG_FAULT_STATUS        0x0F      // Fault Status Register

//-----------------------------------------------------------------------------------------------------------------
// CR0 Bits

#define CR0_AUTOMATIC_CONVERSION  0x80
#define   CR0_1SHOT               0x04
#define CR0_OPENCIRCUIT_FAULT     0x10
#define   CR0_CJ_SENSOR_DISABLE   0x08
#define CR0_FAULT_INT             0x04
#define CR0_FAULT_CLEAR           0x02
#define   CR0_FILTER_50HZ         0x01


// CR1 Bits

#define CR1_1SAMPLE               0x00
#define CR1_2SAMPLE               0x10
#define CR1_4SAMPLE               0x20
#define CR1_8SAMPLE               0x30
#define CR1_16SAMPLE              0x40

#define CR1_TC_B                  0x00
#define CR1_TC_E                  0x01
#define CR1_TC_J                  0x02
#define CR1_TC_K                  0x03
#define CR1_TC_N                  0x04
#define CR1_TC_R                  0x05
#define CR1_TC_S                  0x06
#define CR1_TC_T                  0x07
#define CR1_VOLATAGE_GAIN8        0x08
#define CR1_VOLATAGE_GAIN32       0x0C

// FAULT MASK Bits

#define MASK_CJHF                0x20
#define MASK_CJLF                0x10
#define MASK_TC_HF               0x08
#define MASK_TC_LF               0x04
#define MASK_OVorUV              0x02
#define MASK_TC_OC               0x01

#define CR0_INIT   (CR0_AUTOMATIC_CONVERSION + CR0_OPENCIRCUIT_FAULT)
#define CR1_INIT   (CR1_2SAMPLE + CR1_TC_K)
//------------------------------------------------------
CAD Files
Attach or link to any CAD files if relevant to your project.
  • Like
Reactions: vanquyen2407

Blog entry information

Author
Mani Thundiyil
Views
596
Last update

Downloads

More entries in General

Share this entry

Top