Timing non-regular events with millis() -

I'm using millis() to sequence dslr camera shutter events, avoiding delay because PWM is running a heater and/or cooler simultaneously.

Timing of the shutter declares timer = millis() at the beginning of the loop and adds the previous action time to the next and so on. Psuedo code below.

void integrate(){

// capture images here

  if(shutter == LOW) // must be declared or Timer routine will not work
    Timer = millis();  
  if(millis() - Timer >= 3000)
    digitalWrite(shutter, HIGH); // lock up the dslr mirror
  
  if(millis() - Timer >= 3500) // wait a while for vibrations to settle
    digitalWrite(shutter, LOW);

  if(millis() - Timer >= 6500)
    digitalWrite(shutter, HIGH); // fire the shutter again to start image capture
   
  if(millis() - Timer >= (6500 + integration)) // that done, now dither - point camera a little off from last image
    digitalWrite(shutter, LOW);

dither();
}

void dither(){

Timer = millis(); // must declare here or wont work

dither routine here then return to shutter sequence for next exposure

integrate();
}

This works fine except for missing the occasional mirror lock up, which is not ideal but tolerable. Unless Timer = millis(); is declared in void dither() no further action takes place.

When I attempt the same routine in a different loop (where it takes calibration frames) the same routine will not work and ignores the shutter timing, basically bringing everything to a halt. Same code as above, different loop location, does not work.

This is odd because Timer is just taking on the value of millis - when it is called, shouldn't matter.

What am I missing here?

Which part of your code is in the loop() function ?
Seeing your proper code would be helpful in understanding the problem. Something that you consider irrelevant may be having an effect.

geoland:
Psuedo code below.
...
What am I missing here?

What we are missing is the real code.

OK. Here it is. You will notice that I am using delay() in void calibration() because I can't get millis() Timer to work. I'm a beginner so I imagine the code is a bit clunky. I've tried to set it out as neatly as possible.

#include <Stepper.h> // include stepper library
#include <DHT.h> // include dht library

/* Stable version - This code implements millis() as a Timer to sequence DSLR shutter activation avoiding the use of delay(), automating image capture, calibration frame acquisition and PWM lens dew heater and stepper driven dither mechanism. An LED is used to indicate the status of dew heater operation; that is, failure of the dht module (low level heat cycle). To be implemented - interruption to imaging sequence warning...
 */

//set integration time

unsigned long integration = 5000UL; 

//framecount

long framecount = 40;

//wait time before start

unsigned long wait = 7000UL;

//repeat light frames

bool repeat_integrate = false;

//dark frames - yes no

bool darkframes = true;

//calibration

#define biascount 5 //50;
#define flatcount 3 //10;
#define darkcount 3 //10;
int biastime = 10;
int flattime = 100;
int darktime = 3000; 
int biasf = 0;
int flats = 0;
int darks = 0;

//camera settings

int mirror = 3000; // mirror lock-up time
int mkii = 500; // 5D MKII mirror lock-up
int xs = 100; // 1000D mirror lock- up
int settle = 3000; // pause between mirror and shutter
int dithert = 2000; // time before dither

//dither parameters

int frames = 0;
int steps;

//pin allocations

#define pin2 2
#define pin3 3
#define shutter 8
#define pwmPin 9
#define cap 11
#define ledPin 12

int pinval2;
int pinval3;

//time

unsigned long Timer;

//dew heater

#define DHTPIN 10 // pin 10

#define DHTTYPE DHT22

int Td; // dew point

DHT dht(DHTPIN, DHTTYPE);

//motor

#define motorSteps 200

#define motorspeed 5

#define motorPin1 4
#define motorPin2 5
#define motorPin3 6
#define motorPin4 7

Stepper stepper(motorSteps, motorPin1,motorPin2,motorPin3,motorPin4);

//dither pattern
#define steps0 50
#define steps1 50
#define steps2 -50
#define steps3 50
#define steps4 50
#define steps5 -50
#define steps6 50
#define steps7 50
#define steps8 -50
#define steps9 50
#define steps10 -50
#define steps11 50
#define steps12 50
#define steps13 -50
#define steps14 50
#define steps15 -50
#define steps16 50
#define steps17 50
#define steps18 -50
#define steps19 50
#define steps20 -50
#define steps21 50
#define steps22 -50
#define steps23 50
#define steps24 50
#define steps25 -50
#define steps26 50
#define steps27 -50
#define steps28 50
#define steps29 -50
#define steps30 50
#define steps31 50
#define steps32 -50
#define steps33 50
#define steps34 -50
#define steps35 50
#define steps36 -50
#define steps37 50
#define steps38 -50
#define steps39 50

//integration starts

void integrate(){

  if(shutter == LOW) //shutter is low
    Timer = millis();   
  if(millis() - Timer >= wait) // switch debounce lock-up mirror
    digitalWrite(shutter, HIGH);

  if(millis() - Timer >= (wait + mkii))
    digitalWrite(shutter, LOW);

  if(millis() - Timer >= (wait + mkii + settle)) //settle time for mirror slap - fire shutter
    digitalWrite(shutter, HIGH);

  if(millis() - Timer >= (wait + mkii + settle + integration)) //stop integration 
    digitalWrite(shutter, LOW);

  if(millis() - Timer >= (wait + mkii + settle + integration + dithert))

    dither(); 
}

void dither(){

  Timer = millis();

  frames++;  

  Serial.println(frames);

  if (frames == 1)
    steps = steps0;
  if (frames == 2)
    steps = steps1;
  if (frames == 3)
    steps = steps2;
  if (frames == 4)
    steps = steps3;
  if (frames == 5)
    steps = steps4;
  if (frames == 6)
    steps = steps5;
  if (frames == 7)
    steps = steps6;
  if (frames == 8)
    steps = steps7;
  if (frames == 9)
    steps = steps8;
  if (frames == 10)
    steps = steps9;
  if (frames == 11)
    steps = steps10;
  if (frames == 12)
    steps = steps11;
  if (frames == 13)
    steps = steps12;
  if (frames == 14)
    steps = steps13;
  if (frames == 15)
    steps = steps14;
  if (frames == 16)
    steps = steps15;
  if (frames == 17)
    steps = steps16;
  if (frames == 18)
    steps = steps17;
  if (frames == 19)
    steps = steps18;
  if (frames == 20)
    steps = steps19;
  if (frames == 21)
    steps = steps20;
  if (frames == 22)
    steps = steps21;
  if (frames == 23)
    steps = steps22;
  if (frames == 24)
    steps = steps23;
  if (frames == 25)
    steps = steps24;
  if (frames == 26)
    steps = steps25;
  if (frames == 27)
    steps = steps26;
  if (frames == 28)
    steps = steps27;
  if (frames == 29)
    steps = steps28;
  if (frames == 30)
    steps = steps29;
  if (frames == 31)
    steps = steps30;
  if (frames == 32)
    steps = steps31;
  if (frames == 33)
    steps = steps32;
  if (frames == 34)
    steps = steps33;
  if (frames == 35)
    steps = steps34;
  if (frames == 36)
    steps = steps35;
  if (frames == 37)
    steps = steps36;
  if(frames == 38)
    steps = steps37;
  if (frames == 39)
    steps = steps38;
  if (frames == 40)
    steps = steps39;   

  // run stepper
  stepper.setSpeed(motorspeed);
  stepper.step(steps);

  integrate();
}

//calibration frames - flat, bias and dark

void calibration() { 

  if(flats < flatcount) { 
    digitalWrite(cap, HIGH);
    flat();
  }
  else
  { 
    if (biasf < biascount && flats == flatcount) {
      digitalWrite(cap, LOW);
      bias();
    }
    else
    {
      if (darkframes == false) {
        end();
      }
      else
      {
        if(darks < darkcount && biasf == biascount && flats == flatcount)
          dark(); 
        if (darks >= darkcount) {
          end();
        }
      }
    }
  }
}

void flat() {  

  delay(1000);

  // mirror lock up 
  digitalWrite(shutter, HIGH); 
  delay(mkii);

  // reset shutter_pin LOW 
  digitalWrite(shutter, LOW);

  // wait for mirror to settle 
  delay(mkii);

  // fire shutter 
  digitalWrite(shutter, HIGH); 
  delay(flattime);

  // close shutter 
  digitalWrite(shutter, LOW);

  flats++; 
}

void bias() {

  delay(1000);

  // mirror lock up 
  digitalWrite(shutter, HIGH); 
  delay(mkii);

  // reset shutter 
  digitalWrite(shutter, LOW);

  // fire shutter 
  digitalWrite(shutter, HIGH); 
  delayMicroseconds(biastime);

  // close shutter 
  digitalWrite(shutter, LOW);

  biasf++;
}

void dark() {

  delay(1000);

  // mirror lock up 
  digitalWrite(shutter, HIGH); 
  delay(mkii);

  // reset camera_pin LOW 
  digitalWrite(shutter, LOW);

  // fire shutter 
  digitalWrite(shutter, HIGH);

  delay(darktime);

  // close shutter 
  digitalWrite(shutter, LOW);

  darks++;
}

//pause

void pause() {

  digitalWrite(shutter, LOW);
  digitalWrite(cap, LOW);
  stepper.setSpeed(0);
  stepper.step(0); 
}

//stop

void end() {

  digitalWrite(shutter, LOW);
  digitalWrite(cap, LOW); 
  stepper.setSpeed(0);
  stepper.step(0);

  while (1) {
  }
}

void setup(){ 

  // hardware switch - set as input
  pinMode(pin2, INPUT);
  pinMode(pin3, INPUT);

  // pull-up resistors
  digitalWrite(pin2, HIGH);
  digitalWrite(pin3, HIGH);

  // set as output  
  pinMode(shutter, OUTPUT);
  pinMode(cap, OUTPUT); 
  pinMode(ledPin, OUTPUT);

  Serial.println("DHT test");

  dht.begin();

  Serial.begin(38400);
}

void loop() {

  //pause

  pinval2 = digitalRead(pin2);
  pinval3 = digitalRead(pin3);
  while (pinval2 == HIGH && pinval3 == LOW) {
    pause();
    break;
  }

  //imaging session integration and dither - exits on pause

  pinval2 = digitalRead(pin2);
  pinval3 = digitalRead(pin3);
  if (pinval2 == HIGH && pinval3 == HIGH) {
    integrate();
  }

  //calibration

  pinval2 = digitalRead(pin2);
  pinval3 = digitalRead(pin3);
  Serial.print(pinval2);
  Serial.println(pinval3);
  if (pinval2 == LOW && pinval3 == LOW) {
    calibration();    
  } 

  //end session automatically on light frame count

  if (frames >= framecount) {
    end();
  }
  else // keep going
  {
    if (repeat_integrate == true)
      integrate();
  }

  // read humidity and temp

    float h = dht.readHumidity();
  float t = dht.readTemperature();

  // are returns valid NaN

  if (isnan(t) || isnan(h)) {

    Serial.println("Failed to read from DHT");

    // set a low level PWM value

    analogWrite(pwmPin, 127);
    digitalWrite(ledPin, HIGH);
  }
  else 
  {
    // calculate dew point

    Td = t - ((100 - h)/5);

    Serial.print("Dewpoint: ");
    Serial.println(Td);

    // maintain dew point plus 4C

    int pwmD = (255 / 100)*h;

    if(t <= Td + 4) {

      analogWrite(pwmPin, 200);
    }
    else
    {
      if(t > Td + 4) {

        analogWrite(pwmPin, pwmD);  
      }
    }
  }
}
//dither pattern
#define steps0 50
#define steps1 50
#define steps2 -50
#define steps3 50
#define steps4 50
#define steps5 -50
#define steps6 50
#define steps7 50
#define steps8 -50
#define steps9 50
#define steps10 -50
#define steps11 50
#define steps12 50
#define steps13 -50
#define steps14 50
#define steps15 -50
#define steps16 50
#define steps17 50
#define steps18 -50
#define steps19 50
#define steps20 -50
#define steps21 50
#define steps22 -50
#define steps23 50
#define steps24 50
#define steps25 -50
#define steps26 50
#define steps27 -50
#define steps28 50
#define steps29 -50
#define steps30 50
#define steps31 50
#define steps32 -50
#define steps33 50
#define steps34 -50
#define steps35 50
#define steps36 -50
#define steps37 50
#define steps38 -50
#define steps39 50

Without even looking any further, this just screams for a code rewrite.

OK. I looked further. I was right. An array of values looks to be in order.

Some comments in that code would be good. It's hard to tell what the problem might be because there is nothing that says what the code is supposed to be doing.

  while (pinval2 == HIGH && pinval3 == LOW) {
    pause();
    break;
  }

This use of a while loop is bizarre, to say the least. A simple if statement would be a lot more appropriate.

Calibration is usually a one time thing, not done every pass through loop. Given that that code is full of delay()s, and is blocking, using millis() anywhere else seems pointless.

Timer is a lousy name for a variable. Giving is a meaningful name might help.

On the other hand, probably not. integrate() seems to expect that some waiting will somehow happen before the code continues. That is not the purpose of replacing delay() with millis(). The idea is that integrate() will be called every time through loop(), and that it will cause things to happen at the right time, in a non-blocking fashion. But, given that the rest of loop() calls stuff that is blocking, the events that integrate() (another horrid name, by the way) is supposed to be causing to happen are not going to happen at the right times.

Yes Paul, I realise its not the best. The post exceeded the character limit and comments were abbreviated.

As you can see the 3 main functions, integration, calibration and pause are called from loop - a 3 way sliding switch.

integrate(){ sequences the camera shutter. First, a wait time for switch debounce, next lock up the camera mirror, then wait again to let mirror settle, before firing the shutter open. When the exposure is complete the camera is repositioned by 10 - 15 sensor pixels. As the imaging session progresses, the camera describes a widening spiral. Repositioning the camera mount is performed by a stepper motor, operating in a specific pattern of steps and direction. The resulting image stack is offset frame by frame in such a way as to increase the SNR and very effectively diminish or remove sensor and optical artifacts. Frankly, I could not find a way to do this, except the table. I am working out how to do this better, but for now I want to remove delay()'s from the sketch so that they don't interfere with analog reading of temperature and humidity sensors, used for heating and eventually cooling temperature control of various ancillary services, associated with my photographical setup.

If I understand your expert jargon, correctly, I should call the shutter and dither functions from within void loop() { which would be non-blocking. As I understand it, from your explanation, the various loops are blocking and should only contain the specific digitalWrite action for each sequence called from the main loop? If that's what you mean, I get it. I see the flaw.. I was expecting different behaviour.

What about the dither table? As you may see, I want the sequence and pattern of image capture and dithering to be just so!

Calibration is no different, except that dithering is not needed. Flats are taken with an EL panel over the lens. This acts as a lens cover when switched off, to take the bias and dark frames. That is, EL panel on, take flat images, turn EL panel off, take bias and dark current images - end()

The PWM lens heater is running in the background, in loop. This could be a temperature regulated cooling system for the camera sensor or both.

I agree with PaulS here. Rewrite with some arrays and you will be able to better see what is going on.

Tips about asynchronous processing: Gammon Forum : Electronics : Microprocessors : How to do multiple things at once ... like cook bacon and eggs

Thanks Nick - from sunny Geelong. The flasher library might be what I'm looking for, I cant see how to achieve asynchronous switching using blink without delay or millis for that matter, hence marking the time with millis and adding the desired intervals end on end. That works fine unless you want to vary the timing of the shutter 4 or 5 times in the same loop and switch out of one operation to another - add another switch? Might be easier.

geoland:
Thanks Nick - from sunny Geelong.

It's been sunny all around ... today. :wink:

I wish : :slight_smile: