Trying to use millis() to extend up time of a digital output.

Hello all. After hours and hours of trying to make this code work, and doing oodles of research, I have met no success. This is a first attempt at using millis(), and while I believe that I have finally started to understand how it functions, I have been unable to get the code to produce what I’m looking for.

I am trying to have a laser(for the ease of coding, I have left this coded as an LED on output pin 12) strobe in time to an analog input(in this application sound amplitude on pin a0). Every time the Laser turns on, I want it to remain on for a given interval, then, return to its previous state of turning on when a certain threshold is reached on a0.

This happens while, separately, a stepper motor turns in speed with amplitude changes on the same analog input.

The first part, strobing in time with amplitude changes, was very easy to implement utilizing the IfStatementConditional example. Where I’m having issues, is with having the laser stay on for a given interval, and then returning to its previous state of turning on when a certain threshold is reached on a0.

I’ve tried countless variations of multiple examples given in tutorials around the web, but just don’t seem to be getting something about this process. I apologize if this seems a bit messy… I’ll certainly clean it up a bit, once I get this problem part working. Thanks!

#include <Stepper.h>

const int stepsPerRevolution = 512;  // change this to fit the number of steps per revolution
// for your motor


// initialize the stepper library on pins 8 through 11:
Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11);

int stepCount = 0;  // number of steps the motor has taken

           const int analogPin = A0;    // pin that the sensor is attached to
           const int ledPin = 12;       // pin that the LED is attached to
           const int threshold = 200;   // an arbitrary threshold level that's in the range of the analog input
           const int motorThreshold = 615.4;

unsigned long previousMillis = 0;
           
const unsigned int onTime = 1000;
        
const unsigned int offTime = 500;

// Interval is how long we wait
int interval = onTime;
 
// Used to track if LED should be on or off
boolean LED12state = true;
 
// Usual Setup Stuff


void setup() {
  // nothing to do inside the setup
   // initialize serial communications (for debugging only):
  Serial.begin(9600);
           pinMode(ledPin, OUTPUT);
         
              }

void loop() {
            int analogValue = analogRead(analogPin);
            
           
        
  // read the sensor value:
  int sensorReading = analogRead(A0);
  // map it to a range from 0 to 100:
  int motorSpeed = map(sensorReading, 0, 615.4, 15, 100);
  // set the motor speed:
  if (motorSpeed > 0) {
    myStepper.setSpeed(motorSpeed);
    // step 1/100 of a revolution:
    myStepper.step(stepsPerRevolution / 100);

              }
  // read the input on analog pin 0:
  int sensorValue = analogRead(A0);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float voltage = sensorValue * (5.0 / 1023.0);
  // print out the value you read:
  Serial.println(voltage);

        
         
  
         // if the analog value is high enough, turn on the LED:
         
            if (analogValue > threshold) 
            {
            digitalWrite(ledPin, HIGH);
            }
             
             unsigned long currentMillis = millis();
             
             if ((unsigned long)(currentMillis - previousMillis) <= interval)
             {
             digitalWrite(ledPin, HIGH);
              {
                interval = onTime;
              }
             }
              else
              {
                 if (analogValue < threshold)
                 
                 {
                 digitalWrite(ledPin, LOW);
                 }
              
             
            
            }
            previousMillis = millis();
                     
            };

I too found timing code hard to get right. the biggest problem I think is poor choice of variable names used, in particular previousmillis which is used to start the timing sequence. something that isnt clear in most examples. also a flag variable is needed. Here is an example that I think more clearly shows how it can be used. with better variable names. note that startTime=millis(); needs to be inside of a conditional statement, currently you have it in loop() so it is reset every pass thru loop()

EDIT: I just realized that a flag variable is NOT needed if you simply reset startTime to 0 when the timing sequence is complete. Revised the code

unsigned long startTime = 0;     
const unsigned long interval = 1000;  // 1 second = 1000 millis       

void setup() {
}

void loop() {
  if(something you want to Time){
   startTime = millis(); // starts the timing sequence
   // do stuff here when timing starts
  }

  unsigned long currentTime = millis();
  if (currentTime - startTime >= interval) {
    startTime = 0; // reset timing sequence 
   // do stuff here when time is up
  }
}

The big problem is that the stepper motor library is blocking. To do something similar (move a stepper but having it stop in the middle of the move if a button was pressed), I wound up operating the stepper myself by manipulating the pins. It wasn't super difficult.

I just realized that a flag variable is NOT needed if you simply reset startTime to 0 when the timing sequence is complete. revised the code

Yes, but a flag can be good to prevent the analogRead from occurring when it doesn’t need to (like when the light is on) as analogRead can be slow. Also, I am not sure how digitalWrite works internally (it is probably ignored if the pin is already set to your write value) but it’s not the best practice to be calling digitalWrite every loop when startTime = 0 (which would constantly happen when startTime is 0 and currentTime >= internal). However, you really don’t need another variable for a flag, instead you can just use “if (startTime > 0)” which would accomplish the same thing as a flag (as long as you reset startTime to 0 when the pin goes LOW).

I have edited Dantor19’s code (which should now work) to show this method:

#include <Stepper.h>

const int stepsPerRevolution = 512;  // change this to fit the number of steps per revolution
// for your motor


// initialize the stepper library on pins 8 through 11:
Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11);

int stepCount = 0;  // number of steps the motor has taken

const int analogPin = A0;    // pin that the sensor is attached to
const int ledPin = 12;       // pin that the LED is attached to
const int threshold = 200;   // an arbitrary threshold level that's in the range of the analog input
const int motorThreshold = 615.4;

unsigned long previousMillis = 0;

const unsigned int onTime = 1000;

const unsigned int offTime = 500;

// Interval is how long we wait
int interval = onTime;

// Used to track if LED should be on or off
//Should start off so begin with false
boolean LED12state = false;

// Usual Setup Stuff


void setup() {
	// nothing to do inside the setup
	// initialize serial communications (for debugging only):
	Serial.begin(9600);
	pinMode(ledPin, OUTPUT);

}

void loop() {
	// read the sensor value:
	int sensorReading = analogRead(A0);
	// map it to a range from 0 to 100:
	int motorSpeed = map(sensorReading, 0, 615.4, 15, 100);
	// set the motor speed:
	if (motorSpeed > 0) {
		myStepper.setSpeed(motorSpeed);
		// step 1/100 of a revolution:
		myStepper.step(stepsPerRevolution / 100);	
	}

	
	unsigned long currentMillis = millis();
	if (previousMillis != 0)
	{
		//Timer for led
		if ((currentMillis - previousMillis) >= interval)
		{
			digitalWrite(ledPin, LOW);
			previousMillis = 0;
		}
	}
	else
	{
		// read the input on analog pin 0:
		int sensorValue = analogRead(A0);
		// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
		float voltage = sensorValue * (5.0 / 1023.0);
		// print out the value you read:
		Serial.println(voltage);
	
		// if the analog value is high enough, turn on the LED:
		if (sensorValue > threshold) 
		{
			digitalWrite(ledPin, HIGH);
			
			//Record current time
			previousMillis = currentMillis;
		}
	}
		
};

@ SRegan

Good Point

How do you handle the audio? Do you have a peak measuring sound detector module, or a straight through preamplifier? It makes a difference because the term "amplitude" of an audio signal usually means an averaged value not an instantaneous value. The software is different for the two cases. Have you experimented with the sound threshold detection yet?

Thanks guys for all the input, but I’m still a little lost on a couple points. One, is with “<” “>” use.
In the code that follows, I’m reading:
If the LED has been on for a time that is greater than or equal to interval, then turn LED off.
What I believe I want to say is:
When analog input is greater than threshold turn on LED, if not, turn off, but don’t turn off unless the LED has been on for a time that is less than or equal to interval.

{
		//Timer for led
		if ((currentMillis - previousMillis) >= interval)
		{
			digitalWrite(ledPin, LOW);
			previousMillis = 0;
		}

Also, looking at SRegan’s modified version of my code example below, I don’t see anything related to comparing analogValue and threshold to turn on or off the LED. Am I missing something there? This is the main function that I’m trying to add this timing variable to.

SRegan:
Yes, but a flag can be good to prevent the analogRead from occurring when it doesn’t need to (like when the light is on) as analogRead can be slow. Also, I am not sure how digitalWrite works internally (it is probably ignored if the pin is already set to your write value) but it’s not the best practice to be calling digitalWrite every loop when startTime = 0 (which would constantly happen when startTime is 0 and currentTime >= internal). However, you really don’t need another variable for a flag, instead you can just use “if (startTime > 0)” which would accomplish the same thing as a flag (as long as you reset startTime to 0 when the pin goes LOW).

I have edited Dantor19’s code (which should now work) to show this method:

#include <Stepper.h>

const int stepsPerRevolution = 512;  // change this to fit the number of steps per revolution
// for your motor

// initialize the stepper library on pins 8 through 11:
Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11);

int stepCount = 0;  // number of steps the motor has taken

const int analogPin = A0;    // pin that the sensor is attached to
const int ledPin = 12;      // pin that the LED is attached to
const int threshold = 200;  // an arbitrary threshold level that’s in the range of the analog input
const int motorThreshold = 615.4;

unsigned long previousMillis = 0;

const unsigned int onTime = 1000;

const unsigned int offTime = 500;

// Interval is how long we wait
int interval = onTime;

// Used to track if LED should be on or off
//Should start off so begin with false
boolean LED12state = false;

// Usual Setup Stuff

void setup() {
// nothing to do inside the setup
// initialize serial communications (for debugging only):
Serial.begin(9600);
pinMode(ledPin, OUTPUT);

}

void loop() {
// read the sensor value:
int sensorReading = analogRead(A0);
// map it to a range from 0 to 100:
int motorSpeed = map(sensorReading, 0, 615.4, 15, 100);
// set the motor speed:
if (motorSpeed > 0) {
myStepper.setSpeed(motorSpeed);
// step 1/100 of a revolution:
myStepper.step(stepsPerRevolution / 100);
}

unsigned long currentMillis = millis();
if (previousMillis != 0)
{
	//Timer for led
	if ((currentMillis - previousMillis) >= interval)
	{
		digitalWrite(ledPin, LOW);
		previousMillis = 0;
	}
}
else
{
	// read the input on analog pin 0:
	int sensorValue = analogRead(A0);
	// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
	float voltage = sensorValue * (5.0 / 1023.0);
	// print out the value you read:
	Serial.println(voltage);

	// if the analog value is high enough, turn on the LED:
	if (sensorValue > threshold) 
	{
		digitalWrite(ledPin, HIGH);
		
		//Record current time
		previousMillis = currentMillis;
	}
}

};

Also, looking at SRegan's modified version of my code example below, I don't see anything related to comparing analogValue and threshold to turn on or off the LED.

Look at the bit of the modified code that I posted that starts with "// read the input on analog pin 0:" everything below that is to turn the led on when it's within the analog threshold.

This bit:

        // read the input on analog pin 0:
        int sensorValue = analogRead(A0);
        // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
        float voltage = sensorValue * (5.0 / 1023.0);
        // print out the value you read:
        Serial.println(voltage);

        // if the analog value is high enough, turn on the LED:
        if (sensorValue > threshold) 
        {
            digitalWrite(ledPin, HIGH);

            //Record current time
            previousMillis = currentMillis;
        }

When analog input is greater than threshold turn on LED, if not, turn off, but don't turn off unless the LED has been on for a time that is less than or equal to interval.

The code snippet above is used for turning the LED on when input is greater than the threshold and then this part of the code is used to turn it off after the right interval/ amount of time has passed:

//Timer for led if ((currentMillis - previousMillis) >= interval) { digitalWrite(ledPin, LOW); previousMillis = 0; }

So these two code snippets work together. The first one turns the LED on when the input is greater than the threshold while the other turns it off after X amount of time.