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