Delay without Delay()?

I've done a bit of digging, and found multiple methodologies to delay without delay(), but I cannot for the life of me integrate any of them to this code.
The issue with delay, is I want the 'else if' function to still work as that is an over ride to turn the device off. (actual run time is going to be 45mins, not 5 secs)

The two 'values' correspond to buttons 1 and 2 on an RF TX.

My Code (so far) is;

/*
Modifed simple receiving code created by the awesome people at:
  http://code.google.com/p/rc-switch/
*/

#include <RCSwitch.h>

RCSwitch mySwitch = RCSwitch();

void setup() {
  Serial.begin(9600);
  mySwitch.enableReceive(0);  // Receiver on inerrupt 0 => that is pin #2
  pinMode(4, OUTPUT);  // Location of Mosfet
  pinMode(6, OUTPUT);  // Operation Indicator LED (optional)
}

void loop() {
  if (mySwitch.available()) {
    
    unsigned long value = mySwitch.getReceivedValue();
    
    if (value == 7419184) // This is the value received when button one on TX is pushed
    {
      Serial.print("Mosfet ON - Value: ");  // This and the next three lines are for debug and testing purposes
      Serial.print(mySwitch.getReceivedValue() );
      Serial.println("");
      digitalWrite(4, HIGH);   // set the Mosfet On
      digitalWrite(6, HIGH);   // set the LED on
      delay(5000);              // wait for 5 seconds
      digitalWrite(4, LOW);   // set the Mosfet Off
      digitalWrite(6, LOW);   // set the LED Off
    }
    else if (value == 7419139) // This is the value received when button two on TX is pushed
    {
      Serial.print("Mosfet OFF - Value: ");  // This and the next two lines are for debug and testing purposes
      Serial.print(mySwitch.getReceivedValue() );
      Serial.println("");
      digitalWrite(4, LOW);   // set the Mosfet Off
      digitalWrite(6, LOW);   // set the LED Off
    } 
    else // If neither of the above two values are received it activates this message.
    {
      Serial.print("Received Other Value: "); // This and the next two lines are for debug and testing purposes
      Serial.print(mySwitch.getReceivedValue() );
      Serial.println("");
    }

    mySwitch.resetAvailable();
  }
}

Would appreciate any help on this, it's taken me nearly 6 hours to get this far (tonight anyway...) I'm by no means a professional at this.

I'm not familiar with the RCSwitch library, so forgive me if it's within the library, but I don't see anything in your code that implements any type of delay, be it directly or indirectly.

but I don't see anything in your code that implements any type of delay, be it directly or indirectly.

You can't?

      digitalWrite(6, HIGH);   // set the LED on
      delay(5000);              // wait for 5 seconds
      digitalWrite(4, LOW);   // set the Mosfet Off

Right there, in the middle.

OP: You need to think about how you would do something like this, on a longer time line, for instance. Suppose your friend turns the oven on, and asks you to turn it off 5 hours later. How would you turn the oven off on time? You could go stand next to the oven with a timer running. When the timer beeped, you'd turn the oven off. Of course, you'd have to stand there for 5 hours, so as not no miss the beep. You'd miss plenty of other stuff, though.

Alternatively, you can write down the time the oven was turned on. Then, periodically, you check to see if enough time has elapsed to warrant turning off the oven. If it has, you turn the oven off. If not, you watch some more of the ball game, and drink some more beer. If you miss by a second or two noting the time to turn the oven off, it probably won't really matter. And, even if it does, you'll have drunk enough beer to not care.

The delay() function is like you standing next to the oven listening for the timer to beep. Nothing else can happen while you are waiting.

The blink with delay example shows how to use millis() like your watch, and some variables like the paper where you wrote down when the oven was turned on, and how long it should be on.

Now, the Arduino will check it's watch a lot more often than you would, so it won't miss when to turn the oven off, and it won't be drinking beer and watching the ball game while waiting. It's be doing laundry and mowing the grass and walking the dog (or something like that), instead. The point is that it won't miss when to the the oven off.

So, what you need to do is record when the oven (read MOSFET) was turned on, and periodically (read every pass through loop) see if the oven (read MOSFET) is on (the on time is not 0), and, if it is, whether it is time to turn the oven (read MOSFET) off.

Can you manage that? Try something, and post your code. It really isn't that hard.

PaulS:
You can't?

Apparently, I'm blind.

For this kind of operation I use what is known as a "state machine". - You can read up on them on Wikipedia here

Basically, your program is in one of a set number of "states". The buttons, when you press them, change the state. The state is used to determine what the program is actively doing at any moment in time. Any other action can be used to change the state as well as a button, for instance a counter getting to a specific number.

Take this as an (untested) example:

#define STATE_LED_OFF 0
#define STATE_LED_SWITCH_ON 1
#define STATE_LED_ON 2
#define STATE_LED_SWITCH_OFF 3

void loop()
{
  static unsigned char state = STATE_LED_OFF;
  static unsigned long timer = 0;

  switch(state)
  {
    case STATE_LED_SWITCH_ON:
      digitalWrite(13,HIGH);
      state = STATE_LED_ON;
      timer = millis();
      break;
    case STATE_LED_SWITCH_OFF:
      digitalWrite(13,LOW);
      state = STATE_LED_OFF;
      break;
    case STATE_LED_ON:
      if(millis() - timer > 5000)
        state = STATE_LED_SWITCH_OFF;
      break;
  }

  if(digitalRead(2)==HIGH)
  {
    state = STATE_LED_SWITCH_ON;
  }

  if(digitalRead(3)==HIGH)
  {
    state = STATE_LED_SWITCH_OFF;
  }
}

Basically, that will turn on the LED if input 2 goes high, and switch it off if input 3 is high. If 5000 milliseconds have passed while the LED is on (5 seconds) then switch it off. Note the use of "static" variables, which retain their value over successive calls to the loop() function. You could use global variables instead, but I prefer to keep variables in-scope as much as possible - it's good programming practice.

You need to use some flags.
So take this part out

digitalWrite(4, HIGH); // set the Mosfet On
digitalWrite(6, HIGH); // set the LED on
delay(5000); // wait for 5 seconds
digitalWrite(4, LOW); // set the Mosfet Off
digitalWrite(6, LOW); // set the LED Off

put in
startTime = millis();
startFlag = 1;

then add some other if statement after the else{}:

if (startFlag ==1 & ( millis() - startTime < duration){
do the high writes;
}
if (startFlag ==1 & ( millis() - startTime >=duration){
clear the flag:
startFlag = 0;
}
and in this else
else if (value == 7419139)
clear the flag
startFlag = 0;

and add 1 more check:

if (startFlag == 0){
do the low writes
}

startTime & duration are unsigned long variable types (32 bit numbers)

I've had a go using the for(int expression as i thought with the delay only being 1ms it would work, but still no joy.

There are a lot of libraries that implement timers, but all of the examples are a repeating scenario, somthing that flashes, where as I need a one shot.

/*
Modifed simple receiving code created by the awesome people at:
  http://code.google.com/p/rc-switch/
*/

#include <RCSwitch.h>

RCSwitch mySwitch = RCSwitch();

int Mosfet = 3;  // Pin that activates your Mosfet
int LED1 = 11;     // Pin that activates an LED
int LED2 = 12;
int RunMosfet = 5000; /// Run times can be calculated as ms, or S * 1000 where S is your seconds, M * 60 * 1000 where M is your minutes and H * 60 * 60 * 1000 where H is hours ///


void setup() {
        Serial.begin(9600);
        mySwitch.enableReceive(0);  // Receiver on inerrupt 0 => that is pin #2
        pinMode(Mosfet, OUTPUT); 
        pinMode(LED1, OUTPUT);
        pinMode(LED2, OUTPUT);
        
      /// Run times can be calculated as ms, or S * 1000 where S is your seconds, M * 60 * 1000 where M is your minutes and H * 60 * 60 * 1000 where H is hours ///
       
}

void loop() {
  if (mySwitch.available()) {
    
    unsigned long value = mySwitch.getReceivedValue();
   
    
    if (value == 7419184) // This is the value received when button one on TX is pushed
    {
      Serial.print("Mosfet, ON - Value: ");  // This and the next three lines are for debug and testing purposes
      Serial.print(mySwitch.getReceivedValue() );
      Serial.println("");
      digitalWrite(LED1, HIGH);
      for (int x=0; x < 150; x++) {     // Wait for 1 second
      delay(1); }
      digitalWrite(LED1, LOW);
      digitalWrite(Mosfet, HIGH);
      for (int x=0; x < RunMosfet; x++) {     // Wait for 1 second
      delay(1); }
      digitalWrite(Mosfet, LOW);
      digitalWrite(LED1, HIGH);
      for (int x=0; x < 150; x++) {     // Wait for 1 second
      delay(1); }
      digitalWrite(LED1, LOW);
    }
    else if (value == 7419139) // This is the value received when button two on TX is pushed
    {
      Serial.print("Mosfet, OFF - Value: ");  // This and the next two lines are for debug and testing purposes
      Serial.print(mySwitch.getReceivedValue() );
      Serial.println("");
      digitalWrite(Mosfet, LOW);   // set the Mosfet, Off
      digitalWrite(LED1, HIGH);
      for (int x=0; x < 500; x++) {     // Wait for 1 second
      delay(1); }
      digitalWrite(LED1, LOW);
    } 
    else // If neither of the above two values are received it activates this message.
    {
      Serial.print("Received Other Value: "); // This and the next two lines are for debug and testing purposes
      Serial.print(mySwitch.getReceivedValue() );
      Serial.println("");
      digitalWrite(LED2, HIGH);
      for (int x=0; x < 2000; x++) {     // Wait for 1 second
      delay(1); }
      digitalWrite(LED2, LOW);

    }

    mySwitch.resetAvailable();
  }
}

A Library I found is a Timer library by simon Monk. I can make the code work, but not as part of my IF statement...

delay() uses milliseconds. 1 second --- delay(1000)

But you don't want to use delay(), it ties the processor up completely.

but all of the examples are a repeating scenario

Your repeating scenario is the Arduino keeping an eye on those buttons via RC. Using delay() defeats that purpose.

The examples are to show how to have something change state (blinking led changes between on and off states) based on time passed even as the MCU is able to do other things, like watch for user input or run motors.

      for (int x=0; x < 150; x++) {     // Wait for 1 second
      delay(1); }

This is functionally identical to:

      delay(150);

The only way is to have the loop around the ENTIRE code - which is what the loop() does anyway - and make decisions about what should be done depending on historical events - i.e., store values in variables that don't get erased and look to see what happened in the past.

I.e., the state machine I mentioned earlier.

Hi Majenko

I wrote a state machine code that worked, I think, but I had trouble getting my head around the time code at the base, what I have so far is;

/*
  
 #include <RCSwitch.h>

RCSwitch mySwitch = RCSwitch();

void setup() {
  // initialize serial communication:
     Serial.begin(9600);
     mySwitch.enableReceive(0);  // Receiver on inerrupt 0 => that is pin #2
   // initialize the pins:
     for (int Pins = 2; Pins 4; thisPin++) {
     pinMode(Pins, OUTPUT);
      } 
}

void loop() {
  if (mySwitch.available()) {
    
    unsigned long value = mySwitch.getReceivedValue();


    switch (getReceivedValue()) {
    case 7419184:
      digitalWrite(Pins, HIGH);
      Serial.print("Mosfet ON - Value: ");  // This and the next three lines are for debug and testing purposes
      Serial.print(mySwitch.getReceivedValue() );
      Serial.println(""); 
      break;
    case 7419139:    
      digitalWrite(Pins, LOW);
      Serial.print("Mosfet OFF - Value: ");  // This and the next two lines are for debug and testing purposes
      Serial.print(mySwitch.getReceivedValue() );
      Serial.println("");
      break;
    default:
      Serial.print("Received Other Value: "); // This and the next two lines are for debug and testing purposes
      Serial.print(mySwitch.getReceivedValue() );
      Serial.println("");
      }
    } 
  }
}

I think it was getting late so I filed it in the THB (too hard basket) for now, I think the #Define states at the top were confusing me so i was trying to work around them(without them), but then when I got to the time code at the base I totally lost track of what did what as I wasn't using the states, each state needs to apply to two pins, so i wasnt sure how to do that either. , i forget if it worked at all now, once I have the parts back I will do some more testing.

And the parts I need for testing are with a friend who is making a mockup of a custom shield to mount the three modules I'm using (RX, VReg, Mosfet)

    unsigned long value = mySwitch.getReceivedValue();


    switch (getReceivedValue()) {

This looks wrong.

You also didn't write a state machine. A state machine has some sort of state variable that changes within each state after some condition is met. You just implemented your if/else into a switch statement.

I think the #Define states at the top were confusing me

The #define bits are there to un-confuse you.

All they are are symbolic names for numbers. So, instead of trying to remember which number equates to which state in your head, you just tell the compiler that these names, or "macros", are to be replaced by the numbers (or whatever you specify) when it compiles. Then, instead of referring to "state 3", or "state 6", you can just use the understandable names you gave to the states.

So, where you write:

if(state==STATE_LED_ON)

the compiler actually sees:

if(state==2)

and you don't have to remember that state 2 is the LED turned on.

...
So, where you write:

if(state==STATE_LED_ON)

the compiler actually sees:

if(state==2)

and you don't have to remember that state 2 is the LED turned on.
[/quote]

Ahhhh, so the chip counts the number of states and it applies a number to each one for you? I was confused as I didn't see a numeric reference in the upper states, but there were in the lower.

cpjfox:
Ahhhh, so the chip counts the number of states and it applies a number to each one for you?

What define does is it tells the preprocessor to find every instance of the the first value and replace it with the second value before it compiles. It's really just a text replacement command that occurs before the code is actually compiled. The chip doesn't even see the #defines. It's literal text replacement, so you have to be careful with it because this code:

#define i 0

would be very bad, because it would change this:

if (digitalRead(myPin) == HIGH) {
  Serial.println("Hello");
}

to this:

0f (d0g0talRead(myP0n) == HIGH) {
  Ser0al.pr0ntln("Hello");
}

Another fun one to really mess with people:

#define true false

In my example code I create the defines:

#define STATE_LED_OFF 0
#define STATE_LED_SWITCH_ON 1
#define STATE_LED_ON 2
#define STATE_LED_SWITCH_OFF 3

So whenever you use STATE_LED_OFF, the preprocessor replaces it with 0 as that is what is specified in the #define. For STATE_LED_SWITCH_ON it replaces it with 1 etc.

The syntax is:

#define A B

and it equates to:

When you see A, replace it with B

cpjfox:
Ahhhh, so the chip counts the number of states and it applies a number to each one for you?

No. You have to write your own bugs. Don't worry, you're doing fine there. :stuck_out_tongue: :grin:
But you are on the way to working code.

  if (mySwitch.available()) {
  
    unsigned long value = mySwitch.getReceivedValue();

    switch (getReceivedValue()) {

Here you put the received value into a variable named 'value', but instead of using that result for switch-case you ....
well are you trying to use what you stored in 'value' or are you trying to read that switch -again-?

If the former then switch (value) would be more right.
If the latter then think again; you got the data that was available and would need to check if more data is available before getting it --- but why collect twice to make one decision? That makes no sense in this case. I think you got tired/confused even before you wrote that.

A state machine is a way to write code that may behave differently each time around depending on a value in a variable that your code sets.
Like if my code is looking at serial data for numbers and turn those into ints, I might set state to zero when no number is yet found and then to one when it has found a digit or more in a row then back to zero when the number is complete.
The code is then reduced checking on data available and if so then to one of two different actions:
During state zero my code is looking for that first digit. When it finds a digit, it sets an int to equal that value and sets the state to one. If not a digit then state stays zero.
During state one if any non-digit is found the state is set to zero and the int value is used for whatever it is for. If another digit is found then the int value is multiplied by ten and the new digit is added, but state does not change.
You see how simple the control is? State either changes or it doesn't. That is the control.

State can mean anything you want and be much more complex than above. I usually include an error state if -anything- unexpected/unwanted can happen. Then maybe I print an error and... set state to zero.
A state machine can save you from needing a mess of nested loops and logic that make debugging a real PITA.

I think I'm getting pretty close, but I am not quite sure what I should be putting for the switch(state) part, I beleive I need to change state to be looking for somthing else, I tried value, but that didn't work.

Looking at the serial feed with the code as follows it is turning itself off before it starts, it is picking up the various button presses and entering the right value, but before it can act the state is set back to STATE_MOSFET_SWITCH_OFF...

#include <RCSwitch.h>
RCSwitch mySwitch = RCSwitch();

//STATE INSTRUCTIONS//
#define STATE_MOSFET_OFF 0
#define STATE_MOSFET_SWITCH_ON 1
#define STATE_MOSFET_ON 2
#define STATE_MOSFET_SWITCH_OFF 3

//VARIABLES//
#define BUTTON1 7419184
#define BUTTON2 7419139
#define theMOSFET 10 // Pin that activates your Mosfet
#define REDLED 4
#define GRNLED 6
#define RUNTIME 2700000 /// Run times can be calculated as ms, or S * 1000 where S is your seconds, M * 60 * 1000 where M is your minutes and H * 60 * 60 * 1000 where H is hours ///

void setup() {
        Serial.begin(9600);
        mySwitch.enableReceive(0);  // Receiver on inerrupt 0 => that is pin #2
        pinMode(theMOSFET, OUTPUT); 
        pinMode(REDLED, OUTPUT);
        pinMode(GRNLED, OUTPUT);
}
void loop(){
  if (mySwitch.available()); {
  unsigned long value = mySwitch.getReceivedValue();
  static unsigned char state = STATE_MOSFET_OFF;
  static unsigned long timer = 0;

  switch(state)
  {
    case STATE_MOSFET_SWITCH_ON:
      digitalWrite(theMOSFET,HIGH);
      digitalWrite(GRNLED,HIGH);
      Serial.print("Mosfet Run - button: ");  // This and the next three lines are for debug and testing purposes
      Serial.println(mySwitch.getReceivedValue() );
      state = STATE_MOSFET_ON;
      timer = millis();
      break;
    case STATE_MOSFET_SWITCH_OFF:
      Serial.print("Mosfet Off - button: ");  // This and the next two lines are for debug and testing purposes
      Serial.println(mySwitch.getReceivedValue() );
      digitalWrite(theMOSFET,LOW);
      digitalWrite(GRNLED,LOW);
      state = STATE_MOSFET_OFF;
      break;
    case STATE_MOSFET_ON:
      if(millis() - timer > 5000)
        state = STATE_MOSFET_SWITCH_OFF;
      break;
  }

  if(value==BUTTON1);
  {
    state = STATE_MOSFET_SWITCH_ON;
  }

  if(value==BUTTON2);
  {
    state = STATE_MOSFET_SWITCH_OFF;
  }
    mySwitch.resetAvailable();
  }
}

Close but no cigar, am going to look at it again tomorrow once I have a fresh head.

Thanks all for your help so far, sorry if i'm a bit slow picking it up, C is a new language for me.

I see 4 states but only 3 cases.

Hi cpjfox

It's far easier than you'r setting yourself up to.
To put things in some perspective: you probably know traditional event-driven programming. Something is clicked and the appropriate code is fired. If you hook a boolean variable up to the click-event and 'gives' the clicker a true/false .. you've got a state. Every time the event is fired the boolean variable change value .. and you now can choose code to fire depending on the 'state' and not just the click.

Say you've gota click that sets a future time (in milliseconds) .. the click should get a 'state' where it starts code in the loop that checks if millis() has reached the future time (if ( future < millis() ) then ... ). .. Or put another way: you've got the state-variable to use for choosing two different code-path.

How difficult can it be?

I know that I sound condescending, but that's not intended. I spent most yesterday on a somewhat similar problem ;o/

If you've got a 'state' you move from 'event' to a less detailed situation. The four states you've build is somewhat moving in the uppersit direction and doesnt really give you anything .. or it's misleading to 'think' of those four as 'states'

Arrch:
What define does is it tells the preprocessor to find every instance of the the first value and replace it with the second value before it compiles. It's really just a text replacement command that occurs before the code is actually compiled. The chip doesn't even see the #defines. It's literal text replacement, so you have to be careful with it because this code:

#define i 0

would be very bad, because it would change this:

if (digitalRead(myPin) == HIGH) {

Serial.println("Hello");
}



to this:


0f (d0g0talRead(myP0n) == HIGH) {
  Ser0al.pr0ntln("Hello");
}

Uhhhhh. NO!
This is not the way the C preprocessor works.
It is not that simplistic. The substitution method described above would create all sorts of problems.
The C pre-processor tokenizes the text before substitutions.
A letter, like the "i" in the middle of a "function" name as in these examples
would not be substituted. Just like an "i" in the middle of literal quoted string would not be affected.
These examples are incorrect.

The code above is not affected in any way by the example #define of i to 0

See the gcc cpp documentation for further information:

look at sections 1.2 and 1.3 for details.

--- bill