Pages: [1] 2   Go Down
Author Topic: Ladyada's multi-button checker - how to?  (Read 1364 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Full Member
***
Karma: 0
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,

i found the Ladyada's multi-button checker: http://www.adafruit.com/blog/2009/10/20/example-code-for-multi-button-checker-with-debouncing/
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.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 613
Posts: 49270
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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.

Quote
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.
Logged

Offline Offline
Full Member
***
Karma: 0
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

so in
Code:
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?

Quote
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?
Logged

East Anglia (UK)
Offline Offline
Faraday Member
**
Karma: 114
Posts: 4255
May all of your blinks be without delay()
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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.
Logged

Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 613
Posts: 49270
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Offline Offline
Full Member
***
Karma: 0
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hm, I'm unsure how where i have to replace the "i" with the button number.
Code:
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:
Code:
#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();
}
Logged

Offline Offline
Full Member
***
Karma: 0
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This is the code i have on my Nano 328 where the buttons are connected:
Code:
#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:
Code:
<?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();
?>
Logged

Offline Offline
Full Member
***
Karma: 0
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have the code now on an Mega and set:
Code:
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:
Code:
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.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 613
Posts: 49270
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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.
Logged

Offline Offline
Full Member
***
Karma: 0
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

In Ladyada's code it's:
Code:
// 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.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 613
Posts: 49270
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Offline Offline
Full Member
***
Karma: 0
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

East Anglia (UK)
Offline Offline
Faraday Member
**
Karma: 114
Posts: 4255
May all of your blinks be without delay()
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

Offline Offline
Full Member
***
Karma: 0
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, it must be the button naming.
Can somebody tell me where i have to change the "i" to a button number:
Code:
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.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 613
Posts: 49270
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Can somebody tell me where i have to change the "i" to a button number:
In the square brackets.

Quote
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.
Logged

Pages: [1] 2   Go Up
Jump to: