Ladyada's multi-button checker - how to?

Hi,

i found the Ladyada's multi-button checker: Example code for multi-button checker with debouncing « Adafruit Industries – Makers, hackers, artists, designers and engineers!
First thing i don't get is how i can trigger something with the code (for example turn on an LED).
And the second thing i don't understand is that interrupts are used on all the pins in the second example.

First thing i don't get is how i can trigger something with the code (for example turn on an LED).

In loop, there is a call to check_switches(). After that is a loop that shows the state of each of the switches. In the appropriate if block, add the code you want.

And the second thing i don't understand is that interrupts are used on all the pins in the second example.

What don't you understand? The switches don't trigger the interrupt. The clock does.

so in

if (justreleased[i])

i have to replace the "i" with the button number.
This works.

But my code gets send to many times.
As long as i press the button if i use "pressed".
So how to make sure it gets send just one time?

The switches don't trigger the interrupt. The clock does.

So this interrupt has nothing to do with the Arduino hardware interrupts that i have only on
some pins like 2 and 3?

MrGlasspoole:
As long as i press the button if i use "pressed".
So how to make sure it gets send just one time?

Check justreleased instead of pressed_. It will tell you that the button has been released and will not be true again until the button is pressed and released again. Or check the value of justpressed to find out if the button has just been pressed and may or may not have been released. Which way you do it depends on whether you want something to happen when a button is pressed or when it is released._

So this interrupt has nothing to do with the Arduino hardware interrupts that i have only on
some pins like 2 and 3?

Correct.

Hm, I'm unsure how where i have to replace the "i" with the button number.

 if (justreleased[i]) {
      justreleased[i] = 0;
      // remember, check_switches() will CLEAR the 'just pressed' flag
    }

It works a view times and then it stops working.
And how do i make it that the light goes on when i press and send my code just one time?

But here is the whole thing:
I want to switch the light from one Arduino on another Arduino via MQTT over my windows server.
This is the code that i have on my Mega 2560 where the light is connected:

#include <SPI.h>
#include <Ethernet.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <PubSubClient.h>

/**************************************************************
*                         Connections                         *
**************************************************************/
#define ONE_WIRE_BUS 5 // oneWire pin is D5
#define showerPin 6    // Shower Light is D6

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

/**************************************************************
*                      Ethernet Settings                      *
**************************************************************/
// Ethernet Card Mac Address
byte mac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
// IPv4 address
byte ip[] = {192, 168, 0, 131};
// Subnet mask
byte subnet[] = {255, 255, 255, 0};
// Default gateway
byte gateway[] = {192, 168, 0, 1};
// MQTT Server
byte server[] = { 192, 168, 0, 1 };
// Preferred DNS sever
// byte dns[] = {192, 168, 0, 1};

/**************************************************************
*                           MQTT                              *
**************************************************************/
EthernetClient ethClient;
PubSubClient client(server, 1883, callback, ethClient);

void callback(char* topic, byte* payload, unsigned int length)
{
  Serial.print("New message from broker on topic:");
  Serial.println(topic);

  Serial.print("Payload:");
  Serial.write(payload, length);
  
  if (strcmp(topic,"foo/shower")==0) { 
    if (payload[0] == '0') {
      digitalWrite(showerPin, LOW); // Turn off Shower Light
    } else if (payload[0] == '1') {
      digitalWrite(showerPin, HIGH); // Turn on Shower Light
   }
  }
}

/**************************************************************
*                           Setup                             *
**************************************************************/
void setup()
{
  byte i;
  byte dsAddress[8];
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
    while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  Serial.println( "Searching for DS18B20..." );
  oneWire.reset_search(); // Start the search with the first device
  if( !oneWire.search(dsAddress) )
  {
    Serial.println( "none found. Using default MAC address." );
  } else {
    Serial.println( "success. Setting MAC address:" );
    Serial.print( " DS18B20 ROM  =" );
    for( i = 0; i < 8; i++)
    {
      Serial.write(' ');
      Serial.print( dsAddress[i], HEX );
    }
    Serial.println();
    
    // Offset array to skip DS18B20 family code, and skip mac[0]
    mac[1] = dsAddress[3];
    mac[2] = dsAddress[4];
    mac[3] = dsAddress[5];
    mac[4] = dsAddress[6];
    mac[5] = dsAddress[7];
  }

  Serial.print( " Ethernet MAC =" );
  for( i = 0; i < 6; i++ )
  {
    Serial.write( ' ' );
    Serial.print( mac[i], HEX );
  }
  Serial.println();

  Ethernet.begin(mac, ip, subnet, gateway);
  Serial.print(" IPv4 address: ");
  Serial.println(Ethernet.localIP());

  // Start up the Dallas Temperature library
  sensors.begin(); // IC Default 9 bit. If you have troubles consider upping it 12. Ups the delay giving the IC more time to process the temperature measurement

  // Connect to MQTT Broker, give it Switch-O-Matic as the name
  if (client.connect("Switch-O-Matic")) {
    // Publish a message to the status topic
    client.publish("status","Switch-O-Matic is now online");
    // Listen for messages on the control topic
    client.subscribe("foo/#");
  }

  pinMode(showerPin, OUTPUT);
  digitalWrite(showerPin, LOW);
   
}
 
void loop()
{
  client.loop();
}

This is the code i have on my Nano 328 where the buttons are connected:

#include <SPI.h>
#include <Ethernet.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <PubSubClient.h>

/**************************************************************
*                         Connections                         *
**************************************************************/
#define ONE_WIRE_BUS 5 // oneWire pin is D5

#define DEBOUNCE 10  // button debouncer, how many ms to debounce, 5+ ms is usually plenty
// here is where we define the buttons that we'll use. button "1" is the first, button "6" is the 6th, etc
byte buttons[] = {14, 15, 16, 17, 18, 19}; // the analog 0-5 pins are also known as 14-19
// This handy macro lets us determine how big the array up above is, by checking the size
#define NUMBUTTONS sizeof(buttons)
// we will track if a button is just pressed, just released, or 'currently pressed' 
volatile byte pressed[NUMBUTTONS], justpressed[NUMBUTTONS], justreleased[NUMBUTTONS];

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

/**************************************************************
*                      Ethernet Settings                      *
**************************************************************/
// Ethernet Card Mac Address
byte mac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
// IPv4 address
byte ip[] = {192, 168, 0, 132};
// Subnet mask
byte subnet[] = {255, 255, 255, 0};
// Default gateway
byte gateway[] = {192, 168, 0, 1};
// MQTT Server
byte server[] = { 192, 168, 0, 1 };
// Preferred DNS sever
// byte dns[] = {192, 168, 0, 1};

void callback(char* topic, byte* payload, unsigned int length) {
  // handle message arrived
}
// Fire up our PubSub client
EthernetClient ethClient;
PubSubClient client(server, 1883, callback, ethClient);

/**************************************************************
*                           Setup                             *
**************************************************************/
void setup()
{
  byte i;
  byte dsAddress[8];
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
    while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  Serial.println( "Searching for DS18B20..." );
  oneWire.reset_search(); // Start the search with the first device
  if( !oneWire.search(dsAddress) )
  {
    Serial.println( "none found. Using default MAC address." );
  } else {
    Serial.println( "success. Setting MAC address:" );
    Serial.print( " DS18B20 ROM  =" );
    for( i = 0; i < 8; i++)
    {
      Serial.write(' ');
      Serial.print( dsAddress[i], HEX );
    }
    Serial.println();
    
    // Offset array to skip DS18B20 family code, and skip mac[0]
    mac[1] = dsAddress[3];
    mac[2] = dsAddress[4];
    mac[3] = dsAddress[5];
    mac[4] = dsAddress[6];
    mac[5] = dsAddress[7];
  }

  Serial.print( " Ethernet MAC =" );
  for( i = 0; i < 6; i++ )
  {
    Serial.write( ' ' );
    Serial.print( mac[i], HEX );
  }
  Serial.println();

  Ethernet.begin(mac, ip, subnet, gateway);
  Serial.print(" IPv4 address: ");
  Serial.println(Ethernet.localIP());

  // Start up the Dallas Temperature library
  sensors.begin(); // IC Default 9 bit. If you have troubles consider upping it 12. Ups the delay giving the IC more time to process the temperature measurement
   
  // Connect to MQTT Broker, give it Bath-O-Matic as the name
  if (client.connect("Bath-O-Matic")) {
    // Publish a message to the status topic
    client.publish("status","Bath-O-Matic is now online");
  }

  // Make input & enable pull-up resistors on switch pins
  for (i=0; i< NUMBUTTONS; i++) {
    pinMode(buttons[i], INPUT);
    digitalWrite(buttons[i], HIGH);
  }
  
  // Run timer2 interrupt every 15 ms 
  TCCR2A = 0;
  TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20;

  //Timer2 Overflow Interrupt Enable
  TIMSK2 |= 1<<TOIE2;
}


SIGNAL(TIMER2_OVF_vect) {
  check_switches();
}

void check_switches()
{
  static byte previousstate[NUMBUTTONS];
  static byte currentstate[NUMBUTTONS];
  static long lasttime;
  byte index;

  if (millis() < lasttime) {
     // we wrapped around, lets just try again
     lasttime = millis();
  }
  
  if ((lasttime + DEBOUNCE) > millis()) {
    // not enough time has passed to debounce
    return; 
  }
  // ok we have waited DEBOUNCE milliseconds, lets reset the timer
  lasttime = millis();
  
  for (index = 0; index < NUMBUTTONS; index++) {
     
    currentstate[index] = digitalRead(buttons[index]);   // read the button
    
    /*     
    Serial.print(index, DEC);
    Serial.print(": cstate=");
    Serial.print(currentstate[index], DEC);
    Serial.print(", pstate=");
    Serial.print(previousstate[index], DEC);
    Serial.print(", press=");
    */
    
    if (currentstate[index] == previousstate[index]) {
      if ((pressed[index] == LOW) && (currentstate[index] == LOW)) {
          // just pressed
          justpressed[index] = 1;
      }
      else if ((pressed[index] == HIGH) && (currentstate[index] == HIGH)) {
          // just released
          justreleased[index] = 1;
      }
      pressed[index] = !currentstate[index];  // remember, digital HIGH means NOT pressed
    }
    //Serial.println(pressed[index], DEC);
    previousstate[index] = currentstate[index];   // keep a running tally of the buttons
  }
}

void loop() {
  
  for (byte i = 0; i < NUMBUTTONS; i++) {

    if (justreleased[0]) {
      justreleased[0] = 0;
      client.publish("foo/shower","1"); // Turn off Shower Light
    }

  }
}

At the moment the DS18B20's are just to set unique MAC addresses.
On my Windows 2012 Server i have Mosquitto running.
I think the code on the Mega is ok cause i can turn the light on and off many times with PHP
without problems:

<?php
require("phpMQTT.php");
$mqtt = new phpMQTT();
/* broker(broker address, broker port, client id); */
$mqtt->broker("192.168.0.1", 1883, "PHP MQTT Client");
$mqtt->connect();
/* publish( topic, message, qos); */
$mqtt->publish("foo/shower","0",0);
$mqtt->close();
?>

I have the code now on an Mega and set:

byte buttons[] = {54, 55, 56, 57, 58, 59};

But it's the same. It works and after some time stops working (also without doing anything).

In the command prompt on my windows server i receive nothing when it stops working.
If it works i see:

Client mosqsub/4868-SIREN received PUBLISH (d0, q0, r0, m0, 'foo/shower', ... (1 bytes)) 0

if Mosquitto is started with "mosquitto_sub -d -t foo/#" in the command prompt.

I have the code now on an Mega and set:

What exactly does buttons represent? Not pin numbers, since those are not valid pin numbers for a Mega.

In Ladyada's code it's:

// here is where we define the buttons that we'll use. button "1" is the first, button "6" is the 6th, etc
byte buttons[] = {14, 15, 16, 17, 18, 19}; // the analog 0-5 pins are also known as 14-19

So for the Mega i thought it's "54, 55, 56, 57, 58, 59".
And looks like this is correct cause the button (A0) works.

Sorry. I read the page for the Mega, and saw 54 digital pins. For some reason, I forgot about the analog pins.

Is there a way to debug this stuff to find out why it stops working after some time?

Just about the only way is to put Serial.println()s in your code so that you can follow the flow of the program and the value of variables

Ok, it must be the button naming.
Can somebody tell me where i have to change the "i" to a button number:

if (justreleased[i]) {
      justreleased[i] = 0;
      Serial.print(i, DEC);
      Serial.println(" Just released");
      client.publish("foo/shower","0"); // Turn off Shower Light
}

I don't get it.
And maybe how to make it work with "just pressed" and sending the code just one time.

Can somebody tell me where i have to change the "i" to a button number:

In the square brackets.

And maybe how to make it work with "just pressed" and sending the code just one time.

If you are having problems with that happening, perhaps you need a sketch that illustrates JUST that problem. I fail to see how all that code dealing with the ethernet class, etc. is related to your problem with the switches.

The code is send over ethernet to the second arduino.

if (justreleased[0]) {
      justreleased[0] = 0;
      Serial.print(i, DEC);
      Serial.println(" Just released");
}

Sometimes shows:
5 Just released
3 Just released
2 Just released
3 Just released
in the serial Monitor.
But also if i see for example "5 Just released" i receive the code in Mosquitto until it stops working.

Why are you explicitly testing the 0 element, and then printing i?

What are you REALLY trying to do?

I'm trying to figure out where the problem is.
I thought that:

if (justpressed[0]) {
justpressed[0] = 0;
Serial.print(i, DEC);
Serial.println(" Just pressed");
client.publish("foo/shower","0"); // Turn off Shower Light
// remember, check_switches() will CLEAR the 'just pressed' flag
}

shows me if the first button (pin 54 on the Mega) is pressed?
It's the only button that is connected at the moment.
But sometimes i get:
5 Just pressed
3 Just pressed
2 Just pressed
instead of "0 Just pressed" if i press button 0/54

The funny thing is that also if the serial monitor says for example "3 Just pressed", my button 0 code works.

You are testing the 0 switch, and then printing some random value of i. Why is that so hard to see? Or fix?