Compatibility between ezButton and SX1509?

Hi all, I'm making a project on a Nano that uses an SX1509 to read an array of 32 buttons that play individual sound files. Does the ezButton library support reading these buttons? Below are my two code samples. Program 1 is my code without ezButton. It runs, but the sound stutters if I hold the button down, which is not what I want. Program 2 is a snippet that I made for a prototype using a single button and ezButton. The stuttering effect is gone, but of course the button is hooked directly to a specific pin, which is no longer the case.

In the ezButton examples, there's the code for a button array, however those buttons are hooked directly to pins, which is not my case. So I'm a bit lost. Can I use ezButton to read from an SX1509? Thanks in advance for any guidance.

PROGRAM 1:

#include <Wire.h>
#include <SparkFunSX1509.h>
#include <DFPlayerMini_Fast.h>

// SX1509 I2C address (set by ADDR1 and ADDR0 (00 by default):
const byte SX1509_ADDRESS = 0x3E; // SX1509 I2C address
SX1509 io;                        // Create an SX1509 object to be used throughout

#define KEY_ROWS 8
#define KEY_COLS 4

#if !defined(UBRR1H)
#include <SoftwareSerial.h>
SoftwareSerial mySerial(10, 11); // RX, TX
#endif

DFPlayerMini_Fast myMP3;

char keyMap[KEY_ROWS][KEY_COLS] = {
    {'1', '2', '3', '4'},
    {'5', '6', '7', '8'},
    {'9', '10', '11', '12'},
    {'13', '14', '15', '16'},
    {'17', '18', '19', '20'},
    {'21', '22', '23', '24'},
    {'25', '26', '27', '28'},
    {'29', '30', '31', '32'}};

const byte ARDUINO_INTERRUPT_PIN = 2;

unsigned int previousKeyData = 0;         // Stores last key pressed
unsigned int releaseCount = 0; // Count durations
const unsigned int releaseCountMax = 100; // Release limit

void setup()
{
  Serial.begin(115200);
  // Serial.println("SX1509 Example");

  Wire.begin();

  #if !defined(UBRR1H)
    mySerial.begin(9600);
    myMP3.begin(mySerial, true);
  #else
    Serial1.begin(9600);
    myMP3.begin(Serial1, true);
  #endif
  
  Serial.println("Setting volume to 20");
  myMP3.volume(20);
  myMP3.EQSelect(2);

  // Call io.begin(<address>) to initialize the SX1509. If it successfully communicates, it'll return 1.
  if (io.begin(SX1509_ADDRESS) == false)
  {
    Serial.println("Failed to communicate. Check wiring and address of SX1509.");
    while (1); // If we fail to communicate, loop forever.
  }
  
  // Sleep time range: 128 ms - 8192 ms (powers of 2) 0=OFF
  byte sleepTime = 0;
  // Scan time range: 1-128 ms, powers of 2
  byte scanTime = 8; // Scan time per row, in ms
  // Debounce time range: 0.5 - 64 ms (powers of 2)
  byte debounceTime = 1; // Debounce time
  // Scan time must be greater than debounce time!

  io.keypad(KEY_ROWS, KEY_COLS,
            sleepTime, scanTime, debounceTime);
  pinMode(ARDUINO_INTERRUPT_PIN, INPUT_PULLUP);
}

void loop()
{
  // If the SX1509 INT pin goes low, a keypad button has been pressed:
  if (digitalRead(ARDUINO_INTERRUPT_PIN) == LOW)
  {
    // Use io.readKeypad() to get the raw keypad row/column
    unsigned int keyData = io.readKeypad();
    // Then use io.getRow() and io.getCol() to parse that data into row and column values.
    byte row = io.getRow(keyData);
    byte col = io.getCol(keyData);
    // Then plug row and column into keyMap to get which key was pressed.
    char key = keyMap[row][col];

    // If it's a new key pressed
    if (keyData != previousKeyData)
    {
      Serial.println(String(key)); // Print the key
      // OPEN FRET
      if (key == '1')   // E - open
      {
        myMP3.play(1);
      }
      if (key == '2')   // A - open
      {
        myMP3.play(2);
      }
      if (key == '3')   // D - open
      {
        myMP3.play(3);
      }
      if (key == '4')   // G - open
      {
        myMP3.play(4);
      }

      // FRET ONE
      if (key == '5')   // E -F
      {
        myMP3.play(5);
      }
      if (key == '6')   // A - Bb
      {
        myMP3.play(6);
      }
      if (key == '7')   // D - Eb
      {
        myMP3.play(7);
      }
      if (key == '8')   // G - Ab
      {
        myMP3.play(8);
      }

      // FRET TWO
      if (key == '9')   // E - Gb
      {
        myMP3.play(9);
      }
      if (key == '10')  // A - B
      {
        myMP3.play(10);
      }
      if (key == '11')  // D - E
      {
        myMP3.play(11);
      }
      if (key == '12')  // G - A
      {
        myMP3.play(12);
      }

      // FRET THREE
      if (key == '13')  // E - G
      {
        myMP3.play(13);
      }
      if (key == '14')  // A - C
      {
        myMP3.play(14);
      }
      if (key == '15')  // D - F
      {
        myMP3.play(15);
      }
      if (key == '16')  // G - Bb
      {
        myMP3.play(16);
      }

      // FRET FOUR
      if (key == '17')  // E - Ab
      {
        myMP3.play(17);
      }
      if (key == '18')  // A - Db
      {
        myMP3.play(18);
      }
      if (key == '19')  // D - G
      {
        myMP3.play(19);
      }
      if (key == '20')  // G - B
      {
        myMP3.play(20);
      }

      // FRET FIVE
      if (key == '21')  // E - A
      {
        myMP3.play(21);
      }
      if (key == '22')  // A - D
      {
        myMP3.play(22);
      }
      if (key == '23')  // D - G
      {
        myMP3.play(23);
      }
      if (key == '24')  // G - C
      {
        myMP3.play(24);
      }

      // FRET SIX
      if (key == '25')  // E - Bb
      {
        myMP3.play(25);
      }
      if (key == '26')  // A - Eb
      {
        myMP3.play(26);
      }
      if (key == '27')  // D - Ab
      {
        myMP3.play(27);
      }
      if (key == '28')  // G - Db
      {
        myMP3.play(28);
      }

      // FRET SEVEN
      if (key == '29')  // E - B
      {
        myMP3.play(29);
      }
      if (key == '30')  // A - E
      {
        myMP3.play(30);
      }
      if (key == '31')  // D - A
      {
        myMP3.play(31);
      }
      if (key == '32')  // G - D
      {
        myMP3.play(32);
      }
    }
    
    releaseCount = 0;          // Clear the releaseCount variable
    previousKeyData = keyData; // Update previousKeyData
  }

  // If no keys have been pressed we'll continuously increment releaseCount,
  // eventually creating a release, once the count hits the max.
  releaseCount++;
  if (releaseCount >= releaseCountMax)
  {
    releaseCount = 0;
    previousKeyData = 0;
  }
  delay(1); // Gives releaseCountMax a more intuitive unit
}

PROGRAM 2:

#include <DFPlayerMini_Fast.h>
#include <ezButton.h>

ezButton button(4);
int last_button_state = HIGH;

#if !defined(UBRR1H)
#include <SoftwareSerial.h>
SoftwareSerial mySerial(10, 11); // RX, TX
#endif

DFPlayerMini_Fast myMP3;

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

#if !defined(UBRR1H)
  mySerial.begin(9600);
  myMP3.begin(mySerial, true);
#else
  Serial1.begin(9600);
  myMP3.begin(Serial1, true);
#endif
  
  Serial.println("Setting volume to 20");
  myMP3.volume(20);

}

void loop()
{
  button.loop();

  if (last_button_state == HIGH && button.getStateRaw() == LOW)
  {
    myMP3.play(1);
    last_button_state = LOW;
  }

  if (button.isReleased())
  {
    last_button_state = HIGH;
  }
}

Your keymap is bad.

It is of type char, short for character, and should be full of character constants.

In this context, character constants appear as one character inside single quotes.

Just use any single digits or upper or lower case letters as the names for all your keys.

I look more when I am not moving to see if there is any other thing messing you up.

a7

Posting a link to its datasheet provides more replies.

Here's an example that may use the library and moduke a bit differently:

HTh

a7

Sorry, here you go: https://cdn.sparkfun.com/datasheets/BreakoutBoards/sx1509.pdf

Yes, that's what I based my code on. You said my keymap is bad, but that's what's in the sample code from the library.

It's an I/O expander. I doubt ez button will handle it.

No. I think that you already figured out that ezButton only can use pins connected directly to the Arduino.

No, it's not. The sample code has the digits '0' to '9' and the characters '*' and '#'. Nowhere is a '10' etc.

If you want to use something like 0..31 (you might need 1..32), change your map to

char keyMap[KEY_ROWS][KEY_COLS] = {
  {1, 2, 3, 4},
  {5, 6, 7, 8},
  {9, 10, 11, 12},
  {13, 14, 15, 16},
  {17, 18, 19, 20},
  {21, 22, 23, 24},
  {25, 26, 27, 28},
  {29, 30, 31, 32}
};

And all these

      if (key == '1')   // E - open
      {
        myMP3.play(1);
      }
      ...
      ...
      if (key == '32')  // G - D
      {
        myMP3.play(32);
      }

can be replaced by a one statement myMP3.play(key).

if(key >= 1 && key <= 32)
{
  myMP3.play(key);
}

Oh I see what you mean now, my bad. I'll give that a go when I can. Maybe it will solve the issue of the audio being played multiple times with a long keypress, or maybe it won't. We'll see. Thanks for the input!

Hi again, I ultimately went with upper and lower case letters in single quotes. Using numbers in the keyMap spat out squares in the serial monitor (user error, I'm sure) so I switched to letters so I could actually see what keys were being pressed.

I'm just using the keypadInterrupt example for now, but the key gets repeated for as long as I hold down the button. I just want it to execute once per keystroke and only repeat if it's released and pressed again. Increasing the debounce time only made it repeat slightly slower. I'll admit I'm in over my head here.

As an aside, should I start a new thread under a new topic since the "compatibility between ezButton and SX1509" subject is no longer relevant?

#include <Wire.h>
#include <SparkFunSX1509.h>

// SX1509 I2C address (set by ADDR1 and ADDR0 (00 by default):
const byte SX1509_ADDRESS = 0x3E; // SX1509 I2C address
SX1509 io;                        // Create an SX1509 object to be used throughout

#define KEY_ROWS 8 // Number of rows in the keypad matrix
#define KEY_COLS 4 // Number of columns in the keypad matrix

// keyMap maps row/column combinations to characters:
char keyMap[KEY_ROWS][KEY_COLS] = {
  {'A', 'B', 'C', 'D'},
  {'E', 'F', 'G', 'H'},
  {'I', 'J', 'K', 'L'},
  {'M', 'N', 'O', 'P'},
  {'Q', 'R', 'S', 'T'},
  {'U', 'V', 'W', 'X'},
  {'Y', 'Z', 'a', 'b'},
  {'c', 'd', 'e', 'f'}};

const byte ARDUINO_INTERRUPT_PIN = 2;

void setup()
{
  Serial.begin(115200);
  Serial.println("SX1509 Example");

  Wire.begin();

  // Call io.begin(<address>) to initialize the SX1509. If it
  // successfully communicates, it'll return 1.
  if (io.begin(SX1509_ADDRESS) == false)
  {
    Serial.println("Failed to communicate. Check wiring and address of SX1509.");
    while (1)
      ; // If we fail to communicate, loop forever.
  }

  // Scan time range: 1-128 ms, powers of 2
  byte scanTime = 4; // Scan time per row, in ms
  // Debounce time range: 0.5 - 64 ms (powers of 2)
  byte debounceTime = 1; // Debounce time
  // Sleep time range: 128 ms - 8192 ms (powers of 2) 0=OFF
  byte sleepTime = 0;
  // Scan time must be greater than debounce time!
  io.keypad(KEY_ROWS, KEY_COLS,
            sleepTime, scanTime, debounceTime);

  // Set up the Arduino interrupt pin as an input w/
  // internal pull-up. (The SX1509 interrupt is active-low.)
  pinMode(ARDUINO_INTERRUPT_PIN, INPUT_PULLUP);
}

// Compared to the keypad in keypad.ino, this keypad example
// is a bit more advanced. We'll use these varaibles to check
// if a key is being held down, or has been released. Then we
// can kind of emulate the operation of a computer keyboard.
unsigned int previousKeyData = 0;         // Stores last key pressed
unsigned int holdCount, releaseCount = 0; // Count durations
const unsigned int holdCountMax = 15;     // Key hold limit
const unsigned int releaseCountMax = 100; // Release limit

void loop()
{
  // If the SX1509 INT pin goes low, a keypad button has
  // been pressed:
  if (digitalRead(ARDUINO_INTERRUPT_PIN) == LOW)
  {
    // Use io.readKeypad() to get the raw keypad row/column
    unsigned int keyData = io.readKeypad();
    // Then use io.getRow() and io.getCol() to parse that
    // data into row and column values.
    byte row = io.getRow(keyData);
    byte col = io.getCol(keyData);
    // Then plug row and column into keyMap to get which
    // key was pressed.
    char key = keyMap[row][col];

    // If it's a new key pressed
    if (keyData != previousKeyData)
    {
      holdCount = 0;               // Reset hold-down count
      Serial.println(String(key)); // Print the key
    }
    else // If the button's beging held down:
    {
      holdCount++;                  // Increment holdCount
      if (holdCount > holdCountMax) // If it exceeds threshold
        Serial.println(key);        // Print the key
    }
    releaseCount = 0;          // Clear the releaseCount variable
    previousKeyData = keyData; // Update previousKeyData
  }

  // If no keys have been pressed we'll continuously increment
  //  releaseCount. Eventually creating a release, once the
  // count hits the max.
  releaseCount++;
  if (releaseCount >= releaseCountMax)
  {
    releaseCount = 0;
    previousKeyData = 0;
  }
  delay(1); // Gives releaseCountMax a more intuitive unit
}

const unsigned int holdCountMax = 15; // Key hold limit

else // If the button's being held down:
    {
      holdCount++;                  // Increment holdCount
      if (holdCount > holdCountMax) // If it exceeds threshold
        Serial.println(key);        // Print the key
    }

What happens if you increase the value of holdCountMax from 15 to 150?

What happens if you comment out the Serial.println(key) in the conditional when the holdCount is greater than holdCountMax?

1 Like

The comment does not describe the code accurately.

This if statement

  // If the SX1509 INT pin goes low, a keypad button has
  // been pressed:
  if (digitalRead(ARDUINO_INTERRUPT_PIN) == LOW)

is true when the pin is low, not when it goes low. It totally explains your observations.

You must code for when that signal goes low. This is generally called state change (or edge) detection and is a hole thing in this part of control systems.

See

https://docs.arduino.cc/built-in-examples/digital/StateChangeDetection/

I'm not in the lab, I will link a simulation L8R where you can see it in action. It just means remembering what the signal was, and seeing if it is different.

If it was HIGH and is now LOW, then do whatever.

A single variable can hold the previously seen value.

HTH

a7

I don't think this is correct. The state change detection is built into the conditional code block with this

unsigned int keyData = io.readKeypad();

if (keyData != previousKeyData)
    { }

  previousKeyData = keyData;

I see what you mean.

Unfortunately this exceeds my current ability to think it through and agree with you, here's where I would have to run the code, or hack it as I have no SX1509 at hand… the hold and release count stuff looks more like something that should be done elsewhere.

I looked again, probably should have just gone back to sleep:

    else // If the button's beging held down:
    {
      holdCount++;                  // Increment holdCount
      if (holdCount > holdCountMax) // If it exceeds threshold
        Serial.println(key);        // Print the key
    }

If the same key is seen to be still pressed, once holdCount exceeds holdCountMax a flood of the key pressed will print.

I'll look for what I am missing.

a7

I think no matter what, this code needs to be lost:

    else // If the button's beging held down:
    {
      holdCount++;                  // Increment holdCount
      if (holdCount > holdCountMax) // If it exceeds threshold
        Serial.println(key);        // Print the key
    }

It is some kind of auto-repeat. I think you'll still get repeated keystrokes if you hold the button down, but a necessary step away from the problem.

a7

1 Like

Both of you have equally workable solutions, thank you!

@cattledog Increasing holdCountMax still repeats endlessly after a short period of time, but considering that each sound file is only three seconds long, I foresee any given key being held for shorter so the delay then repeat is likely a non-issue.

@alto777 Commenting out that block makes it not repeat at all, which is exactly what I want.

Cheers to the both of you! Hopefully I can put together some final code soon.

I just added the slide fader, after fiddling with the slide switches for a while, so the code that does the thing is @qubits-us's.

The SX1509 is a module to remember, lotsa good stuff in there. I didn't find any thorough documentation and the datasheet for it needs to be met more than halfway.

I put one in my sparkfun wish-cart.

a7

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.