Go Down

Topic: Gas Chromatograph (Read 5077 times) previous topic - next topic

Harristotle

Apr 28, 2013, 06:13 pm Last Edit: Apr 28, 2013, 06:16 pm by Harristotle Reason: 1
Hi to all.
I would like to present you with my gas chromatograph machine.
It uses a 9mm borosilicate glass pipe filled with silica gel to separate and identify halogenated alkanes, like dichloromethane and refrigeration gas.
This glass pipe or "column" is placed in an "oven" controlled by an arduino. The oven consists of the physical copper jacket, a power mosfet to switch a 2.5 ohm (when cold) coil of nichrome wire, and a lm35 temperature sensor.
Sample is fed to the column by injecting 0.1-0.3ml of gas containing the substance of interest into some silicone tubing attached to an aquarium pump and to the column.
One of dem fancy arduino witchcraft thingies controls the oven, with a PID controller, and senses the intense blue light made when the column output is fed into a gas flame containing a loop of copper. (Copper gives a beautiful blue flame with halogenated alkanes - quite different to the green you get from a copper salt in a flame).
Output is collected from the serial monitor, and cut and pasted into an OpenOffice spreadsheet (MS Excel would be just as good too, but I haven't tried it) for graphing.

A picture of the setup is shown below:

http://www.screencast.com/t/waz6AdEPccsD


And a circuit diagram can be seen here:

http://www.screencast.com/t/pwdLbebEmC


Code will follow, due to post size limits.

Do enjoy!
Harristotle


Harristotle

And here is the code

Code: [Select]
#include <LiquidCrystal.h> // library for AC1602 type 16x2 lcd display

#include <FlexiTimer2.h> // timer library

#include <PID_v1.h> // proportional integral/derivative library for heater

#include <EEPROM.h> // used to store last column temperature

/* Arduino halogenated hydrocarbon Gas Chromatograph
*/
const char version[]="0.2";

/* by Leon Harris


GC performs the following simple functions:


1)   Displays welcome message
2)   reads default set temperature
3)   prompts user to enter set temperature
4)   warms column to required temperature then
5) waits for "inject button" to be pushed, then
6) Start timing, and report ldr value, temperature and time
7) Write out value to serial port
8) Reports conditions to lcd

parts
ldr: infinity to 30k
lm35 sensor for column temperature
push button to detect start
red led for running indicator
green led for ready indicator
lcd 2 line Hitachi HD44780 type

Pin Assignments

Analog
A0    column temperature sensor lm35
A1    LDR detector
A2    up/down push buttons; up to 27k, down to 10k pulldown 10k
     this gives ADC down = 512 counts, up =  276 counts,
     both simultaneously = 592 counts. Use +/-5% in logic

Digital
D2    Run/Stop button
D3    Ready LED (Red)
D4    Busy LED (Green)


D5    lcd DB7, pin 14 of lcd
D6    lcd DB6, pin 13 of lcd
D7    LCD DB5, pin 12 of lcd
D8    LCD DB4, pin 11 of lcd

D10   PWM drive for mosfet heater controller

D11   Enable, pin 6 of lcd
D12   Register Select, pin 4 of lcd



*/


//Globals -> I have the programming equivalent of poor hygiene with my globals:TODO clean up

const int buttonPin = 2; //use an interupt on pin 2 for start button
const int readyLED = 3;           // digital pin the heartbeat led is on
const int busyLED = 4;             // digital pin the BUSY LED is on
const int columnTempSensor = A0;  // front LM35 column sensor
const int detectorLDR = A1;  // ldr for detecting green flame
const int setButtons = A2;  // ldr for detecting green flame
const int mosfetDrive = 10;  // drive pin for heater

volatile int flashpin =1;
volatile int serviceInterrupt = 0;
double temperature;

double epromTemperature;  // holds the temperature read from the eprom
int epromaddy=0;   // just writes to address 0. Uses var in case I upgrade to a better save record like a struct (ie for multi-temp runs)  

double settemperature;  
double input,output;  // needed for pid for column heater

int photoDetector;
int runFlag = 1;
unsigned long time;
unsigned long startTime;
int serInLen = 25;  // used for Gobetwino
char serInString[25]; // used for Gobetwino

LiquidCrystal lcd(12, 11, 8, 7, 6, 5);
PID myPID(&temperature, &output, &settemperature,2.5,0.2,0, DIRECT);    // Kp,Ki,Kd values generated from autotune sketch run previously
/*


10            Mosfet pwm drive pin for heater
*/

enum stat {
 INITIALISING,
 WAITING,
 RUNNING,
 BUSY
};
stat Status;


float getTemp ()
{

 float tempTemp;
 int aRead=0;
 int span=5;
 // Temperature is so noisy, this damps it down
 for (int i=1; i< span; i++) {
   aRead = aRead + analogRead(columnTempSensor);
 }

 //tempTemp=(5.0 * aRead *100) /1024;
 tempTemp=(aRead* 0.4882812) /5;
 return (tempTemp);     //convert voltage to temperature

}

void controlTemp() {
 // runs PID controller to keep temperature constant


}
int readLDR () {
 int numReadings=10;
 int rawLight=0;
 for (int i=0; i<numReadings; i++) {
   rawLight=rawLight + analogRead(detectorLDR);
 }
 rawLight=rawLight/numReadings;
 return (rawLight);
}

void changeStatus()
{
 Status=RUNNING;
 runFlag=1;
}

// Prints data to console, incsv, where it can be picked up by mouse and fed into excel

void logData( long unsigned value1, int value2, float value3 )
{

 Serial.print(value1);
 Serial.print(",");
 Serial.print(value2);
 Serial.print(",");
 Serial.println(value3);
// readSerialString(serInString,1000);
 // There ought to be a check here for a non 0 return value indicating an error and some error handeling
}

//read a string from the serial and store it in an array
//you must supply the array variable - return if timeOut ms passes before the sting is read
void readSerialString (char *strArray,long timeOut)
{
 long startTime=millis();
 int i;

 while (!Serial.available()) {
   if (millis()-startTime >= timeOut) {
     return;
   }
 }
 while (Serial.available() && i < serInLen) {
   strArray[i] = Serial.read();
   i++;
 }
}

void flashWaitStatus ()
{
 digitalWrite(readyLED, flashpin);
 digitalWrite(busyLED,0);
 flashpin = !flashpin;
}

void flashBusyStatus ()
{
 digitalWrite(readyLED, flashpin);
   digitalWrite(busyLED, flashpin);
 flashpin = !flashpin;
}

void flashRunStatus ()
{

 digitalWrite(busyLED, flashpin);
 digitalWrite(readyLED, 0);
 flashpin = !flashpin;
 serviceInterrupt=1;
 
}

void servicePID() {
   temperature=double(getTemp());
       myPID.Compute();
     analogWrite(mosfetDrive, output);
}

void setup()
{

  // Set initial welcome message
 lcd.begin(16, 2);
 // Print Banner
 lcd.print("G Chromatograph");
 lcd.setCursor(0, 1);
 lcd.print("by L Harris V");
 lcd.print(version);
 
 delay(2000);
 lcd.clear();
 
 Status = INITIALISING;
 pinMode(readyLED, OUTPUT);
 pinMode(busyLED, OUTPUT);
 pinMode(mosfetDrive,OUTPUT);   // setup pin 9 for pwm

 attachInterrupt(0, changeStatus, CHANGE); // interupt 0 sits on pin D2



 Serial.begin(9600);
 Serial.print("Gas Chromatograph version " );
 Serial.println(version);



// set up  column temperature
epromTemperature = double(EEPROM.read(epromaddy)); // get the last stored temp
settemperature = epromTemperature;

while (Status==INITIALISING){
 int button=0;  // initial value for inc and dec buttons
   lcd.setCursor(0, 0);
   lcd.print("temp? press both");
   lcd.setCursor(0, 1);
   lcd.print ("to save)");
   lcd.setCursor(12, 1);
   lcd.print(settemperature);
   button=analogRead(setButtons);
   if (button != 0) {
     if (button > 500 && button < 530) {  // down pressed
      settemperature -=1;
     delay(200); // for debounce
     }

    else if (button > 250 && button < 300) {  // up pressed
     settemperature +=1;
    delay(200);
    }
   
   
    else if ( button > 580 && button < 600) { //both pressed - save
    Status=BUSY;
    if (settemperature != epromTemperature) {
      EEPROM.write(epromaddy,settemperature);
    }
    }
     }
 
}  
   
lcd.clear();
lcd.print("set temp =");
lcd.print(settemperature);
lcd.print(" oC");
lcd.setCursor(0, 1);
lcd.print("warming column");

 //create a pid object to control the column temperature

   myPID.SetMode(AUTOMATIC);    
     



 Status = BUSY;
  FlexiTimer2::set(500, flashBusyStatus); // 500ms period
 FlexiTimer2::start();
 
 // just lock us out until the column heats up
 while (Status == BUSY) {
   servicePID();

     if (temperature > settemperature ){
       Status = WAITING;
 FlexiTimer2::stop();
   FlexiTimer2::set(500, flashWaitStatus); // 500ms period
     FlexiTimer2::start();
     
     lcd.clear();
     lcd.print("column= ");
   
     lcd.print(temperature);
     lcd.print(" oC");
     lcd.setCursor(0, 1);
     lcd.print("Press run");
     lcd.blink();  // wants input from us, so blink
   }
     
   Serial.println(temperature);
   
   
 }
 
 Serial.println("we have reached column temp");  


}


void loop()
{
 while (Status==WAITING) {
   servicePID();
   if ( runFlag == 1) {

     Serial.print("column temperature is ");
     Serial.println(temperature);
     Serial.print("photodetector reading is ");
     Serial.println(readLDR());
     runFlag=0;
   }




   /*digitalWrite(readyLED,HIGH);
   delay(500);
   digitalWrite(readyLED, LOW);
   delay(500);
*/
 }

 if (runFlag==1) {  
   runFlag=0;
   startTime=millis();

   FlexiTimer2::stop();
   flashpin=0;
   flashWaitStatus();
   
   FlexiTimer2::set(1000, flashRunStatus); // 500ms period
 FlexiTimer2::start();
 
   lcd.noBlink(); // doesn't want anything, so stop blinking
   while (Status == RUNNING)
   {
   //main running loop goes here
   
   //wait 1 sec
   
//read ldr
//read temp

if (serviceInterrupt ==1 ){
 
 time=(millis()-startTime)/1000;
 temperature=double(getTemp());
  photoDetector=readLDR();
 
 logData(time, photoDetector, temperature);
 
 lcd.clear();
 lcd.print("temp=");
 lcd.print(temperature);
 lcd.print(" oC");
 lcd.setCursor(0, 1);
 lcd.print("detector=");
 lcd.print(photoDetector);
 lcd.print(" units");

 
 serviceInterrupt=0;
}
     servicePID();
   //log time since start, ldr, and temp to csv file via Gobetweeno
   // later write time and ldr to lcd
   //do it all again until status is stopped
   }
 }

}




robtillaart

#2
Apr 29, 2013, 03:46 pm Last Edit: Apr 29, 2013, 03:50 pm by robtillaart Reason: 1
Never seen gas chromatography before !  (There are a few nice liquid chromatograph sketches around)

Can you post some "screen shots" of results (xls files) ?
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Harristotle

Sure. A chromatogram of dichloromethane at 22 degrees (room temp) and at 55 degrees can be seen here:

http://screencast.com/t/i5y7MFSh3MG

More details of the detection system can be seen here:
http://www.screencast.com/t/iPuce3g2R


Some more details on the Chemistry and other details can be found here:
http://www.sciencemadness.org/talk/viewthread.php?tid=24110


Thanks for the interest!
H.


nitrous

I know this topic is a little old but wanted to ask if you have explored other detector methods.

In particular, SAW technology, (Surface Acoustic Wave) might be an elegant way to improve/expand the GC project.
Any thoughts on this?

Great project, by the way.

Nitrous

Harristotle

Hi Nitrous,
good comment, and yes.
I have been working on integrating into excel with PLX-DAQ - this has mostly been done, and I now have real time output.

I don't know enough about SAW technologies, but I am interested in the tin oxide types of resistive sensors, such as Figaro 822 and MCQ-3. They work by an analyte of interest sticking to the SnO (SnO2?) and changing its resistance. There is also a heater in such devices so that detected substances don't just "bind, stick and clog" the sensor. It occurs to me that as such a range of substances such as methane to methanol can be detected by different sensors, that the major difference in these sensors is the possibly just the temperature that the chip is held at by the resistive heater. My current work is to see if a very polar sensor (such as Figaro 822, or MCQ-3) can be tuned by "pwm 'ing" the heater pin so that they are good for testing for other substances. If this is so, you can have  a very cheap general sensor for many, many substances. Other detectors such as FID (thoriated welding rod, methan flame, 2x 9v batteries in series and a MPS63 or similar high gain darlington) are also possible, but my experiments with these (not coupled to a column) show  a lot of drift, and I have abandoned them for this time while I pursue the more promising Sn/O sensors.

One thing is for sure: GC, Thin Layer Chromatography and paper chromatography have made their way into Year 11 Chemistry syllabus in the Australian National Curriculum (2016). I will probably run another Arduino workshop for my local Science Teachers association on how to build a GC - so I better get off my bum and finish the project! I am waiting for boards to arrive from ebay right now!
Thanks for your interest.
H.

Go Up