How to check for button status while leds are blinking

I have a routine which is to check for the status of a toggle switch - if the toggle is pressed the leds blink until the toggle is released.
I do not want to use the delay as other routines have to be available. can anyone help with this code - thanks in advace

void do_Crossing(int whichAction)
{
  if (DEBUG) {
    Serial.print(" do Crossing;   ");
    Serial.println(whichAction);
  }

  unsigned long currentMillis = millis();
  int btn3State = button3.getState();
 
  switch (whichAction)
  {
    
     
    case (0):// turn on

     while(btn3State == 0)
      {
          digitalWrite(crossingL1, HIGH); delay(300);
          digitalWrite(crossingL1, LOW); delay(300);
          digitalWrite(crossingR1, HIGH); delay(300);
          digitalWrite(crossingR1, LOW); delay(300);
      }
      break;
    case (1):// turn off and keep off
      digitalWrite(crossingL1, LOW);
      digitalWrite(crossingR1, LOW);
      break;
    default:
      break;

  }
  // save the last time you blinked the LED
  previousMillis = currentMillis;
}

We like to see the whole sketch, so we can test it ourselves.
Using millis() starts with the Blink Without Delay example: https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay.

If you have more millis-timers, then each one should have its own 'previousMillis' variable.

You need a few lines of code to start a millis-timer and you need the millis-timer itself running at the main level of the Arduino loop().

Like this?

#include <blinker.h>
#include <mechButton.h>

#define LED_PIN   13
#define BTN_PIN    2


blinker LEDBlinker(LED_PIN);  // A  blinking object.
mechButton  ourBtn(BTN_PIN);  // A button debouncer.

void setup() { }

void loop() {

   idle();                                   // idle() lets the magic happen.
   LEDBlinker.setOnOff(!ourBtn.trueFalse()); // Bink if the button is pressed.
   // DO whatever else you want here. NO BLOCKING!
}

You will need to grab LC_baseTools from the library manager to compile this.

-jim lee

You could also check my tutorial on Multi-tasking in Arduino and combine that with How to write Timers and Delays in Arduino
multitaskingDiagramSmall

consider (need to avoid delay() in this case)

// railroad crossing

#define Button  A1
#define LED     10

enum { Off = HIGH, On = LOW };

const unsigned long Interval = 500;

void
flasher (bool on)
{
    if (! on)  {
        digitalWrite (LED, Off);
        return;
    }

    static unsigned long msecLst = 0;
           unsigned long msec    = millis ();

    if ((msec - msecLst) > Interval)  {
        msecLst = msec;

        digitalWrite (LED, ! digitalRead (LED));
    }
}


// -----------------------------------------------------------------------------
bool
chkButton ()
{
    static byte butState = Off;
           byte but      = digitalRead (Button);

    if (butState != but)  {
        butState = but;

        if (On == but)  {
            return 1;
        }

        delay (10);     // debounce
    }

    return 0;
}


// -----------------------------------------------------------------------------
bool flasherState = 0;

void
loop ()
{
    flasher (flasherState);

    if (chkButton ())
        flasherState = ! flasherState;
}

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

    pinMode (Button, INPUT_PULLUP);

    digitalWrite (LED, Off);
    pinMode (LED,    OUTPUT);
}

of course, you may just want the flasher to be on while the button (or pin) is active.

    flasher (On == digitalRead (butPin));

i imagine a future mod is to control more than one flasher ?

From the original post, the two LEDs should blink alternately for 300ms, with 300ms gaps while no LED is on. The sequence therefore takes 1200ms.

I suggest simply:

int x;

void loop() {
  x=millis()%1200/300;
  digitalWrite(crossingL1, x==0 && (!digitalRead(button3)) );
  digitalWrite(crossingR1, x==2 && (!digitalRead(button3)) );
}

The milis() is divided by the remainder operator to give an integer incrementing every millisecond but going back to zero on reaching 1200. Then ordinary division by 300 gives an integer (x) incrementing every 300ms but going back to zero on reaching 4.

Other routines can now be inserted into the loop.

1 Like

Hello,
try this sketch. You have to fix the pinning to your hardware configuration.

const int OutPutPins[]  {2,3};
const int InPutPins[]  {A0};
const int crossingLED[] {OutPutPins[0],OutPutPins[1]};
const int button3 {InPutPins[0]};
struct TIMER {
  unsigned long stamp;
  unsigned long duration;
};
TIMER crossingTimer {0,300}; 
void setup() { // put your setup code here, to run once:
  for (auto &pin:OutPutPins) pinMode(pin,OUTPUT); 
  for (auto &pin:InPutPins) pinMode(pin,INPUT_PULLUP);
}
void loop() {  // put your main code here, to run repeatedly:
  if (millis()- crossingTimer.stamp >= crossingTimer.duration ){
    crossingTimer.stamp=millis();
    if (!digitalRead(button3)) {
    static int crossingL1R1;
    digitalWrite(crossingLED[(crossingL1R1&2)&&2], !(crossingL1R1&1));
    crossingL1R1++;
    crossingL1R1=crossingL1R1%4;
    } else digitalWrite(crossingLED[0],LOW), digitalWrite(crossingLED[1],LOW);
  }
}

This sketch is a good example to train the differences between a binary AND & and a logical AND &&.
Enjoy the evening and have fun.

This sketch lets you turn a blinking led on and off, reporting in serial monitor.
Be sure to check, serial rate should be 115200 to clear the print buffer faster.

This sketch also tells how many times void loop ran in the last second. Interrupts are not needed for buttons.

See if you like it:

// buttonsLedAndLoopSpeed  2021 by GoForSmoke @ Arduino.cc Forum
// Free for use, 6/4/21 by GFS. Compiled on Arduino IDE 1.6.9.
/*  A Button Debounce Example with loop counter to indicate speed.

  --- for this example connect a button between pin 7 and GND
  --- or stick a jumper in pin 7 and while holding the board steady
  --- tap the free end onto the grounded USB port box to press.

  Yes I'm using a 16 bit micros timer to time fractions of millis as micros.
  The button reader only reads 1 button per call so as to not block void loop().
  Each button has a history byte that holds the last 8 reads with 256 possible
  states but only 4 of them being significant.
  0 is the button held down
  255 is the button left up
  127 is the buton transitioning from up to down, button just released.
  128 is the button transititioning from down to up, button just pressed.
  everything else is to be ignored as bounce.

  For multiple buttons on 1:1 pins the between-reads time is reduced and each
  pin is read in turn. This demo makes pin 0 be ON-RESET/OFF and pin 1 adjusts
  blink time.
*/

#define microsInOneSecond 1000000UL

void LoopCounter() // tells the average response speed of void loop()
{ // inside a function, static variables keep their value from run to run
  static unsigned long count, countStartMicros; // only this function sees these

  count++; // adds 1 to count after any use in an expression, here it just adds 1.
  if ( micros() - countStartMicros >= microsInOneSecond ) // 1 second
  {
    countStartMicros += microsInOneSecond; // for a regular second
    Serial.println( count ); // 32-bit binary into decimal text = many micros
    count = 0; // don't forget to reset the counter 
  }
}


// multi_buttons vars
const byte buttons = 2;
byte buttonIndex, lastIndex; // only process 1 button every waitButtonTime micros
byte buttonPin[ buttons ] = { 6, 7 };
byte buttonHistory[ buttons ];
word markButtonTime;        // 16-bit micros timers
const word waitButtonTime = 250; // micros, 20 to 500 / more to fewer buttons.
// type word as micros can time across 65.535 millis before rollover, can be a few late

// added sketch task, on-off blinker vars
byte ledState, ledPin = 13; // use byte for small values, int cost 2 bytes
word startBlink, waitBlink; // 16 bit millis is good to time 65.535 seconds


void multiButtonsTask()
{ // read twice per milli, bits 0 to 6 all same sets the state
  if ( word( micros()) - markButtonTime >= waitButtonTime ) // read occaisoinally
  {
    buttonHistory[ buttonIndex ] <<= 1; // if you don't know <<= look it up in the Arduino Reference
    // keep a browser open to that page when you use the IDE.
    buttonHistory[ buttonIndex ] += digitalRead( buttonPin[ buttonIndex ] ); // read history streams through buttonHistory
    markButtonTime = micros(); // gets the low 16 bits of micros(), time to 60 ms + margin
  }

  // ++buttonIndex pre-increments buttonIndex before comparing to buttons
  if ( ++buttonIndex >= buttons )   buttonIndex = 0;
}

void OnOffBlinker() // only blinks if there's a wait time, can be switched on/off
{
  if ( waitBlink > 0 ) // this is the on/off switch
  {
    // word( millis()) gets the low 16 bits of the 32-bit millis() return.
    if ( word( millis()) - startBlink >= waitBlink ) // difference in time by subtracting start from end
    {
      ledState = !ledState;  // ! is NOT: not_0/true becomes 0/false else 0 becomes 1.
      digitalWrite( ledPin, ledState ); // the led changes state.
      startBlink += waitBlink; // next blink starts when it should, where diff > wait.
    }
  }
  else if ( ledState > 0 ) // waitBlink == 0 turns blinking off
  {
    digitalWrite( ledPin, ledState = LOW ); //  make sure the led is OFF
  } // yes, you can set a variable during calculation in C, the write here is LOW.
}


void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\n  Button Debounce Example, free by GoForSmoke\n" ));
  Serial.println( F( "This code shows use of struct, union, bit fields and 16 bit timers." ));
  Serial.println( F( "It can work with many buttons, uses structs to hold button data." ));
  Serial.println( F( "Button data structs can be arrayed even for matrix arrangements." ));
  Serial.println( F( "\n-- for this example connect a button between pin 7 and GND" ));
  Serial.println( F( "--- or stick a jumper in pin 7 and while holding the board steady" ));
  Serial.println( F( "--- tap the free end onto the grounded USB port box to press." ));

  pinMode( ledPin, OUTPUT );

  for ( byte i = 0; i < buttons; i++ )
  {
    pinMode( buttonPin[ i ], INPUT_PULLUP );
    buttonHistory[ i ] = 255;
  }
};


void loop()
{
  LoopCounter();
  multiButtonsTask();
  /*
    0 is the button held down
    255 is the button left up
    127 is the buton changing from up to down, button just released.
    128 is the button changing from down to up, button just pressed.
    everything else is to be ignored as bounce.
  */

  if ( buttonIndex != lastIndex )
  {
    lastIndex = buttonIndex;

    switch ( buttonHistory[ buttonIndex ] ) // buttonHistory does not change as fast as this runs
    {
      case 128 : // pin is HIGH in bit 7, LOW for 7 reads, up to down detected
        buttonHistory[ buttonIndex ] = 0; // change detected, make it into no change now
        Serial.print( F( "button " ));
        Serial.print( buttonIndex );
        Serial.print( F( "  press detected     " ));
        Serial.println( millis());
        if ( buttonIndex == 0 )
        {
          if ( waitBlink == 0 ) // toggle action tied to button press
          {
            waitBlink = 500; // makes the blinking start
            startBlink = millis(); // gets the low 16 bits
          }
          else // press button 2 changes the blink rate
          {
            waitBlink = 0; // makes the blinking stop
          }
        }
        else
        {
          if (( waitBlink -= 100 ) < 100 ) // setting a var in an expression is okay C
          {
            waitBlink = 1000;
          }
        }
        break;
      case 127 : // pin is LOW in bit 7, HIGH for 7 reads, down to up detected
        buttonHistory[ buttonIndex ] = 255; // change detected, make it into no change now
        Serial.print( F( "button " ));
        Serial.print( buttonIndex );
        Serial.print( F( "  release detected   " ));
        Serial.println( millis());
        break;
    }
  }

  OnOffBlinker();
}

#define BlinkTime 300
#define BTNpin 2
#define crossingL1 3
unsigned long Mils = 0;
bool State = false;

setup() {
  pinMode(BTNpin, INPUT_PULLUP);
  pinMode(crossingL1, OUTPUT);
  //............
}

loop() {
  State = digitalRead(BTNpin);
  if (State == false && Mils < millis() ) {
    Mils = millis() + BlinkTime;
    digitalWrite(crossingL1, !digitalRead(crossingL1));
    State = true;
  }
  if ( State == true ) {
    Mils = millis();
    digitalWrite(crossingL1, LOW);
    State = false;
  }
//.........................................put other commands here
}

it is also possible to set up a PWM on some pin with frequency of 2Hz and it will be set on
analogWrite(pin,255);

LED will flashing by
analogWrite(pin,127);

and set off can be so:
analogWrite(pin,0);

I fear that we're all competing with each other and the OP left the room quite awhile ago..

-jim lee

I hope not and the OP takes the best solution for his model railroad.
Aber irgendwas ist ja immer.

So I want to add my opinion about "the best solution":

at first giving an overview about the programming-technique that does the trick with an all day example

taking the OPs code and transform it in multiple steps where each step is small enough that it is easy to follow the change to the final version that does it.

or adding quite a lot serial output that makes visible what you code is doing.

Yes this is more effort than posting an example with your personal favorite programming style.
best regards Stefan

Thank you all - it seems there are a thousand ways to get from A to B. I will look at each one of your suggested ways (some are above my pay scale). I do prefer coding in a way that I can remember what's what in 10 days time.
Model Railways are very popular - I am surprised that no one has put together a group of routines to handle what is needed - multi tasking is certainly at the forefront.
Thanks again to all you....

1 Like

Oh there is a library that is written by a modelrailway fan.
It is called MoBaTools from the german word of model-railway Modell-Bahn
It does not cover everything but quite some very useful functions.
And a pretty good documentation in english and in german.

best regards Stefan

Thanks for this info - I will look into this as well - lots of reading now for a while.

So I advertise for the MobaTools

  • MoToSoftLed Soft fading in and out of Leds. All digital pins are permissible as connection pins.

  • MoToTimer With this class it is easy to create time delays without blocking the Sketch.

  • MoToButtons Manage up to 32 buttons and switches with debounce and event handling (pressed,
    released, short press, long press , single click, double click). The buttons/switches
    can be read in via a user callback function. This enables matrix arrangements and e.g.
    I2C port expander to be used..

  • MoToPwm This class exists only for ESP8266. On ESP8266, the integrated tone(),
    analogWrite() and Servo() functions cannot be used in parallel with MobaTools due
    to limited timer resources. MoToPwm provides methods to replace tone() and
    analogWrite()

best regards Stefan
@JimLee: the better the documentation is the more users you win.

@StefanL38 Well, would you like to collaborate?

-jim lee

Can I get back to later I am going away for the week.

#define BlinkTime 300
#define BTNpin 2
#define crossingL1 3
unsigned long Mils = 0;
bool State = false;

setup() {
  pinMode(BTNpin, INPUT_PULLUP);
  pinMode(crossingL1, OUTPUT);
  //............
}

loop() {
  State = digitalRead(BTNpin);
  if (State == false && Mils < millis() ) {
    Mils = millis() + BlinkTime;
    digitalWrite(crossingL1, !digitalRead(crossingL1));
    State = true;
  }
  if ( State == true ) {
    Mils = millis();
    digitalWrite(crossingL1, LOW);
    State = false;
  }
//.........................................put other commands here
}

You handle time incorrectly.
What makes the end time - start time = elapsed time always work is the unsigned subtraction.

This just straightens out this part, dunno if it will do what it should.

  State = digitalRead(BTNpin);
  if (State == false && ( millis() - Mils >=  BlinkTime ) {
    digitalWrite(crossingL1, !digitalRead(crossingL1));
    State = true;
  }
  if ( State == true ) {
    Mils += BlinkTime;  // as opposed to Mils = millis();
    digitalWrite(crossingL1, LOW);
    State = false;
  }