/*
Feeding experimentation device 3 (FED3)
This script demonstrates how to write a menu system so you can access multiple programs from the FED3 startup screen. You will scroll through programs by assigning them to "modes".
In this example, we run four modes:
// FEDmodes:
// 0 Free feeding
// 1 FR1_both
// 2 LR
// 3 Stop
// 4 w/ delay
December, 2020
This project is released under the terms of the Creative Commons - Attribution - ShareAlike 3.0 license:
Copyright (c) 2020 Lex Kravitz
*/
#include <FED3.h> //Include the FED3 library
String sketch = "Menu"; //Unique identifier text for each sketch
FED3 fed3 (sketch); //Start the FED3 object
//variables for PR tasks
//(you can set and use any variables you want for your custom tasks)
int poke_num = 0; // this variable is the number of pokes since last pellet
int pokes_required = 1; // increase the number of pokes required each time a pellet is received using an exponential equation
void setup() {
fed3.FED3Menu = true; //Activate the menu function at startup
fed3.begin(); //Setup the FED3 hardware
}
void loop() {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Mode 1: Free feeding
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (fed3.FEDmode == 0) {
fed3.sessiontype = "Free_feed"; //The text in "sessiontype" will appear on the screen and in the logfile
fed3.DisplayPokes = false; //Turn off poke indicators for free feeding mode
fed3.UpdateDisplay(); //Update display for free feeding session to remove poke displayt (they are on by default)
fed3.Feed();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Modes 2: Fixed Ratio 1 both sides
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (fed3.FEDmode == 1) {
fed3.sessiontype = "FR1_both"; //The text in "sessiontype" will appear on the screen and in the logfile
if (fed3.Left) { //Log left poke
fed3.logLeftPoke();
fed3.pixelsOn(0, 0, 10, 0);
if (fed3.LeftCount % fed3.FR == 0) { //if fixed ratio is met
fed3.Feed(); //deliver pellet
}
}
if (fed3.Right) { //If right poke is triggered
fed3.logRightPoke(); //Log right poke
fed3.pixelsOn(0, 0, 10, 0);
fed3.Feed(); //Deliver pellet
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Mode 3: LeftRight Sequence
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (fed3.FEDmode == 2) {
fed3.sessiontype = "LeftRight_sequence"; //The text in "sessiontype" will appear on the screen and in the logfile
FED3 fed3 (sketch); //Start the FED3 object
bool leftTriggered = false;
unsigned long poketime = 0; //time of poke
byte resetInterval = 3; //How long do they have to poke right (in seconds)?
// if (fed3.PelletCount == 60) resetInterval = 30; // after 40 pellets make left-right reset interval = 9
// if (fed3.PelletCount == 120) resetInterval = 10; // after 40 pellets make left-right reset interval = 8
// if (fed3.PelletCount == 160) resetInterval = 8; // after 80 pellets make left-right reset interval = 7
// if (fed3.PelletCount == 200) resetInterval = 6; // after 120 pellets make left-right reset interval = 6
// if (fed3.PelletCount == 240) resetInterval = 5; // after 120 pellets make left-right reset interval = 5
// if (fed3.PelletCount == 280) resetInterval = 4; // after 120 pellets make left-right reset interval = 4
// if (fed3.PelletCount == 320) resetInterval = 3; // after 120 pellets make left-right reset interval = 3
// //if (fed3.PelletCount == 360) resetInterval = 2; // after 120 pellets make left-right reset interval = 2
//if (fed3.PelletCount == 400) resetInterval = 1; // after 120 pellets make left-right reset interval = 1
//if left poke is triggered
if (fed3.Left) { //If left poke is triggered
Serial.println ("Left");
fed3.BlockPelletCount = millis();
fed3.logLeftPoke(); //Log left poke
leftTriggered = true;
poketime = fed3.unixtime; //update the current time of poke
}
if (fed3.Right and leftTriggered == true) { //If right poke is triggered
Serial.println ("Right_after_left");
fed3.BlockPelletCount = millis();
fed3.logRightPoke(); //Log right poke
fed3.pixelsOn(0, 0, 10, 0);
fed3.Feed(); //Deliver pellet
leftTriggered = false;
}
if (fed3.Right and leftTriggered == false) { //If right poke is triggered
Serial.println ("Right_no_left");
fed3.BlockPelletCount = millis();
fed3.logRightPoke(); //Log right poke
}
if ((fed3.unixtime - poketime >= resetInterval) and leftTriggered == true) { //if the reset interval has elapsed since last poke
Serial.println ("No_poke, left poke reset");
leftTriggered = false;
fed3.BlockPelletCount = millis();
fed3.Event = "NoPoke_Regular_(incorrect)";
fed3.logdata();
fed3.Left = false;
fed3.Right = false;
fed3.Timeout(30);
}
}
// void leftReset() {
// if ((fed3.unixtime - poketime >= resetInterval) and leftTriggered == true) { //if the reset interval has elapsed since last poke
// Serial.print (resetInterval);
// Serial.println ("s, LeftTriggered RESET");
// leftTriggered = false;
// fed3.BlockPelletCount = millis();
// fed3.Event = "RESET";
// fed3.logdata();
// }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Mode 4: StopSignal_nodelay
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (fed3.FEDmode == 3) {
fed3.sessiontype = "SST_ND"; //The text in "sessiontype" will appear on the screen and in the logfile
FED3 fed3 (sketch); //Start the FED3 object
//Poke variables
bool leftTriggered = false;
unsigned long poketime = 0; //time of left poke
byte resetInterval = 3; //How long do they have to poke right (in seconds)?
char buffer[32]; // for serial print statements
//stop sig variables
bool stopTrial = false;
int stopSigTime = 0;
byte stopProb = 30;
//block trial setup
int block_index; //used to index in the block
int block_counter = 5; //used to assign IDs to the block
int block[5]; // array that will hold the block
int block_trial_number = 5;
int trial_type = random(1, 5); //randomize go or noGO trail. 1&2&3 = GO, 4&5 = NOGO
//Call fed.run at least once per loop
if(block_trial_number == 5) { //if we have cycled through the block
for (block_index = 0; block_index < 5; block_index++) { // make array filled with 0s
block[block_index] = 0;
}
block_counter = 5; // reset counter
while (block_counter > 0) { //fill array with 1-5 randomly without repeats
block_index = random(0,5); // get a random number 0...5
if (block[block_index] == 0) { // if this slot is free
block[block_index] = block_counter; // record this number
block_counter--; // count down y
}
}
Serial.print("\n FILLED: \n");
for (block_index = 0; block_index < 5; block_index++) {
sprintf (buffer, "%u ", block[block_index]);
Serial.print (buffer);
}
block_trial_number = 0;
}
trial_type = block[block_trial_number];
/////////////////////////////////
//Right poke without left
/////////////////////////////////
if (fed3.Right and leftTriggered == false) { //If right poke is triggered
fed3.RightCount ++;
//fed3.Click();
Serial.println ("Right_no_left");
fed3.Event = "Right_no_left";
fed3.logdata();
fed3.BlockPelletCount = millis();
fed3.Right = false;
}
/////////////////////////////////
//if left poke is triggered
/////////////////////////////////
if (fed3.Left and leftTriggered == false) { //If left poke is triggered
fed3.LeftCount ++;
Serial.println ("Left");
//fed3.Click();
fed3.BlockPelletCount = millis();
leftTriggered = true;
fed3.run();
poketime = fed3.unixtime; //update the current time of poke
fed3.Event = "LeftPokeInitiating";
fed3.logdata();//update the current time of poke
// Is it a stop trial?
if(trial_type == 1 | trial_type == 2 ) { // if this is a GO trail (same code as GO task)
Serial.println ("Stop signal!");
stopSigTime = fed3.unixtime;
//delay (500);
fed3.Tone (2500, 1000);
stopTrial = true;
fed3.Event = ">Left_Stop_trial";
}
//if it is NOT a stop trial
if(trial_type == 3 | trial_type == 4 | trial_type == 5) { // if this is a NOGO trial
Serial.println ("Regular_trial");
stopTrial = false;
fed3.Event = ">Left_Regular_trial";
}
fed3.logdata();
fed3.Left = false;
}
/////////////////////////////////
//no stop signal - right poke delivers pellet
/////////////////////////////////
if (fed3.Right and leftTriggered == true and stopTrial == false) { //If right poke is triggered
fed3.RightCount ++;
//fed3.Click();
fed3.BlockPelletCount = millis();
Serial.println ("Right_Regular_(correct)");
fed3.Event = "Right_Regular_(correct)";
fed3.logdata();
fed3.pixelsOn(0, 0, 10, 0);
fed3.Feed();
//Deliver pellet
block_trial_number++;
leftTriggered = false;
fed3.Right = false;
}
/////////////////////////////////
//stop signal trial - right poke enters timeout
/////////////////////////////////
if (fed3.Right and leftTriggered == true and stopTrial == true) { //If right poke is triggered
fed3.RightCount ++;
//fed3.Click();
Serial.println ("Right_STOP_SIGNAL");
fed3.BlockPelletCount = millis();
fed3.Event = "Right_STOP_(incorrect)";
fed3.logdata();
fed3.rightPokePixel(0,0,0,50);
fed3.Timeout(1);
fed3.pixelsOff();
fed3.Timeout(29);
block_trial_number++;
leftTriggered = false;
fed3.Right = false;
}
/////////////////////////////////
//stop signal trial - withholding right poke delivers pellet!
/////////////////////////////////
if ((fed3.unixtime - poketime >= resetInterval) and leftTriggered == true and stopTrial == true) { //If right poke is triggered
Serial.println ("Withheld poking, get pellet!");
fed3.Event = "NoPoke_STOP_(correct)";
fed3.BlockPelletCount = millis();
fed3.logdata();
fed3.pixelsOn(0, 0, 10, 0);
fed3.Feed(); //Deliver pellet
block_trial_number++;
//fed3.BNC(500, 1);
leftTriggered = false;
//trial_type = random(1, 5);
}
/////////////////////////////////
// If it is a regular trial but he does NOT poke on right, reset and give timeout
/////////////////////////////////
if ((fed3.unixtime - poketime >= resetInterval) and leftTriggered == true and stopTrial == false) { //if the reset interval has elapsed since last poke
Serial.println ("No_poke, left poke reset");
leftTriggered = false;
fed3.BlockPelletCount = millis();
fed3.Event = "NoPoke_Regular_(incorrect)";
fed3.logdata();
fed3.Left = false;
fed3.Right = false;
fed3.Timeout(30);
block_trial_number++;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Mode 5: StopSignal_delay
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (fed3.FEDmode == 4) {
fed3.sessiontype = "SST_D"; //The text in "sessiontype" will appear on the screen and in the logfile
FED3 fed3 (sketch); //Start the FED3 object
//Poke variables
bool leftTriggered = false;
unsigned long poketime = 0; //time of left poke
byte resetInterval = 3; //How long do they have to poke right (in seconds)?
char buffer[32]; // for serial print statements
//stop sig variables
bool stopTrial = false;
unsigned long stopSigTime = 0;
byte stopProb = 30;
//block trial setup
int block_index; //used to index in the block
int block_counter = 5; //used to assign IDs to the block
int block[5]; // array that will hold the block
int block_trial_number = 5;
int trial_type = random(1, 5); //randomize go or noGO trail. 1&2&3 = GO, 4&5 = NOGO
unsigned long pauses[] = {0, 300, 600, 900, 1200}; //global variable
unsigned long pause;
//Call fed.run at least once per loop
if(block_trial_number == 5) { //if we have cycled through the block
for (block_index = 0; block_index < 5; block_index++) { // make array filled with 0s
block[block_index] = 0;
}
block_counter = 5; // reset counter
while (block_counter > 0) { //fill array with 1-5 randomly without repeats
block_index = random(0,5); // get a random number 0...5
if (block[block_index] == 0) { // if this slot is free
block[block_index] = block_counter; // record this number
block_counter--; // count down y
}
}
Serial.print("\n FILLED: \n");
for (block_index = 0; block_index < 5; block_index++) {
sprintf (buffer, "%u ", block[block_index]);
Serial.print (buffer);
}
block_trial_number = 0;
}
trial_type = block[block_trial_number];
/////////////////////////////////
//Right poke without left
/////////////////////////////////
if (fed3.Right and leftTriggered == false) { //If right poke is triggered
fed3.RightCount ++;
//fed3.Click();
Serial.println ("Right_no_left");
fed3.Event = "Right_no_left";
fed3.logdata();
fed3.BlockPelletCount = millis();
fed3.Right = false;
}
/////////////////////////////////
//if left poke is triggered
/////////////////////////////////
if (fed3.Left and leftTriggered == false) { //If left poke is triggered
fed3.LeftCount ++;
Serial.println ("Left");
//fed3.Click();
fed3.BlockPelletCount = millis();
leftTriggered = true;
fed3.run();
poketime = fed3.unixtime;
fed3.Event = "LeftPokeInitiating";
fed3.logdata();//update the current time of poke
// Is it a stop trial?
if(trial_type == 1 | trial_type == 2 ) { // if this is a GO trail (same code as GO task)
stopTrial = true;
Serial.println ("Stop signal!");
stopSigTime =millis();
pause = pauses[random(0, 5)] ;
Serial.println ("stop signaltime chosen");
sprintf (buffer, "%u ", pause);
Serial.print (buffer);
while ((millis() - stopSigTime) < pause ) { // whil
Serial.println ("Delaying stop signal!");
sprintf (buffer, "%u ",pause);
Serial.print (buffer);
if(fed3.Right) {
stopTrial = false;
block_trial_number--;
fed3.Event = String(pause) + "_" + "Poke_Before_StopSignal";
fed3.logdata();
break;
}
}
if(stopTrial == true){
fed3.Tone (2500, 1000);
fed3.Event = String(pause) + "_" + "Left_Stop_trial";
fed3.logdata();
}
}
//if it is NOT a stop trial
if(trial_type == 3 | trial_type == 4 | trial_type == 5) { // if this is a NOGO trial
Serial.println ("Regular_trial");
stopTrial = false;
fed3.Event = "Left_Regular_trial";
fed3.logdata();
}
fed3.Left = false;
}
/////////////////////////////////
//no stop signal - right poke delivers pellet
/////////////////////////////////
if (fed3.Right and leftTriggered == true and stopTrial == false) { //If right poke is triggered
fed3.RightCount ++;
//fed3.Click();
fed3.BlockPelletCount = millis();
Serial.println ("Right_Regular_(correct)");
fed3.Event = "Right_Regular_(correct)";
fed3.logdata();
fed3.pixelsOn(0, 0, 10, 0);
fed3.Feed();
//Deliver pellet
block_trial_number++;
leftTriggered = false;
fed3.Right = false;
}
/////////////////////////////////
//stop signal trial - right poke enters timeout
/////////////////////////////////
if (fed3.Right and leftTriggered == true and stopTrial == true) { //If right poke is triggered
fed3.RightCount ++;
//fed3.Click();
Serial.println ("Right_STOP_SIGNAL");
fed3.BlockPelletCount = millis();
fed3.Event = "Right_STOP_(incorrect)";
fed3.logdata();
fed3.rightPokePixel(0,0,0,50);
fed3.Timeout(1);
fed3.pixelsOff();
fed3.Timeout(29);
block_trial_number++;
leftTriggered = false;
fed3.Right = false;
}
/////////////////////////////////
//stop signal trial - withholding right poke delivers pellet!
/////////////////////////////////
if ((fed3.unixtime - poketime >= resetInterval) and leftTriggered == true and stopTrial == true) { //If right poke is triggered
Serial.println ("Withheld poking, get pellet!");
fed3.Event = "NoPoke_STOP_(correct)";
fed3.BlockPelletCount = millis();
fed3.logdata();
fed3.pixelsOn(0, 0, 10, 0);
fed3.Feed(); //Deliver pellet
block_trial_number++;
//fed3.BNC(500, 1);
leftTriggered = false;
//trial_type = random(1, 5);
}
/////////////////////////////////
// If it is a regular trial but he does NOT poke on right, reset and give timeout
/////////////////////////////////
if ((fed3.unixtime - poketime >= resetInterval) and leftTriggered == true and stopTrial == false) { //if the reset interval has elapsed since last poke
Serial.println ("No_poke, left poke reset");
leftTriggered = false;
fed3.BlockPelletCount = millis();
fed3.Event = "NoPoke_Regular_(incorrect)";
fed3.logdata();
fed3.Left = false;
fed3.Right = false;
fed3.Timeout(30);
block_trial_number++;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// If a mode greater than 5 is selected
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (fed3.FEDmode > 5) {
fed3.DisplayNoProgram();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Call fed.run at least once per loop
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
fed3.run();
}