diff --git a/examples/Bandit_Task/Bandit_Task.ino b/examples/Bandit_Task/Bandit_Task.ino index 001a0c4..0a2a296 100644 --- a/examples/Bandit_Task/Bandit_Task.ino +++ b/examples/Bandit_Task/Bandit_Task.ino @@ -20,21 +20,22 @@ String ver = "Force"; //unique identifier text Force force(ver); //start FORCE object -int prob_left = 70; // Probability of pellet on the high-reward poke -int prob_right = 30; // Probability of pellet on the low-reward poke +int prob_left = 100; // Probability of pellet on the high-reward poke +int prob_right = 100; // Probability of pellet on the low-reward poke int trialsToSwitch = 20; // # of trials before probabilities on the pokes switch int trial_counter = 0; // Tracks how many pellets have been obtained in this set of probabilities int highp_counter = 0; // Tracks how many pokes in a row in high probability poke int trialTimeout = 5; //timeout duration after each poke, set to 0 to remove the timeout int probs[5] = {10, 30, 50, 70, 90}; -unsigned long trial_start = 0; -unsigned long trial_length = 5000; -bool trial_available = false; bool press = false; void setup() { - force.begin(); //setup FORCE + force.log_lite = true; + force.begin(force.log_lite); //setup FORCE force.trial_window = 60000; + force.ver = prob_left; + force.FRC = prob_right; + force.trials_per_block = trialsToSwitch; } void loop() { @@ -43,12 +44,14 @@ void loop() { // This is the non-stationary part of the task /// // meaning that probabilities change every trialsToSwitch trials /// /////////////////////////////////////////////////////////////////////// - force.run(false); + force.run(); if(trial_counter == trialsToSwitch) { highp_counter = 0; trial_counter = 0; prob_left = probs[random(0,5)]; prob_right = 100-prob_left; + force.ver = prob_left; + force.FRC = prob_right; } else if (highp_counter == 8) { @@ -56,6 +59,11 @@ void loop() { trial_counter = 0; prob_left = probs[random(0,5)]; prob_right = 100-prob_left; + + force.ver = prob_left; + force.FRC = prob_right; + //force.dispense_delay = highp_counter; + force.dispense_amount = trial_counter; } ///////////////////////////////////////////////////////////////// @@ -65,18 +73,16 @@ void loop() { //////////////////////////////////////////////////////////////// force.readPoke(); if (force.poke) { - trial_start = millis(); + force.run(); + force.event = "CENTER"; + force.logdata_lite(); + force.trial_start = millis(); force.Tone(); - trial_available = true; + force.trial_available = true; } - while (((millis()-trial_start) < force.trial_window) && trial_available == true) { - force.ratioLeft = prob_left; - force.ratioRight = prob_right; - force.trials_per_block = trial_counter; - force.FRC = highp_counter; - force.library_version = trialsToSwitch; - force.run(true); + while (((millis()-force.trial_start) < force.trial_window) && force.trial_available) { + force.run(); ///////////////////////////////////////////////////////////// //// If mouse presses the left lever /// @@ -88,17 +94,21 @@ void loop() { else { highp_counter = 0; } - delay(500); + //force.dispense_delay = highp_counter; + force.loglite_Left(); + if (random(100) < prob_left) { - force.Tone(); + force.event = "RewardLeft"; force.DispenseLeft(); trial_counter ++; + force.dispense_amount = trial_counter; + force.logdata_lite(); } else { force.Tone(300,600); } press = true; - trial_available = false; + force.trial_available = false; } ////////////////////////////////////////////////////////////// @@ -111,34 +121,40 @@ void loop() { else { highp_counter = 0; } - delay(500); + //force.dispense_delay = highp_counter; + force.loglite_Right(); + if (random(100) < prob_right) { - force.Tone(); + force.event = "RewardRight"; force.DispenseRight(); trial_counter ++; + force.dispense_amount = trial_counter; + force.logdata_lite(); + } else { force.Tone(300,600); } press = true; - trial_available = false; + force.trial_available = false; } } ////////////////////////////////////////////////////////////// //// If there was no press during the time window //// ///////////////////////////////////////////////////////////// - if (trial_available && press == false) { + if (force.trial_available && press == false) { force.Tone(300,600); + force.trial_available = false; } ////////////////////////////////////////////////////////////// //// Finish trial and start inter-trial timeout //// ///////////////////////////////////////////////////////////// - if (trial_available) { + if (press) { press = false; - trial_available = false; + force.trial_available = false; force.Timeout(trialTimeout); - force.Tone(); } + } diff --git a/src/Force.cpp b/src/Force.cpp index b12420b..4cddb30 100644 --- a/src/Force.cpp +++ b/src/Force.cpp @@ -8,15 +8,20 @@ FatFileSystem fatfs; File myFile; ///////////////////////////////////////////////////////////////////////// -// Initialize FORCE! +//////////////// Initialize FORCE! ////////// ///////////////////////////////////////////////////////////////////////// Force::Force(String ver) { library_version = ver; } -///////////////////////////////////////////////////////////////////////// -// RTC Functions -///////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +/////////////// Setup functions //////////// +////////////////////////////////////////////////////////////////////////// + +//////////////////////////////// +////// RTC Function /////// +//////////////////////////////// RTC_PCF8523 rtc; void dateTime(uint16_t* date, uint16_t* time) { @@ -28,9 +33,9 @@ void dateTime(uint16_t* date, uint16_t* time) { *time = FAT_TIME(now.hour(), now.minute(), now.second()); } -///////////////////////////////////////////////////////////////////////// -// Load from settings.txt on SPI flash -///////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////// +////Load from settings.txt on SPI flash ////// +////////////////////////////////////////////////// void Force::load_settings() { Serial.println("*****************************"); Serial.println("Loading device Settings:"); @@ -72,9 +77,9 @@ void Force::load_settings() { } -///////////////////////////////////////////////////////////////////////// -// Save to settings.txt on SPI flash -///////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////// +////Save settings.txt on SPI flash /////////// +////////////////////////////////////////////////// void Force::save_settings() { for (int i = 0; i < 18; i++) Serial.print(settings[i]); Serial.println("*****************************"); @@ -135,9 +140,9 @@ void Force::save_settings() { } } -///////////////////////////////////////////////////////////////////////// -// reset settings -///////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////// +//////// Reset settings /////////// +////////////////////////////////////////////////// void Force::reset_settings() { Serial.println("*****************************"); Serial.println("Reseting device settings:"); @@ -162,358 +167,830 @@ void Force::reset_settings() { } ///////////////////////////////////////////////////////////////////////// -// print settings -///////////////////////////////////////////////////////////////////////// -void Force::print_settings() { - Serial.println("*****************************"); - Serial.println("Printing local device settings:"); - Serial.print("Device#: "); Serial.println(FRC); - Serial.print("Trial window: "); Serial.println(trial_window); - Serial.print("Left Active: "); Serial.println(LeftActive); - Serial.print("Right Active: "); Serial.println(RightActive); - Serial.print("Req_Left: "); Serial.println(reqLeft); - Serial.print("Req_Right: "); Serial.println(reqRight); - Serial.print("hold_time_Left: "); Serial.println(hold_timeLeft); - Serial.print("hold_time_Right: "); Serial.println(hold_timeRight); - Serial.print("ratio_Left: "); Serial.println(ratioLeft); - Serial.print("ratio_Right: "); Serial.println(ratioRight);\ - Serial.print("dispense_delay: "); Serial.println(dispense_delay); - Serial.print("dispense_amount: "); Serial.println(dispense_amount); - Serial.print("timeout_length: "); Serial.println(timeout_length); - Serial.print("calibration_factor_Left: "); Serial.println(calibration_factor_Left); - Serial.print("calibration_factor_Right: "); Serial.println(calibration_factor_Right); - if (PR==0) Serial.println("Fixed Ratio: Inactive"); - if (PR==1) Serial.println("Prog Ratio: Active"); - Serial.print ("Trials per block: "); Serial.println(trials_per_block); - Serial.print ("Max force: "); Serial.println(max_force); - Serial.println(" "); - - Serial.print("Reading from SPI flash..."); - myFile = fatfs.open("settings.txt"); - if (myFile) { - Serial.println("reading contents of settings.txt..."); - // read from the file until there's nothing else in it: - while (myFile.available()) { - Serial.write(myFile.read()); - } - // close the file: - myFile.close(); - Serial.println("done."); - } -} - - -///////////////////////////////////////////////////////////////////////// -// Begin +/////////// FORCE Menu Functions ////////// ///////////////////////////////////////////////////////////////////////// -void Force::begin() { - Serial.begin(9600); - - if (!ss.begin()) { - Serial.println("seesaw couldn't be found!"); - while (1); - } - // Initialize pins - pinMode(A0, OUTPUT); - pinMode(BEEPER, OUTPUT); - pinMode(POKE, INPUT); - - pinMode(LICK1, INPUT_PULLDOWN); - pinMode(PUMP1, OUTPUT); - digitalWrite(PUMP1, LOW); - - pinMode(LICK2, INPUT_PULLDOWN); - pinMode(PUMP2, OUTPUT) ; - digitalWrite(PUMP2, LOW); +////////////////////////////////////////////////// +//////// Menu Setup /////////// +////////////////////////////////////////////////// - // Initialize display - ss.tftReset(); // Reset the display - ss.setBacklight(1); // Adjust backlight (this doesn't really seem to work unless you do -1 to turn it off) - tft.initR(INITR_MINI160x80); // Initialize a ST7735S chip, mini display - tft.setRotation(1); +void Force::start_up_menu() { + calibrate_active = false; + float start_timer = millis(); + int option = 0; tft.fillScreen(ST77XX_BLACK); + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + while (start_up == true) { + int page = 1; + ////////////////////////////////////////////// + ////////////////////// PAGE 1 //////////////// + ////////////////////////////////////////////// + while (page == 1) { + if ((millis() - start_timer) > 10000) start_up = false; //after 10 seconds of start up menu, start session + uint32_t buttons = ss.readButtons(); + tft.setCursor(40, 5); + tft.setTextColor(ST7735_MAGENTA); + tft.println("Menu"); + tft.setCursor(0, 20); + tft.setTextColor(ST7735_CYAN); - // Initialize RTC - if (!rtc.initialized() || rtc.lostPower()) { - rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); - } - - rtc.start(); - - // Initialize neopixel - pixels.begin(); + //option 0 + tft.print("device #: "); + tft.println(FRC); + if (option == 0) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + FRC ++; + delay (250); + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - //start SPI flash - Serial.print("flash begin..."); - flash.begin(); - // Open file system on the flash - if ( !fatfs.begin(&flash) ) { - Serial.println("Error: filesystem is not existed. Please try SdFat_format example to make one."); - while (1) yield(); - } - Serial.println("done."); + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + FRC --; + delay (250); + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } - load_settings(); + //option 1 + tft.print("Trial window: "); + tft.println(trial_window); + if (option == 1) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + trial_window ++; + delay (250); + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - // Initialize load cells - analogWriteResolution(12); // turn on 12 bit resolution - - scaleLeft.begin(DOUT1, CLK1); - scaleLeft.tare(); - scaleLeft.set_scale(calibration_factor_Left); - - scaleRight.begin(DOUT2, CLK2); - scaleRight.tare(); - scaleRight.set_scale(calibration_factor_Right); + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + trial_window --; + if (trial_window < 0) trial_window = 0; + delay (250); + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } - //start up menu - start_up_menu(); - tft.fillScreen(ST77XX_BLACK); + //option 2 + tft.print("Left lever: "); + if (LeftActive == 1) { + tft.println("Active"); + } else if (LeftActive == 0) { + tft.println("Inactive"); + } else { + tft.println(LeftActive); + } + if (option == 2) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + delay (250); + LeftActive = 1; + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - // Initialize SD - SdFile::dateTimeCallback(dateTime); - CreateDataFile(); - writeHeader(); + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + delay (250); + LeftActive = 0; + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } + + //option 3 + tft.print("Right lever: "); + if (RightActive == 1) tft.println("Active"); + if (RightActive == 0) tft.println("Inactive"); + if (option == 3) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + delay (250); + RightActive = 1; + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar -} + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + delay (250); + RightActive = 0; + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } + + //option 4 + tft.print("force_req_Left: "); + tft.print(reqLeft); + tft.println(" g"); + if (option == 4) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + reqLeft ++; + delay (250); + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + reqLeft --; + delay (250); + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } + + //option 5 + tft.print("force_req_Right: "); + tft.print(reqRight); + tft.println(" g"); + if (option == 5) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + reqRight ++; + delay (250); + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + reqRight --; + delay (250); + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } -///////////////////////////////////////////////////////////////////////// -//Run function to updates things on every loop/////////////////////////// -///////////////////////////////////////////////////////////////////////// -void Force::run(bool log_data) { - SenseLeft(); - SenseRight(); - UpdateDisplay(); - DateTime now = rtc.now(); - unixtime = now.unixtime(); - check_buttons(); - if (log_data == true) logdata(); -} - + //button up + if (! (buttons & TFTWING_BUTTON_DOWN)) { + option --; + start_timer = millis(); + Click(); + if (option < 0) { + tft.fillScreen(ST77XX_BLACK); + option = 11; + page = 0; + } + tft.fillRect(0, ((option+1) * 8) + 19, 160, 9, ST7735_BLACK); // erase current bar + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + delay (150); + save_settings(); + } -///////////////////////////////////////////////////////////////////////// -// Buttons Functions -///////////////////////////////////////////////////////////////////////// -void Force::check_buttons() { - uint32_t buttons = ss.readButtons(); - - if (! (buttons & TFTWING_BUTTON_A)) { - pixels.setPixelColor(0, pixels.Color(0, 0, 50)); //Light Neopixel blue - pixels.show(); - } - - if ((! (buttons & TFTWING_BUTTON_A)) and ! (buttons & TFTWING_BUTTON_B)){ - delay (1000); - uint32_t buttons = ss.readButtons(); - if ((! (buttons & TFTWING_BUTTON_A)) and ! (buttons & TFTWING_BUTTON_B)){ - pixels.setPixelColor(0, pixels.Color(50, 0, 0)); //Light Neopixel red - tft.fillScreen(ST77XX_BLACK); - tft.setCursor(40, 35); - tft.setTextColor(ST7735_WHITE); - tft.println("Pump flush"); - pixels.show(); - digitalWrite(PUMP1, HIGH); - digitalWrite(PUMP2, HIGH); - tft.setCursor(40, 50); - tft.print("500"); - delay (100); - tft.print("400"); - delay (100); - tft.print("300"); - delay (100); - tft.print("200"); - delay (100); - tft.print("1"); - delay (100); - digitalWrite(PUMP1, LOW); - digitalWrite(PUMP2, LOW); - tft.fillScreen(ST77XX_BLACK); + //button down + if (! (buttons & TFTWING_BUTTON_UP)) { + option ++; + start_timer = millis(); + Click(); + tft.fillRect(0, ((option-1) * 8) + 19, 160, 9, ST7735_BLACK); // erase current bar + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + delay (150); + if (option > 5) { + tft.fillScreen(ST77XX_BLACK); + page = 2; + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + save_settings(); + } + + //button select + if (! (buttons & TFTWING_BUTTON_SELECT)) { + delay (500); + uint32_t buttons = ss.readButtons(); + if (! (buttons & TFTWING_BUTTON_SELECT)) { + Click(); + tft.fillScreen(ST77XX_BLACK); + tft.setCursor(40, 35); + tft.setTextColor(ST7735_WHITE); + save_settings(); + tft.println("Starting FORCE!"); + delay (250); + start_time = millis(); + start_up = false; + page = 0; + } + } } - } -} + + ////////////////////////////////////////////// + ////////////////////// PAGE 2 //////////////// + ////////////////////////////////////////////// + tft.fillScreen(ST77XX_BLACK); + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + while (page == 2) { + if ((millis() - start_timer) > 10000) start_up = false; //after 10 seconds of start up menu, start session + uint32_t buttons = ss.readButtons(); + tft.setCursor(40, 5); + tft.setTextColor(ST7735_MAGENTA); + tft.println("Menu"); + tft.setCursor(0, 20); + tft.setTextColor(ST7735_CYAN); -///////////////////////////////////////////////////////////////////////// -// Logging Functions -///////////////////////////////////////////////////////////////////////// -void Force::CreateDataFile() { - //put this next line *Right Before* any file open line: - SdFile::dateTimeCallback(dateTime); + //option 6 + tft.print("hold_Left: "); + tft.print(hold_timeLeft); + tft.println(" ms"); + if (option == 6) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + Click(); + hold_timeLeft += 10; + delay (250); + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - // see if the card is present and can be initialized: - if (!SD.begin(chipSelect, SD_SCK_MHZ(4))) { - error(1); - } + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + Click(); + hold_timeLeft -= 10; + delay (250); + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } + + //option 7 + tft.print("hold_Right: "); + tft.print(hold_timeRight); + tft.println(" ms"); + if (option == 7) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + Click(); + hold_timeRight += 10; + delay (250); + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - // Name filename in format F###_MMDDYYNN, where MM is month, DD is day, YY is year, and NN is an incrementing number for the number of files initialized each day - strcpy(filename, "FRC_____________.CSV"); // placeholder filename - getFilename(filename); + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + Click(); + hold_timeRight -= 10; + delay (250); + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } + + //option 8 + tft.print("ratio Left: "); + tft.println(ratioLeft); + if (option == 8) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + ratioLeft ++; + delay (250); + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - logfile = SD.open(filename, FILE_WRITE); - if ( ! logfile ) { - Serial.println("SD Create error"); - error(2); - } -} + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + ratioLeft --; + if (ratioLeft < 0) ratioLeft = 0; + delay (250); + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } -// Write data header to file of uSD. -void Force::writeHeader() { - logfile.println("MM:DD:YYYY hh:mm:ss, Seconds, Library_Version, Program, Device_Number, ProgressiveRatio, Grams_req, Hold_time, Ratio, Dispense_amount, Dispense_delay, Timeout, Trials_per_block, Max_force, TrialLeft, TrialRight, Press, Lever1_Grams, Lever2_Grams, LickLeft, LickRight, Dispense, Random_Num, Shock_trial"); -} + //option 9 + tft.print("ratio Right: "); + tft.println(ratioRight); + if (option == 9) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + ratioRight ++; + delay (250); + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar -// Print data and time followed by pellet count and motorturns to SD card -void Force::WriteToSD() { - DateTime now = rtc.now(); - logfile.print(now.month()); - logfile.print("/"); - logfile.print(now.day()); - logfile.print("/"); - logfile.print(now.year()); - logfile.print(" "); - logfile.print(now.hour()); - logfile.print(":"); - if (now.minute() < 10) - logfile.print('0'); // Trick to add leading zero for formatting - logfile.print(now.minute()); - logfile.print(":"); - if (now.second() < 10) - logfile.print('0'); // Trick to add leading zero for formatting - logfile.print(now.second()); - logfile.print(","); - - logfile.print((millis()-start_time)/1000.0000); //print seconds since start - logfile.print(","); + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + ratioRight --; + if (ratioRight < 0) ratioRight = 0; + delay (250); + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } + + //option 10 + tft.print("dispense_delay: "); + tft.print(dispense_delay); + tft.println("s"); + if (option == 10) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + dispense_delay += 1; + delay (250); + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - logfile.print(ver); // Print library version - logfile.print(","); - - logfile.print(library_version); // Print code or program version - logfile.print(","); + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + dispense_delay -= 1; + delay (250); + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } + + //option 11 + tft.print("dispense_amount: "); + tft.print(dispense_amount); + if (option == 11) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + dispense_amount += 100; + delay (250); + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - logfile.print(FRC); // Print device name - logfile.print(","); - - if (PR==1) logfile.print("true"); // Print - if (PR==0) logfile.print("false"); // Print - logfile.print(","); - - logfile.print(reqLeft); // Print for requirement - logfile.print(","); - - logfile.print(hold_timeLeft); - logfile.print(","); - - logfile.print(ratioLeft); - logfile.print(","); - - logfile.print(dispense_amount); - logfile.print(","); - - logfile.print(dispense_delay); - logfile.print(","); - - logfile.print(timeout_length); - logfile.print(","); - - logfile.print(trials_per_block); - logfile.print(","); - - logfile.print(max_force); - logfile.print(","); - - logfile.print(trialLeft); - logfile.print(","); - - logfile.print(trialRight); - logfile.print(","); - - logfile.print(pressesLeft); - logfile.print(","); - - logfile.print(gramsLeft); - logfile.print(","); + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + dispense_amount -= 100; + delay (250); + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } + + //button up + if (! (buttons & TFTWING_BUTTON_DOWN)) { + option --; + start_timer = millis(); + Click(); + if ((option <= 11) and (option > 5)){ + tft.fillRect(0, ((option - 5) * 8) + 19, 160, 9, ST7735_BLACK); // erase current bar + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + delay (150); + if (option < 6) { + tft.fillScreen(ST77XX_BLACK); + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + page = 1; + } + save_settings(); + } + + //button down + if (! (buttons & TFTWING_BUTTON_UP)) { + option ++; + start_timer = millis(); + Click(); + if (option <=11){ + tft.fillRect(0, ((option - 7) * 8) + 19, 160, 9, ST7735_BLACK); // erase current bar + tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + delay (150); + if (option > 11) { + tft.fillScreen(ST77XX_BLACK); + tft.fillRect(0, ((option - 12)* 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + page = 3; + } + save_settings(); + } + + //button select + if (! (buttons & TFTWING_BUTTON_SELECT)) { + delay (500); + uint32_t buttons = ss.readButtons(); + if (! (buttons & TFTWING_BUTTON_SELECT)) { + Click(); + tft.fillScreen(ST77XX_BLACK); + tft.setCursor(40, 35); + tft.setTextColor(ST7735_WHITE); + save_settings(); + tft.println("Starting FORCE!"); + delay (250); + start_time = millis(); + start_up = false; + page = 1; + } + } + } + + ////////////////////////////////////////////// + ////////////////////// PAGE 3 //////////////// + ////////////////////////////////////////////// + tft.fillScreen(ST77XX_BLACK); + tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + while (page == 3) { + if ((millis() - start_timer) > 10000) start_up = false; //after 10 seconds of start up menu, start session + uint32_t buttons = ss.readButtons(); + tft.setCursor(40, 5); + tft.setTextColor(ST7735_MAGENTA); + tft.println("Menu"); + + tft.setCursor(0, 20); + tft.setTextColor(ST7735_CYAN); + + //option 12 + tft.print("timeout: "); + tft.print(timeout_length); + tft.println("s"); + if (option == 12) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + Click(); + timeout_length += 1; + delay (250); + tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + Click(); + timeout_length -= 1; + delay (250); + tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } + + //option 13 + tft.print("Prog ratio: "); + if (PR == 0) tft.println("off"); + if (PR == 1) tft.println("on"); + if (option == 13) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + delay (250); + PR = 1; + ratioLeft = 1; //all PR sessions will have the FR ratio of 1 + tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + delay (250); + PR = 0; + tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } + + //option 14 + tft.print("Trials per block: "); + tft.println(trials_per_block); + if (option == 14) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + delay (250); + trials_per_block ++; + tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + delay (250); + trials_per_block --; + tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } + + //option 15 + tft.print("Max force: "); + tft.print (max_force); + tft.println(" g"); + if (option == 15) { + if (! (buttons & TFTWING_BUTTON_LEFT)) { + start_timer = millis(); + delay (250); + max_force ++; + tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + if (! (buttons & TFTWING_BUTTON_RIGHT)) { + start_timer = millis(); + delay (250); + max_force--; + tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } + + //option 16 + tft.setTextColor(ST7735_RED); + tft.println("Calibrate FORCE"); + if (option == 16) { + if (! (buttons & TFTWING_BUTTON_LEFT) or ! (buttons & TFTWING_BUTTON_SELECT)) { + start_timer = millis(); + Tone(); + delay (250); + calibrate_active = true; + Calibrate(); + tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + } + + //option 17 + tft.setTextColor(ST7735_RED); + tft.println("Reset settings"); + if (option == 17) { + if (! (buttons & TFTWING_BUTTON_LEFT) or ! (buttons & TFTWING_BUTTON_SELECT)) { + start_timer = millis(); + delay (250); + tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + reset_settings(); + } + } + + if (! (buttons & TFTWING_BUTTON_DOWN)) { + option --; + start_timer = millis(); + Click(); + if ((option <= 17) and (option > 11)){ + tft.fillRect(0, ((option - 11) * 8) + 19, 160, 9, ST7735_BLACK); // erase current bar + tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + delay (150); + if (option < 12) { + tft.fillScreen(ST77XX_BLACK); + tft.fillRect(0, ((option-6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + page = 2; + } + save_settings(); + } + + if (! (buttons & TFTWING_BUTTON_UP)) { + option ++; + start_timer = millis(); + Click(); + if (option <=17){ + tft.fillRect(0, ((option - 13) * 8) + 19, 160, 9, ST7735_BLACK); // erase current bar + tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + delay (150); + if (option > 17) { + tft.fillScreen(ST77XX_BLACK); + option = 0; + tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + page = 1; + } + save_settings(); + } + + //button select + if (! (buttons & TFTWING_BUTTON_SELECT)) { + delay (500); + uint32_t buttons = ss.readButtons(); + if (! (buttons & TFTWING_BUTTON_SELECT)) { + Click(); + tft.fillScreen(ST77XX_BLACK); + tft.setCursor(40, 35); + tft.setTextColor(ST7735_WHITE); + save_settings(); + tft.println("Starting FORCE!"); + delay (250); + start_time = millis(); + start_up = false; + page = 1; + } + } + } + } +} + + +////////////////////////////////////////////////// +///// Check Buttons and Flush function //////// +////////////////////////////////////////////////// +void Force::check_buttons() { + uint32_t buttons = ss.readButtons(); + + if (! (buttons & TFTWING_BUTTON_A)) { + pixels.setPixelColor(0, pixels.Color(0, 0, 50)); //Light Neopixel blue + pixels.show(); + } - logfile.print(gramsRight); - logfile.print(","); + if ((! (buttons & TFTWING_BUTTON_A)) and ! (buttons & TFTWING_BUTTON_B)){ + delay (1000); + uint32_t buttons = ss.readButtons(); + if ((! (buttons & TFTWING_BUTTON_A)) and ! (buttons & TFTWING_BUTTON_B)){ + pixels.setPixelColor(0, pixels.Color(50, 0, 0)); //Light Neopixel red + tft.fillScreen(ST77XX_BLACK); + tft.setCursor(40, 35); + tft.setTextColor(ST7735_WHITE); + tft.println("Pump flush"); + pixels.show(); + digitalWrite(PUMP1, HIGH); + digitalWrite(PUMP2, HIGH); + tft.setCursor(40, 50); + tft.print("500"); + delay (100); + tft.print("400"); + delay (100); + tft.print("300"); + delay (100); + tft.print("200"); + delay (100); + tft.print("1"); + delay (100); + digitalWrite(PUMP1, LOW); + digitalWrite(PUMP2, LOW); + tft.fillScreen(ST77XX_BLACK); + } + } +} + + + +////////////////////////////////////////////////////////////////////////// +/////////////// Cakibration Functions //////////// +////////////////////////////////////////////////////////////////////////// + +////////////// +void Force::Tare() { + if (millis() - start_timer > 5000) { + if (scaleChangeLeft < 1000) { // this sets sensitivity for delaying taring + pixels.setPixelColor(0, pixels.Color(0, 10, 10)); + pixels.show(); + scaleLeft.tare(); + } + if (scaleChangeRight < 1000) { + pixels.setPixelColor(0, pixels.Color(10, 10, 0)); + pixels.show(); + scaleRight.tare(); + } + start_timer = millis(); + scaleChangeLeft = 0; + scaleChangeRight = 0; + } +} + +void Force::Calibrate(){ + bool lever1 = true; + tft.fillScreen(ST77XX_BLACK); + scaleLeft.tare(); + scaleRight.tare(); + while (calibrate_active==true){ + uint32_t buttons = ss.readButtons(); + if (! (buttons & TFTWING_BUTTON_A)) lever1 = true; + if (! (buttons & TFTWING_BUTTON_B)) lever1 = false; + + float calibrate_timer = millis(); + tft.setCursor(40, 15); + tft.setTextColor(ST7735_WHITE); + tft.println("Calibrate levers:"); + gramsLeft = (scaleLeft.get_units()); + gramsRight = (scaleRight.get_units()); + tft.setCursor(40, 30); + tft.print("Lever Left:"); + tft.println(gramsLeft,1); + tft.setCursor(40, 45); + tft.print("Lever Right:"); + tft.println(gramsRight,1); + delay (100); + if (lever1 == true){ + tft.fillRect(0, 30, 160, 12, ST7735_BLUE); // highlight active bar + tft.fillRect(0, 43, 160, 12, ST7735_BLACK); // highlight active bar + + if (! (buttons & TFTWING_BUTTON_UP)) { + calibration_factor_Left += 100; + scaleLeft.set_scale(calibration_factor_Left); + } + + if (! (buttons & TFTWING_BUTTON_DOWN)) { + calibration_factor_Left -= 100; + scaleLeft.set_scale(calibration_factor_Left); + } + } + + if (lever1 == false){ + tft.fillRect(0, 30, 160, 12, ST7735_BLACK); // highlight active bar + tft.fillRect(0, 43, 160, 12, ST7735_BLUE); // highlight active bar + if (! (buttons & TFTWING_BUTTON_UP)) { + calibration_factor_Right += 100; + scaleRight.set_scale(calibration_factor_Right); + } + + if (! (buttons & TFTWING_BUTTON_DOWN)) { + calibration_factor_Right -= 100; + scaleRight.set_scale(calibration_factor_Right); + } + } + + if (! (buttons & TFTWING_BUTTON_SELECT)) { + Click(); + start_timer = millis(); + tft.fillScreen(ST77XX_BLACK); + tft.setCursor(40, 15); + tft.println("Remove weights"); + calibrated = true; + delay (2000); + tft.fillScreen(ST77XX_BLACK); + tft.setCursor(40, 15); + tft.println("Calibrated!"); + delay (1000); + start_up_menu(); + } + } +} + +////////////////////////////////////////////////////////////////////////// +/////////////// Initial setup Functions //////////// +////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////// +///// Runs once to initialize pins ///// +///// and set everything up ///// +//////////////////////////////////////////////////// + +void Force::begin(bool log_lite) { + Serial.begin(9600); + + if (!ss.begin()) { + Serial.println("seesaw couldn't be found!"); + while (1); + } + + // Initialize pins + pinMode(A0, OUTPUT); + pinMode(BEEPER, OUTPUT); + pinMode(POKE, INPUT); - logfile.print(lickLeft); - logfile.print(","); + pinMode(LICK1, INPUT_PULLDOWN); + pinMode(PUMP1, OUTPUT); + digitalWrite(PUMP1, LOW); - logfile.print(lickRight); - logfile.print(","); + pinMode(LICK2, INPUT_PULLDOWN); + pinMode(PUMP2, OUTPUT) ; + digitalWrite(PUMP2, LOW); + + // Initialize display + ss.tftReset(); // Reset the display + ss.setBacklight(1); // Adjust backlight (this doesn't really seem to work unless you do -1 to turn it off) + tft.initR(INITR_MINI160x80); // Initialize a ST7735S chip, mini display + tft.setRotation(1); + tft.fillScreen(ST77XX_BLACK); + + // Initialize RTC + if (!rtc.initialized() || rtc.lostPower()) { + rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); + } + + rtc.start(); + + // Initialize neopixel + pixels.begin(); + + //start SPI flash + Serial.print("flash begin..."); + flash.begin(); + // Open file system on the flash + if ( !fatfs.begin(&flash) ) { + Serial.println("Error: filesystem is not existed. Please try SdFat_format example to make one."); + while (1) yield(); + } + Serial.println("done."); - logfile.print(dispensing); - logfile.print(","); + load_settings(); - logfile.print(random_number); - logfile.print(","); + // Initialize load cells + analogWriteResolution(12); // turn on 12 bit resolution - logfile.println(shock); + scaleLeft.begin(DOUT1, CLK1); + scaleLeft.tare(); + scaleLeft.set_scale(calibration_factor_Left); + + scaleRight.begin(DOUT2, CLK2); + scaleRight.tare(); + scaleRight.set_scale(calibration_factor_Right); - logfile.flush(); + //start up menu + start_up_menu(); + tft.fillScreen(ST77XX_BLACK); - if ( ! logfile ) { - error(2); - } -} + // Initialize SD + SdFile::dateTimeCallback(dateTime); + CreateDataFile(); + writeHeader(log_lite); -/******************************************************** - If any errors are detected with the SD card print on the screen -********************************************************/ -void Force::error(uint8_t errno) { - tft.setCursor(5, 48); - tft.print("Check SD card"); } -/******************************************************** - This function creates a unique filename for each file that - starts with "FRC", then the date in MMDDYY, - then an incrementing number for each new file created on the same date -********************************************************/ -void Force::getFilename(char *filename) { - DateTime now = rtc.now(); - filename[3] = FRC / 100 + '0'; - filename[4] = FRC / 10 + '0'; - filename[5] = FRC % 10 + '0'; - filename[7] = now.month() / 10 + '0'; - filename[8] = now.month() % 10 + '0'; - filename[9] = now.day() / 10 + '0'; - filename[10] = now.day() % 10 + '0'; - filename[11] = (now.year() - 2000) / 10 + '0'; - filename[12] = (now.year() - 2000) % 10 + '0'; - filename[16] = '.'; - filename[17] = 'C'; - filename[18] = 'S'; - filename[19] = 'V'; - for (uint8_t i = 0; i < 100; i++) { - filename[14] = '0' + i / 10; - filename[15] = '0' + i % 10; - if (! SD.exists(filename)) { - break; - } - } - return; +//////////////////////////////////////////////////// +//// function to update things on every loop ///// +//////////////////////////////////////////////////// +void Force::run() { + SenseLeft(); + SenseRight(); + UpdateDisplay(); + DateTime now = rtc.now(); + unixtime = now.unixtime(); + check_buttons(); + check_lastDispense(); } -void Force::logdata() { - WriteToSD(); -} + ///////////////////////////////////////////////////////////////////////// -// Display Functions +//////// Display functions ////////// ///////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////// +//////// Update display /////////// +////////////////////////////////////////////////// void Force::UpdateDisplay(){ graphLegend(); graphData(); graphDateTime(); } +////////////////////////////////////////////////// +//////// Graph Data /////////// +////////////////////////////////////////////////// void Force::graphData() { //Calculate datapoints to graph lasty = y; @@ -544,6 +1021,10 @@ void Force::graphData() { if (x == 160) x = 0; } +////////////////////////////////////////////////// +//////// Graph Date and time /////////// +////////////////////////////////////////////////// + void Force::graphDateTime() { DateTime now = rtc.now(); tft.setTextColor(ST7735_WHITE); @@ -563,6 +1044,9 @@ void Force::graphDateTime() { tft.print(now.minute(), DEC); } +////////////////////////////////////////////////// +//////// Graph Legend /////////// +////////////////////////////////////////////////// void Force::graphLegend() { // Print force output on F1 and F2 if (gramsLeft > 1 or gramsRight >1){ //only clear F1 ans F2 values if levers are being pushed @@ -591,769 +1075,335 @@ void Force::graphLegend() { if (gramsLeft > 1 or gramsRight >1){ tft.fillRect(80, 5, 24, 12, ST7735_BLACK); // clear task data on each trial } - tft.print(trialLeft); + tft.print(rewardLeft); // Print trial right tft.setCursor(45,17); tft.setTextColor(ST7735_YELLOW); tft.print("Right:"); - if (gramsLeft > 1 or gramsRight > 1){ - tft.fillRect(80, 17, 24, 12, ST7735_BLACK); - } - tft.print(trialRight); - - // Print FR ratio - tft.setCursor(110, 5); - tft.setTextColor(ST7735_YELLOW); - if (PR ==0) tft.print("FR:"); - if (PR ==1) tft.print("PR:"); - tft.print(ratioLeft); - - //Print remaining trial window - if ((trial_length < trial_window) && (trial_length != 0)) { - tft.setCursor(45,30); - tft.setTextColor(ST7735_WHITE); - tft.print("Trial Available"); - //tft.print((trial_window - trial_length)/1000, 3); - //tft.fillRect(90, 30, 100, 12, ST7735_BLACK); - } else { - tft.fillRect(45, 30, 100, 12, ST7735_BLACK); - } - - - - - // Print current press - /*tft.setCursor(110, 17); - tft.setTextColor(ST7735_YELLOW); - tft.print("Press:"); - if (gramsLeft > 1 or gramsRight >1){ - tft.fillRect(143, 17, 28, 12, ST7735_BLACK); // clear press data on each trial - } - */tft.print(pressesLeft); - - //Indicate licks - tft.fillRect(0, 27, 40, 12, ST7735_BLACK); // clear the text after label - if (lickLeft == true) { - tft.setTextColor(ST7735_WHITE); - tft.setCursor(0, 28); - tft.print ("Lick left"); - digitalWrite(A3, HIGH); //CHECK THIS, MIGHT NOT BE THE RIGHT PIN - DateTime now = rtc.now(); - lickTime = now.unixtime(); - } - - if (lickLeft == false) { - digitalWrite(A3, LOW); //CHECK THIS, MIGHT NOT BE THE RIGHT PIN - - } - - if (lickRight == true) { - tft.setTextColor(ST7735_WHITE); - tft.setCursor(0, 28); - tft.print ("Lick right"); - digitalWrite(A3, HIGH); //THIS IS DEFINITELY NOT RIGHT PIN! - DateTime now = rtc.now(); - lickTime = now.unixtime(); - } - - if (lickRight == false) { - digitalWrite(A3, LOW); //THIS IS DEFINITELY NOT RIGHT PIN! - - } - - if (calibrated == false){ - tft.setCursor(85, 56); - tft.print ("Uncalibrated"); - } -} - - -///////////////////////////////////////////////////////////////////////// -// Calibration function -///////////////////////////////////////////////////////////////////////// -void Force::Tare() { - if (millis() - start_timer > 5000) { - if (scaleChangeLeft < 1000) { // this sets sensitivity for delaying taring - pixels.setPixelColor(0, pixels.Color(0, 10, 10)); - pixels.show(); - scaleLeft.tare(); - } - if (scaleChangeRight < 1000) { - pixels.setPixelColor(0, pixels.Color(10, 10, 0)); - pixels.show(); - scaleRight.tare(); - } - start_timer = millis(); - scaleChangeLeft = 0; - scaleChangeRight = 0; - } -} - -void Force::Calibrate(){ - bool lever1 = true; - tft.fillScreen(ST77XX_BLACK); - scaleLeft.tare(); - scaleRight.tare(); - while (calibrate_active==true){ - uint32_t buttons = ss.readButtons(); - if (! (buttons & TFTWING_BUTTON_A)) lever1 = true; - if (! (buttons & TFTWING_BUTTON_B)) lever1 = false; - - float calibrate_timer = millis(); - tft.setCursor(40, 15); - tft.setTextColor(ST7735_WHITE); - tft.println("Calibrate levers:"); - gramsLeft = (scaleLeft.get_units()); - gramsRight = (scaleRight.get_units()); - tft.setCursor(40, 30); - tft.print("Lever Left:"); - tft.println(gramsLeft,1); - tft.setCursor(40, 45); - tft.print("Lever Right:"); - tft.println(gramsRight,1); - delay (100); - if (lever1 == true){ - tft.fillRect(0, 30, 160, 12, ST7735_BLUE); // highlight active bar - tft.fillRect(0, 43, 160, 12, ST7735_BLACK); // highlight active bar - - if (! (buttons & TFTWING_BUTTON_UP)) { - calibration_factor_Left += 100; - scaleLeft.set_scale(calibration_factor_Left); - } - - if (! (buttons & TFTWING_BUTTON_DOWN)) { - calibration_factor_Left -= 100; - scaleLeft.set_scale(calibration_factor_Left); - } - } - - if (lever1 == false){ - tft.fillRect(0, 30, 160, 12, ST7735_BLACK); // highlight active bar - tft.fillRect(0, 43, 160, 12, ST7735_BLUE); // highlight active bar - if (! (buttons & TFTWING_BUTTON_UP)) { - calibration_factor_Right += 100; - scaleRight.set_scale(calibration_factor_Right); - } - - if (! (buttons & TFTWING_BUTTON_DOWN)) { - calibration_factor_Right -= 100; - scaleRight.set_scale(calibration_factor_Right); - } - } - - if (! (buttons & TFTWING_BUTTON_SELECT)) { - Click(); - start_timer = millis(); - tft.fillScreen(ST77XX_BLACK); - tft.setCursor(40, 15); - tft.println("Remove weights"); - calibrated = true; - delay (2000); - tft.fillScreen(ST77XX_BLACK); - tft.setCursor(40, 15); - tft.println("Calibrated!"); - delay (1000); - start_up_menu(); - } - } -} - - -///////////////////////////////////////////////////////////////////////// -// FORCE menu -///////////////////////////////////////////////////////////////////////// -void Force::start_up_menu() { - calibrate_active = false; - print_settings(); - float start_timer = millis(); - int option = 0; - tft.fillScreen(ST77XX_BLACK); - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - while (start_up == true) { - int page = 1; - ////////////////////////////////////////////// - ////////////////////// PAGE 1 //////////////// - ////////////////////////////////////////////// - while (page == 1) { - if ((millis() - start_timer) > 10000) start_up = false; //after 10 seconds of start up menu, start session - uint32_t buttons = ss.readButtons(); - tft.setCursor(40, 5); - tft.setTextColor(ST7735_MAGENTA); - tft.println("Menu"); - tft.setCursor(0, 20); - tft.setTextColor(ST7735_CYAN); - - //option 0 - tft.print("device #: "); - tft.println(FRC); - if (option == 0) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - FRC ++; - delay (250); - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + if (gramsLeft > 1 or gramsRight > 1){ + tft.fillRect(80, 17, 24, 12, ST7735_BLACK); + } + tft.print(rewardRight); - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - FRC --; - delay (250); - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } + // Print FR ratio + tft.setCursor(110, 5); + tft.setTextColor(ST7735_YELLOW); + if (PR ==0) tft.print("FR:"); + if (PR ==1) tft.print("PR:"); + tft.print(ratioLeft); + + //Print remaining trial window + if (trial_available) { + tft.setCursor(60,50); + tft.setTextColor(ST7735_WHITE); + tft.print("Trial Available"); + //tft.print((trial_window - trial_length)/1000, 3); + //tft.fillRect(90, 30, 100, 12, ST7735_BLACK); + } else { + tft.fillRect(45, 30, 100, 12, ST7735_BLACK); + } - //option 1 - tft.print("Trial window: "); - tft.println(trial_window); - if (option == 1) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - trial_window ++; - delay (250); - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - trial_window --; - if (trial_window < 0) trial_window = 0; - delay (250); - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } + //Indicate licks + tft.fillRect(0, 27, 40, 12, ST7735_BLACK); // clear the text after label + if (lickLeft == true) { + tft.setTextColor(ST7735_WHITE); + tft.setCursor(0, 28); + tft.print ("Lick left"); + digitalWrite(A3, HIGH); //CHECK THIS, MIGHT NOT BE THE RIGHT PIN + DateTime now = rtc.now(); + lickTime = now.unixtime(); + } - //option 2 - tft.print("Left lever: "); - if (LeftActive == 1) { - tft.println("Active"); - } else if (LeftActive == 0) { - tft.println("Inactive"); - } else { - tft.println(LeftActive); - } - if (option == 2) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - delay (250); - LeftActive = 1; - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + if (lickLeft == false) { + digitalWrite(A3, LOW); //CHECK THIS, MIGHT NOT BE THE RIGHT PIN - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - delay (250); - LeftActive = 0; - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } - - //option 3 - tft.print("Right lever: "); - if (RightActive == 1) tft.println("Active"); - if (RightActive == 0) tft.println("Inactive"); - if (option == 3) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - delay (250); - RightActive = 1; - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + + if (lickRight == true) { + tft.setTextColor(ST7735_WHITE); + tft.setCursor(0, 28); + tft.print ("Lick right"); + digitalWrite(A3, HIGH); //THIS IS DEFINITELY NOT RIGHT PIN! + DateTime now = rtc.now(); + lickTime = now.unixtime(); + } - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - delay (250); - RightActive = 0; - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } - - //option 4 - tft.print("force_req_Left: "); - tft.print(reqLeft); - tft.println(" g"); - if (option == 4) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - reqLeft ++; - delay (250); - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + if (lickRight == false) { + digitalWrite(A3, LOW); //THIS IS DEFINITELY NOT RIGHT PIN! - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - reqLeft --; - delay (250); - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } - - //option 5 - tft.print("force_req_Right: "); - tft.print(reqRight); - tft.println(" g"); - if (option == 5) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - reqRight ++; - delay (250); - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + } + + if (calibrated == false){ + tft.setCursor(85, 56); + tft.print ("Uncalibrated"); + } +} - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - reqRight --; - delay (250); - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } +///////////////////////////////////////////////////////////////////////// +//////// Logging Functions ////////// +///////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////// +//////// Create data file /////////// +////////////////////////////////////////////////// +void Force::CreateDataFile() { + //put this next line *Right Before* any file open line: + SdFile::dateTimeCallback(dateTime); - //button up - if (! (buttons & TFTWING_BUTTON_DOWN)) { - option --; - start_timer = millis(); - Click(); - if (option < 0) { - tft.fillScreen(ST77XX_BLACK); - option = 11; - page = 0; - } - tft.fillRect(0, ((option+1) * 8) + 19, 160, 9, ST7735_BLACK); // erase current bar - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - delay (150); - save_settings(); - print_settings(); - } + // see if the card is present and can be initialized: + if (!SD.begin(chipSelect, SD_SCK_MHZ(4))) { + error(1); + } - //button down - if (! (buttons & TFTWING_BUTTON_UP)) { - option ++; - start_timer = millis(); - Click(); - tft.fillRect(0, ((option-1) * 8) + 19, 160, 9, ST7735_BLACK); // erase current bar - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - delay (150); - if (option > 5) { - tft.fillScreen(ST77XX_BLACK); - page = 2; - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - save_settings(); - print_settings(); - } + // Name filename in format F###_MMDDYYNN, where MM is month, DD is day, YY is year, and NN is an incrementing number for the number of files initialized each day + strcpy(filename, "FRC_____________.CSV"); // placeholder filename + getFilename(filename); - //button select - if (! (buttons & TFTWING_BUTTON_SELECT)) { - delay (500); - uint32_t buttons = ss.readButtons(); - if (! (buttons & TFTWING_BUTTON_SELECT)) { - Click(); - tft.fillScreen(ST77XX_BLACK); - tft.setCursor(40, 35); - tft.setTextColor(ST7735_WHITE); - save_settings(); - print_settings(); - tft.println("Starting FORCE!"); - delay (250); - start_time = millis(); - start_up = false; - page = 0; - } - } - } - - ////////////////////////////////////////////// - ////////////////////// PAGE 2 //////////////// - ////////////////////////////////////////////// - tft.fillScreen(ST77XX_BLACK); - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - print_settings(); - while (page == 2) { - if ((millis() - start_timer) > 10000) start_up = false; //after 10 seconds of start up menu, start session - uint32_t buttons = ss.readButtons(); - tft.setCursor(40, 5); - tft.setTextColor(ST7735_MAGENTA); - tft.println("Menu"); + logfile = SD.open(filename, FILE_WRITE); + if ( ! logfile ) { + Serial.println("SD Create error"); + error(2); + } +} - tft.setCursor(0, 20); - tft.setTextColor(ST7735_CYAN); +////////////////////////////////////////////////// +///// Write header to data file /////// +////////////////////////////////////////////////// +void Force::writeHeader(bool log_lite) { + if (log_lite == true) { + logfile.println("MM:DD:YYYY hh:mm:ss, Library_Version, Program, Device_Number, Dispense_amount, Dispense_delay, Timeout, Trials_per_block, Max_force, Trials_left, Trials_right, Left_rewarded, Right_rewarded"); + } + else if (log_lite == false) { + logfile.println("MM:DD:YYYY hh:mm:ss, Seconds, Library_Version, Program, Device_Number, ProgressiveRatio, Grams_req, Hold_time, Ratio, Dispense_amount, Dispense_delay, Timeout, Trials_per_block, Max_force, TrialLeft, TrialRight, Press, Lever1_Grams, Lever2_Grams, LickLeft, LickRight, Dispense"); + } + +} + +////////////////////////////////////////////////// +///// Write data to file /////// +////////////////////////////////////////////////// +void Force::logdata() { + DateTime now = rtc.now(); + logfile.print(now.month()); + logfile.print("/"); + logfile.print(now.day()); + logfile.print("/"); + logfile.print(now.year()); + logfile.print(" "); + logfile.print(now.hour()); + logfile.print(":"); + if (now.minute() < 10) + logfile.print('0'); // Trick to add leading zero for formatting + logfile.print(now.minute()); + logfile.print(":"); + if (now.second() < 10) + logfile.print('0'); // Trick to add leading zero for formatting + logfile.print(now.second()); + logfile.print(","); + + logfile.print((millis()-start_time)/1000.0000); //print seconds since start + logfile.print(","); - //option 6 - tft.print("hold_Left: "); - tft.print(hold_timeLeft); - tft.println(" ms"); - if (option == 6) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - Click(); - hold_timeLeft += 10; - delay (250); - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + logfile.print(ver); // Print library version + logfile.print(","); + + logfile.print(library_version); // Print code or program version + logfile.print(","); - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - Click(); - hold_timeLeft -= 10; - delay (250); - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } - - //option 7 - tft.print("hold_Right: "); - tft.print(hold_timeRight); - tft.println(" ms"); - if (option == 7) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - Click(); - hold_timeRight += 10; - delay (250); - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + logfile.print(FRC); // Print device name + logfile.print(","); + + if (PR==1) logfile.print("true"); // Print + if (PR==0) logfile.print("false"); // Print + logfile.print(","); + + logfile.print(reqLeft); // Print for requirement + logfile.print(","); + + logfile.print(hold_timeLeft); + logfile.print(","); + + logfile.print(ratioLeft); + logfile.print(","); + + logfile.print(dispense_amount); + logfile.print(","); + + logfile.print(dispense_delay); + logfile.print(","); + + logfile.print(timeout_length); + logfile.print(","); + + logfile.print(trials_per_block); + logfile.print(","); + + logfile.print(max_force); + logfile.print(","); + + logfile.print(rewardLeft); + logfile.print(","); + + logfile.print(rewardRight); + logfile.print(","); + + logfile.print(pressesLeft); + logfile.print(","); + + logfile.print(gramsLeft); + logfile.print(","); + + logfile.print(gramsRight); + logfile.print(","); + + logfile.print(lickLeft); + logfile.print(","); + + logfile.print(lickRight); + logfile.print(","); - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - Click(); - hold_timeRight -= 10; - delay (250); - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } - - //option 8 - tft.print("ratio Left: "); - tft.println(ratioLeft); - if (option == 8) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - ratioLeft ++; - delay (250); - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + logfile.print(dispensing); + logfile.print(","); - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - ratioLeft --; - if (ratioLeft < 0) ratioLeft = 0; - delay (250); - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } + logfile.flush(); - //option 9 - tft.print("ratio Right: "); - tft.println(ratioRight); - if (option == 9) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - ratioRight ++; - delay (250); - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + if ( ! logfile ) { + error(2); + } +} - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - ratioRight --; - if (ratioRight < 0) ratioRight = 0; - delay (250); - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } - - //option 10 - tft.print("dispense_delay: "); - tft.print(dispense_delay); - tft.println("s"); - if (option == 10) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - dispense_delay += 1; - delay (250); - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar +void Force::logdata_lite() { + DateTime now = rtc.now(); + logfile.print(now.month()); + logfile.print("/"); + logfile.print(now.day()); + logfile.print("/"); + logfile.print(now.year()); + logfile.print(" "); + logfile.print(now.hour()); + logfile.print(":"); + if (now.minute() < 10) + logfile.print('0'); // Trick to add leading zero for formatting + logfile.print(now.minute()); + logfile.print(":"); + if (now.second() < 10) + logfile.print('0'); // Trick to add leading zero for formatting + logfile.print(now.second()); + logfile.print(","); - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - dispense_delay -= 1; - delay (250); - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } - - //option 11 - tft.print("dispense_amount: "); - tft.print(dispense_amount); - if (option == 11) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - dispense_amount += 100; - delay (250); - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + logfile.print(ver); // Print library version + logfile.print(","); + + logfile.print(library_version); // Print code or program version + logfile.print(","); - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - dispense_amount -= 100; - delay (250); - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } - - //button up - if (! (buttons & TFTWING_BUTTON_DOWN)) { - option --; - start_timer = millis(); - Click(); - if ((option <= 11) and (option > 5)){ - tft.fillRect(0, ((option - 5) * 8) + 19, 160, 9, ST7735_BLACK); // erase current bar - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - delay (150); - if (option < 6) { - tft.fillScreen(ST77XX_BLACK); - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - page = 1; - } - save_settings(); - print_settings(); - } + logfile.print(FRC); // Print device name + logfile.print(","); + + logfile.print(dispense_amount); + logfile.print(","); + + logfile.print(dispense_delay); + logfile.print(","); + + logfile.print(timeout_length); + logfile.print(","); + + logfile.print(trials_per_block); + logfile.print(","); + + logfile.print(event); + logfile.print(","); - //button down - if (! (buttons & TFTWING_BUTTON_UP)) { - option ++; - start_timer = millis(); - Click(); - if (option <=11){ - tft.fillRect(0, ((option - 7) * 8) + 19, 160, 9, ST7735_BLACK); // erase current bar - tft.fillRect(0, ((option - 6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - delay (150); - if (option > 11) { - tft.fillScreen(ST77XX_BLACK); - tft.fillRect(0, ((option - 12)* 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - page = 3; - } - save_settings(); - print_settings(); - } + logfile.print(trials_left); + logfile.print(","); + + logfile.print(trials_right); + logfile.print(","); - //button select - if (! (buttons & TFTWING_BUTTON_SELECT)) { - delay (500); - uint32_t buttons = ss.readButtons(); - if (! (buttons & TFTWING_BUTTON_SELECT)) { - Click(); - tft.fillScreen(ST77XX_BLACK); - tft.setCursor(40, 35); - tft.setTextColor(ST7735_WHITE); - save_settings(); - print_settings(); - tft.println("Starting FORCE!"); - delay (250); - start_time = millis(); - start_up = false; - page = 1; - } - } - } + logfile.print(rewardLeft); + logfile.print(","); - ////////////////////////////////////////////// - ////////////////////// PAGE 3 //////////////// - ////////////////////////////////////////////// - tft.fillScreen(ST77XX_BLACK); - tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - print_settings(); - while (page == 3) { - if ((millis() - start_timer) > 10000) start_up = false; //after 10 seconds of start up menu, start session - uint32_t buttons = ss.readButtons(); - tft.setCursor(40, 5); - tft.setTextColor(ST7735_MAGENTA); - tft.println("Menu"); + logfile.println(rewardRight); - tft.setCursor(0, 20); - tft.setTextColor(ST7735_CYAN); - - //option 12 - tft.print("timeout: "); - tft.print(timeout_length); - tft.println("s"); - if (option == 12) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - Click(); - timeout_length += 1; - delay (250); - tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + logfile.flush(); - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - Click(); - timeout_length -= 1; - delay (250); - tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } - - //option 13 - tft.print("Prog ratio: "); - if (PR == 0) tft.println("off"); - if (PR == 1) tft.println("on"); - if (option == 13) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - delay (250); - PR = 1; - ratioLeft = 1; //all PR sessions will have the FR ratio of 1 - tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - delay (250); - PR = 0; - tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } - - //option 14 - tft.print("Trials per block: "); - tft.println(trials_per_block); - if (option == 14) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - delay (250); - trials_per_block ++; - tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar + if ( ! logfile ) { + error(2); + } +} - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - delay (250); - trials_per_block --; - tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } +void Force::loglite_Left() { + event = "Left"; + trials_left ++; + logdata_lite(); +} - //option 15 - tft.print("Max force: "); - tft.print (max_force); - tft.println(" g"); - if (option == 15) { - if (! (buttons & TFTWING_BUTTON_LEFT)) { - start_timer = millis(); - delay (250); - max_force ++; - tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - if (! (buttons & TFTWING_BUTTON_RIGHT)) { - start_timer = millis(); - delay (250); - max_force--; - tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } +void Force::loglite_Right() { + event = "Right"; + trials_right ++; + logdata_lite(); +} - //option 16 - tft.setTextColor(ST7735_RED); - tft.println("Calibrate FORCE"); - if (option == 16) { - if (! (buttons & TFTWING_BUTTON_LEFT) or ! (buttons & TFTWING_BUTTON_SELECT)) { - start_timer = millis(); - Tone(); - delay (250); - calibrate_active = true; - Calibrate(); - tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - } - - //option 17 - tft.setTextColor(ST7735_RED); - tft.println("Reset settings"); - if (option == 17) { - if (! (buttons & TFTWING_BUTTON_LEFT) or ! (buttons & TFTWING_BUTTON_SELECT)) { - start_timer = millis(); - delay (250); - tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - reset_settings(); - } - } - - if (! (buttons & TFTWING_BUTTON_DOWN)) { - option --; - start_timer = millis(); - Click(); - if ((option <= 17) and (option > 11)){ - tft.fillRect(0, ((option - 11) * 8) + 19, 160, 9, ST7735_BLACK); // erase current bar - tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - delay (150); - if (option < 12) { - tft.fillScreen(ST77XX_BLACK); - tft.fillRect(0, ((option-6) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - page = 2; - } - save_settings(); - print_settings(); - } - - if (! (buttons & TFTWING_BUTTON_UP)) { - option ++; - start_timer = millis(); - Click(); - if (option <=17){ - tft.fillRect(0, ((option - 13) * 8) + 19, 160, 9, ST7735_BLACK); // erase current bar - tft.fillRect(0, ((option - 12) * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - } - delay (150); - if (option > 17) { - tft.fillScreen(ST77XX_BLACK); - option = 0; - tft.fillRect(0, (option * 8) + 19, 160, 9, ST7735_BLUE); // highlight active bar - page = 1; - } - save_settings(); - print_settings(); - } +/******************************************************** + If any errors are detected with the SD card print on the screen +********************************************************/ +void Force::error(uint8_t errno) { + tft.setCursor(5, 48); + tft.print("Check SD card"); +} - //button select - if (! (buttons & TFTWING_BUTTON_SELECT)) { - delay (500); - uint32_t buttons = ss.readButtons(); - if (! (buttons & TFTWING_BUTTON_SELECT)) { - Click(); - tft.fillScreen(ST77XX_BLACK); - tft.setCursor(40, 35); - tft.setTextColor(ST7735_WHITE); - save_settings(); - print_settings(); - tft.println("Starting FORCE!"); - delay (250); - start_time = millis(); - start_up = false; - page = 1; - } - } +/******************************************************** + This function creates a unique filename for each file that + starts with "FRC", then the date in MMDDYY, + then an incrementing number for each new file created on the same date +********************************************************/ +void Force::getFilename(char *filename) { + DateTime now = rtc.now(); + + filename[3] = FRC / 100 + '0'; + filename[4] = FRC / 10 + '0'; + filename[5] = FRC % 10 + '0'; + filename[7] = now.month() / 10 + '0'; + filename[8] = now.month() % 10 + '0'; + filename[9] = now.day() / 10 + '0'; + filename[10] = now.day() % 10 + '0'; + filename[11] = (now.year() - 2000) / 10 + '0'; + filename[12] = (now.year() - 2000) % 10 + '0'; + filename[16] = '.'; + filename[17] = 'C'; + filename[18] = 'S'; + filename[19] = 'V'; + for (uint8_t i = 0; i < 100; i++) { + filename[14] = '0' + i / 10; + filename[15] = '0' + i % 10; + + if (! SD.exists(filename)) { + break; } } + return; } ///////////////////////////////////////////////////////////////////////// -/////////////////////////Poke functions //////////////////////////////// +/////// Task functions //////// ///////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////// +///// Read Poke /////// +//////////////////////////////////////// void Force::readPoke() { poke = digitalRead(POKE) == LOW; } -///////////////////////////////////////////////////////////////////////// -/////////////////////////Timeout function//////////////////////////////// -///////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// +///// Timeout function ////// +//////////////////////////////////////// + void Force::Timeout(int timeout_length) { dispense_time = millis(); while ((millis() - dispense_time) < (timeout_length * 1000)){ @@ -1361,7 +1411,7 @@ void Force::Timeout(int timeout_length) { tft.setTextColor(ST7735_WHITE); tft.print("Timeout:"); tft.print((-(millis() - dispense_time - (timeout_length*1000))/ 1000),1); - run(false); + run(); tft.fillRect(84, 43, 80, 12, ST7735_BLACK); if ((gramsLeft > 1.5) or (gramsRight > 1.5)) { //reset timeout if either lever pushed Timeout(timeout_length); @@ -1371,9 +1421,10 @@ void Force::Timeout(int timeout_length) { tft.fillRect(12, 0, 38, 24, ST7735_BLACK); // clear the text after F1 F2 labels } -///////////////////////////////////////////////////////////////////////// -// Sound Functions -///////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////// +///// Sound functions ////// +//////////////////////////////////////// void Force::Tone(int frequency, int duration) { tone(BEEPER, frequency, duration); } @@ -1388,8 +1439,8 @@ void Force::Click() { void Force::DispenseLeft() { dispensing = true; - trialLeft++; - Tone(); + rewardLeft ++; + Tone(4000,200); float successTime = millis(); while ((millis() - successTime) < (dispense_delay * 1000)){ tft.setCursor(85, 44); @@ -1407,8 +1458,9 @@ void Force::DispenseLeft() { //digitalWrite(13,HIGH); // RED LED for (int i=0; i < 20; i++) { digitalWrite(PUMP1, HIGH); - delayMicroseconds(100); + delayMicroseconds(600); digitalWrite(PUMP1, LOW); + delayMicroseconds(200); } DateTime now = rtc.now(); dispenseTime = now.unixtime(); @@ -1417,6 +1469,7 @@ void Force::DispenseLeft() { pressTimeLeft = millis(); pressLengthLeft = 0; dispensing = false; + last_dispense = millis(); } /////////////////////////// @@ -1425,8 +1478,8 @@ void Force::DispenseLeft() { void Force::DispenseRight() { dispensing = true; - trialRight++; - Tone(); + rewardRight++; + Tone(4000,200); float successTime = millis(); while ((millis() - successTime) < (dispense_delay * 1000)){ tft.setCursor(85, 44); @@ -1440,20 +1493,18 @@ void Force::DispenseRight() { tft.fillRect(12, 0, 38, 24, ST7735_BLACK); // clear the text after label } } - //digitalWrite(A2,HIGH); //A2 will be "reward dispensed" pin - //digitalWrite(13,HIGH); // RED LED for (int i=0; i < 20; i++) { digitalWrite(PUMP2, HIGH); - delayMicroseconds(100); + delayMicroseconds(600); digitalWrite(PUMP2, LOW); + delayMicroseconds(200); } DateTime now = rtc.now(); dispenseTime = now.unixtime(); - //digitalWrite(A2, LOW); - //digitalWrite(13, LOW); pressTimeRight = millis(); pressLengthRight = 0; dispensing = false; + last_dispense = millis(); } /////////////////////////// @@ -1473,7 +1524,6 @@ void Force::SenseLeft() { if (gramsLeft > reqLeft) { pressLengthLeft = (millis() - pressTimeLeft); } - outputValueLeft = map(gramsLeft, 0, 200, 0, 4095); @@ -1529,4 +1579,32 @@ void Force::SenseRight() { Tare(); } +////////////////////////////// +//// Prime dispense //// +///////////////////////////// + +void Force::prime_dispense() { + for (int i=0; i < 20; i++) { + digitalWrite(PUMP1, HIGH); + delayMicroseconds(600); + digitalWrite(PUMP1, LOW); + delayMicroseconds(200); + } + for (int i=0; i < 20; i++) { + digitalWrite(PUMP2, HIGH); + delayMicroseconds(600); + digitalWrite(PUMP2, LOW); + delayMicroseconds(200); + } +} + +/////////////////////////////////// +//// Check last dispense //// +////////////////////////////////// +void Force::check_lastDispense() { + if ((millis() - last_dispense) > 300*1000) { + last_dispense = millis(); + prime_dispense(); + } +} diff --git a/src/Force.h b/src/Force.h index de0009e..c75e8d1 100644 --- a/src/Force.h +++ b/src/Force.h @@ -54,8 +54,8 @@ class Force { String library_version = "undef"; // --- Basic functions --- // - void begin(); - void run(bool log_data = false); + void begin(bool log_lite = true); + void run(); void check_buttons(); void readPoke(); @@ -107,19 +107,28 @@ class Force { char filename[21]; // Array for file name data logged to named in setup const int chipSelect = 10; void CreateDataFile(); - void writeHeader(); - void WriteToSD(); + void writeHeader(bool log_lite = true); void error(uint8_t errno); void getFilename(char *filename); void logdata(); + void logdata_lite(); + void loglite_Left(); + void loglite_Right(); unsigned long unixtime = 0; + + bool log_lite = true; + bool dispensing = true; // --- Pump functions --- // float dispense_time = 0; int dispense_amount = 2000; + unsigned long last_dispense = 0; void DispenseLeft(); void DispenseRight(); + void prime_dispense(); + void check_lastDispense(); + // --- Lever functions --- // int dispense_delay = 4; @@ -152,13 +161,15 @@ class Force { int reqLeft = 2; int ratioLeft = 1; int hold_timeLeft = 350; - int trialLeft = 0; + int rewardLeft = 0; + int trials_left = 0; int pressesRight = 0; int reqRight = 2; int ratioRight = 1; int hold_timeRight = 350; - int trialRight = 0; + int rewardRight = 0; + int trials_right = 0; unsigned long pressStart = 0; int trials_per_block = 10; @@ -166,9 +177,8 @@ class Force { unsigned long start_time = 0; unsigned long lickTime = 0; unsigned long dispenseTime = 0; - int random_number = 0; - bool shock = false; - bool dispensing = false; + + String event = "None"; // --- Serial out--- // void SerialOutput();