Timelapse controller for Science!

Hello all,
I am seeking guidance on a sort of time-lapse project I have been working on. First off, I am a complete beginner with arduino, just as disclaimer.

My goal has been to make a simple device to control a set of grow lights for specific on/off intervals. The application is for time-lapse microscopy of cyanobacteria, which grow best under high light conditions.

General setup is with an arduino uno, small lcd display, and a power switch tail II. The program starts after a single button press.

My first prototype worked alright, I could get the lights on and off for different periods of time (1min off/ 14min on), but have been running into the issue that the timing on the arduino desynchronizes with the microscope control. Ie. the arduino isn't accurately measuring time. I've figured out that this is most likely because I have been relying on delay() to track seconds.

The basic structure of the code was:

-check for button press in void loop()
-when button is pressed, start the off interval, which is a separate function
-run the off interval for 1 minute using a while() loop,

  • seconds counted for each loop with delay (1000)
  • print a countdown timer to lcd
    -when the off interval ends, start the on interval, another separate function
  • operates in the same manner as the off interval

I have been trying to use millis() instead of delay() to fix the problem and get accurately timed intervals, but am running into the problem that I can't reset the millis() timer at the start of each the interval functions. Since I ultimately need this to work over 30+ hours of timelapse, I need a robust but preferably simple solution.

I'm getting a bit lost with this. If anyone could offer some guidance or advice I'd be very appreciative!

As i understand from your description, what you want to achieve is a state machine that corresponds to the attached state diagram. Is that correct?

time lapse state diagram.png

You never have to reset millis(). You just check the difference from one moment to the next. See the demo several things at a time. That code works correctly even when millis() rolls over from 232 to 0

You have not siad by how much the Arduino is deviating from your microscope. The Arduino is not an accurate timekeeper and you may need to use a Real Time Clock module to keep it in sync.

And, of course, the other options may be ( A ) to use the Arduino to control the microscope or ( B ) to have the Arduino take its cue from the microscope.

...R

Thank you much for the suggestions. I have done my best to incorporate the use of millis() and multiple functions, but am not making much progress....

I can start the program on a button push, but the timing and display of the countdown doesn't work. I get a rapid countdown, less than 5s to the on state, and then a massive number for counting down to the off state.

Ultimately I need this to be able to cycle back and forth between a 14 min lights on state, and a 1 min lights off state, while displaying the countdown to the next switch.

Any advice would be a huge help, as I need this up and running asap for an experiment I am scheduled to run next week!! Haha.

I have copied my program below:

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR 0x27 // <<----- Add your address here. Find it from I2C Scanner
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7

int n = 1;
int x = 0;

LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

//--------constant variables--------//

const int buttonPin = 2; // the number of the pushbutton pin
const int lightPin = 13; // the number of the light pin
const int on_Interval = 5000; // time for lights on in ms
const int off_Interval = 5000; // time for lights off in ms

//-------variables--------//

int buttonState = LOW;
int light_State = LOW;
int lastButtonState = buttonState;

unsigned long currentMillis = 0; // this will store the value of millis() in each iteration
unsigned long previous_LightMillis = 0; // stores last time the light state was updated

boolean running = false;

//==========================

void setup() {
lcd.begin (20,4); //
lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);

Serial.begin(9600);
// initialize the LED pin as an output:
pinMode(lightPin, OUTPUT);
// initialize the pushbutton pin as an input, with pullup resistor to ensure default to HIGH
pinMode(buttonPin, INPUT_PULLUP);
}

//=========================

void loop() {
int buttonState = digitalRead (buttonPin);
if (buttonState != lastButtonState) {
if (buttonState = HIGH) {
delay (200);
running =! running;
lcd.setBacklight(HIGH);
lcd.home ();
}
}
if (running == true) {
currentMillis = millis();
updateLightState();
lightswitch();
lcd.setCursor (4,0);
lcd.print ("Status: ON");
lcd.setCursor (2,1);
lcd.print ("Interval:");
}
}

void updateLightState() {
if (light_State == LOW){
lcd.setCursor (10,1);
lcd.print (off_Interval);
lcd.setCursor (1,2);
lcd.print ("Remaining:");
lcd.print ((off_Interval) - currentMillis);
if (currentMillis - previous_LightMillis >= (off_Interval)){
light_State = HIGH;
previous_LightMillis += off_Interval;

}
else {
if (currentMillis - previous_LightMillis >= (on_Interval)){
light_State = LOW;
previous_LightMillis += on_Interval;
}
}
}
}

//====================

void lightswitch() {
digitalWrite(lightPin, light_State);
digitalWrite(buttonPin, buttonState);
}

void loop() {
	currentMillis = millis();
	updateLightState();
	lightswitch();
}

void updateLightState() {
	if (light_State == LOW){
		if (currentMillis - previous_LightMillis >= (off_Interval)){
			light_State = HIGH;
			previous_LightMillis += off_Interval;
		}
	}
	else {
		if (currentMillis - previous_LightMillis >= (on_Interval)){
		  light_State = LOW;
		  previous_LightMillis += on_Interval;
		}
	}
}

I simplified this so I could see what is happening. I think in your version you have missed a } before the ELSE so that it was not part of the correct IF. That may have happened because your indentation was not very clear.

I notice that you still have a delay(200) - why not replace that with millis() also?

Also, please make it easier for people to help by using the code button (the scroll with the <>)

...R