More on running a task until triggered to do otherwise

In the recent thread Run a task until triggered to do otherwise? I tried the various helpful suggestions made here, and a few others following further research. But at this stage of my learning I’m still finding state-based and other advanced methods hard-going. So, impatient to have it finished, I used my own IF-based approach (plus a bit of millis). I find this more intuitive. I’ve also moved from the LED analogy to my actual servo-based project.

Hopefully my heavily commented code shows what I want it to do. The remaining exception that’s so far defeated me is this: directly after a low trigger on D6 has caused the servo tasks to run (which take about 30s), it should start timing the 160s afresh. But instead it seems to take another ‘keep-alive’ photo a few second later, not 160s.

I know it will prove to be something very simple but hours in I still haven’t spotted it!

Terry

// Friday 13 November 2020, 16:34, house;
// Using a servo to press a CUBE camera button, this sketch takes a photo at intervals
// (say 160s or 10/20s for tests) UNLESS D6 triggered tasks are in progress.
// Otherwise the camera automatically powers off after 3 mins of non-activity.
// It uses pin 10 (green LED) to indicate that the tasks triggered by pin 6 are underway.
// The green LED is acting as a flag, but is also a useful visual aid while testing.
// Pin 4 input sets the video duration, typically 30s (5-8s while testing).
// Pin 6 input LOW triggers the tasks (servo actions to record a video).
// Pin 8 LED HIGH when CUBE video recording
// Pin 9 is CUBE servo control
// Pin 10 drives the green LED

#include <VarSpeedServo.h> // Uses the VarSpeedServo library 

// Create servo object
VarSpeedServo myCUBEservo;

const byte outPosCUBE = 35; // Trial & error; needs regular re-checking
const byte inPosCUBE = 15; // Ditto

const byte triggerPin = 6;
const byte redledPin = 8;
const byte greenledPin = 10; // (Green LED on bb)
const byte switchPin = 4;
int videoLength; // Declared here, then set in Setup

// From 'Basic-goTime' sketch
int nextTime = 160000;  // Do this every 20s
long int goTime;

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

void setup()
{
  Serial.println("CUBE powered up and recovery delay ended");
  Serial.begin(115200);
  Serial.println("");
  Serial.println("CUBE-VideosPhotosKA-13Nov20");

  pinMode(switchPin, INPUT_PULLUP);
  pinMode(triggerPin, INPUT_PULLUP);
  pinMode(redledPin, OUTPUT);
  pinMode(greenledPin, OUTPUT);

  // Initialise the CUBE servo
  myCUBEservo.write(outPosCUBE); // Initial arm pos'n
  delay(100);
  myCUBEservo.attach(9); // White wires
  Serial.println("CUBE servo attached");
  // Test D4 to choose value of videolength
  if (digitalRead(switchPin) == HIGH)
  {
    videoLength = 30000;
  }
  else
  {
    videoLength = 6000;
  }
  Serial.print("Video length = ");
  Serial.print(videoLength/1000);
  Serial.println(" seconds");
  Serial.print("outPosCUBE/inPosCUBE = ");
  Serial.print(outPosCUBE);
  Serial.print("/");
  Serial.println(inPosCUBE);

  // Power up CUBE, which must be OFF before sketch started
  CUBEPowerUp();
  // Allow a long delay for battery recovery in case triggered
  // very soon after
  delay(2500);
  
  Serial.println("Setup finished; waiting for low D6");
  Serial.print("But also taking 'keep-alive' photos at intervals of ");
  Serial.print(nextTime/1000);
  Serial.println(" seconds, UNLESS the triggered tasks are in progress");
  Serial.println("--------------------------------------------");
  // From 'Basic-goTime' sketch
  goTime = millis(); // Start time
}

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

void loop()

{
  if (millis() >= goTime)
  {
    TakePhoto();
    Serial.println("Keep-alive photo taken");
  }
  
  // Run only when a LOW on D6 is detected
  if (digitalRead(triggerPin) == LOW)
  {
    Serial.println("Triggered by D6; TASKS STARTED");
    // TASK #1: Take greenledPin (D10) high, indicating triggered taskw started
    digitalWrite(greenledPin , HIGH);
    // TASK #3: Start CUBE video with two presses
    CUBESingleButtonPress();
    delay(100);
    // TASK #4: Start video with two presses
    CUBESingleButtonPress();
    Serial.println("Started CUBE video");
    digitalWrite(redledPin , HIGH) ;
    // TASK #5: Delay for recording videos
    delay(videoLength); // ACTUAL individual durations will be different
    // TASK #6: Stop CUBE video with single press
    CUBESingleButtonPress();
    digitalWrite(redledPin , LOW) ;
    Serial.println("CUBE video ended");
    // TASK #8: Take greenledPin (D10) low, indicating triggered task ended
    digitalWrite(greenledPin , LOW);
    delay(2000); // Brief pause while CUBE powering down, may be redundant
  }
  exit(0); // To temporarily stop the sketch looping; Comment out to run
}

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

void CUBEPowerUp()
{
  // Power up CUBE with long press > 3 s
  myCUBEservo.write(inPosCUBE);
  delay(3500); // Press for > 3s to toggle power
  myCUBEservo.write(outPosCUBE);
  delay(200);
}


void CUBESingleButtonPress()
{
  // Take photo or stop video with single button press
  myCUBEservo.write(inPosCUBE);
  delay(200);
  myCUBEservo.write(outPosCUBE);
  delay(200);
}

void TakePhoto()
{
  // Take a photo with CUBE: single press, except if video tasks underway
  CUBESingleButtonPress();
  goTime = millis() + nextTime; // set when to do it again
}
EXTRACT FROM SERIAL MONITOR

14:45:21.958 -> CUBE-VideosPhotosKA-13Nov20
14:45:22.051 -> CUBE servo attached
14:45:22.051 -> Video length = 30 seconds
14:45:22.051 -> outPosCUBE/inPosCUBE = 35/15
14:45:28.225 -> Setup finished; waiting for low D6
14:45:28.225 -> But also taking 'keep-alive' photos at intervals of 28 seconds, UNLESS the triggered tasks are in progress
14:45:28.225 -> --------------------------------------------
14:45:28.648 -> Keep-alive photo taken
14:45:57.975 -> Keep-alive photo taken
14:46:27.302 -> Keep-alive photo taken
14:46:56.617 -> Keep-alive photo taken
14:47:25.971 -> Keep-alive photo taken
14:47:55.296 -> Keep-alive photo taken
14:48:24.615 -> Keep-alive photo taken
14:48:53.959 -> Keep-alive photo taken
14:49:08.224 -> Triggered by D6; TASKS STARTED
14:49:09.161 -> Started CUBE video
14:49:39.568 -> CUBE video ended
14:49:41.931 -> Keep-alive photo taken

The immediate problem is that you probably intended the last line in loop to be:

  goTime = millis() + nextTime; // set when to do it again

Also, depending on which Arduino you're using, 160000 may not fit in an int.

Thanks wildbill, look forward to fixing it first thing in the morning. So I should delete it from the TakePhoto function, where I'd put it, yes?

That's one of the trickiest aspects for me at present: knowing exactly where a key command should be executed.

Terrypin:
Thanks wildbill, look forward to fixing it first thing in the morning. So I should delete it from the TakePhoto function, where I'd put it, yes?

Probably not. I suspect you'll want it in both places.

Incidentally, adding numbers to time variables is not recommended. When millis rollover happens at ~49 days, such things won't work well. Worry about that later though.

It seems my original version was correct, confirmed by testing the various possibilities I've summarised below. Directly after taking a photo, at time millis(), captured as goTime, the next one should be taken at goTime + nextTime. On reflection my variable name wasn't a good choice; 'interval' would have been better than 'nextTime'!

(Note: by 'Never?' I mean I waited for about 30-40s before assuming another photo would not be taken.)

You were right about 160000 being OTT for int, thanks. I've corrected it to long int.

Anyway, all that leaves the flaw I raised unresolved, so any further thoughts would be appreciated please.

P.S. Re adding to time variables, rather than the recommended subtraction, it's rare if any of my home hobby projects run for more than a day, much less 49/50!

Set ms	When again?		Location		Observed interval
------	------------------	-----------------------	-----------------
10000	goTime = millis();	TakePhoto (my original)	10s (correct)
10000	goTime = millis();	End of main loop	Never?
10000	goTime = millis();	Both			Never?
			
10000	goTime = millis() 	TakePhoto (my original)	20s
10000	goTime = millis() 	End of main loop	Never?
10000	goTime = millis() 	Both			Never?

Can you post the latest code?

Pleased to report that I’ve now fixed that remaining flaw. I’ve highlighted the extra line in the code below. (Well, I tried, but my blue colouring hasn’t shown up in Preview. It’s at line 126.) As you see, it was simple - once the penny had dropped. Although my test has been brief, I’m pretty sure it’s now ready for an overnight run.

Note: I fixed one other minor flaw that I didn’t mention in my opening post. The first photo was being taken almost immediately after setup, which was obviously unwanted; the camera had just been powered up and would not need ‘keeping alive’ for about another three mins.

P.S: I’m now trying to show timestamps in the serial monitor for videos started and photos taken, in hh:mm:ss since program was begun or reset, rather than raw seconds. Looking for sources to save re-inventing it myself. :wink:

// Sunday 15 November 2020, house;
// PURPOSE of SKETCH:
// Record videos with CUBE camera, triggered when pin 6 goes low
// But also takes 'keep alive' photos at intervals of 160s
// because otherwise CUBE powers off automatically after 3 mins.
// Uses pin 10 (green LED) to indicate triggered tasks are in progress
// The green LED is acting as a flag, but is also a useful visual indicator
// Pin 4 input sets video duration
// Pin 6 input LOW triggers loop
// Pin 8 HIGH = CUBE video recording
// Pin 9 is CUBE servo control
// Pin 10 is greenledPin

#include <VarSpeedServo.h> // Uses the VarSpeedServo library 

// Create servo object
VarSpeedServo myCUBEservo;

const byte outPosCUBE = 35; // Trial & error; needs regular re-checking
const byte inPosCUBE = 15; // Ditto

const byte triggerPin = 6;
const byte redledPin = 8;
const byte greenledPin = 10; // (Green LED on bb)
const byte switchPin = 4;
int videoLength; // Declared here, then set in Setup

// From 'Basic-goTime' sketch
//long int nextTime = 160000;  // Do this every 160s
long int nextTime =10000;  // Testing with 10s
long int goTime;

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

void setup()
{
  Serial.begin(115200);
  Serial.println("");
  Serial.println("==============================================");
  Serial.println("CUBE-VideosPhotosKA-15Nov20-latest");

  pinMode(switchPin, INPUT_PULLUP);
  pinMode(triggerPin, INPUT_PULLUP);
  pinMode(redledPin, OUTPUT);
  pinMode(greenledPin, OUTPUT);

  // Initialise the CUBE servo
  myCUBEservo.write(outPosCUBE); // Initial arm pos'n
  delay(100);
  myCUBEservo.attach(9); // White wires
  Serial.println("CUBE servo attached");
  // Test D4 to choose value of videolength
  if (digitalRead(switchPin) == HIGH)
  {
    videoLength = 30000;
  }
  else
  {
    videoLength = 4000; // Testing with this value
  }
  Serial.print("Video length = ");
  Serial.print(videoLength / 1000);
  Serial.println(" seconds");
  Serial.print("outPosCUBE/inPosCUBE = ");
  Serial.print(outPosCUBE);
  Serial.print("/");
  Serial.println(inPosCUBE);
  Serial.print("nextTime (interval between photos) = ");
  Serial.print(nextTime / 1000);
  Serial.println(" seconds");


  // Power up CUBE, which must be OFF before sketch started
  CUBEPowerUp();
  // Allow a long delay for battery recovery in case triggered
  // very soon after
  delay(2500);

  Serial.println("Setup finished");
  Serial.println("");
  Serial.print("Now taking 'keep-alive' photos at intervals of ");
  Serial.print(nextTime / 1000);
  Serial.println(" seconds,");
  Serial.println(" UNLESS D6 is taken low, triggering the video tasks");
  Serial.println("----------------------------------------------");

  // From 'Basic-goTime' sketch
  goTime = millis(); // Start time
}

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

void loop()

{
  if (millis() >= goTime + nextTime)
  {
    TakePhoto();
    Serial.println("Keep-alive photo taken");
  }

  // Run only when a LOW on D6 is detected
  if (digitalRead(triggerPin) == LOW)
  {
    Serial.println("Triggered by D6; TASKS STARTED");
    // TASK #1: Take greenledPin (D10) high, indicating triggered taskw started
    digitalWrite(greenledPin , HIGH);
    // TASK #3: Start CUBE video with two presses
    CUBESingleButtonPress();
    delay(100);
    // TASK #4: Start video with two presses
    CUBESingleButtonPress();
    Serial.println("Started CUBE video");
    digitalWrite(redledPin , HIGH) ;
    // TASK #5: Delay for recording videos
    delay(videoLength); // ACTUAL individual durations will be different
    // TASK #6: Stop CUBE video with single press
    CUBESingleButtonPress();
    digitalWrite(redledPin , LOW) ;
    Serial.println("CUBE video ended");
    // TASK #8: Take greenledPin (D10) low, indicating triggered task ended
    digitalWrite(greenledPin , LOW);
    delay(2000); // Brief pause while CUBE powering down, may be redundant

    // Add code to ensure next photo is 160s later 
    goTime = millis();

  }
  //  exit(0); // To temporarily stop the sketch looping; Comment out to run
}

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

void CUBEPowerUp()
{
  // Power up CUBE with long press > 3 s
  myCUBEservo.write(inPosCUBE);
  delay(3500); // Press for > 3s to toggle power
  myCUBEservo.write(outPosCUBE);
  delay(200);
}


void CUBESingleButtonPress()
{
  // Take photo or stop video with single button press
  myCUBEservo.write(inPosCUBE);
  delay(200);
  myCUBEservo.write(outPosCUBE);
  delay(200);
}

void TakePhoto()
{
  // Take a photo with CUBE: single press, except if video tasks underway
  CUBESingleButtonPress();
  goTime = millis(); // set when to do it again
}
18:10:06.458 -> ==============================================
18:10:06.458 -> CUBE-VideosPhotosKA-15Nov20-latest
18:10:06.552 -> CUBE servo attached
18:10:06.552 -> Video length = 4 seconds
18:10:06.552 -> outPosCUBE/inPosCUBE = 35/15
18:10:06.552 -> nextTime (interval between photos) = 10 seconds
18:10:12.740 -> Setup finished
18:10:12.740 -> 
18:10:12.740 -> Now taking 'keep-alive' photos at intervals of 10 seconds,
18:10:12.740 ->  UNLESS D6 is taken low, triggering the video tasks
18:10:12.786 -> ----------------------------------------------
18:10:23.163 -> Keep-alive photo taken
18:10:33.587 -> Keep-alive photo taken
18:10:38.072 -> Triggered by D6; TASKS STARTED
18:10:39.009 -> Started CUBE video
18:10:43.366 -> CUBE video ended
18:10:55.800 -> Keep-alive photo taken
18:11:06.179 -> Keep-alive photo taken
18:11:16.612 -> Keep-alive photo taken
etc

So.. you want something to happen every x ms.? I guess you want a pin toggled to take a picture? Doo I have this right?

Or has this been solved already?

-jim lee

Which part of the thread, if any, did you read?

I read a bunch of it here and there. Just sort of skimmed over it. Seemed like you were having issues with the millis() thing. I couldn't figure out how you were taking the pictures. The difference between what seemed like a servo and the camera seemed a little blurry to me.

But don't mind me, I'll stay out of it.

-jim lee

jimLee:
But don’t mind me, I’ll stay out of it.

-jim lee

Wise decision, me too. :o

jimLee:
I couldn't figure out how you were taking the pictures. The difference between what seemed like a servo and the camera seemed a little blurry to me.

First few lines of code in my carefully composed opening post:

// Using a servo to press a CUBE camera button, this sketch takes a photo at intervals
// (say 160s or 10/20s for tests) UNLESS D6 triggered tasks are in progress.
// Otherwise the camera automatically powers off after 3 mins of non-activity.