Go Down

Topic: Event-driven code example. (Read 702 times) previous topic - next topic

GoForSmoke

I've made it as clean and clear as I could within reason. With Lots Of Comments.
It's tested on an UNO with only 1 jumper wire, but a button can be used instead.
It also uses 16 bit unsigned values to time less than long intervals.
I have a version that debounces with byte for time but jumpers for buttons need more.

It's simple to me but I was familiar with the idea since long ago.

Code: [Select]

// All you need to do this example is an Arduino and a jumper in pin 2.
// Push and let go the button once, led blinks. Again, led turns off.
// Button is debounced and wired directly to ground or, the button can
// be a jumper from pin 2 grounded on USB connector. Tested with jumper.
// By GoForSmoke for free public use. Using Arduino 1.0.5-r2

/*
This sketch uses millis() timing but only the low bytes. Unsigned math
lets this work just as well as with all 32 bits.
This sketch has 3 separated code blocks.
--- 1 for the button that changes buttonState only after a debounced change.
--- 1 that processes changes in buttonState to control led blink.
--- 1 that blinks the led when it's supposed to.
Each code block could be replaced by another with minimal change to the others.
More blocks can be added, they don't have to deal with the led but they could.
If you want to merge sketches then here is one strategy to do it.
*/

// Look Ma, no extra libraries!

const byte ledPin =  13;      // this is the onboard led pin
const byte buttonPin = 2;


byte ledPinState = 0;
byte blinkLed = 0; // led stays off when 0, blinks when 1
const word ledBlinkMs = 1000U; // blink interval
word ledBlinkNowMs = 0U; // low 16 bits of millis()
word ledBlinkStartMs = 0U; // 16 bit blink (on or off) time ms

// button variables
byte buttonRead;  // wired for pullup -- if down then LOW, if up then HIGH
byte lastButtonRead = HIGH; // so debounce knows previous read
byte checkDebounce = 0; // only checks decounce after every button pin change
byte lastButtonState = 0; // last stable button state
byte buttonState = 0;  // stable button state
// 0 = button is up after debounce
// 1 = button is down after debounce
// button debounce timing variables
const word debounceMs = 300; // debounced when reads for debounceMs ms are the same
word debounceMillsLowByte = 0; // only need to count to 10, don't need 32 bits
word msNow = 0; // don't worry, unsigned rollover makes it work


byte processState = 0;  // the button gets pushed and released twice
// 0 = 1st press has not happened, led blink is OFF, looking for press
// 1 = 1st press is down, led blink is ON, looking for release
// 2 = 1st push relesased, led blink stays ON, looking for 2nd press
// 3 = 2nd press is down, led blink is OFF, looking for release
// there is no state 4, 2nd push release changes state to 0


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

  pinMode( ledPin, OUTPUT );  // default is LOW
  pinMode( buttonPin, INPUT_PULLUP ); // my button connects to ground, not 5V
  // however that means that the pin is LOW when the button is pressed.
}

void loop() // make sure that loop() runs fast and stays in the "now".
{

  // BUTTON CODE BLOCK, it handles debouncing.
  // the task is to set the variable buttonState when a Stable Button State is reached.
  // other sensor code could change the same variable if desired

  // read the pin which may be changing fast and often as the jumper contacts the housing
  buttonRead = digitalRead( buttonPin ); // momentary state

  // msNow gets the low byte of millis() to time the very short debounce time
  msNow = (word)( millis() & 0xFFFF ); // set once, used in 2 places

  if ( buttonRead != lastButtonRead )
  {
    debounceMillsLowByte = msNow;
    checkDebounce = 1;
  }
  else if ( checkDebounce )
  {
    if ( msNow - debounceMillsLowByte >= debounceMs ) // stable button state achieved
    {
      buttonState = !buttonRead; // mission accomplished, button is stable
      // note that buttonState is opposite buttonRead
      checkDebounce = 0; // stop debounce checking until pin change
    }
  }
  lastButtonRead = buttonRead;
  //
  // End of the BUTTON CODE BLOCK

  //==================================================================================

  // LED CONTROL CODE BLOCK that uses buttonState and processState
  if ( lastButtonState != buttonState )
  {
    lastButtonState = buttonState;

    // debug type prints -- also testing some loading, does it alter the blink much?
    Serial.println( F( "============================================================" ));   
    Serial.print( F( "processState " ));
    Serial.print( processState );
    Serial.print( F( "  buttonState " ));
    Serial.println( buttonState );
    Serial.println( F( "============================================================" ));   

    switch ( processState )
    {
    case 0: // 0 = 1st press has not happened, led is OFF, looking for press
      if ( buttonState == 1 ) // button is pressed
      {
        processState = 1;
        blinkLed = 1;
        ledPinState = 0;
        ledBlinkStartMs = (word) ( millis() & 0xFFFF )- ledBlinkMs;
      }
      break; // note that until the 1st press, this case runs over and over

    case 1: // 1 = 1st press is down, led is ON, looking for release
      if ( buttonState == 0 ) // button is released
      {
        processState = 2;
      }
      break; // note that until the 1st release, this case runs over and over

    case 2: // 2 = 1st push relesased, led stays ON, looking for 2nd press
      if ( buttonState == 1 ) // button is pressed
      {
        processState = 3;
        blinkLed = 0;
      }
      break; // note that until the 2nd press, this case runs over and over

    case 3: // 3 = 2nd press is down, led is OFF, looking for release
      if ( buttonState == 0 ) // button is released
      {
        processState = 0; // back to the start
      }
      break; // note that until the 2nd release, this case runs over and over
    }
  }
  // End of the LED CONTROL CODE

  //==================================================================================

  // LED BLINK CODE BLOCK
  if ( blinkLed )
  {
    ledBlinkNowMs = (word) ( millis() & 0xFFFF );

    if ( ledBlinkNowMs - ledBlinkStartMs >= ledBlinkMs )
    {
      ledPinState = !ledPinState;
      digitalWrite(ledPin, ledPinState );
      ledBlinkStartMs = ledBlinkNowMs;
    }
  }
  else
  {
    ledPinState = LOW;
    digitalWrite(ledPin, LOW );
  }
  // End of the LED BLINK CODE

  //==================================================================================

  // Want to add serial commands and args input?
  // this is a good spot. Serial is so slow it can usually go last.
}


Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts


GoForSmoke

I turned it into an 8 switch version with the switch data in an array, the same code works for every switch.

The big trick is only check 1 switch per pass through loop() and index that with a static or global counter.
- Checking 8 switches every loop before allowing anything else like the blinking status light reduces response and might show in the blink timing.
- Checking 8 switches in 8 passes through loop() allows the blink and other tasks if any to be more responsive.

Here's a trick to detect pin change. Uses 1 byte.

byte pinReads = 0;

pinReads = digitalRead( myPin );

// some time later

pinReads = ( pinReads * 2 ) & 3;
pinReads += digitalRead( myPin ); // result will be current and last reads as 0 to 3 combined

If pinReads == 0, the latest read was 0 and the last read was 0 ....... no change
If pinReads == 1, the latest read was 1 and the last read was 0 .......... state change LOW to HIGH
If pinReads == 2, the latest read was 0 and the last read was 1 ......... state change HIGH to LOW
If pinReads == 0, the latest read was 1 and the last read was 1 .......... no change

Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

Go Up