Hi all,
I have an Arduino Uno, and I am trying to use it to control a camera and LED in an imaging system I assembled.
My goal is to use the Arduino to send trigger signals to the camera telling it to turn exposure on, wait for 1 millisecond, then send a trigger to the LED to turn on and wait 10 milliseconds with the camera and LED on, then turn LED off and wait 1 millisecond until turning the camera off as well. With both camera and LED off, I want to wait 38 milliseconds so that overall, the frame rate of the camera is 20 Hz.
In my first attempt, I used the delay() function to send HIGH signals, wait, then send LOW signals, but I found out that the delay function is not very precise. I have since tried using the millis() function to get more accurate timing, but I have found that if I record for 1 minute, the final time is off by around 80ms (might be getting delayed by the serial printing of the time itself?).
I am interesting in recording for 10-20 minutes, so being off by around 1 second is not ideal (if I could keep the error to under the 10s of milliseconds, that would be great). I attached my code below, but any assistance regarding how I could use the Arduino to get millisecond precision would be great. Thank you!
/*
Turns LEDs and cameras on and off to record fluorescent reflectance and emission
*/
#define blue_LED 9
#define xyla_CAM 7
#define DAQoutpin 11
char magic = '!';
// for 20Hz frame rate, we want 50ms overall frame time
const unsigned long idleTime = 1000; //1thousand millis means 1 second of delay before ANYTHING
const unsigned long deltaTime = 1; // in millis, THIS is timing interval for arduino doing anything (period) (1ms)
const unsigned long camOnledOffDarkframe = 12; // this is a 12ms long dark frame
const unsigned long camOnledOffInterval = 1; // 1ms delay of cam on led off
const unsigned long camOnledOnInterval = 10; // 10ms cam and led both on
const unsigned long camOffledOffInterval = 19; // 19ms (x2 is 38ms) of cam and led off
unsigned long startTime;
unsigned long currentTime;
unsigned long phaseStartTime;
const int numDarkFrames = 20; //this will be checked against darkFrameCounter
const int numLightFrames = 1200; //this will be checked against lightFrameCounter (60sec*20Hz = 1200frames)
int darkFrameCounter = 0; //updates to determine next time in which the if statement should execute
int lightFrameCounter = 0;
unsigned int time = 0; // testing
int phase = 1;
byte state = LOW; //toggle whether to collect sample
bool started = false;
// the setup function runs once when you press reset or power the board
void setup() {
pinMode(blue_LED, OUTPUT);
pinMode(xyla_CAM, OUTPUT);
pinMode(DAQoutpin, OUTPUT);
Serial.begin(115200);
// Identify device
while (true) {
if (Serial.available() > 0) {
char id_input = Serial.read();
if (id_input == magic) {
// Serial.write(deviceID,3);
Serial.println("Starting...");
Serial.println("Time, Phase");
break;
}
}
}
}
void loop() {
time = micros();
if (!started) {
startTime = millis(); // this is the time in milliseconds when we start (i.e. t=0)
started = true;
digitalWrite(DAQoutpin, HIGH);
}
else {
currentTime = millis(); // this the current time used to check against start time for loop iterations
if (currentTime - startTime >= deltaTime) { // if at least a millisecond has elapsed since entering the loop
switch (phase) {
case 0: // idle, wait 1 second before collecting dark frames
if (currentTime - startTime >= idleTime) {
phaseStartTime = currentTime;
phase = 1;
digitalWrite(xyla_CAM, LOW);
digitalWrite(blue_LED, LOW);
}
break;
case 1: // have cam and led off before collecting frame
digitalWrite(xyla_CAM, LOW);
digitalWrite(blue_LED, LOW);
if (currentTime - phaseStartTime >= camOffledOffInterval) {
phaseStartTime = currentTime;
phase = 2;
digitalWrite(xyla_CAM, HIGH);
digitalWrite(blue_LED, LOW);
}
break;
case 2: // camera on and led off for dark frame collection
digitalWrite(xyla_CAM, HIGH);
digitalWrite(blue_LED, LOW);
if (currentTime - phaseStartTime >= camOnledOffDarkframe) {
phaseStartTime = currentTime;
phase = 3;
digitalWrite(xyla_CAM, LOW);
digitalWrite(blue_LED, LOW);
}
break;
case 3: // cam and led off for inter frame interval
digitalWrite(xyla_CAM, LOW);
digitalWrite(blue_LED, LOW);
if (currentTime - phaseStartTime >= camOffledOffInterval) {
phaseStartTime = currentTime;
darkFrameCounter++; //this increases dark frame counter by 1 every time
if (darkFrameCounter >= numDarkFrames) {
phase = 4;
} else {
phase = 1;
}
digitalWrite(xyla_CAM, LOW);
digitalWrite(blue_LED, LOW);
}
break;
case 4: // starting actual light frame collection, starting w cam and led off
digitalWrite(xyla_CAM, LOW);
digitalWrite(blue_LED, LOW);
if (currentTime - phaseStartTime >= camOffledOffInterval) {
phaseStartTime = currentTime;
phase = 5;
digitalWrite(xyla_CAM, HIGH);
digitalWrite(blue_LED, LOW);
}
break;
case 5: // camera on before LED turns on
digitalWrite(xyla_CAM, HIGH);
digitalWrite(blue_LED, LOW);
if (currentTime - phaseStartTime >= camOnledOffInterval) {
phaseStartTime = currentTime;
phase = 6;
digitalWrite(xyla_CAM, HIGH);
digitalWrite(blue_LED, HIGH);
}
break;
case 6: // camera on and LED on
digitalWrite(xyla_CAM, HIGH);
digitalWrite(blue_LED, HIGH);
if (currentTime - phaseStartTime >= camOnledOnInterval) {
phaseStartTime = currentTime;
phase = 7;
digitalWrite(xyla_CAM, HIGH);
digitalWrite(blue_LED, LOW);
}
break;
case 7: // camera on and LED off
digitalWrite(xyla_CAM, HIGH);
digitalWrite(blue_LED, LOW);
if (currentTime - phaseStartTime >= camOnledOffInterval) {
phaseStartTime = currentTime;
phase = 8;
digitalWrite(xyla_CAM, LOW);
digitalWrite(blue_LED, LOW);
}
break;
case 8: // camera off and LED off, update counter
digitalWrite(xyla_CAM, LOW);
digitalWrite(blue_LED, LOW);
if (currentTime - phaseStartTime >= camOffledOffInterval) {
phaseStartTime = currentTime;
lightFrameCounter++; //this increases light frame counter by 1 every time
if (lightFrameCounter >= numLightFrames) {
phase = 9;
digitalWrite(DAQoutpin, LOW);
} else {
phase = 4;
}
digitalWrite(xyla_CAM, LOW);
digitalWrite(blue_LED, LOW);
}
break;
case 9: // make sure everything turns off
digitalWrite(xyla_CAM, LOW);
digitalWrite(blue_LED, LOW);
break;
}
//if (phase != 9) {
//Serial.println(String(currentTime - startTime) + "," + String(phase));
//}
}
}
if (phase = 9){
Serial.println(String(currentTime - startTime));
}
//Serial.println(time, DEC);
//Serial.println(String(currentTime - startTime));
}