Button and LED demo codes

Here are two sketches made to do the same thing.

There is a blinking LED.
Push the button top stop the blinking.
Push it again to restart the blinking.

The first sketch uses delay().
The second sketch uses millis().

I have not tested these but they do compile. Help find "features"?
There's not much code here....

Sketch 1

// BlockedSketchLedAndButton by GoForSmoke 03/08/24 
// compiles, untested so far
// expect: ground pin 7 to toggle blink off/on
// expect this to take a while to notice button action.
// this is a Before Sketch. The After Sketch does not Block Execution.

const byte debounce = 10; // ms delay
const byte blinkPin = 13;
const byte buttonPin = 7;
byte buttonStateNow, buttonStatePrev;
byte blinkState;
word blinkInterval = 500;
const word interval = 500;

void setup()
{
  pinMode( blinkPin, OUTPUT ); // LOW by default
  // blinkState = 0; // by default
  pinMode( buttonPin, INPUT_PULLUP );
  buttonStateNow = buttonStatePrev = 1;
}

void blink()
{
  if ( blinkInterval > 0 ) // button toggled to ON by default
  {
    blinkState = !blinkState; // 0 <==> 1
    digitalWrite( blinkPin, blinkState ); // led ON
    delay( blinkInterval );
  }
}

void button()
{
  buttonStateNow = digitalRead( buttonPin );
  if ( buttonStateNow == 0 && buttonStatePrev == 1 ) // press detected
  {
    delay( debounce );
    if ( blinkInterval > 0 )
    {
      blinkInterval = 0; // stop blinking
      blinkState = 0;
      digitalWrite( blinkPin, blinkState );
    }
    else
    {
      blinkInterval = interval; // blink at interval
    }
  }
  else if ( buttonStateNow == 1 && buttonStatePrev == 0 ) // release detected
  {
    delay( debounce );
  }

  buttonStatePrev = buttonStateNow;
}

void loop()
{
  button();
  blink();
}

Sketch 2

// UnblockedSketchLedAndButton by GoForSmoke 03/08/24
// compiles, untested so far
// expect: ground pin 7 to toggle blink off/on

const byte blinkPin = 13;
const byte buttonPin = 7;
byte buttonStateNow, buttonStatePrev;
const word debounce = 10; // ms delay
word debounceStart; // interval is debounce above
byte blinkState;
unsigned long blinkStart, blinkInterval = 500;
const unsigned long interval = 500;


void setup()
{
  pinMode( blinkPin, OUTPUT ); // LOW by default
  // blinkState is 0 by default
  pinMode( buttonPin, INPUT_PULLUP );
  buttonStateNow = buttonStatePrev = 1;
}

void blink()
{
  static unsigned long waitStart, waitMs;
  if ( blinkInterval == 0 )
  {
    return;
  }

  if ( waitMs > 0 ) // if-timer runs when set then turns itself off
  {
    if ( millis() - waitStart >= waitMs )  // see if wait is over
    {
      waitMs = 0; // wait is over, turn wait off!
    }
  }
  else
  {
    blinkState = !blinkState; // 0 <==> 1
    digitalWrite( blinkPin, blinkState ); // led ON
    waitMs = blinkInterval;
    waitStart = millis();
  }
  return;
}


void button()
{
  static byte State = 0;  //  local variable only the function uses
  static unsigned long waitStart, waitMs;  // local vars

  if ( waitMs > 0 ) // if-timer for state machine delays
  {
    if ( millis() - waitStart >= waitMs )
    {
      waitMs = 0;
    }
  }
  else
  {
    switch ( State )
    {
      case 0 :
        buttonStateNow = digitalRead( buttonPin );
        if ( buttonStateNow == 0 && buttonStatePrev == 1 ) // press detected
        {
          waitMs = debounce;
          waitStart = millis();
          State = 1;
        }
        else if ( buttonStateNow == 1 && buttonStatePrev == 0 ) // release detected
        {
          waitMs = debounce;
          waitStart = millis();
        }
        buttonStatePrev = buttonStateNow;
        break;

      case 1 :
        if ( blinkInterval > 0 )
        {
          blinkInterval = 0; // stop blinking
          digitalWrite( blinkPin, LOW );
        }
        else
        {
          blinkInterval = interval; // blink at interval
        }
        State = 0;
        break;
    }
  }
  return;
}

void loop()
{
  button();
  blink();
}

Pardon me for i don't get the word "Features" . I like code with millis rather than delay .Because using millis kind feel like it is multitasking . The coding part of millis if difficult compared to delay. If using millis in loops may cause sdt reset in bords like Nodemcu if the loops are not handled correctly

A "feature" is a bug that doesn't wreck the code. Read: bugs.

Using time/millis does all tasking on a single thread. That started for me in the early 80's.

I don't know NodeMCU.
The only loop I want is void loop. If I want to iterate, I keep indexes as needed and operate on 1 element per void loop() pass. If I want to repeat actions, I put them in a state machine and repeat cases until done. I was at this for a while long ago.

This is a step one demo. The button handler has led code in it to pare the concepts down so not pure, it's a slow pitch for simplicity.

1 Like

That's what I noticed, and without using pencil and paper or running the code, every way I thought to "purify" you examples did seem to make a bit of a mess of them.

Which sounds contradictory, but would not necessarily be in the context of a larger sketch.

Just now looking again I thnik

void blink()
{
  if ( blinkInterval > 0 )
  {
    blinkState = !blinkState; // 0 <==> 1
    digitalWrite( blinkPin, blinkState ); 
    delay( blinkInterval );
  }
  else {
    blinkState = 0;
    digitalWrite( blinkPin, blinkState ); // LED off too
  }
}

doesn't seem too bad, giving blonk() the responsibility for fully stopping. The timing by delay() will mean no short on period, which either is what you want or not, but delay() does have implications always.

And… I don't hate

void blink()
{
  static unsigned long waitStart, waitMs;

  if ( blinkInterval == 0 )
  {
    blinkState = 0;
    digitalWrite( blinkPin, 0); // led Off
    waitMs = 0;

    return;
  }

  if ( waitMs > 0 ) // if-timer runs when set then turns itself off
  {
    if ( millis() - waitStart >= waitMs )  // see if wait is over
    {
      waitMs = 0; // wait is over, turn wait off!
    }
  }
  else
  {
    blinkState = !blinkState; // 0 <==> 1
    digitalWrite( blinkPin, blinkState ); // led ON
    waitMs = blinkInterval;
    waitStart = millis();
  }
  return;
}

In both cases there would be changes to button() as it is relieved of that duty.

You've a few comments that are not right

    digitalWrite( blinkPin, blinkState ); // led ON

and I noticed the explicit return; from functions returning void, but not in all cases.

I've never minded return at a point that isn't the last line of a function no matter the return type, some style guides are against that, so.

In the second sketch, the use of the same name for local variables confused me. I am easily confused, and I suppose it is good to see that local variables are, um, local. You have not shared the words that will accompany your demo. I assume you mean to add this to the other heroic efforts to write a good tutorial on these important skillz. Part of which will have to address variable scope and do some 'splaining about static variables.

In the second sketch, there's a bit of a painted corner. If the interval is dialed up, it is easy to see that re-starting the blink will first have to run out any remaining time on the millis() internal timer... which given that the LED is off by logic means we might have to wait some time to know the press registered. If, that is, blinking is turned off when the LED is on... I think. It seemed to go on near instantly if the turn off blinking press came during the LED off period. It is, however, tired and I am getting late, so see for yourself if I am wrong.

Changing that looks like it would get ugly.

I saw word as a type. You use byte and unsigned long so why not int? I don't see word around much.

It is different to the normal state change detecting to explicitly look for one or the other edge. Far more common is seeing that there is change, then going on to handle changed to pressed and/or changed to not pressed. You must have reasons for doing. Again, I look forward to the words.

Also, there's that 0 false LOW stuff… which don't get me started, I am never going to consistently use

    digitalWrite(somePin, blinkState ? HIGH : LOW) { //... 

here or the other places one should do that kind of thing.

I have many reason to wish I am dead. I hope I am by the time it actually makes a difference and breaks everything I've ever written. I have followed discussions around the matter, and taken part once or more; I have never gone away from any convinced that it wasn't a mistake. Call me old fashioned, call me old. I like 0 and not 0 and they have never been the problem when I had problems. With my code.

a7

I use int where I use signed values. Nothing there needs negatives.

I left bumps an warts where removing them would require introducing tangents before the basic pernt.
These tutorials are to get one main idea into a less than cluttered view. I do not count on everyone picking it up, just delighted when anyone does! I've gotten to see that here over time.

I could do a slower development, maybe comments can do that.
It starts with a blink sketch and button as an added step. I took care of (but not fully) the led state at the button because that was the soonest most direct way is all. Introducing Process Tasks and reasons why... maybe not on Step One?

It's a balancing act. I keep showing that what can run alone with delay() and other blocking code +++can+++ be converted to run together. Yes, the aim is to get people writing tasking code as opposed to grand Knot Code, aka Can-Of-Worms Code when it gets older.

So here is the delay version with lots more comments.
I do need to tighten up the non-blocking blink restart, I'll set a new start time right in the button code that yeah should be split into Process and Output tasks.
I don't just want to show non-blocking. I want to show the tasking that non-blocking allows.

// BlockedSketchLedAndButton v1.1 by GoForSmoke 03/13/24 
 for 1.1 a lot of comments have been added.

// This sketch is not PERFECT. 
// It explores doing 2 things "at once" with delay-code.
// How responsive the button acts says how well it works.
// this began with a simple Blink With Delay Sketch.
// This sketch adds an ON/OFF toggle button with delay debounce.

// compiles, untested so far
// expect: ground pin 7 to toggle blink off/on
// expect this to take a while to notice button action.
// expect the button response to be quicker when blink is OFF.
// this is a Before Sketch. The After Sketch does not Block Execution.

// note for Beginners.. I use Arduino variable types.
// Arduino variable type byte can be 0 to 255, easy to type.
// Arduino variable type word can be 0 to 65535, easy to type.
// Where an int uses 16 bits of RAM, Arduino byte uses only 8.
// When I only need a byte, I don't use more on Arduinos.

const byte debounce = 10; // ms delay to ignore contact bouncing.
const byte blinkPin = 13; // LED pin to blink
const byte buttonPin = 7; // the name says it

byte buttonStateNow;  // pin state may be 0 (LOW) or not-0 (HIGH).
byte buttonStatePrev; // see if pin state changed from Prev to Now.
byte blinkState;  // the LED is 0 (LOW) or not-0 (HIGH)
// using 0 for LOW and not-0 for HIGH gives options not used here
// and that is the way the chip works. 
// When you have 8 bits for one T/F... they can ALL be used.

word blinkInterval = 500; // so far the blink ON time = OFF time
const word interval = 500; // to restore the working value
// the button works by making blinkInterval = 0 or = interval

void setup() 
{
  pinMode( blinkPin, OUTPUT ); // LOW by default
  // blinkState = 0; // by default
  pinMode( buttonPin, INPUT_PULLUP );  // up is 1, down is 0
  buttonStateNow = buttonStatePrev = 1; // start values initialized
}

void blink() // can be turned OFF and ON through blinkInterval
{
  if ( blinkInterval > 0 ) // button toggled to ON by default
  {
    blinkState = !blinkState; // 0 <==> 1
    digitalWrite( blinkPin, blinkState ); // led blink
    delay( blinkInterval );
  }
}

void button() // works by changing blinkInterval, OFF turns LED OFF.
{
  buttonStateNow = digitalRead( buttonPin );

  // button press detect, state was 1, now 0
  if ( buttonStateNow == 0 && buttonStatePrev == 1 ) // press detected
  {
    delay( debounce ); // contact switch changes are briefly DIRTY
    .
    if ( blinkInterval > 0 )  // if blink is ON, turn it OFF
    {
      blinkInterval = 0; // stop blinking
      blinkState = 0;
      digitalWrite( blinkPin, blinkState );
    }
    else    // if blink is OFF, turn it ON
    {
      blinkInterval = interval; // blink interval restored
    }
  }
  // button release detect, state was 0, now 1
  else if ( buttonStateNow == 1 && buttonStatePrev == 0 ) // release detected
  {
    delay( debounce ); // DIRTY on both press and release, a few ms.
  }

  buttonStatePrev = buttonStateNow;  // button done, make Prev = Now
}

void loop()
{
  button();  // which comes first doesn't really matter
  blink();   // try running blink first if yer not sure!
}

How is that?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.