Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Viola IntroiniDean Kos
Published © GPL3+

Temperature Controlled Container for Sample Transportation

Thermally insulated box that maintains sensitive samples like vaccines and cells at the desired temperature via battery or 12V sources.

AdvancedFull instructions providedOver 1 day16,970
Temperature Controlled Container for Sample Transportation

Things used in this project

Hardware components

PT 100 Temperature sensor
×1
Arduino UNO
Arduino UNO
×1
Lithium ion battery
10400 mAh
×1
Polystyrene Box
Recycled from conventional delivery of samples/chemicals
×1
Copper blocks
Two copper blocks approx. 80x50x10mm, to be machined for heat sink and Peltier attachment, with holes for heat pipes.
×1
Adjustable Boost Power Supply Board Module ULS
×1
Heatpipe cooler
×1
Digital potentiometer
×1
Magic Gel
To improve heat transfer we could insert metal rods in between samples
×1
DC converter
×1
Peltier module
×1
Capacitor 3.3 uF
×1
Heat pipes
×1
Power supply for mains
×1
DC splitter for power supply
×1
LCD screen Adafruit
×1
Heater mat
For heating samples
×1
Terminal block
×1
Plastic enclosure
×1
Panel mount DC plug
×1
Four pole double throw switch
×1
Single pole double throw switch
×1
Plastic enclosure
External plastic box for electronics
×1
Terminal block connector, 3 way
×1
Terminal block connector, 5 way
×1
Hook and Loop DualLock tape
×1
Adafruit Proto-screwshield
For firmer screw terminal wire connections to Arduino
×1
Adafruit Lithium-ion USB battery charger
×1
Adafruit PT100 Temperature sensor amplifier
×1

Software apps and online services

GitHub
We used sleemanj's library for the MCP41100 Digital Potentiometer (thank you sleemanj!). We also used Adafruit's libraries for each of the Adafruit modules we used; those are available from the product page.
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Dremel 4000
Vise
Drill
Cable stripping tool, pliers, cutters
Manual mill
For machining copper parts

Story

Read more

Schematics

Circuit diagram

Schemcatic circuit diagram with all main components of our temperature controlled container.

Wiring

Wiring schematic requested by another user of the community. It's the one we used to assemble the wiring, so it's ugly and was never intended for publication! But maybe can be helpful.

Potentiometer wiring

Wiring of the potentiometer to other components.

Code

PID_LCD_controller_v03

Arduino
Arduino code to connect to the modules used by Mr ThermoParcel, run the PID controller, display information on the LCD panel and receive user input.
#include <Adafruit_MAX31865.h>          // import PT100 temperature sensor library
#include <MCP41_Simple.h>               // import digital potentiometer library
#include <Adafruit_RGBLCDShield.h>      // import LCD  display & buttons shield library
#include <utility/Adafruit_MCP23017.h>  // import I2C expander library
#include <Wire.h>

// Setting up digital potentiometer
MCP41_Simple digitalPotentiometer;  // create digital potentiometer object
const uint8_t digitalPotentiometer_CS = 10;

// Setting up PT100 temperature sensor
// Use software SPI for PT100 temperature sensor: CS, DI, DO, CLK
Adafruit_MAX31865 PT100amplifier = Adafruit_MAX31865(2, 3, 4, 5);
// Set value of the Rref resistor. Use 430.0 for PT100 temperature sensor.
#define RREF      430.0
// Nominal 0-degrees-C resistance of the sensor, 100.0ohm for PT100
#define RNOMINAL  100.0

// Setting up LCD display shield with buttons
Adafruit_RGBLCDShield LCD_shield = Adafruit_RGBLCDShield();
//#define OFF 0x0   // ON and OFF states can be used to turn on/off the LCD backlight
//#define ON 0x1

void setup() {
  Serial.begin(115200);
  Serial.println("Mr ThermoParcel, starting operation...");
  PT100amplifier.begin(MAX31865_4WIRE);  // set to 2WIRE or 4WIRE as necessary, 4-wire RTD in this case

  // Initialise digital potentiometer
  digitalPotentiometer.begin(digitalPotentiometer_CS);
  // Set the wiper to an arbitrary point between 0 and 255
  digitalPotentiometer.setWiper( 200 );

  // Initialise LCD display shield
  // set up the LCD's number of columns and rows: 
  LCD_shield.begin(16, 2);
  // set up setpoint and measured T text on LCD with the correct spacings
  LCD_shield.print("Tsetpoint:   C");
  LCD_shield.setCursor(0, 1);
  LCD_shield.print("Tsample:     C");
}

// Initialise PID constants, temperature related variables and shield button value
int powerMode = 1;   // powerMode=1 for battery power, powerMode=-1 for mains power; used to prevent battery overload
int operationMode = 1;   // operationMode=1 for cooling, operationMode=-1 for heating; used to correct sign of PID terms
float PT100ratio;   // Define resistance ratio variable for PT100 sensor
uint8_t buttonsPressed = 0;
float kp = 500.0;  //;   int ki = 5;   int kd = 3.9;
float PID_p = 0.0;  //  int PID_i = 0;    int PID_d = 0;
float Tmeasured = -1.0;
float Tsetpoint = 22.0;   // Start around room temperature
float PID_error = 5;
float PID_value = 0;

// Define print_Tsetpoint function to correctly print the temperature setpoint on the LCD screen
static char TsetpointString[3];
void print_Tsetpoint(int T) {
  // Print Tsetpoint in the correct place 
  LCD_shield.setCursor(10, 0);
  dtostrf(T, 3, 0, TsetpointString);
  LCD_shield.print(TsetpointString);
}

// Define print_Tmeasured function to correctly print the measured temperature on the LCD screen
static char TmeasuredString[4];
void print_Tmeasured(float T) {
  // Print Tmeasured in the correct place
  LCD_shield.setCursor(8, 1);
  dtostrf(T, 5, 1, TmeasuredString);
  LCD_shield.print(TmeasuredString);
}

// Define print_powerMode function to correctly print the power mode (B, battery; M, mains)
void print_powerMode() {
  LCD_shield.setCursor(15, 0);
  if (powerMode == 1) {
    LCD_shield.print("B");
  }
  else if (powerMode == -1) {
    LCD_shield.print("M");
  }
}

// Define print_operationMode function to correctly print the power mode (C, Peltier cooler; H, heating mat)
void print_operationMode() {
  LCD_shield.setCursor(15, 1);
  if (operationMode == 1) {
    LCD_shield.print("C");
  }
  else if (operationMode == -1) {
    LCD_shield.print("H");
  }
}

// *** main loop ***
void loop() {
  // Read temperature
  uint16_t rtd = PT100amplifier.readRTD();
  PT100ratio = rtd;
  PT100ratio /= 32768;
  Tmeasured = PT100amplifier.temperature(RNOMINAL, RREF);
  Serial.print("Setpoint Temperature = "); Serial.println(Tsetpoint);
  Serial.print("Temperature = "); Serial.println(Tmeasured);

  // Print temperature values and modes
  print_Tsetpoint(Tsetpoint);
  print_Tmeasured(Tmeasured);
  print_powerMode();
  print_operationMode();

  // Calculate the error between setpoint and measured value
  PID_error = Tmeasured - Tsetpoint;
  //Calculate the P value
  PID_p = operationMode * kp * PID_error;

  // Calculate total PID value, if above maximum (255) keep at 255, if below minimum (0) keep at 0
  PID_value = (int) PID_p; //+ PID_i + PID_d;
  Serial.print("PID_p = "); Serial.println(PID_p);
  Serial.print("powerMode = "); Serial.println(powerMode);
  Serial.print("operationMode = "); Serial.println(operationMode);
  Serial.print("PID_error = "); Serial.println(PID_error);
  Serial.print("PID_value = "); Serial.println(PID_value);

  // If in battery mode (powerMode=1) limit output to avoid battery overload
  // If in mains mode (powerMode=-1) allow full power (255)
  if (powerMode == 1) {
    if (PID_value < 0)
      { PID_value = 0; }
    if (PID_value > 120)  
      { PID_value = 120; }
  }
  else if (powerMode == -1) {
    if (PID_value < 0)
      { PID_value = 0; }
    if (PID_value > 255)  
      { PID_value = 255; }
  }
  Serial.print("Adjusted PID_value = "); Serial.println(PID_value);

  // Set digital potentiometer resistance from PID value
  digitalPotentiometer.setWiper(255 - PID_value);

  // Detect any buttons pressed, change the setpoint value if needed, and display measured and setpoint T
  // delay() funcion calls ensure that enough time is given to press buttons and see the values change
  delay(1000);
  buttonsPressed = LCD_shield.readButtons();
  if (buttonsPressed & BUTTON_SELECT) {
    // Highlight that system in edit mode by blinking the cursor
    LCD_shield.setCursor(14, 0);
    LCD_shield.blink();
    delay(1000);
    buttonsPressed = 0;
    // Stay in edit mode until SELECT button is pressed again. Buttons UP and DOWN change Tsetpoint.
    // LEFT toggles operation mode (heating/cooling). RIGHT toggles power mode (battery/mains).
    while (not (buttonsPressed & BUTTON_SELECT)) {
      buttonsPressed = LCD_shield.readButtons();
      if (buttonsPressed & BUTTON_UP) {
        Tsetpoint += 1;
      }
      if (buttonsPressed & BUTTON_DOWN) {
        Tsetpoint -= 1;
      }
      if (buttonsPressed & BUTTON_RIGHT) {
        powerMode *= -1;
        print_powerMode();
      }
      if (buttonsPressed & BUTTON_LEFT) {
        operationMode *= -1;
        print_operationMode();
      }
      print_Tsetpoint(Tsetpoint);
      LCD_shield.setCursor(14, 0);
      delay(500);
    }   // Exit edit mode and stop blinking cursor
    LCD_shield.noBlink();
    buttonsPressed = 0;
  }
  Serial.println();
}

Credits

Viola Introini

Viola Introini

1 project • 4 followers
Dean Kos

Dean Kos

0 projects • 4 followers

Comments