1

I have this simple code who send a signal to a android phone when I press a button:

const int botonPinD = 8;
const int relayPin =  12;  
int retardo = 100;
int finBoton=1000;

void setup() {
  pinMode(relayPin, OUTPUT);      

  pinMode(botonPinA, INPUT); 
  pinMode(botonPinB, INPUT);  
  pinMode(botonPinC, INPUT); 
  pinMode(botonPinD, INPUT);  
}
void loop(){
    botonD = digitalRead(botonPinD);
    if (botonD == HIGH){
   // for simplicity remove botonA, botonB and botonC code, but it's the same with a few digitalWrite
        digitalWrite(relayPin, HIGH);
        delay(retardo);
        digitalWrite(relayPin, LOW);
        delay(retardo);
        digitalWrite(relayPin, HIGH);
        delay(retardo);
        digitalWrite(relayPin, LOW);
        delay(retardo);
        digitalWrite(relayPin, HIGH);
        delay(retardo);
        digitalWrite(relayPin, LOW);
        delay(retardo);
        digitalWrite(relayPin, HIGH);
        delay(retardo);
        digitalWrite(relayPin, LOW);
        delay(retardo);
        delay(finBoton);
      }
      else{
        digitalWrite(relayPin, LOW);
      }
}

But if I press the button fast (maybe a half a second between clicks) I only receive once the signal.

I'm not very sure about what's happened here (I'm an Arduino newbie). The first signal, has it locked the channel? Can signals be queued? Can I control the way that they are queued?

1
  • 1
    Hi, and welcome to the Arduno Stack Exchange. Unfortunately you appear to have only posted a small portion of your code (known as a "snippet") which is lacking the most important portion - the portion that is doing the communication (and thus the "queuing") with the Android device. Without that information we will not be able to help you. Please help us to help you by posting your entire program.
    – Majenko
    Aug 31 '15 at 16:18
1

The code below is closer to what you seem to be asking about:

// For Mega2560

const int BUTTONS = 4;
const int FIRST_SWITCH = 18;
const int FIRST_RELAY =  41;  
const int CLICKS = 5;
const unsigned long DEBOUNCE_TIME = 20;  // ms

int retardo  = 300;
int finBoton = 1000;

volatile byte counts [BUTTONS];
unsigned long lastPress [BUTTONS];

void doInterrupts (const int which)
 {
 // debounce
 if (millis () - lastPress [which] < DEBOUNCE_TIME)
   return;

 lastPress [which] = millis ();
 counts [which]++;
 }  // end of doInterrupts

void pin18Pressed ()
  {
  doInterrupts (0);  
  }  // end of pin18Pressed

void pin19Pressed ()
  {
  doInterrupts (1);  
  }  // end of pin19Pressed

void pin20Pressed ()
  {
  doInterrupts (2);  
  }  // end of pin20Pressed

void pin21Pressed ()
  {
  doInterrupts (3);  
  }  // end of pin21Pressed

void setup() 
  {
  // configure inputs / outputs
  for (int i = 0; i < BUTTONS; i++)
    {
    pinMode (FIRST_SWITCH + i, INPUT_PULLUP);
    pinMode (FIRST_RELAY + i, OUTPUT);
    }

  attachInterrupt (5, pin18Pressed, FALLING);
  attachInterrupt (4, pin19Pressed, FALLING);
  attachInterrupt (3, pin20Pressed, FALLING);
  attachInterrupt (2, pin21Pressed, FALLING);

  } // end of setup

// turn relay on for a particular switch
void handleSwitchPress (const int which)
  {
  for (int i = 0; i < CLICKS; i++)
  {
    digitalWrite(FIRST_RELAY + which, HIGH);
    delay(retardo);
    digitalWrite(FIRST_RELAY + which, LOW);
    delay(retardo);
  }
  delay(finBoton);
  } // end of handleSwitchPress

// main loop
void loop()
  {
 for (int i = 0; i < BUTTONS; i++)
   if (counts [i] > 0)
     {
     handleSwitchPress (i);
     counts [i]--;  
     }
  }  // end of loop

I'll explain parts of it from above:

Interrupts

To queue up switch presses we can use an interrupt. The Mega has quite a few external interrupts (See: attachInterrupt()) so I'll use those. You can read about interrupts on my page about interrupts.

This code attaches four interrupts handlers:

  attachInterrupt (5, pin18Pressed, FALLING);
  attachInterrupt (4, pin19Pressed, FALLING);
  attachInterrupt (3, pin20Pressed, FALLING);
  attachInterrupt (2, pin21Pressed, FALLING);

Because of the way the hardware works we have to use pins 18, 19, 20 and 21 (there are a couple more but these are in sequence).

I set the switches to be INPUT_PULLUP so that they are normally HIGH when not pressed, and go LOW when you press them. Hence the interrupts are looking for a FALLING edge.


Handle one interrupt

An interrupt handler is like this:

void pin18Pressed ()
  {
  doInterrupts (0);  
  }  // end of pin18Pressed

Since they all do much the same thing I put all of the switch management into one function and used an array of switches, like this:

volatile byte counts [BUTTONS];
unsigned long lastPress [BUTTONS];

void doInterrupts (const int which)
 {
 // debounce
 if (millis () - lastPress [which] < DEBOUNCE_TIME)
   return;

 lastPress [which] = millis ();
 counts [which]++;
 }  // end of doInterrupts

In order to "queue" presses the interrupt handler adds one to a counter, for that array item. (So, pin 18 is position 0, pin 19 is position 1 and so on).


Debouncing

Debouncing of the switches is done by ignoring multiple presses in quick succession:

 // debounce
 if (millis () - lastPress [which] < DEBOUNCE_TIME)
   return;

 lastPress [which] = millis ();

Handling a queued press

Now in the main loop we just check if the counter has gone up, for each switch:

 for (int i = 0; i < BUTTONS; i++)
   if (counts [i] > 0)
     {
     handleSwitchPress (i);
     counts [i]--;  
     }

If so, we call handleSwitchPress which toggles the relay for the desired number of times.


Code for Uno

If you are using a Uno instead of a Mega2560 you don't have four external interrupts. The alternative version below uses pin change interrupts instead of external interrupts.

Pin change interrupts work on all pins of the Uno, however they are in batches of three: Pins A0 to A5, pins D0 to D7 and pins D8 to D13. In the code below I use pins 2, 3, 4 and 5 which are in the batch D0 to D7.

With a pin-change interrupt you only get notification of a change to the pin, so the code has to detect whether the pin changed from high to low or low to high.

// For Atmega328 (eg. Uno)

const int BUTTONS = 4;
const int FIRST_BUTTON = 2;
const int FIRST_RELAY =  8;  
const int CLICKS = 5;
const unsigned long DEBOUNCE_TIME = 20;  // ms

int retardo  = 300;
int finBoton = 1000;

volatile bool oldState [BUTTONS];
volatile byte counts [BUTTONS];
unsigned long lastPress;

// handle pin change interrupt for D0 to D7 here
ISR (PCINT2_vect)
 {

 // debounce
 if (millis () - lastPress < DEBOUNCE_TIME)
   return;

 lastPress = millis ();

 // check each switch
 for (int i = 0; i < BUTTONS; i++)
   {
   byte state = digitalRead (FIRST_BUTTON + i);
   if (state != oldState [i])
     {
     oldState [i] = state;  // detect state changes
     if (state == LOW)
       counts [i]++;
     }   // end of state change
   }  // end of for each button

 }  // end of PCINT2_vect


void setup() 
  {

  // configure inputs / outputs
  for (int i = 0; i < BUTTONS; i++)
    {
    pinMode (FIRST_BUTTON + i, INPUT_PULLUP);
    oldState [i] = HIGH;
    pinMode (FIRST_RELAY + i, OUTPUT);
    }

  // pin change interrupt (example for D9)
  PCMSK2 |= bit (PCINT18) | bit (PCINT19) | bit (PCINT20) | bit (PCINT21);  // want pins 2, 3, 4, 5
  PCIFR  |= bit (PCIF2);   // clear any outstanding interrupts
  PCICR  |= bit (PCIE2);   // enable pin change interrupts for D0 to D7
  } // end of setup

// turn relay on for a particular switch
void handleSwitchPress (const int which)
  {
  for (int i = 0; i < CLICKS; i++)
  {
    digitalWrite(FIRST_RELAY + which, HIGH);
    delay(retardo);
    digitalWrite(FIRST_RELAY + which, LOW);
    delay(retardo);
  }
  delay(finBoton);
  } // end of handleSwitchPress

// main loop
void loop()
  {
 for (int i = 0; i < BUTTONS; i++)
   if (counts [i] > 0)
     {
     handleSwitchPress (i);
     counts [i]--;  
     }
  }  // end of loop
2

The main problem I guess you are suffering from is the good old delay() blockage.

delay() is a terrible function and should be avoided at all costs. Nothing at all can happen while you are running delay() - that means you can't be reading the state of your button - until all your delay() calls have finished.

If you want to queue the pressed of the button you will need to do three things:

  1. You will need to recognise when the button changes from LOW to HIGH, not just "when it is HIGH" as you do now.
  2. You will have to queue that button press somewhere. The simplest way is to maintain a count of how many times the button has been pressed.
  3. You will have to process any pending button presses ("while the count > 0") by triggering your output with the right timing without using delay(), which means using millis() and maintaining state information.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.