top of page

Mouse Tactile Operant: Arduino code

 

/////////////////MAIN SESSION VARIABLES
// set these parameters for different trial permutations

String programName = "headfix_2pHallTerm_111312a.pde" ;

//MOST IMPORTANT
int rewSolDur = 80; // time (in ms) that reward valve is open (determines reward volume): 30ms for box #1 (COM4), 9ms for box#4 (COM7)
int rewDir = 2;  // specifies top (1) vs. bottom (2) reward
int contigTrial = 4; // max number of contiguous trials of same type (fixed 091311 so that number is actual, not n-1)
int rewStimFreq = 70; // percent of rewarded trials
int catchFreq = 0;  // percent of catch trials (for Aniruddha experiment)
int cueToneOn = 0; // specify cue tone on (1) or off (0)

//LESSER
int iti = 1000;  // inter-trial interval (time after trial when no new trial can occur)
int levPressThresh = 400;//(how long lever has to be pressed to trigger trial)
int choiceLevThresh = 50; // how long lever has to be lifted to signal a choice

int stimDur = 3000;  // duration of whisker stimulus
int drinkDur = 3000; // time given for animal to drink reward water before stimulus ends

int nostimTO = 6000; // nostim lever press punishment timeout
int unrewTO = 8000;  // unrewarded stim lever press punishment timeout

int cueToneDur = 1000;
int cueFreq = 1000;  // frequency of reward cue tone with Tone library
int rewToneDur = 1000;
int rewFreq = 2000;  // frequency of reward tone with Tone library


////////////LIBRARIES
// Initialize Truerandom random number generator library
//#include <TrueRandom.h>

// Initialize TONE library info
#include <Tone.h>
Tone cueTones[1];

// Initialize STEPPER library info
#include <Stepper.h>

int motorPin1 = 13;  // mapping for new boxes, Jan. 2011
int motorPin2 = 12;   
int motorPin3 = 7;
int motorPin4 = 6;  

#define motorSteps 200  // 200steps/rot with Pololu steppers

Stepper upperStepper(motorSteps, motorPin1, motorPin2);
Stepper lowerStepper(motorSteps, motorPin3, motorPin4);

int enable1 = 11;
int enable2 = 5;

//stepper variables
int delayTime = 5;
int stepCW = -20;  // step size (50 = 1/4 rotation of arm)
int stepCCW = 20;
int stepCCWtop = 20;

int hallSensPin1 = 0;  // analog pin for TOP Hall effect sensor (for stim arm placement)
int hallSensVal1 = 500;   
int hallSensPin2 = 1;  // analog pin for BOT Hall effect sensor (for stim arm placement)
int hallSensVal2 = 500;
int hallThresh = 200;


///////////// OTHER VARIABLES (NON-LIBRARY)

long randNumber;    // random number for whisker stimulus
long levPressTime = 0;

long del = 0;    // initialize variables for making white noise stimulus (now presented during timeouts) 3/4/10                    
long del2 = 0;

// various time counter variables
long currTime = 0;
long elapTime = 0;  // variable for how long lever has been pressed (for triggering stimulus) or released (for triggering reward)
long elapTime2 = 0;
long elapTime3 = 0;  // variable for how long to play punishment white noise
long levPressDur = 0;

// defining lever pins and variables
int leverPin = 9;    // define pin for mouse lever
int leverPin2 = 8;    // define pin for exp lever
int levState  = 0;
int levState2  = 0;
int prevLevState = 0;

// defining speaker and solenoid pins
int rewPin = 4;      //  define pin for turing solenoid valve ON
//int ledPin = 13;                // LED connected to digital pin 13
int vacPin = 3;      // define pin for turning vacuum line ON
int airpuffPin = 2;      // define pin for turning vacuum line OFF
int speakerOut = 10;    // define PWM pin for speaker/tone output

// trial type alternation variables
int stimType = 1;
int prevType = 1;
int stimCount = 0;

// other variables
int totalEntries = 0;    // variable for number of total beam breaks
int trigNum = 0;    // variable for number of actual reward administrations
int trigDuration = 0;  // variable for continuous duration of a single beam break

int state = 0;    // state variables for logging new beam breaks versus continuous
int prevState = 0;

int sensorValue = 100;   // initialize sensor value to something greater than threshold

// time variables
long time = 0;    // initialize time since beginning of trial (I think Processing resets this, conveniently)
long trigTime1 = 0;  // initialize times of beam breaks
long trigTime2 = 0;  // and times of reward

long trialTime = 0;
long endTime = 0;


//SETUP////////////////////////////////
void setup()                   
{
  pinMode(rewPin, OUTPUT);  // sets digital pin for turing solenoid valve ON
  pinMode(vacPin, OUTPUT);  // sets digital pin for turing solenoid valve ON
  pinMode(airpuffPin, OUTPUT);
  pinMode(leverPin, INPUT);
  pinMode(leverPin2, INPUT);

  cueTones[0].begin(speakerOut);
  //pinMode(speakerOut, OUTPUT); // use this when not using Tone library

  upperStepper.setSpeed(80);  
  lowerStepper.setSpeed(80);

  pinMode(enable1, OUTPUT);  // ENABLE pin for motor 1 (had forgotten these before)
  pinMode(enable2, OUTPUT);  // ENABLE pin for motor 2

  Serial.begin(9600);    // initialize serial for output to Processing sketch
  randomSeed(analogRead(2));
  Serial.println("Program name:");Serial.println(programName);Serial.println("rewarded direction = (1=top, 2=bot)");
  Serial.println(rewDir);Serial.println("reward amount/duration =");Serial.println(rewSolDur);
  Serial.println("choiceLevThresh =");Serial.println(choiceLevThresh);Serial.println("stimDur =");Serial.println(stimDur);
  Serial.println("BEGIN DATA");
}

//LOOP/////////////////////////////////
void loop()          {           // run over and over again

  // check for lever presses during this round when no stimulus is present
  levState = digitalRead(leverPin);
  levState2 = digitalRead(leverPin2);

  /////////////// see how long lever has been pressed
  // see if animal is pressing lever to start trial
  if (levState == HIGH) { //   && prevLevState == 0) {    // if no stim lever press
    if (prevLevState == 0) {
      levPressTime = millis();
      Serial.println("lever press start");
      Serial.println(levPressTime);
      prevLevState = 1;
    }
    else {  // ELSE if lever was already pressed
      time = millis();
      levPressDur = time - levPressTime;  // record how long ago
    }
  }
  else {  // ELSE if lever not pressed
    if (levState == LOW) {
      prevLevState = 0;    // and if no lever press, goes to FALSE
      levPressDur = 0;
    }
  }

  ////////////////////START STIMULUS TRIAL
  if (levPressDur > levPressThresh)  {  // start trial if animal has held lever down long enough
    digitalWrite(airpuffPin, HIGH);  // included in 2p room scripts to trigger linescan frames in ScanImage
    delay(100);
    digitalWrite(airpuffPin, LOW);

    // record time of stimulus trigger
    trigTime2 = millis();

    randNumber = random(0,100);
    ////// for pseudorandom stimulus presentation...

    //////// CALCULATE STIM TYPE
    // First, see what type of trial the randomizer pulls out
    if ((randNumber <= (rewStimFreq+catchFreq)) && (randNumber > catchFreq)){
      stimType = 1;  // for Rewarded trials
    }
    else if (randNumber < catchFreq) {
      stimType = 3;
    }
    else {
      stimType = 2; // for Unrewarded trials
    }

    // Then, compare this trial type with the previous
    if (stimType == 1) {
      if (prevType == 1) { // if previous type is the same as current
        stimCount = stimCount + 1;  // add one to the trial type counter
      }
      else {
        stimCount = 1;  // or if it's different, restart the counter
      }
    }
    else if (stimType == 2) {  // same with the other trial type
      if (prevType == 2) {
        stimCount = stimCount + 1;
      }
      else {stimCount = 1;}
    }

    // And finally, change trial type if there have been too many in a row
    if (stimCount > contigTrial) {
      if (stimType == 1) {
        stimType = 2;stimCount = 1;
      }
      else {
        stimType = 1;stimCount = 1;
      }
    }

    // GIVE REWARDED STIMULUS
    if (stimType == 1) {
      Serial.println("stim trigger (bottom/rewarded)");
      Serial.println(trigTime2);

      // MOVE TOP/BOTTOM STIMULUS, WHICHEVER ONE IS REWARDED THIS SESSION
      if (rewDir == 1) {
        digitalWrite(enable1, HIGH);
        delay(100);
        hallSensVal1 = analogRead(hallSensPin1);
        while (hallSensVal1 >hallThresh) {
          upperStepper.step(-1);
          delay(1);  // slight delay after movement to ensure proper step before next
          hallSensVal1 = analogRead(hallSensPin1);
        }
      }
      else {
        digitalWrite(enable2, HIGH);
        delay(100);
        hallSensVal2 = analogRead(hallSensPin2);
        while (hallSensVal2 >hallThresh) {
          lowerStepper.step(1);
          delay(1);  // slight delay after movement to ensure proper step before next
          hallSensVal2 = analogRead(hallSensPin2);
        }
      }

      // PLAY CUE TONE (if turned on)
      if (cueToneOn == 1) {
        cueTones[0].play(cueFreq, cueToneDur);
      }

      // LEVER PRESS FOR REWARD: check for a little while to see if the mouse presses the lever
      elapTime = 0;prevLevState = 0;levPressDur = 0;

      while (elapTime <= stimDur) {   // for duration of the stimulus, check for lever presses
        time = millis();
        elapTime = time -  trigTime2;

        // read lever state
        levState = digitalRead(leverPin);
        levState2 = digitalRead(leverPin2);

        // see how long animal lifts his hand off the lever during stim presentation
        if (levState == LOW || levState2 == HIGH) {  //HIGH && prevLevState == 0) {  //|| levState2 == HIGH) {    // and if the mouse is still in the beam path, activate reward
          if (prevLevState == 0) {
            levPressTime = millis();
            prevLevState = 1;
          }
          else {
            levPressDur = millis() - levPressTime;
          }
        }
        else {
          prevLevState = 0;
          levPressDur = 0;
        }

        // trigger REWARD if animal lifts paw during rewarded stim. (for long enough)
        if (levPressDur >= choiceLevThresh) {
          Serial.println("REWARD!!!");
          Serial.println(levPressTime);

          // PLAY TONE
          cueTones[0].play(rewFreq, rewToneDur);  
          // REWARD SEQUENCE go through reward/vacuum solenoid sequence
          digitalWrite(rewPin, HIGH);    // open solenoid valve for a short time
          delay(rewSolDur);    digitalWrite(rewPin, LOW); delay(10);
          digitalWrite(vacPin, HIGH);  delay(50); digitalWrite(vacPin, LOW); delay(drinkDur);
          elapTime = stimDur + 1;  // break out of the reward stimulus loop after receiving reward
        }  // end IF for levPressDur > 100 (REWARD sequence)
      } // end WHILE elapTime <= stimDur (check for lever lifts)

      if (rewDir == 1) {
        upperStepper.step(stepCCW); // 042110: now using Stepper library for stim control
        delay(200);  digitalWrite(enable1, LOW);
      }
      else {
        lowerStepper.step(stepCW); // 042110: now using Stepper library for stim control
        delay(200);  digitalWrite(enable2, LOW);
      }
      prevLevState = 0;   // reset state variable to allow for consecutive triggers
      prevType = 1; // for stimCount to promote alternation
    }    // end IF conditional for rand numb/reward (or Bot/REW stim vs. Top 092809)

    // GIVE INCORRECT/UNREWARDED STIMULUS
    else if (stimType == 2) {    // ELSE if odd rand numb give Top stim
      Serial.println("stim trigger (top)");
      Serial.println(trigTime2);

      if (rewDir == 1) {
        digitalWrite(enable2, HIGH);
        delay(100);
        hallSensVal2 = analogRead(hallSensPin2);
        while (hallSensVal2 >hallThresh) {
          lowerStepper.step(1);
          delay(1);  // slight delay after movement to ensure proper step before next
          hallSensVal2 = analogRead(hallSensPin2);
        }
      }
      else {
        digitalWrite(enable1, HIGH); delay(100);
        hallSensVal1 = analogRead(hallSensPin1);
        while (hallSensVal1 >hallThresh) {
          upperStepper.step(-1);
          delay(1);  // slight delay after movement to ensure proper step before next
          hallSensVal1 = analogRead(hallSensPin1);
        }
      }

      // record lever presses during non-rewarded stimuli
      elapTime = 0;
      prevLevState = 0;
      levPressDur = 0;

      while (elapTime <= stimDur) {
        time = millis();
        elapTime = time - trigTime2;
        levState = digitalRead(leverPin);
        levState2 = digitalRead(leverPin2);

        if (levState == LOW || levState2 == HIGH) { 
          if (prevLevState == 0) {
            levPressTime = millis();
            prevLevState = 1;
          }
          else {
            levPressDur = millis() - levPressTime;
          }
        }
        else {
          prevLevState = 0;
          levPressDur = 0;
        }

        if (levPressDur >= choiceLevThresh) {
          Serial.println("unrewarded lever press");
          Serial.println(levPressTime);
          delay(10);  // changed this from 2000 because air puff goes on falling phase
          elapTime3 = 0;
          while (elapTime3 < unrewTO) {
            del = random(1,7);
            cueTones[0].play(del*1000); //notes[del]);
            currTime = millis();
            elapTime3 = currTime - levPressTime;
          }
          cueTones[0].stop();
          elapTime = stimDur + 1;
        }
      }

      //// move the top/unrewarded stimulus back
      if (rewDir == 1) {
        lowerStepper.step(stepCW); delay(200);  digitalWrite(enable2, LOW);
      }
      else {
        upperStepper.step(stepCCW);
        hallSensVal1 = analogRead(hallSensPin1);
        delay(200);digitalWrite(enable1, LOW);
      }
      prevLevState = 0;
      prevType = 2; // for stimCount to promote alternation
    }    // end ELSE cond for unrewarded/Top stim.
}  // end loop() section

bottom of page