Advice on code for interactive installation and parts

Hi everyone, I'm reposting this here hoping to get some reply.
The previous post had some views and no reply and I'm really in need of some guidance. (original post)

I'm developing an Arduino project for an installation for a kids audience and need some coding help.
It is supposed to be an interactive carpet interfaced with Millumin software that will run several videos projected on this carpet.
I'd like to use some buttons in specific places to trigger different medias and make the installation variable.
The kids will trigger the buttons and make specific events start.
Can some body point me on the direction of the type of buttons to use? Those should stay under the carpet (which is a dance carpet, so a quite heavy PVC carpet) and be triggered by the pressure applied by the audience (mostly very small kids) but have a very low profile. I was thinking to membrane buttons but maybe somebody knows a better solution.

On the Arduino side:

The buttons are simple buttons connected with a resistor and each button is connected to a digital pin of the board.

I succeded on making the buttons work with the arduino and interface the arduino with the software Millumin that uses it's libraries to talk with the board.

I'd like to add to the code some kind of debounce, so the buttons can be triggered only after a specific time if they were triggered before. I mean that if button n° 1 is pressed, it can be triggered again after eg.10 sec, but in the mean while the others could be triggered.
I'd like that every button beheaves this way.

I compiled this code without debounce

/*
  streaming.ino
  Created by Anomes, 2016.
  Creative Commons Attribution-NonCommercial 4.0 International Public License
*/

#include <Arduino.h>
#include <MilluminStream.h>
 
  // First define pins array
int pushButton[] = {
  2, 3, 4, 5, 6
};

int pinCount = 5;

////////////////////////////////////////////////////////////////////////////////
// Initialisation
void setup()
{
  // We setup MilluminStream
  MilluminStream::setup();
  // pin Digital array as input to stream its sensor value
   for (int thisPin = 0; thisPin < pinCount; thisPin++)
  pinMode(pushButton[thisPin], INPUT_PULLUP);
}



////////////////////////////////////////////////////////////////////////////////
// Loop
void loop()
{

  // First, we need to update MilluminStream
  MilluminStream::update();

  for (int thisPin = 0; thisPin < pinCount; thisPin++) 
  // Then, we stream pin Digital array
  MilluminStream::sendDigitalForID(pushButton[thisPin]); // Add the value of D3 pin to the frame
 // delay(3000); 
  // Stream a custom value
 // uint16_t variable = random(0, 255);
 // MilluminStream::sendVariableForID(0, variable); // Add a custom value to the frame

  // Finally we send just one frame with all our values
  MilluminStream::sendFrame();

}

It works fine and I read each button state.

Then I tried to implement debounce in this way

/*
  streaming.ino
  Created by Anomes, 2016.
  Creative Commons Attribution-NonCommercial 4.0 International Public License
*/

#include <Arduino.h>
#include <MilluminStream.h>

// First define pins array
int pushButton[] = {
  2, 3, 4, 5, 6
};

int pinCount = 5;
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin

// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

////////////////////////////////////////////////////////////////////////////////
// Initialisation
void setup()
{
  // We setup MilluminStream
  MilluminStream::setup();
  // pin Digital array as input to stream its sensor value
  for (int thisPin = 0; thisPin < pinCount; thisPin++)
    pinMode(pushButton[thisPin], INPUT_PULLUP);
}



////////////////////////////////////////////////////////////////////////////////
// Loop
void loop()
{
  // read the state of the switch into a local variable:
  int reading = digitalRead(pushButton[thisPin]);

  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH), and you've waited long enough
  // since the last press to ignore any noise:

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

      // First, we need to update MilluminStream
      MilluminStream::update();

      for (int thisPin = 0; thisPin < pinCount; thisPin++)
        // Then, we stream pin Digital array
        MilluminStream::sendDigitalForID(pushButton[thisPin]); // Add the value of Digital pin to the frame

      // Finally we send just one frame with all our values
      MilluminStream::sendFrame();

    }
  }
}

But it doesn't work and compiles with an error.

I'm not a programmer and my understanding of code is quite limited.
Can anybody please help me?
Any help will be very appreciated!
Many thanks
aigore

For buttons under the carpet, you're looking for Force Sensitive Resistors or FSR. The old-style pressure mats used in ancient burglar alarms are still available. You could probably even find a few DIY guides to build your own.

How long is this installation expected to last? If it's going to be more than a week then reliability is going to be the biggest problem. The cost of having staff pull up the carpet each night to re-solder wires that broke will exceed the purchase cost very quickly.

  // First define pins array

int pushButton[] = {
  2, 3, 4, 5, 6
};

Great! You're halfway there. Now you just need 2 more arrays to record the state and time for each of those buttons. Maybe you could define those arrays to use these names...

bool lastButtonState[pinCount];

unsigned long lastDebounceTime[pinCount];

a) A thick PVC carpet will likely spread the pressure across a wide area and with little peoples' weight may not be enough to trigger a membrane button. Needs testing.

b) Think about capacitive sensing.

c) Any kind of truly useful de-bouncing begins with a capacitor across the input to ground.

d) "But it doesn't work and compiles with an error." Post the error.

e) Have a great day.

Here's an example strategy:

  • keep an array of the last button pressed times
  • when the button is pressed mark the time in the last pushed array
  • if the button is active (last pressed time is zero) read the button
  • if the button is not active check whether it should be turned back on
  • call the library with the button value

This strategy seemed sound to me until I looked at the library source code and saw that the sendDigitalForID method reads the pin. It would be so much better if you could pass the desired value.

I've never used that library and cannot compile the code since I don't have it, so I'm doing some guessing. I think my strategy might work if the library was changed. Some code to give you an idea of what I'm talking about:

/*
  streaming.ino
  Created by Anomes, 2016.
  Creative Commons Attribution-NonCommercial 4.0 International Public License
*/

#include <Arduino.h>
#include <MilluminStream.h>

// First define pins array
int pushButton[] = {
  2, 3, 4, 5, 6
};

unsigned long buttonInactiveTime = 10000UL;             // how long button will be inactive
unsigned long buttonPaushed = { 0UL, 0UL, 0UL, 0UL, 0UL }; // last time button was pushed

int pinCount = 5;

////////////////////////////////////////////////////////////////////////////////
// Initialisation
void setup()
{
  // We setup MilluminStream
  MilluminStream::setup();
  // pin Digital array as input to stream its sensor value
  for (int thisPin = 0; thisPin < pinCount; thisPin++)
    pinMode(pushButton[thisPin], INPUT_PULLUP);

  Serial.begin(9600);
}



////////////////////////////////////////////////////////////////////////////////
// Loop
void loop()
{

  // First, we need to update MilluminStream
  MilluminStream::update();

  for (int thisPin = 0; thisPin < pinCount; thisPin++)
  {
    int buttonVal = LOW;

    // is button active?
    if ( buttonPaushed[thisPin] == 0UL )
    {
      // read button
      buttonVal = pushButton[thisPin];

      Serial.print("read button ");
      Serial.println(thisPin);

      // if button is down
      if ( buttonVal == HIGH )
      {
        // mark this time
        buttonPaushed[thisPin] = millis();
      }

    } else {

      Serial.print(thisPin);
      Serial.println(" is inactive ");

      // time to make button active again?
      if ( millis() - buttonPaushed[thisPin] >= buttonInactiveTime )
      {
        //  yes, clear last pushed value
        buttonPaushed[thisPin] = 0UL;

      } // if

    } // else


    MilluminStream::sendDigitalForID(pushButton[thisPin], buttonVal);

    // Then, we stream pin Digital array
    //  MilluminStream::sendDigitalForID(pushButton[thisPin]); // Add the value of D3 pin to the frame


  }
  // delay(3000);
  // Stream a custom value
  // uint16_t variable = random(0, 255);
  // MilluminStream::sendVariableForID(0, variable); // Add a custom value to the frame

  // Finally we send just one frame with all our values
  MilluminStream::sendFrame();

}

Proposed addition to the library:

  void sendDigitalForID(char id, uint8_t byte)
  {
    if( bufferIndex+3 < BUFFER_SIZE)
    {
     // remove from original  uint8_t byte = digitalRead(id);
      buffer[bufferIndex++] = 'D';
      buffer[bufferIndex++] = id;
      buffer[bufferIndex++] = byte;
    }
}

Maybe you could use FSRs for the buttons? Might not work the thick map and lack of weight. (Typing while MorganS was replying).

Have you looked into dance game mats? Here ...

Hi everyone! Many thanks for all the replies.
Lot's of cool hints.

I'll try the code hints in the next days and give you an update.

Just to give you more informations

MorganS:
How long is this installation expected to last? If it's going to be more than a week then reliability is going to be the biggest problem. The cost of having staff pull up the carpet each night to re-solder wires that broke will exceed the purchase cost very quickly.

The installation will be fixed in one space for a week, non need to pull it off. Attendants will have only to turn it on and off and I can make all the start up automatic. It will be a continuous cycle of videos and sounds played by the Millumin software with Arduino taking care of the buttons and interaction.

MorganS:
For buttons under the carpet, you're looking for Force Sensitive Resistors or FSR. The old-style pressure mats used in ancient burglar alarms are still available. You could probably even find a few DIY guides to build your own.

I have two FSR, but what I've found are quite small and I could need to make an array of those to make them work. My thought about FSR is that maybe the carpet will be quite heavy and there could be no room for a possible threshold and the maximum pressure readable by the FSR? I have not much experience with those.

ChrisTenone:
Have you looked into dance game mats? Here ...

I'll have a look on those mats

Thanks Blue Eyes and DKWatson also.
I'll try all the coding and get back to you all.
Thanks again!

Hi everyone, I'm trying to wrap my head around the coding but, even with your suggestions, I'm not able to set a debounce time for the buttons connected to my arduino.

My last code update is this:

/*
  streaming.ino
  Created by Anomes, 2016.
  Creative Commons Attribution-NonCommercial 4.0 International Public License
*/

#include <Arduino.h>
#include <MilluminStream.h>

// First define pins array
int pushButton[] = {
  2, 3, 4, 5, 6
};

const int pinCount = 5;
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin

// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

bool lastButtonState[pinCount];

unsigned long lastDebounceTime[pinCount]; 

////////////////////////////////////////////////////////////////////////////////
// Initialisation
void setup()
{
  // We setup MilluminStream
  MilluminStream::setup();
  // pin Digital array as input to stream its sensor value
  for (int thisPin = 0; thisPin < pinCount; thisPin++)
    pinMode(pushButton[thisPin], INPUT_PULLUP);
}



////////////////////////////////////////////////////////////////////////////////
// Loop
void loop()
{

  for (int thisPin = pinCount - 1; thisPin >= 0; thisPin--)
  // read the state of the switch into a local variable:
  int reading = digitalRead(pushButton[thisPin]);

  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH), and you've waited long enough
  // since the last press to ignore any noise:

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

      // First, we need to update MilluminStream
      MilluminStream::update();

      for (int thisPin = 0; thisPin < pinCount; thisPin++)
        // Then, we stream pin Digital array
        MilluminStream::sendDigitalForID(pushButton[thisPin]); // Add the value of Digital pin to the frame

      // Finally we send just one frame with all our values
      MilluminStream::sendFrame();

    }
  }
}

But I'm getting those errors:

Arduino:1.8.5 (Mac OS X), Scheda:"Arduino/Genuino Uno"

Millumin-button_array-debounce:24: error: conflicting declaration 'bool lastButtonState [5]'
 bool lastButtonState[pinCount];
                              ^
/Users/xxxx/Documents/Arduino/Millumin-button_array-debounce/Millumin-button_array-debounce.ino:17:5: note: previous declaration as 'int lastButtonState'
 int lastButtonState = LOW;   // the previous reading from the input pin
     ^
Millumin-button_array-debounce:26: error: conflicting declaration 'long unsigned int lastDebounceTime [5]'
 unsigned long lastDebounceTime[pinCount]; 
                                        ^
/Users/xxxx/Documents/Arduino/Millumin-button_array-debounce/Millumin-button_array-debounce.ino:21:15: note: previous declaration as 'long unsigned int lastDebounceTime'
 unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
               ^
/Users/xxxx/Documents/Arduino/Millumin-button_array-debounce/Millumin-button_array-debounce.ino: In function 'void loop()':
Millumin-button_array-debounce:55: error: 'reading' was not declared in this scope
   if (reading != lastButtonState) {
       ^
Millumin-button_array-debounce:65: error: 'reading' was not declared in this scope
     if (reading != buttonState) {
         ^
exit status 1
conflicting declaration 'bool lastButtonState [5]'

I'm not a coder and new to this word so I can't really understand
I was trying just to implement the informations in this tutorial Debounce

into this code that is compiled directly from the software Millumin that has an auto compiler for building interactions with Arduino and works for what I need.

/*
  streaming.ino
  Created by Anomes, 2016.
  Creative Commons Attribution-NonCommercial 4.0 International Public License
*/

#include <Arduino.h>
#include <MilluminStream.h>
 
  // First define pins array
int pushButton[] = {
  2, 3, 4, 5, 6
};

int pinCount = 5;

////////////////////////////////////////////////////////////////////////////////
// Initialisation
void setup()
{
  // We setup MilluminStream
  MilluminStream::setup();
  // pin Digital array as input to stream its sensor value
   for (int thisPin = 0; thisPin < pinCount; thisPin++)
  pinMode(pushButton[thisPin], INPUT_PULLUP);
}



////////////////////////////////////////////////////////////////////////////////
// Loop
void loop()
{

  // First, we need to update MilluminStream
  MilluminStream::update();

  for (int thisPin = 0; thisPin < pinCount; thisPin++) 
  // Then, we stream pin Digital array
  MilluminStream::sendDigitalForID(pushButton[thisPin]); // Add the value of D3 pin to the frame
 // delay(3000); 
  // Stream a custom value
 // uint16_t variable = random(0, 255);
 // MilluminStream::sendVariableForID(0, variable); // Add a custom value to the frame

  // Finally we send just one frame with all our values
  MilluminStream::sendFrame();

}

But for the nature of the installation and the public, that is composed mostly of very little kids, setting a debounce to try not to oversaturate the buttons inputs is the safest way to go in my opinion.

Please can you help me to finish implementing the code? Any help will be very much appreciated.
Many thanks

You can't have two variables with the same name. "Conflicting declaration" says you tried to do that.

Hi everybody. Again thanks for all the advices.
I'm writing just because I don't want to let the post die without a reply.

I'm studying a bit more the coding, I hope I'll find an answer and learn a bit more how to code the Arduino stuff that's amazing.

As for the installation I'm developing a different kind of interaction, so I will use a different type of switches to trigger the events.

Again, many thanks to everybody that spent time to help me out.