Galvanic skin response sensor

A sensor to measure changes in skin resistance which can be hooked up to an Arduino board. I kept finding partial descriptions of how to make one of these so decided to pull together all the snippets in one place to make life a little easier.

The following circuit is a simple proven design that won’t win any awards for accuracy, but it does ‘work’, with one key limitation in that the contact points tend to corrode if used over long periods of time, though these can easily be refreshed with a quick rub of sandpaper.

The many examples I have seen specify any resistor value between 2k and 300k, though my experiments performed best with a 10k resistor. I used copper pennies as the skin contacts as these performed better than foil tape which was suggested in a number of places. I simply sanded the pennies; soldered wires to them, then stuck them to my skin with medical tape.

In order to test the circuit was working correctly I utilised some Arduino and processing code which I copied from this very helpful site: http://cwwang.com/2008/04/13/gsr-reader I’ve created a copy below, just in case it disappears.

Parts

10k resistor 0.1uf capacitor 2 X 1 pence pieces Sand paper (to refresh contacts) Medical tape or sticking plasters (to hold sensors to skin)

Arduino Code

void setup(){
  Serial.begin(9600);
}

void loop(){
  int a=analogRead(0);
  if (Serial.available() > 0) {

    byte inbyte=Serial.read();
    if(inbyte=='a'){
      Serial.print(a,BYTE);

    }
  }
}

Processing Code

import processing.serial.*;
Serial myPort;  

int hPosition = 1;     // the horizontal position on the graph
float currentReading;
float lastReading;
int count=0;
int zeroLinePos=0;

float gsrAverage,prevGsrAverage;
float baseLine=0;
long lastFlatLine=0;
color graphColor=color(255,255,255);
int baselineTimer=10000;
int gsrValue;
int gsrZeroCount=0;
float gsrRange=0;
int downhillCount=0;
int uphillCount=0;
boolean downhill;
boolean peaked=false;
float peak, valley;

void setup () {
  size(900, 450);
  // List all the available serial ports
  //println(Serial.list());

  myPort = new Serial(this, Serial.list()[0], 9600);
  currentReading=0;
  lastReading=0;
  gsrAverage=0;
  background(0);

  smooth();
}

void draw () {
  //best delay setting for gsr readings
  delay(50);
  //image(myMovie, 0, 0);

  if (gsrValue<15 &&gsrValue>-15){
    if( gsrZeroCount>10){
      currentReading=0;//flatline
      gsrAverage=0;
      baseLine=0;
      lastFlatLine=millis();
      gsrZeroCount=0;
      // println("reset");

    }
    gsrZeroCount++;
  }
  else{
    currentReading=gsrValue-baseLine;
    gsrZeroCount=0;
  }

  if(millis()-lastFlatLine>baselineTimer){
    baseLine=gsrAverage;
  }

  //graph colors
  if(gsrAverage>0 && gsrAverage<height/2.0*.25) graphColor=color(255,255,255);
  else if(gsrAverage>height/2.0*.25 && gsrAverage<height/2.0*.5) graphColor=color(255,250,100);
  else if(gsrAverage>height/2.0*.5 && gsrAverage<height/2.0*.75) graphColor=color(255,250,0);
  else if(gsrAverage>height/2.0*.75) graphColor=color(255,100,0);

  gsrRange=peak-valley;

  // at the edge of the screen, go back to the beginning:
  if (hPosition >= width) {
    hPosition = 0;

    //cover last drawing
    fill(0,200);
    noStroke();
    rect(0,0,width,height);
  }
  else {
    hPosition+=1;
  }

  gsrAverage=smooth(currentReading,.97,gsrAverage);

  //draw stuff

  //spike
  noStroke();
  if(gsrRange>200){
    fill(255);
    ellipse(10,10,20,20);
  }
  else{
    fill(0);
    ellipse(10,10,20,20);
  }

  //graph
  strokeWeight(.5);
  stroke(graphColor);
  line(hPosition-1, height/2.0-lastReading, hPosition, height/2.0-currentReading);
  stroke(255,0,100);
  line(hPosition-1,height/2.0-prevGsrAverage,hPosition,height/2.0-gsrAverage);

  //draw peaks
  int thres=7;

  noFill();
  stroke(255,0,0);
  strokeWeight(2);

  if (currentReading-thres>lastReading&& peaked==true){
    downhill=false;
    //println(downhillCount);
    uphillCount++;
    downhillCount=0;
    point(hPosition-1, height/2.0-lastReading);
    valley=lastReading;
    peaked=false;

  }
  if(currentReading+thres<lastReading && peaked==false){
    //println(uphillCount);
    downhill=true;
    uphillCount=0;
    downhillCount++;
    point(hPosition-1, height/2.0-lastReading);
    peak=lastReading;
    peaked=true;
  }

  prevGsrAverage=gsrAverage;
  lastReading=currentReading;
  //send 'a' for more bytes
  myPort.write('a');
}

void serialEvent (Serial myPort) {
  int inByte=myPort.read();
  //0-255
  gsrValue=inByte;
}

void keyPressed(){
  if (keyCode==DOWN)zeroLinePos+=3;
  if (keyCode==UP)zeroLinePos-=3;

  strokeWeight(1);
  stroke(255,0,0);
  line(0,zeroLinePos,2,zeroLinePos);
}

int smooth(float data, float filterVal, float smoothedVal){
  if (filterVal > 1){      // check to make sure param's are within range
    filterVal = .99;
  }
  else if (filterVal <= 0){
    filterVal = 0;
  }
  smoothedVal = (data * (1 - filterVal)) + (smoothedVal  *  filterVal);
  return (int)smoothedVal;
}