Perhaps this topic is too specific, but I've run out of options. A vague description of what I'm doing: I've designed a behavioral apparatus for an ongoing experiment, wherein a triggering event activates of one of several relays and linked solenoid valves for different physical outputs. These outputs are then logged via a Processing sketch via the serial port. I'm simultaneously monitoring two behavioral apparatuses, so the timing of these events is controlled using the millis() function, and recording the initiation time of each event.
My setup:
My issues / questions:
-
All of the stated time intervals (airTime, waterTime, timeOut, etc.) appear to be doubled when I run the code. Interestingly, when I output the time differences between solenoid open and close, they read as expected (152-154ms, in the case of waterTime, for instance). Is there any obvious reason why this would be the case?
-
I occasionally lose serial communication, but the arduino otherwise continues to run as expected (continues to activate relays, etc). Maybe I'm just eating up too much SRAM?
The code:
//set pins
const int nosePk1 = 2; // nosepoke output pin (pin 1 from the nosepoke)
const int fill1 = 3; //water purge button
const int irPin1 = 4; // nosepoke IR beam
const int airRelay1 = 7; //relay 1 on sainsmart board, will dispense air
const int waterRelay1 = 6;
const int clickRelay1 = 5;
const int scheduleSwitch1 = 8; // reward schedule switch
const int pressureSensor = A5; // MPX4250A
//Omron pins (obfuscated)
//Note: Omron IR LED is on by default, and reads LOW in event of poke
// used to monitor beam-breaking
int pokeState1 = LOW;
int prevPokeState1 = LOW;
int pokeState2 = HIGH; //note that Omron reads opposite
int prevPokeState2 = HIGH;
long lastWater1 = 0;
long lastAir1 = 0;
long lastClick1 = 0;
long lastWater2 = 0;
long lastAir2 = 0;
long lastClick2 = 0;
long rightNow;
//all times doubled. Unclear why.
const int waterTime = 150; //water solenoid open time (ms)
const int airTime = 250; //air solenoid
const int clickTime = 150; //click solenoid
const int timeOut = 1000; //time-out period for pokes
long lastPoke1 = 0; //holds time (in ms) of last nose-poke
long lastPoke2 = 0;
//for reward activation and recording (not important)
//schedules determined here
//for random clicking
long intrvl;
long lastClick = 0;
const long maxInterval = 600000;
void setup()
{
Serial.begin(9600);
// initialize the outputs
pinMode(irPin1, OUTPUT);
pinMode(airRelay1, OUTPUT);
pinMode(waterRelay1, OUTPUT);
pinMode(clickRelay1, OUTPUT);
pinMode(airRelay2, OUTPUT);
pinMode(waterRelay2, OUTPUT);
pinMode(clickRelay2, OUTPUT);
//intialize buttons/inputs
//prevents signal leaking, other issues
//uses pull-up resistor
pinMode(scheduleSwitch1, INPUT_PULLUP);
pinMode(scheduleSwitch2, INPUT_PULLUP);
pinMode(fill1, INPUT_PULLUP);
pinMode(fill2, INPUT_PULLUP);
//close all of the relays
digitalWrite(airRelay1, HIGH);
digitalWrite(waterRelay1, HIGH);
digitalWrite(clickRelay1, HIGH);
digitalWrite(airRelay2, HIGH);
digitalWrite(waterRelay2, HIGH);
digitalWrite(clickRelay2, HIGH);
// initialize the inputs (nosepokes)
pinMode(nosePk1, INPUT);
pinMode(nosePk2, INPUT);
// IR LED left on continuously for monitoring
digitalWrite(irPin1, HIGH);
//set up random integer generation
//A0 must be left empty for random seeding!
randomSeed(analogRead(A0));
intrvl = random(maxInterval);
}
void loop()
{
//close solenoids, as necessary. Will perform periodically even without input.
rightNow = millis();
if ((rightNow - lastWater1) >= waterTime)
{
digitalWrite(waterRelay1, HIGH);
}
if ((rightNow - lastAir1) >= airTime)
{
digitalWrite(airRelay1, HIGH);
}
if ((rightNow - lastClick1) >= clickTime)
{
digitalWrite(clickRelay1, HIGH);
}
if ((rightNow - lastWater2) >= waterTime)
{
digitalWrite(waterRelay2, HIGH);
}
//get the current time
rightNow = millis();
pokeState1 = digitalRead(nosePk1);
pokeState2 = digitalRead(nosePk2);
// Island motion monitoring
// Reads HIGH during poke, else LOW
if (pokeState1 != prevPokeState1)
{
//if the beam is broken, deliver reward, etc.
if (pokeState1 == HIGH && (rightNow - lastPoke1) >= timeOut)
{
cage = 1;
//set last poke to current time
toFailOrNotToFail = random(100) + 1; //returns number between 1 & 100, thereby making response probabilistic
//theoretically runs "failRate" times in 100 trials
if (toFailOrNotToFail <= failRate)
{
dispensation1 = dispense(failSchedule1, cage); //logs failure, dispenses click
}
//theoretically runs (100-failRate) times in 100 trials
else
{
dispensation1 = dispense(curSchedule1, cage); //otherwise: dispense standard schedule, log it
}
prevPokeState1 = HIGH;
dispensation1 = dispensation1 + String(cage); //include cage number of analysis
}
// if the beam has been restored, collect remaining data
else if(pokeState1==LOW)
{
lastPoke1 = millis();
dispensation1 = dispensation1 + "," + String(holdPressure) + "," + String(minPressure);
Serial.println(dispensation1);
dispensation1 = "";
minPressure = 700000;
prevPokeState1 = LOW;
holdPressure = (getPressure() + getPressure() + getPressure()) / 3;
}
}
// Omron monitoring
// Reads LOW during poke, else HIGH
//otherwise identical to above
//if interval has completed, send a random click
if ((rightNow - lastClick) >= intrvl)
{
dispense(randSchedule,1); //cage number doesn't matter in this case
intrvl = random(maxInterval);
lastClick = millis();
Serial.println("RC");
}
delay(10); //absolutely necessary for normal operation
}
String dispense(boolean valves[6], int cageNumber)
{
//for recording rewards dispensed
String airDispensed = "FALSE";
String waterDispensed = "FALSE";
String clickDispensed = "FALSE";
float holdPressure = 0; //all will read as 0 for Omron cage
float minPressure = 0;
String dispensed;
//runs if valves[0] (air valve) is true
//There will never be simualtaneous cage activation, so redundancy is OK
if (valves[0])
{
//activate valve 1 (island motion air valve)
digitalWrite(airRelay1, LOW);
lastAir1 = millis();
airDispensed = "TRUE";
}
if (valves[1])
{
//activate valve 2 (water valve)
digitalWrite(waterRelay1, LOW);
lastWater1 = millis();
waterDispensed = "TRUE";
}
if (valves[2])
{
//activate valve 3 (empty valve)
digitalWrite(clickRelay1, LOW);
lastClick1 = millis();
clickDispensed = "TRUE";
}
if (valves[3])
{
digitalWrite(airRelay2, LOW);
lastAir2 = millis();
airDispensed = "TRUE";
}
//you get the idea
dispensed = airDispensed + "," + waterDispensed + "," + clickDispensed + ",";
if (cageNumber == 1)
{
dispensation1 = dispensed;
}
else
{
dispensation2 = dispensed;
}
return dispensed;
}
I'm somewhat of a novice to this, so I'm happy to take any feedback you may have to offer on style, etc.! Any help you may have to offer would be greatly appreciated.