Example data demonstrating the PCR works

Green channel was plotted to compare. Heat conditions:

Samples

  1. beta-actin - PCR with taqman probe
  2. blank - water control with master mix

Note: after 30 cycles signal was visable

Steps for building a mini thermocycler

Custom Parts:thermoblock_holder_1inch_half.stl

1. Milling thermoblock. It costs $100 to make this. thermoblock_holder_1inch_half.stl
2. Lid with 2 3mm set screws drillblock_1inch_lid_half.stl
3. Block holder hermoblock_holder_1inch_half.stl
4. Extrusion connects tslot_hinge_thermalblock.stl
5. Brd base thermocycle_brd.stl
6. PCB board pcbs_thermominiblock.zip Parts: gerberfiles_parts.zip
7. Single tip imager singletip_imager.stl

Off the shelf ($43) + assembled brd is around $20 a unit + pipetters $20:

1. Power supply
2. Peltier cooling system
3. Mosfets
4. Diodes
5. Heater/Thermistors
6. Arduino Nano
import time,datetime,os
from datetime import datetime
import json,re
import serial
import subprocess



def timestamp(gg):
 now = datetime.now() # current date and time
 date_time = now.strftime("%m/%d/%Y, %H:%M:%S")
 print("date and time:",date_time)
 gg.write("date and time: " + date_time)

def openport(prt):
  try:
   ser = serial.Serial(prt, 115200, timeout=0.2)
   time.sleep(2)
  except:
   print("its not connecting")
  return ser


def whatstheports():
 try: 
  output = subprocess.check_output("python3 -m serial.tools.list_ports -v", shell=True).decode()
  b = re.split("\n", output)
  if len(b)>1:
      for i in b:
          if re.match('^ ', i):
              pass
          else: 
              try: 
               dada = (re.sub(' .*', '',i))
               if re.match('^.*AMA0.*$', dada):
                pass
               else:
                #ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=0.2)
                ser = serial.Serial(dada, 115200, timeout=0.2)
                time.sleep(2)
                ser.write(b'info\n')
                time.sleep(0.2)
                resp = (ser.readlines()[0].decode())
                ser.close()
                if re.match('^tempcontrol.*', resp):
                    return dada
              except: 
               pass
  else:
     print("You need to plug in the device")
 except:
  print("please plug the device in")


def read():
  port = whatstheports()
  ser = openport(port)
  ser.write(b'readlevel\n')
  b = (ser.readlines())
  temp = float(b[0].decode().rstrip())
  print(temp)



def incubate(stemp):
  print("Beginning to incubate at this temp "+stemp)
  print("To stop, ^c to kill the program")
  port = whatstheports()
  ser = openport(port)
  ser.write(b'settemp '+stemp.encode()+b'\n')
  ser.write(b'readlevel\n')
  time.sleep(0.2)
  b = (ser.readlines())
  temp = float(b[0].decode().rstrip())
  a = 1
  while a >0:
   ser.write(b'readlevel\n')
   time.sleep(0.2)
   b = (ser.readlines())
   temp = float(b[0].decode().rstrip())
   print(temp)


def coolon():
  print("cooling ... ")
  port = whatstheports()
  ser = openport(port)
  ser.write(b'fanon\n')
  time.sleep(0.5)
  ser.write(b'coolon\n')
  time.sleep(0.5)
  ser.write(b'settemp 0\n')
  a = 1
  while a > 0:
   ser.write(b'readlevel\n')
   time.sleep(0.2)
   b = (ser.readlines())
   temp = float(b[0].decode().rstrip())
   print(temp)


def flashon():
  port = whatstheports()
  ser = openport(port)
  ser.write(b'strobmic\n')
  return ser

def flashoff(ser):
  ser.write(b'off\n')
  ser.close()

def runcycle(dtemp,dtemptm,etemp,etemptm,cy):
  print(int(cy))
  ccy = int(cy)
  gg = open("log.txt", "w")
  timestamp(gg)
  gg.write("Thermcycler denature "+dtemp+"C time "+dtemptm+" extend "+etemp+" time "+etemptm+" cycle "+cy+"\n")
  
  print("Thermcycler denature "+dtemp+"C time "+dtemptm+" extend "+etemp+" time "+etemptm+" cycle "+cy)
  print("To stop, ^c to kill the program\n")
  port = whatstheports()
  ser = openport(port)
  time.sleep(1)
  #ser.write(b'setp 300\n')
  for i in range(0,ccy):
   print("cycle "+str(i))
   gg.write("cycle "+str(i)+"\n")
   cycle(dtemp,dtemptm,etemp,etemptm,cy,ser,i,gg)
  print("finished thermocycling")
  timestamp(gg)
  gg.write("finished thermocycling\n")
  gg.close()


def cycle(dtemp,dtemptm,etemp,etemptm,cy,ser,cycle,gg):
  print("Cycle "+str(cycle))
  print("Setting denaturing temperature to "+str(dtemp)+"C")
  gg.write("Cycle "+str(cycle)+"\n")
  gg.write("Setting denaturing temperature to "+str(dtemp)+"C\n")
  ser.write(b'settemp '+dtemp.encode()+b'\n')
  ser.write(b'readlevel\n')
  time.sleep(0.2)
  b = (ser.readlines())
  temp = float(b[0].decode().rstrip())
  while temp < (int(dtemp)+1):
   ser.write(b'readlevel\n')
   time.sleep(0.2)
   b = (ser.readlines())
   temp = float(b[0].decode().rstrip())
   print(temp)
   gg.write(str(temp)+"\n")
   if temp == (int(dtemp)):
       break
  time.sleep(int(dtemptm))
  ser.write(b'settemp 0\n')
  print("Setting extend temperature to "+str(etemp)+"C")
  print("Cooling ... ")
  gg.write("Setting extend temperature to "+str(etemp)+"C\n")
  gg.write("Cooling ... \n")
  ser.write(b'fanon\n')
  time.sleep(0.3)
  ser.write(b'coolon\n')
  time.sleep(0.3)
  ser.write(b'readlevel\n')
  time.sleep(0.2)
  b = (ser.readlines())
  temp = float(b[0].decode().rstrip())
  while temp > (int(etemp)+1):
   ser.write(b'readlevel\n')
   time.sleep(0.2)
   b = (ser.readlines())
   temp = float(b[0].decode().rstrip())
   print(str(cycle) + ' ' + str(temp))
   gg.write(str(cycle) + ' ' + str(temp)+'\n')
   if temp == (int(etemp)):
       break
  ser.write(b'fanoff\n')
  time.sleep(0.3)
  ser.write(b'cooloff\n')
  time.sleep(0.3)
  print("extend temperature begins")
  gg.write("extending \n")
  time.sleep(0.3)
  ser.write(b'settemp '+etemp.encode()+b'\n')
  while temp > (int(etemp)+1):
   ser.write(b'readlevel\n')
   time.sleep(0.2)
   b = (ser.readlines())
   temp = float(b[0].decode().rstrip())
   print(temp)
   gg.write(str(temp)+"\n")
  gg.write("incubating at extend temperaure\n")
  print("incubating at extend temperaure\n")
  time.sleep(int(etemptm))



input_var = input("Thermobock controller [help|h, about|a, incubate|i, cycle|c], read|r, cool, flashon: ")
if re.match("help|h", input_var):
    print("This controller the thermocycler. Here are the available commands")
    print("\thelp[h] - Provides the options")
    print("\tabout[a] - About the program, the product its used for and the developer")
    print("\tincubate[i] - Incubate at specific temperate. The command goes like this -incubate[i]_[your temperature]. So something like this 'i_55' or 'incubate_55' for incubating at 55C")
    print("\tread[r] - Read temperature sensor, it reports in Celcius")
    print("\tcycle - Cycling conditions. Argument goes like this -cycle[c] extent_time_denature_time_cycles. Like cycle_94_30_55_180_40 for 55C for 30 seconds then 55 for 180 seconds")
    print("\tflashon - Turns on LED")
elif re.match("port", input_var):
    whatstheports()
elif re.match("read|r", input_var):
    read()
elif re.match("cool", input_var):
    coolon()
elif re.match("incubate_.*|i_.*", input_var):
    [arg, temp] = re.split('_', input_var)
    incubate(temp)
elif re.match("cycle_.*|c_.*", input_var):
    [arg, dtemp,dtemptm,etemp,etemptm,cy] = re.split('_', input_var)
    runcycle(dtemp,dtemptm,etemp,etemptm,cy)
elif re.match("flashon", input_var):
    ser = flashon()
    floff = input("Press [enter] to turn off")
    flashoff(ser)
else:
    print("command not recognized")

#include <Servo.h>
#include <PID_v1.h>
//#include <FastPID.h>
Servo myservo;  // create servo object to control a servo

#include <Wire.h>
#include <SoftwareSerial.h>


#define THERMISTOR_PIN A7
#define TEMPCONTROL 3
#define FANPIN 5
#define THERMOELECTRIC_COOL 6
#define LED 9 

//Define the aggressive and conservative Tuning Parameters

double aggKp=150, aggKi=0.5, aggKd=0;


int thermoelectricval = 255;
int ledval = 255;
int tempcontrol = 3;
String command;
float currpos;
int celsius;
int levelstreamon;
double settemp = 0;
double Setpoint, Input, Output;
int tme = 250;

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

#define NUMTEMPS 20
short temptable[NUMTEMPS][2] = {
   {1, 841},
   {54, 255},
   {107, 209},
   {160, 184},
   {213, 166},
   {266, 153},
   {319, 142},
   {372, 132},
   {425, 124},
   {478, 116},
   {531, 108},
   {584, 101},
   {637, 93},
   {690, 86},
   {743, 78},
   {796, 70},
   {849, 61},
   {902, 50},
   {955, 34},
   {1008, 3}
};


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  analogWrite(TEMPCONTROL, 0); 
  analogWrite(FANPIN, 0); 

  Setpoint = settemp;
  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  analogWrite(THERMOELECTRIC_COOL,0);
  analogWrite(LED,0);
}

void loop() {
   Input = read_temp();
   
   double gap = abs(Setpoint-Input); //distance away from setpoint
   myPID.SetTunings(aggKp, aggKi, aggKd);
   myPID.Compute();
   analogWrite(TEMPCONTROL,Output);
   int rawvalue = analogRead(THERMISTOR_PIN);
   float celsius = read_temp();
   int fahrenheit = (((celsius * 9) / 5) + 32);

  if (levelstreamon == 1) {
   Serial.println(celsius);
   delay(500);
  }

 if(Serial.available())
 {
    char c = Serial.read();
    if (c== '\n')
    {
      parseCommand(command);
      command = "";
    }
    else 
    {
      command +=c;
    }
 }
 delay(1);
}

float parseCommand(String com)
{

  if(com.equalsIgnoreCase("info")){
    Serial.println("tempcontrol");
  }
  else if(com.equalsIgnoreCase("levelstreamon")){
   levelstreamon = 1;
  }  
  else if(com.equalsIgnoreCase("defaultpid")){
    double aggKp=19.56, aggKi=0.71, aggKd=134.26;
  }
  else if (com.substring(0,4) == "setp") {
    aggKp = com.substring(5).toDouble();
  }
  else if (com.substring(0,4) == "seti") {
    aggKi = com.substring(5).toDouble();
  }
  else if (com.substring(0,4) == "setd") {
    aggKd = com.substring(5).toDouble();
  }
  else if(com.equalsIgnoreCase("coolon")){
   analogWrite(THERMOELECTRIC_COOL,thermoelectricval);
  }
  else if(com.equalsIgnoreCase("cooloff")){
   analogWrite(THERMOELECTRIC_COOL,0);
  }  
   else if (com.substring(0,10) == "setledval") {
    ledval = com.substring(11).toInt();
  }
  else if(com.equalsIgnoreCase("levelstreamoff")){
   levelstreamon = 0;
  } 
  else if(com.equalsIgnoreCase("fanon")){
   analogWrite(FANPIN, 255);
  } 
  else if(com.equalsIgnoreCase("fanoff")){
   analogWrite(FANPIN, 0);
  } 
  else if(com.equalsIgnoreCase("readlevel")){
     int rawvalue = analogRead(THERMISTOR_PIN);
     float celsius = read_temp();
     Serial.println(celsius);
  }
  else if (com.substring(0,12) == "setthermoval") {
    //Serial.println("setting temperature");
    thermoelectricval = com.substring(13).toInt();
    //Serial.println(settemp);
  }
  else if (com.substring(0,7) == "settemp") {
    //Serial.println("setting temperature");
    settemp = com.substring(8).toInt();
    //Serial.println(settemp);
    Setpoint = settemp;
  }
  else if(com.equalsIgnoreCase("strobmic")){
    strobingmic(ledval, tme);
    delay(10);
  }
}

float read_temp()
{
   int rawtemp = analogRead(THERMISTOR_PIN);
   float current_celsius = 0;

   byte i;
   for (i=1; i<NUMTEMPS; i++)
   {
      if (temptable[i][0] > rawtemp)
      {
         float realtemp  = temptable[i-1][1] + (rawtemp - temptable[i-1][0]) * (temptable[i][1] - temptable[i-1][1]) / (temptable[i][0] - temptable[i-1][0]);

         if (realtemp > 255)
            realtemp = 255; 

         current_celsius = realtemp;

         break;
      }
   }

   // Overflow: We just clamp to 0 degrees celsius
   if (i == NUMTEMPS)
   current_celsius = 0;

   return current_celsius;
}
void strobingmic(int ledval, int tme){
 int strobtimer = 1;
 while (strobtimer > 0 && Serial.available() == 0){
  analogWrite(LED, ledval);
  delayMicroseconds(tme);
  analogWrite(LED, 0);
  delayMicroseconds(tme);
  strobtimer++;
 }
 analogWrite(LED, 0);
}