blink without delay brain problem

I have two timing parts, based on the blink without delay, one works fine, but a simple one to turn on a horn for 4 seconds instantly turns it off again, I must be missing something stupid ! ( Its 4 am )

heres the bit of code, its supposed to sound the horn at the end of countdown on the clock, which triggers fine, but the four seconds doesnt work.. any clues? The first part is the time T countdown part that works...

void loop () 
{

  showT ();  // runs the display countdown and update function

  ////////////////////////////////////////////////////////////

  if (T >= 0 && pause == 0 ) { 

    unsigned long currentMillis = millis();

    if(currentMillis - previousMillis > 1000) {
      // save the last time you blinked the LED 
      previousMillis = currentMillis;   

      T -- ;

      Serial.println(T);
      Serial.println("");
      
    }
  }

 

  if ( T <= 0 && pause == 0 && horn == 0) { // if countdown reaches zero and horn is off
    horn = 1;    
    Serial.print("horn is   ");      
    Serial.println(horn);  
  }

  if ( horn == 1) { 
    pause = 1;   

    unsigned long currenthornMillis = millis();
    
    if(currenthornMillis - previoushornMillis > 4000) {
       previoushornMillis = currenthornMillis;   
   
      horn = 0 ;
    }


    Serial.print("horn is   ");
    Serial.println(horn);

  }

How is T defined?

And what is previoushornMillis initially? Assuming it is zero, then this condition will immediately be met:

 if(currenthornMillis - previoushornMillis > 4000) {
       previoushornMillis = currenthornMillis;   
   
      horn = 0 ;
    }

not sure if these things are significant in the total aspect of the code (not 100% on what you're trying to achieve), but:

your conditional statements, let's call them A, B, and C, where:
A = if (T >= 0 && pause == 0 )
B = if ( T <= 0 && pause == 0 && horn == 0)
C = if ( horn == 1)

if horn == 0 and T == 0 and pause == 0 then A and B will both execute
if horn == 1 and T >= 0 and pause == 0 then A and C will both execute

perhaps that was the intent...

  1. efficiency issue... using "currentMillis" is kind of redundant considering you can call millis() for your "currentMillis".
    also, instead of performing calculations with each check... actually i'd just replace this:
unsigned long currentMillis = millis();
          if(currentMillis - previousMillis > 1000)

with this:

loop(){
     static unsigned long previousMillis;
...
     if(millis() > previousMillis){
          previousMillis = millis() + 1000;
...

that way, your program just has to compare longs rather than subtract and compare.

personally, if i were to achieve what i think you're trying to achieve (if a function completely executes/event occurs then horn will turn on for 4 seconds) i would do it something like this:

void loop(){
     static unsigned long LED_timer, HORN_timer;  //initialize to 0
     static unsigned char LED_flag, HORN_state;   //initialize to 0
     static unsigned long program_Timer;          //placed on its own line for readibility
     
     shotT();  //display countdown and update function

     if(millis() > LED_timer){          //executes once immediately (LED_timer initialized to 0)
          LED_timer = millis() + 1000;  //store time of next execution... right now + 1000ms
          programTimer--;     //decrement program timer -1second
          Serial.print(programTimer,DEC);
          Serial.print("\n\n");
     }
     
     if(millis() > HORN_timer){
          HORN_timer = HORN_timer + 4000;    //next HORN event will happen in (now) + 4000ms
          if(HORN_state){
               HORN_state = 0;
               //turn_horn_on_function()
          }
          else{
               HORN_state = 1;
               //turn_horn_off_function();
          }
     }
}

the theory behind the whole storing-the-timer thing is to eliminate busy-wait's. when the processor is in a busy-wait, nothing else can happen, which is inefficient. instead, just think relatively. instead of saying "wait right here for a while" you're saying "do what you need but be here in a while". so your entire application will have 0 busy-waits, and instead of using "delay(x)" you'll use "if(millis() > x)" and "x" will have been assigned in the previous cycle as (previous_cycle_time)+x. your loop becomes a dispatch manager:

#define TOGGLE(a)   (a==1)?(0):(1) //toggling 1:0:1:0:1...
#define LED_PIN     13
#define A_cycletime 100  //every 100ms
#define B_cycletime 500  //every 500ms
#define C_cycletime 1000 //every 1s
#define D_cycletime 5000 //every 5s

void setup(){
     Serial.begin(9600);
     Serial.println("Initialized");
}

void loop(){
     static unsigned long timerA, timerB, timerC, timerD;
     static unsigned char LED_state;
     
     if(millis() > timerA){
          timerA = millis() + A_cycletime;   //first event should set the clock for accuracy
          subfunctionA();     //call a function for functionA to keep it organized
          Serial.println("A");
     }
     if(millis() > timerB){   //500ms led flasher in here
          timerB = millis() + B_cycletime;
          digitalWrite(LED_PIN,TOGGLE(LED_state));//LED_state is toggled, pin is turned[on/off]
          subfunctionB();
          Serial.println("B");
     }
     if(millis() > timerC){
          timerC = millis() + C_cycletime;
          subfunctionC();
          Serial.println("C");
     }
     if(millis() > timerD){
          timerD = millis() + D_cycletime;
          subfunctionD();
          Serial.println("D");
     }
}


void subfunctionA(){
     //some stuff
}
void subfunctionB(){
     //some stuff
}
void subfunctionC(){
     //some stuff
}
void subfunctionD(){
     //some stuff
}

hopefully you can pick a solution out of all that

Thanks guys, its a bit clearer in the morning !
Heres all the definitions Nick.
(the project is for a sport countdown clock with a horn at the end for 4 seconds ( and an option not started yet for a 2 second "toot" of the horn when required.)

Theres a couple of functions for displaying the time, and for checking the buttons.

I started off with neat functions for each part, but put a lot of it back in the loop for testing.
There is a lot of comented out rubbish still there, I will tidy it up later.. ( after the F1 Grand Prix which is about to start :slight_smile: )

#include <VirtualWire.h>
#define ledPin 13  
#define latchPin 19  // rck
#define clockPin 18  // sck
#define dataPin 16   // ser in
#define blankPin 15  // notG
#define clearPin 17  // not SCLR

int secNibble10;
int secNibble;
int PIN;
int key;
int T = 0;
int toot = 0;
int pause = 1;
int blank = 0;
int horn = 0;
int tens;
int units;
 long previousMillis = 0;        // will store last time LED was updated
 long previoustootMillis = 0; 
 long previoushornMillis = 0; 


int SW0Pin = 3;               // bits to read in unique address - LSB
int SW1Pin = 4;               // bits to read in unique address
int SW2Pin = 5;               // bits to read in unique address
int SW3Pin = 6;               // bits to read in unique address - MSB
int address = 0;            // bits put together afteer reading switches
int add0;
int add1;
int add2;
int add3;



void setup()
{

  pinMode(SW0Pin, INPUT); // LSB of remote Address
  digitalWrite(SW0Pin, HIGH);
  byte add0 = 0; // read the value of SW0
  pinMode(SW1Pin, INPUT); // LSB+1
  digitalWrite(SW1Pin, HIGH);
  byte add1= 0;
  pinMode(SW2Pin, INPUT);  // LSB+2
  digitalWrite(SW2Pin, HIGH);
  byte add2 = 0;
  pinMode(SW3Pin, INPUT);  // MSB of address
  digitalWrite(SW3Pin, HIGH);
  byte add3 = 0;

  // put our address together
  // read our expansion address
  add3 = digitalRead(SW3Pin); 
  // shift it left 3 places
  add3 = add3 << 3;
  add2 = digitalRead(SW2Pin);
  // shift it left 2 places
  add2 = add2 << 2;
  add1 = digitalRead(SW1Pin);
  // shift it left 1 place
  add1 = add1 << 1;
  add0 = digitalRead(SW0Pin);
  // now OR it together
  address = address|add3;
  address = address|add2;
  address = address|add1;
  address = address|add0;
  Serial.println("Rx PIN is: ");
  Serial.println(address, BIN);
  Serial.begin(9600);	// Debugging only
  Serial.println("setup");
  pinMode(ledPin, OUTPUT);
  pinMode(blankPin, OUTPUT);
  pinMode ( latchPin, OUTPUT);
  pinMode ( clockPin, OUTPUT);
  pinMode ( dataPin, OUTPUT);
  pinMode (clearPin , OUTPUT);

  digitalWrite(ledPin, LOW);


  vw_set_rx_pin(9);        // set Rx
  vw_setup(4000);	 // Bits per sec
  vw_rx_start();    
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void loop () 
{

  // insert the T blinkwithoutpause here, doesnt matter about delays if message received as T will always be reset to Tx time

  showT ();  // runs the display countdown and update function

  ////////////////////////////////////////////////////////////

  if (T >= 0 && pause == 0 ) { 

    unsigned long currentMillis = millis();

    if(currentMillis - previousMillis > 1000) {
      // save the last time you blinked the LED 
      previousMillis = currentMillis;   

      T -- ;

      Serial.println(T);
      Serial.println("");
      if ( T== -1 ){ 
        T = 0 ;
      }
    }
  }

  ////////////////////////////////////////////////////////////////





  if ( T <= 0 && pause == 0 && horn == 0) { // if countdown reaches zero and horn is off
    horn = 1;    
    Serial.print("horn is   ");      
    Serial.println(horn);  
  }

  if ( horn == 1) { 
    pause = 1;   

    unsigned long currenthornMillis = millis();
    
    if(currenthornMillis - previoushornMillis > 4000) {
      // save the last time you blinked the LED 
      previoushornMillis = currenthornMillis;   
      //pause = 1;
      horn = 0 ;
    }


    Serial.print("horn is   ");
    Serial.println(horn);

  }

  // }
  //}
  // sound horn for 4 seconds
  //}//  end of endhorn funct







  // endhorn ();  }
  digitalWrite(ledPin, horn);
  //  pause = 1;    
  //}  // if time 0 and still counting down, runs endhorn then pauses countdown

    if ( toot == 1 )   { 
    // must do blinkwithout delay here, not subroutine as is would wait 2 secs before coming back ??
    toot = 0; 
  }   // if toot button pressed runs horn 2 seconds and resets toot to 0 until next toot press

  // look for wireless input
  // Turn off LED ready to flash to show working a message

  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;
  if (vw_get_message(buf, &buflen)) // Non-blocking
  {
    ///////////////////////////////////////////////////////////////////////////////////  if message check PIN
    digitalWrite(ledPin, LOW); // Turn on LED to show working a message

    // Message with a good checksum received, dump it.
    Serial.println("Got: ");  // Show on PC for debugging

    PIN = buf[0];   // read Tx PIN number from buffer 0
    if ( PIN == address ) {   //  everything below only runs if PINs match 
      Serial.println(" PINs match ") ; 

      blank =0;  //  brings display out of standbye with any button pressed

      key = buf[1];  // if PINS match, read key number from buffer 1
      T = buf[2];  // if PINS match, reset time left T from buffer 2

      Serial.print(" Tx PIN address = ");    
      Serial.println(PIN);
      Serial.print(" key number = ");     
      Serial.println(key);
      Serial.print(" seconds remaining = ");    
      Serial.println(T);
      Serial.print(" Rx PIN address = ");    
      Serial.println(address);
      Serial.println(""); // spaces it out for the monitor

      checkbutton ();    ////////////////////////////////////////////////////////////// decode keys

      Serial.print("time = ");      
      Serial.println(T);
      Serial.print("pause = ");      
      Serial.println(pause);
      Serial.print("key = ");      
      Serial.println(key);
      Serial.print("address = ");      
      Serial.println(address);
      Serial.print("toot = ");      
      Serial.println(toot);
      Serial.print("blank = ");      
      Serial.println(blank);
      Serial.println("");

      if ( blank == 1 ) {  
        digitalWrite(blankPin, HIGH); 
        T=0; 
        pause = 1; 
      } // blank the shift reg, reset and stop the clock when in standbye

    } // end of if PINs match
  }//  end of if message received 
}  //  end of loop
  unsigned long currentMillis = millis();
   unsigned long currenthornMillis = millis();

Having these as local to loop() means that they will be reinitialised every time loop() is called.

Make them global or add the "static" directive.

I don't know if that's the cause of you're problem but it won't help.


Rob

Thanks Rob, I have now just used millis() as suggested above, I don't know why "blink without delay " has it unsigned long currentMillis = millis(); ???

For the countdown I tried:-

if(millis() > LED_timer){ //executes once immediately (LED_timer initialized to 0)
LED_timer = millis() + 1000; //store time of next execution... right now + 1000ms
programTimer--; //decrement program timer -1second

as suggested by iklin6, which works, but the first second is skipped through instead of waiting for a second, it is obvious from the code, but not clear how to overcome....

if you need an initial value for the static vars but don't want it reinitialized every time... do this:

void loop(){
     static unsigned long somevariable;
     somevariable = 28;
 
    for(;;){
          //main execution of program
          //goes in here
     } 

}

or something like this using a firstrun var:

void loop(){
static unsigned long somevariable;
static unsigned char firstrun;
if(!firstrun){
firstrun++; //firstrun conditional will never run again
somevariable = [initial value];
}

//main execution of program

}

I still think you need to initialize previousMillis and previoushornMillis to something other than zero (for example, millis ()).

That way the comparisons the first time through are valid.

this:

void loop(){
     static unsigned long LED_timer, HORN_timer;       //initialize to 0
     static unsigned char LED_flag, HORN_state;        //initialize to 0
     static unsigned long program_Timer;               //placed on its own line for readibility
     static unsigned char firstrun;                    //(*new*)  initialized to 0
     
     if(!firstrun){                                    //(*new*)will run only once 
          HORN_timer = millis() + 4000;                //(*new*)IF first run, make it [curr time] + 4000
          LED_timer = millis() + 1000;                 //(*new*)IF first run, make it [curr time] + 1000
          firstrun++;                                  //(*new*)will never be !firstrun again
          Serial.println("First run values applied");  //(*new*) check to make sure this isn't run more than once
     }                                                 //(*new*) end first run IF statement
     
     shotT();                                          //display countdown and update function
     
     if(millis() > LED_timer){  
                                                       //code execution from here
                                                       //.......

Good idea. However:

if(!firstrun){

In other words, put that in setup.

you got it -- remember, if you do that you'll have to make your variables global. i can't say if it'll make any significant runtime different, it's just my personal preference to refrain from using globals whenever i can

One trouble is that whenever I pause the timer and restart it during the countdown, it loses the first second , so I suppose the firstrun approach wouldn't help that.

Nick, I see what you mean by initialising the previoushornMillis to something other than zero (for example, millis ()). but where do I do it ?

If I do it in the setup, it wont help with pausing and restarting the clock? If I do it in the loop it will not be able to check how the time is going each loop ???

Unless I can set it the time of the start button somehow.....

I have got the manual horn working ( toot) for 2 seconds at a time, its OK if I only touch the button on the remote briefly, but if I hold it for too long it throws off the timing of the countdown.

Is there a software interrupt that I can use on the rising edge of my "toot" signal from the receiver decoder( ie , not from either of the normal interrupt pins ?

can't fully picture what you're going for with the SWI (software interrupt), but maybe you could just pass the time back from your toot function? e.g.

void loop(){
     static unsigned long last_toot;
     //loop stuff

     last_toot = tootFunction();

     //more loop stuff
}

unsigned long tootFunction(){
     //toot stuff
     return millis();
}

Nick, I see what you mean by initialising the previoushornMillis to something other than zero (for example, millis ()). but where do I do it ?

OK, well when you turn the horn on, note when you did it:

 if ( T <= 0 && pause == 0 && horn == 0) { // if countdown reaches zero and horn is off
    horn = 1;    

    previoushornMillis = millis ();

    Serial.print("horn is   ");      
    Serial.println(horn);  
  }

That's the logical spot, right? You turn the horn on so you note when you did it.

Is there a software interrupt that I can use on the rising edge of my "toot" signal from the receiver decoder( ie , not from either of the normal interrupt pins ?

Not really, that I am aware of.

You are confusing me BTW with all this stuff about:

  if ( horn == 1) { 
    pause = 1;

How about using some booleans? That makes it more readable...

  if (horn) { 
    pause = true;

When you give things the values 0 and 1, I wonder if they will ever be 2 or 3. But if you say:

boolean toot = false;
boolean pause = true;
boolean blank = false;
boolean horn = false;

... then I know these are booleans (switches) and that they only ever have two valid values: true or false.

Yes I should rather use true or high, I will clean it all up and have a rethink tonight.

On second thoughts make that tomorrow, we are off to the Niel Diamond concert at the Cape Town Stadium tonight !

you could get creative with your booleans and some preprocessor definitions. say you've got your horn connected to PD7 (arduino's digital 7), you can just use that pin to directly represent the horn's state

e.g.

/*HORN(1) will turn horn pin HIGH, HORN(0) will turn pin LOW*/
#define HORN(a)     (a)?(PORTD|=(1<<7)):(PORTD&=~(1<<7))

/*HORN_IS_ON will return 1 if the pin is HIGH, 0 if LOW.  Use:  if(HORN_IS_ON){ ... }     */
#define HORN_IS_ON    (PORTD&(1<<7))?(1):(0)

/*HORN_IS_OFF is the same as HORN_IS_ON, but will return true if horn is LOW, false if horn is HIGH*/
#define HORN_IS_OFF  (PORTD&(1<<7))?(0):(1)

Creative indeed, but I am still getting the hang of basics :~

I tried to work out how to set the previousmillis, but for the one second countdown I cant see how to do it on every pause, so that it restarts at wherever it was.

I worked out a system that would not only time the first second 90% right, but would also restart part way through the second count.

I am now counting in 100mS and dividing by 10 at the display code.

So for 14 seconds timing, I start the timer at 149, and it shows 14 for ( nearly ) the first second. ( the buttons are pressed by somebody with half a second reflexes , so 100mS lost will not make any difference )

If the time is paused half way through a second, it will resume at halfway through when the run button is pressed.

The other advantage is that if required I can easily display the last 10 seconds as seconds and tenth of seconds simply by not dividing by 10 if T < 100 ( last 10 secs ) I have seen this on TV but don't know if it is appropriate for a shot clock.
Everything works great except that one of the preset countdown times required is 60 seconds, so the preset start count should be 609 which doesn't fit in the Virtual Wires one byte transmission, and comes out at the Rx as 97.

Is there a way for VW to handle numbers greater than 256? like sending in two parts and sticking together at the far end?

I have had a look at the VWire pdf manual , but nothing there...