Replacing a delay within an if statement with millis

Hey Arduino forum, I need a hand with some logic. Mostly just not sure where to set my states and millis() for replacing a delay inside an if statement. Tried a few things, but no dice. Its basically supposed to work like: "if the incoming byte is X, set the motor pin to high, after 100ms set the motor pin to low".

Code posted below. Thanks!

int incomingByte = 0;  
const int motorPinOne = 2;
const int motorPinTwo = 3;

int motorState1 = LOW;
unsigned long previousMillis1 = 0; 
long OnTime1 = 100; 

void setup() {
		
		Serial.begin(9600);     
		// opens serial port, sets data rate to 9600 bps
		pinMode(motorPinOne,OUTPUT);
        pinMode(motorPinTwo,OUTPUT);
        digitalWrite(motorPinOne, LOW);
        digitalWrite(motorPinTwo, LOW);
}

void loop() {
       
	if (Serial.available() > 0) {
	  	// read the incoming byte:
	  	incomingByte = Serial.read();
	    Serial.print("I received: ");
	    Serial.println(incomingByte, DEC);   

	    /// I would like This... 
	    if (incomingByte == 0){
	    	unsigned long currentMillis = millis();
	    	motorState1 = HIGH;
	    	if (motorState1 == HIGH) {
	    		digitalWrite(motorPinOne, motorState1); 
	    		if (currentMillis - previousMillis1 >= OnTime1){
	    			Serial.println(OnTime1);
	    			motorState1 = LOW;
	    			digitalWrite(motorPinOne, motorState1);

	    		}
	    	}
	    }
	    /// To work like this... 
	    if (incomingByte == 1) {
	    	digitalWrite(motorPinTwo, HIGH);
	    	delay(100);
	    	digitalWrite(motorPinTwo, LOW);
	    }
	}
}

Changing delay() to millis() is more than a one line code change. Something happens. You record when that happens. Periodically, you check to see if it is time for the next thing to happen.

That philosophy needs to be built in from the beginning, not tacked on later, after you've painted yourself into a corner.

Learn to draw state transition diagrams and understand them, it will pay off. Transitions
depend only on the current state plus the current inputs (or a time-out, which is basically
treating micros() or millis() as another input)

States that timeout typically have an unsigned long variable to capture the start time,
which is compared to the current time thus:

  if (micros () - start_time >= TIMEOUT_MICROS)
  ...

Its up to you to always initialise the start_time when changing to a state that must timeout.

In your example the X triggers a transition to a state with a 100ms timeout, the timout
triggers a transition back again. You need to figure what to do about an X during the second
state - you have a choice to make.

well, I guess what's tripping me up is I don't know where in two nested if statements to start a timer. Right after the loop? After incoming Byte? Before incoming Byte but after Serial? I understand how blink with no delay works. And I know I need to compare a start time to a current time. But for example, something like this makes sense to me, but its obviously wrong.

void loop() {

  if (Serial.available() > 0) {
      if (incomingByte == 0) {
        unsigned long startTime = millis();
         digitalWrite(motorPinOne, HIGH);
        if (millis() - startTime >= timeout){
          digitalWrite(motorPinOne, LOW);
          startTime = millis();
        }
      }
  }

}

timer stuff in Arduino always trips me up, but I still try. This thing works fine using a delay (i mean its reading serial commands ripped out of python), I don't necessarily have to replace them, I just wanted give it a shot.

You need to save the value of millis() at the instant where you want the timing to start.

Later you check whether the required interval has elapsed and if it has you act.

That action might also involve updating the saved value of millis() to start another timing interval.

The important concept is to ensure that loop() can repeat very frequently to allow the elapsed time to be checked.

The demo Several Things at a Time illustrates the use of millis()

I suspect what is confusing is the fact that delay() is something you do whereas the concept in millis() is to NOT do anything.

...R

Yeah that's part of it. re: delay vs millis.

Anyways, I'll eventually figure it out. Thanks for your time!

As others have stated, "replacing" delay() calls with millis() calls isn't a simple replacement, it is a rethinking of how your process should run.

Your original version (in English) is basically:

  1. Check for an incoming character
  2. If there is an incoming character and it is the letter 'X', do this:
    2a. Turn the motor on
    2b. Wait 100ms
    2c. Turn the motor off
  3. Go back to step 1

So now, you have to think about how you would do this without waiting. Maybe something like:

  1. Check for an incoming character
  2. If there is an incoming character and it is the letter 'X', do this:
    2a. Turn the motor on
    2b. Make a note of the time
  3. If the motor is on and 100ms has elapsed (current time - time motor was started > 100ms), do this:
    3a. Turn the motor off
  4. Go back to step 2

--Doug

I was playing around with structures a while back.
Maybe it will give you some ideas or you can incorporate this into your sketch.

//Blink without Delay skeleton (using a structure)
//LarryD

struct timer
{
  //lastMillis = the time this "timer" started
  //waitMillis = delay time (mS) we are looking for
  //restart    = do we start this "timer" again  
  //enableFlag = is this "timer" (enabled) allowed to be accessed
  //**********************
  //For each timer needed: 
  //Example:
  //   timer myTimer = 
  //   {
  //     0, 200, true, true  //lastMillis, waitMillis, restart, enableFlag 
  //   };
  //public access to: myTimer.lastMillis, myTimer.waitMillis, myTimer.restart, myTimer.enableFlag 
  //**********************

  unsigned long lastMillis; 
  unsigned long waitMillis; 
  bool          restart; 
  bool          enableFlag;
  bool CheckTime() //Delay time expired function "CheckTime()"
  {
    //is the time up for this task?
    if (enableFlag && millis() - lastMillis >= waitMillis) 
    {
      //should this start again? 
      if(restart)
      {
        //get ready for the next iteration
        lastMillis += waitMillis;  
      }
      //time was reached
      return true;
    }
    //time was not reached
    return false;
  } //END of CheckTime()

}; //END of structure

//**********************************************************************
//Creating all my timer objects
timer pin13 = 
{
  0, 200, true, true //lastMillis, waitMillis, restart, enableFlag
};
//**********************
timer pin12 = 
{
  0, 3000, true, true //lastMillis, waitMillis, restart, enableFlag
};
//**********************
timer pin11 = 
{
  0, 10000, true, true //lastMillis, waitMillis, restart, enableFlag
};
//**********************
timer pin10 = 
{
  0, 6000, true, true //lastMillis, waitMillis, restart, enableFlag
};
//**********************
timer Toggle10 = 
{
  0, 50, true, true //lastMillis, waitMillis, restart, enableFlag
};
//**********************
timer checkSwitches = 
{
  0, 50, true, true //lastMillis, waitMillis, restart, enableFlag
};

//**********************************************************************

byte lastMySwitchState = 1; //Pin 9
byte counter           = 0; 

const byte Pin13 = 13;
const byte Pin12 = 12;
const byte Pin11 = 11;
const byte Pin10 = 10;
const byte Pin9  = 9;

const byte mySwitch = 2;

//**********************************************************************

void setup()
{
  Serial.begin(9600);

  pinMode(Pin13,OUTPUT);
  pinMode(Pin12,OUTPUT);
  pinMode(Pin11,OUTPUT);
  pinMode(Pin10,OUTPUT);
  pinMode(Pin9, OUTPUT);

  digitalWrite(Pin13,LOW); //Start out OFF/LOW
  digitalWrite(Pin12,LOW);
  digitalWrite(Pin11,LOW);
  digitalWrite(Pin10,LOW);
  digitalWrite(Pin9, LOW);

  pinMode(mySwitch,INPUT_PULLUP);


} //  >>>>>>>>>>>>>> E N D  O F  s e t u p ( ) <<<<<<<<<<<<<<<<<


void loop()
{
  //Below are examples demonstrating different timing situations 

  //***************************
  //toggle Pin13 every 200ms
  if (pin13.CheckTime())
  {
    //Toggle Pin13
    digitalWrite(Pin13,!digitalRead(Pin13));

    //if you only want this section of code to happen once
    //uncomment the next line 
    //pin13.enableFlag = false;
  }

  //***************************
  //after 3 seconds, Pin12 goes and stays HIGH 
  if (pin12.CheckTime())
  {
    //Pin12 HIGH now
    digitalWrite(Pin12,HIGH);
    pin12.enableFlag = false;
  }

  //***************************
  //Pin11 is HIGH for 10 seconds, then goes and stays LOW
  if (pin11.enableFlag && !pin11.CheckTime())
  {
    digitalWrite(Pin11,HIGH);
  }
  //10 seconds is now up now, leave the Pin11 LOW
  else
  {
    digitalWrite(Pin11,LOW);
    //disable above code 
    pin11.enableFlag = false;
  }

  //***************************
  //for 6 seconds, toggle Pin10
  if (pin10.enableFlag && !pin10.CheckTime())
  {
    //Toggling Pin10 every 50mS
    if(Toggle10.CheckTime())
    {  
      digitalWrite(Pin10,!digitalRead(Pin10));    
    }
  }
  //6 seconds is now up, stop toggling, leave Pin10 LOW
  else
  {
    digitalWrite(Pin10,LOW);
    //disable 
    pin10.enableFlag = false;
  }

  //***************************
  //is it time to check the switches?
  if (checkSwitches.CheckTime())
  {
    Switches();      
  } 

  //**********************************
  //Put other non-blocking stuff here
  //**********************************

} //  >>>>>>>>>>>>>> E N D  O F  l o o p ( ) <<<<<<<<<<<<<<<<<


//======================================================================
//                      F U N C T I O N S
//======================================================================


//**********************************************************************
//no minimum switch press time is validated with this code (i.e. No glitch filter)
void Switches()  
{
  boolean thisState; //re-usable for all the switches      

  //*************************** Pin 9 code  
  //check if this switch has changed state
  thisState = digitalRead(mySwitch); 
  if (thisState != lastMySwitchState)
  {  
    //update the switch state
    lastMySwitchState = thisState;  

    //This switch position has changed so do some stuff

    //"HIGH condition code"
    //switch goes from LOW to HIGH
    if(thisState == HIGH)        
    {
      //example: LED on Pin9 is Push ON, Push OFF
      digitalWrite(Pin9,!digitalRead(Pin9)); 
    }

    //"LOW condition code"
    //switch goes from HIGH to LOW
    else                          
    {
      //example: display the current switch push count 
      Serial.println(++counter);      
    }
  } //END of mySwitch (Pin 9) code

  //******************************************  
  //similar code for other switches goes here 
  //******************************************  

} //END of Switches()

//**********************************************************************

//======================================================================
//                        E N D  O F  C O D E
//======================================================================