Reading ButtonState Array

I have a Pink LED moving across 16 pads,
some pads can be latched on (Blue LEDs)
how can i keep the Blue latched on LEDs on when the Pink LED passes?
Latched on =1 and off =0 in my array

int trellisButtonState[16] = {0, 0, 0, 0,
                              0, 0, 0, 0,
                              0, 0, 0, 0,
                              0, 0, 0, 0,
                             };

I want the previous LED to go off only if its array number is 0 (latched off)
and stay Blue if its array number is 1 (latched on)
I tried but i'm unsure how to read that array

if (trellisButtonState[16] = 1) {LED Blue} // because its latched
else {LED OFF}

#include "Adafruit_NeoTrellis.h"

//#define Y_DIM 4 //number of rows of key
//#define X_DIM 4 //number of columns of keys

// ----------------------------------------FSW NEOPIXELS---------------------------------------------
#include <Adafruit_NeoPixel.h>
#define PIXEL_PIN  5 // (Led Pin)
#define PIXEL_COUNT   2
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);
// ----------------------------------------FSW NEOPIXELS---------------------------------------------

Adafruit_NeoTrellis trellis;

int FSW2 = 2;  // Start/stop footswitch
int FSW2State = HIGH;
int button1 = 3; // tap footswitch
int pad = -1;
int ssLedState = -1; //this variable tracks the startStop LED, negative if off, positive if on
int ttLedState = LOW; // ledState used to set the tempo LED
int lastTapState = LOW;  //the last tap button state
int activeButton = 0;
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 500;    // the debounce time; increase if the output flickers
unsigned long currentTimer[2] = { 1500, 1500 };  // array of most recent tap counts
unsigned long timeoutTime = 0;   //this is when the timer will trigger next
unsigned long timeoutTime2 = 0;   //this is when the timer will trigger next
unsigned long lastTap = 0; //this vairable determines the time of the most recent tap, used when recording data when button is pressed
unsigned long indicatorTimeout; //variable used to determine the duration of the led blinks on state
unsigned long indicatorTimeout2; //variable used to determine the duration of the pad X4 blinks on state



int trellisButtonState[16] = {0, 0, 0, 0,
                              0, 0, 0, 0,
                              0, 0, 0, 0,
                              0, 0, 0, 0,
                             };


elapsedMillis button_hold_counter = 0;
int8_t held_button_id = -1;




void setup() {
  Serial.begin(9600);
  trellis.begin();
  trellis.pixels.setBrightness(50);
  pixels.begin(); // for start/stop and tempo LEDs
  pixels.setBrightness(50);
  pixels.setPixelColor(1, 220, 20, 60);
  pixels.show();
  pinMode( button1, INPUT_PULLUP );   //tempo button input
  pinMode(FSW2, INPUT_PULLUP);
  Serial.println("NeoPixel Trellis started");


  //activate momentary pads and set callbacks
  for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) {
    trellis.activateKey(i, SEESAW_KEYPAD_EDGE_RISING);
    trellis.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING);
    trellis.registerCallback(i, momentary);
  }

  //do a little animation to show we're on
  for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
    trellis.pixels.setPixelColor(i, Wheel(map(i, 0, trellis.pixels.numPixels(), 0, 255)));
    trellis.pixels.show();
    delay(25);
  }
  for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
    trellis.pixels.setPixelColor(i, 0x000000);
    trellis.pixels.show();
    delay(25);
  }
}


void loop() {
  trellis.read();  // interrupt management does all the work! :)
  startStopFSW();
  if (ssLedState > 0) {
    runSeq();
  }
//  if (ssLedState < 0) { // if stop led, clear last seq led
//    trellis.pixels.setPixelColor(pad, 0x000000); // always turns last seq led off, no good
//    trellis.pixels.show();
//    return; // just run once? didnt help
//  }
}




void runSeq() {
  int tapState = digitalRead( button1 );   //button is first pressed down

//if (ssLedState < 0) {
//      trellis.pixels.setPixelColor(pad, 0x000000); // doesnt work here either
//    trellis.pixels.show();
//}

  if ( tapState == LOW && tapState != lastTapState ) {
    currentTimer[1] = currentTimer[0];    //this takes the tap time done before it (measured in millis), sets it one back in the array,
    currentTimer[0] = millis() - lastTap;     //and then sets the other slot of the array to the length of the most recent tap time
    lastTap = millis();
    timeoutTime = 0;
    timeoutTime2 = 0;
  }

  lastTapState = tapState; //variable used to determine the most recent state of the button

  if ( millis() >= timeoutTime )  {     //checks to see if it is time to turn on the tap fsw LED
    indicatorTimeout = millis() + 150;  //determines the length of time that the LED is on
    //this will set the new tempo
    timeoutTime = millis() + ((currentTimer[0] + currentTimer[1]) / 2);
  }

  if ( millis() >= timeoutTime2 )  {    //checks to see if it is time to turn on the trellis LEDs
    indicatorTimeout2 = millis() + 10;  //determines the length of time that the trellis LED is on
    //this will set the new tempo
    timeoutTime2 = millis() + ((currentTimer[0] + currentTimer[1]) / 8); // Divide by 8 to make it 4 times faster
    pad++;
    pad %= 16;
  }

  //turns on tempo led for the length of indicatorTimeout and then turns it off
  if ( millis() < indicatorTimeout ) {
    pixels.setPixelColor(0, 0, 255, 255); // Tempo LED on
    pixels.show();
  }

  pixels.setPixelColor(0, 0, 0, 0);  // Tempo LED off
  pixels.show();


  //turns on trellis led for the length of indicatorTimeout, then turns it off, then moves to next led
  if ( millis() < indicatorTimeout2 ) {
    //trellis.pixels.setPixelColor(pad, 0, 255, 255); // trellis led on
    trellis.pixels.setPixelColor(pad, 220, 20, 60);
    trellis.pixels.show();
  }

  trellis.pixels.setPixelColor(pad, 0); // trellis led off
}





void startStopFSW() {
  FSW2State = digitalRead(FSW2);   //sample the state of the button - is it pressed or not?

  if ( (millis() - lastDebounceTime) > debounceDelay) {   //filter out any noise by setting a time buffer
    if ( (FSW2State == LOW) && (ssLedState < 0) ) {     //if the button has been pressed, lets toggle the LED from "off to on" or "on to off"
      pixels.setPixelColor(1, 124, 252, 0);       // start LED lawn green:
      pixels.show();
      // runSeq();  // START runSeq FUNCTION
      Serial.println("Start");
      pad = -1;
      ssLedState = -ssLedState; //now the LED is on, we need to change the state
      lastDebounceTime = millis(); //set the current time
    }

    else if ( (FSW2State == LOW) && (ssLedState > 0) ) {
      pixels.setPixelColor(1, 220, 20, 60);       // stop LED crimson
      pixels.show();
      Serial.println("Stop");
      //trellis.pixels.setPixelColor(0, 0); // trellis led off
      // STOP runSeq FUNCTION
      ssLedState = -ssLedState; //
      lastDebounceTime = millis(); //set the current time
    }
  }//close if
}


TrellisCallback momentary(keyEvent evt) {  // Momentary trellis buttons

  // Check is the pad pressed?
  if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING) {
    Serial.println("NeoPixel Pressed");
    trellis.pixels.setPixelColor(evt.bit.NUM, Wheel(map(evt.bit.NUM, 0, trellis.pixels.numPixels(), 0, 255))); //on rising

    //set corresponding button states array element to 1, so we know which button has been pressed
    //  button_presses[evt.bit.NUM] = 1;

    //reset timer for detecting a long press, which will take us to latching pattern page if held for 0.8secs
    button_hold_counter = 0;
    held_button_id = evt.bit.NUM;
  }
  else if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_FALLING) {
    // or is the pad released?
    //Serial.println("NeoPixel Released");
    trellis.pixels.setPixelColor(evt.bit.NUM, 0); //off falling

    //if the held button has been released, Resetting the held button id means we are no longer tracking that long press.
    if (held_button_id == evt.bit.NUM) {
      if (button_hold_counter >= 800) {
        Serial.println("Long press detected");

        // *Load pattern input page for pressed pad HERE*

        for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) {  // Activate latching trellis buttons
          trellis.registerCallback(i, latching);
        }
      }
      else {
        held_button_id = -1;
      }
    }
  }


  // Turn on/off the neopixels!
  trellis.pixels.show();

  return 0;
}


TrellisCallback latching(keyEvent evt) {  // Latching trellis buttons
  if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING) {

    //reset timer for detecting a long press, which will take us to latching pattern page if held for 0.8secs
    button_hold_counter = 0;
    held_button_id = evt.bit.NUM;

    Serial.println(evt.bit.NUM);
    if (trellisButtonState[evt.bit.NUM] == 0) {
      //trellis.setPixelColor(evt.bit.NUM, Wheel(map(evt.bit.NUM, 0, X_DIM*Y_DIM, 0, 255))); //on rising
      trellis.pixels.setPixelColor(evt.bit.NUM, 0x0000FF); //blue on rising
      trellisButtonState[evt.bit.NUM] = 1;
      trellis.pixels.show();
    }

    else if (trellisButtonState[evt.bit.NUM] == 1) {
      trellis.pixels.setPixelColor(evt.bit.NUM, 0); //turn off on rising
      trellisButtonState[evt.bit.NUM] = 0;
      trellis.pixels.show();
      Serial.println(evt.bit.NUM);
    }
  }

  else

    //if the held button has been released, Resetting the held button id means we are no longer tracking that long press.
    if (held_button_id == evt.bit.NUM) {
      //
      if (button_hold_counter >= 800) {
        Serial.println("Long press detected");

        // *Load main page for momentary pads HERE*

        for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) {  // Activate momentary trellis buttons
          trellis.registerCallback(i, momentary);
        }
      }
      else {
        held_button_id = -1;
      }
    }

  trellis.pixels.show(); // works without, leaving for now
  return 0;
}




/******************************************/

// Input a value 0 to 255 to get a color value.
// The colors are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if (WheelPos < 85) {
    return trellis.pixels.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if (WheelPos < 170) {
    WheelPos -= 85;
    return trellis.pixels.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
    WheelPos -= 170;
    return trellis.pixels.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  return 0;
}

So you did write it almost exactly as it seems you need to:

  if (trellisButtonState[N] == 1) {

// code to make LED Blue

  } 
  else {

//   code to turn LED Off

  }  

I would like to be more sure, so a question or two.

Is this to support the pink traveller, so it doesn't leave carnage (turn off blue LEDs) in its wake?

Is the pink traveller for our amusement?

Where in the code is the pink traveller being computed and displayed? I admit to perusing your code for some time and it is not (ain't) popping out at me.

It will probably to someone not looking through a tiny window, but in the meantime if no one sez clue me in.

L8R

OK, a member of the Umbrella Academy sez here:

  //turns on trellis led for the length of indicatorTimeout, then turns it off, then moves to next led
  if ( millis() < indicatorTimeout2 ) {
    //trellis.pixels.setPixelColor(pad, 0, 255, 255); // trellis led on
    trellis.pixels.setPixelColor(pad, 220, 20, 60);
    trellis.pixels.show();
  }

  trellis.pixels.setPixelColor(pad, 0); // trellis led off

Is she right?

It is a bit confusing to have your calls to show() in several places. Above, for example, you write the pixel OFF, but it doesn't appear off until someone somewhere gets around to calling show().

Since I have no life and it isn't time to eat or sleep, I will go out on a limb with the idea that the quoted code is the traveller.

Setting the color destroys whatever the pixel was. So turning off the pixel should instead be a matter of repairing the pixel.

Same snippet, with repair instead of extinguish:

  //turns on trellis led for the length of indicatorTimeout, then turns it off, then moves to next led
  if ( millis() < indicatorTimeout2 ) {
    //trellis.pixels.setPixelColor(pad, 0, 255, 255); // trellis led on
    trellis.pixels.setPixelColor(pad, 220, 20, 60);
    trellis.pixels.show();
  }

  if (trellisButtonState[pad] == 1) {
// code to make LED Blue (again)
    trellis.pixels.setPixelColor(pad, 0x0000ff);
  } 
  else {
//   code to turn LED Off
    trellis.pixels.setPixelColor(pad, 0);
  }  

// and as yu have it here, but not in a similar place, no call to show

If your sketch is running free, that is to say is all non-blocking code, you might consider reworking the logic so that there was but one call to *show*() for the strip, once per loop.

Here, as I hinted, is code that doesn't do the right thing, I think:

  //turns on tempo led for the length of indicatorTimeout and then turns it off
  if ( millis() < indicatorTimeout ) {
    pixels.setPixelColor(0, 0, 255, 255); // Tempo LED on
    pixels.show();
  }

  pixels.setPixelColor(0, 0, 0, 0);  // Tempo LED off
  pixels.show();

The timed if block turns on and shows the Tempo LED, but it is immediately followed by an unconditional turning off (and showing) of the same pixel.

So if that LED ever is illuminated by those statements, it is for a very tiny you would never see it duration.

a7

1 Like

Your repair was perfect! Thank you again
Each pad can trigger a sample (momentary pads)
Long pressing a pad will enter pattern mode for that pad (latching mode to enter patterns into that pads array)
16 pads, 16 samples, 16 patterns,
The pink traveller will trigger the samples
That’s the plan,
But for now will work on just 2 arrays,
Will add a long press to my start/stop button, for exiting pattern mode, then find a way to put any latched LEDs back on if going back into pattern mode for that pad

When I saw @paulpaulson stopping by, I assumed it was to promote the idea of the IPO model, read about it described in general terms here:

IPO Model

which I came close to adding to my contribution above. Too many words already, too lazy then.

In a totally non-blocking relatively trivial sketches as are in many Arduino projects, you can use the pattern to great effect:

  • get all inputs from wherever, including timers

  • use inputs and the state of progress in your algorithm(s) to compute a new state and appropriate outputs

  • actually publish the outputs

Any monkeying with buttons and LEDs and so forth is in the Input or Output sections exclusively. The Process section is strictly computational.

It makes things very much clearer and easier to get working, fix and enhance or modify.

I don't argue with success, but the more ambitious the undertaking, the more you will appreciate the benefits of trying hard to play within that framework.

Anyway, glad to help with the traveler, which I now see has a real roll in the device.

a7

1 Like

1 Array works great, having problems adding a second array and separating them to work from 2 different button pads,

int trellisButtonState1[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; // pad 0
int trellisButtonState2[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; // pad 1

I'm not sure if loading to the array or saving to the array is the problem,
loading from array seems to load from both arrays

saving:

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    if (trellisButtonState1[evt.bit.NUM] == 0) { // if off latch on              // array 1
      trellis.pixels.setPixelColor(evt.bit.NUM, 0x0000FF); //blue led on
      trellisButtonState1[evt.bit.NUM] = 1; // save state to array 1
    }

    else if (trellisButtonState1[evt.bit.NUM] == 1) {  // if on turn off
      trellis.pixels.setPixelColor(evt.bit.NUM, 0); //turn off led
      trellisButtonState1[evt.bit.NUM] = 0; // save state to array 1
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    if (trellisButtonState2[evt.bit.NUM] == 0) { // if off latch on             // array 2
      trellis.pixels.setPixelColor(evt.bit.NUM, 0x0000FF); //blue on rising
      trellisButtonState2[evt.bit.NUM] = 1; // save state to array 2
    }

    else if (trellisButtonState2[evt.bit.NUM] == 1) {  // if on turn off
      trellis.pixels.setPixelColor(evt.bit.NUM, 0); //turn off on rising
      trellisButtonState2[evt.bit.NUM] = 0; // save state to array 2
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

loading:

void loadPattern() {
  if (held_button_id == 0) { // if held pad is pad 0
    for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { // load stored pattern // array 1
      if (trellisButtonState1[i] == 1) {
        trellis.pixels.setPixelColor(i, 0x0000FF); // stored blue leds on
      }
    }
    // highlight pressed pad
    trellis.pixels.setPixelColor(held_button_id, Wheel(map(held_button_id, 0, trellis.pixels.numPixels(), 0, 255)));
  }

  if (held_button_id == 1) { // if held pad is pad 1
    for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { // load stored pattern // array 2
      if (trellisButtonState2[i] == 1) {
        trellis.pixels.setPixelColor(i, 0x0000FF); //blue on rising
      }
    }
    // highlight pressed pad
    trellis.pixels.setPixelColor(held_button_id, Wheel(map(held_button_id, 0, trellis.pixels.numPixels(), 0, 255)));
  }
}

current sketch:

#include "Adafruit_NeoTrellis.h"

// Array works, need to add more

// ----------------------------------------FSW NEOPIXELS---------------------------------------------
#include <Adafruit_NeoPixel.h>
#define PIXEL_PIN  5 // (Led Pin)
#define PIXEL_COUNT   2
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);
// ----------------------------------------FSW NEOPIXELS---------------------------------------------

Adafruit_NeoTrellis trellis;

int FSW2 = 2;  // Start/stop footswitch
int FSW2State = HIGH;
int button1 = 3; // tap footswitch
int pad = -1;
int ssLedState = -1; //this variable tracks the startStop LED, negative if off, positive if on
int ttLedState = LOW; // ledState used to set the tempo LED
int lastTapState = LOW;  //the last tap button state
int activeButton = 0;
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 500;    // the debounce time; increase if the output flickers
unsigned long currentTimer[2] = { 750, 750 };  // array of most recent tap counts
unsigned long timeoutTime = 0;   //this is when the timer will trigger next
unsigned long timeoutTime2 = 0;   //this is when the timer will trigger next
unsigned long lastTap = 0; //this vairable determines the time of the most recent tap, used when recording data when button is pressed
unsigned long indicatorTimeout; //variable used to determine the duration of the led blinks on state
unsigned long indicatorTimeout2; //variable used to determine the duration of the pad X4 blinks on state



int trellisButtonState1[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; // pad 0
int trellisButtonState2[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; // pad 1

elapsedMillis button_hold_counter = 0;
int8_t held_button_id = -1;

int latchingMode = false;


void setup() {
  Serial.begin(9600);
  trellis.begin();
  trellis.pixels.setBrightness(50);
  pixels.begin(); // for start/stop and tempo LEDs
  pixels.setBrightness(50);
  pixels.setPixelColor(1, 220, 20, 60);
  pixels.show();
  pinMode( button1, INPUT_PULLUP );   //tempo button input
  pinMode(FSW2, INPUT_PULLUP);
  Serial.println("NeoPixel Trellis started");


  //activate momentary pads and set callbacks
  for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) {
    trellis.activateKey(i, SEESAW_KEYPAD_EDGE_RISING);
    trellis.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING);
    trellis.registerCallback(i, momentary);
  }

  //do a little animation to show we're on
  for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
    trellis.pixels.setPixelColor(i, Wheel(map(i, 0, trellis.pixels.numPixels(), 0, 255)));
    trellis.pixels.show();
    delay(25);
  }
  for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
    trellis.pixels.setPixelColor(i, 0x000000);
    trellis.pixels.show();
    delay(25);
  }
}


void loop() {
  trellis.read();  // interrupt management does all the work! :)
  startStopFSW();
  if (ssLedState > 0) {
    runSeq();
  }
}




void runSeq() {
  int tapState = digitalRead( button1 );   //button is first pressed down

  if ( tapState == LOW && tapState != lastTapState ) {
    currentTimer[1] = currentTimer[0];    //this takes the tap time done before it (measured in millis), sets it one back in the array,
    currentTimer[0] = millis() - lastTap;     //and then sets the other slot of the array to the length of the most recent tap time
    lastTap = millis();
    timeoutTime = 0;
    timeoutTime2 = 0;
  }

  lastTapState = tapState; //variable used to determine the most recent state of the button

  if ( millis() >= timeoutTime )  {     //checks to see if it is time to turn on the tap fsw LED
    indicatorTimeout = millis() + 150;  //determines the length of time that the LED is on
    //this will set the new tempo
    timeoutTime = millis() + ((currentTimer[0] + currentTimer[1]) / 2);
  }

  if ( millis() >= timeoutTime2 )  {    //checks to see if it is time to turn on the trellis LEDs
    indicatorTimeout2 = millis() + 10;  //determines the length of time that the trellis LED is on
    //this will set the new tempo
    timeoutTime2 = millis() + ((currentTimer[0] + currentTimer[1]) / 8); // Divide by 8 to make it 4 times faster
    pad++;
    pad %= 16;
  }

  //turns on tempo led for the length of indicatorTimeout and then turns it off
  if ( millis() < indicatorTimeout ) {
    pixels.setPixelColor(0, 0, 255, 255); // Tempo LED on
    pixels.show();
  }

  pixels.setPixelColor(0, 0, 0, 0);  // Tempo LED off
  pixels.show();


  //turns on trellis led for the length of indicatorTimeout, then turns it off, then moves to next led
  if ( millis() < indicatorTimeout2 ) {
    trellis.pixels.setPixelColor(pad, 220, 20, 60); // The Pink Traveller
    trellis.pixels.show();
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  if (trellisButtonState1[pad] == 1 && latchingMode == true) { // array 1
    // code to make LED Blue again after pink traveller passes
    trellis.pixels.setPixelColor(pad, 0x0000ff);
  }
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  //  if (trellisButtonState2[pad] == 1 && latchingMode == true) { // array 2 // not needed until loading/saving works
  //    // code to make LED Blue again after pink traveller passes
  //    trellis.pixels.setPixelColor(pad, 0x0000ff);
  //  }
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////


  else {
    //   code to turn LED Off
    trellis.pixels.setPixelColor(pad, 0);
  }
}





void startStopFSW() {
  FSW2State = digitalRead(FSW2);   //sample the state of the button - is it pressed or not?

  if ( (millis() - lastDebounceTime) > debounceDelay) {   //filter out any noise by setting a time buffer
    if ( (FSW2State == LOW) && (ssLedState < 0) ) {     //if the button has been pressed, lets toggle the LED from "off to on" or "on to off"
      pixels.setPixelColor(1, 124, 252, 0);       // start LED lawn green:
      pixels.show();
      // runSeq();  // START runSeq FUNCTION
      Serial.println("Start");
      pad = -1;
      ssLedState = -ssLedState; //now the LED is on, we need to change the state
      lastDebounceTime = millis(); //set the current time
    }

    else if ( (FSW2State == LOW) && (ssLedState > 0) ) {
      pixels.setPixelColor(1, 220, 20, 60);       // stop LED crimson
      pixels.show();
      Serial.println("Stop");
      for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
        trellis.pixels.setPixelColor(i, 0); // stray pink traveller off
        trellis.pixels.show();
      }
      ssLedState = -ssLedState; //
      lastDebounceTime = millis(); //set the current time
    }
  }//close if
}


void clearTrellis() {

  for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
    trellis.pixels.setPixelColor(i, 0); // trellis leds off
  }
}



void loadPattern() {
  if (held_button_id == 0) { // if held pad is pad 0
    for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { // load stored pattern // array 1
      if (trellisButtonState1[i] == 1) {
        trellis.pixels.setPixelColor(i, 0x0000FF); // stored blue leds on
      }
    }
    // highlight pressed pad
    trellis.pixels.setPixelColor(held_button_id, Wheel(map(held_button_id, 0, trellis.pixels.numPixels(), 0, 255)));
  }

  if (held_button_id == 1) { // if held pad is pad 1
    for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { // load stored pattern // array 2
      if (trellisButtonState2[i] == 1) {
        trellis.pixels.setPixelColor(i, 0x0000FF); //blue on rising
      }
    }
    // highlight pressed pad
    trellis.pixels.setPixelColor(held_button_id, Wheel(map(held_button_id, 0, trellis.pixels.numPixels(), 0, 255)));
  }
}



TrellisCallback momentary(keyEvent evt) {  // Trellis Momentary Mode

  // Check is the pad pressed?
  if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING) {
    Serial.println("NeoPixel Pressed");
    trellis.pixels.setPixelColor(evt.bit.NUM, Wheel(map(evt.bit.NUM, 0, trellis.pixels.numPixels(), 0, 255))); //on rising

    //reset timer for detecting a long press, which will take us to latching pattern page if held for 0.8secs
    button_hold_counter = 0;
    held_button_id = evt.bit.NUM;
  }
  else if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_FALLING) {
    // or is the pad released?
    //Serial.println("NeoPixel Released");
    trellis.pixels.setPixelColor(evt.bit.NUM, 0); //off falling

    //if the held button has been released, Resetting the held button id means we are no longer tracking that long press.
    if (held_button_id == evt.bit.NUM) {
      if (button_hold_counter >= 800) {
        Serial.println("Long press detected");   // *Load pattern input page for pressed pad HERE*

        for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) {  // Activate latching trellis buttons
          trellis.registerCallback(i, latching);
        }
        latchingMode = true;
        loadPattern();

      }
      else {
        held_button_id = -1;
      }
    }
  }


  // Turn on/off the neopixels!
  trellis.pixels.show();

  return 0;
}


TrellisCallback latching(keyEvent evt) {  // Trellis Latching Mode
  if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING) { // if any pad button is pressed

    //reset timer for detecting a long press, which will take us to momentary main page if held for 0.8secs
    button_hold_counter = 0;
    held_button_id = evt.bit.NUM;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    if (trellisButtonState1[evt.bit.NUM] == 0) { // if off latch on              // array 1
      trellis.pixels.setPixelColor(evt.bit.NUM, 0x0000FF); //blue led on
      trellisButtonState1[evt.bit.NUM] = 1; // save state to array 1
    }

    else if (trellisButtonState1[evt.bit.NUM] == 1) {  // if on turn off
      trellis.pixels.setPixelColor(evt.bit.NUM, 0); //turn off led
      trellisButtonState1[evt.bit.NUM] = 0; // save state to array 1
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    if (trellisButtonState2[evt.bit.NUM] == 0) { // if off latch on             // array 2
      trellis.pixels.setPixelColor(evt.bit.NUM, 0x0000FF); //blue on rising
      trellisButtonState2[evt.bit.NUM] = 1; // save state to array 2
    }

    else if (trellisButtonState2[evt.bit.NUM] == 1) {  // if on turn off
      trellis.pixels.setPixelColor(evt.bit.NUM, 0); //turn off on rising
      trellisButtonState2[evt.bit.NUM] = 0; // save state to array 2
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    trellis.pixels.show();

  }

  else

    //if the held button has been released, Resetting the held button id means we are no longer tracking that long press.
    if (held_button_id == evt.bit.NUM) {
      //
      if (button_hold_counter >= 800) {
        Serial.println("Long press detected");

        // press any pad to exit pattern mode without changing current pattern
        if (trellisButtonState1[evt.bit.NUM] == 1) {
          trellis.pixels.setPixelColor(evt.bit.NUM, 0); //turn off on rising
          trellisButtonState1[evt.bit.NUM] = 0;
        }
        else if (trellisButtonState1[evt.bit.NUM] == 0) {  // if on turn off
          trellis.pixels.setPixelColor(evt.bit.NUM, 0); //turn off on rising
          trellisButtonState1[evt.bit.NUM] = 1;
        }  // press any pad to exit pattern mode without changing current pattern

        for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) {  // Activate momentary trellis buttons
          trellis.registerCallback(i, momentary);
        }
        clearTrellis();
        latchingMode = false;
      }

      else {
        held_button_id = -1;
      }
    }

  trellis.pixels.show(); // works without, leaving for now
  return 0;
}




/******************************************/

// Input a value 0 to 255 to get a color value.
// The colors are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if (WheelPos < 85) {
    return trellis.pixels.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if (WheelPos < 170) {
    WheelPos -= 85;
    return trellis.pixels.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
    WheelPos -= 170;
    return trellis.pixels.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  return 0;
}

In transit. I'll look closer L8R.

If you are trying to make two trelliae, you need two of everything that makes a trellis.

Whether that be code logic, physical keypads, arrrays or NeoTrellis objects.

I see one

Adafruit_NeoTrellis trellis;

Now I recommend that you take a break from the excitement, and learn a bit more, because when you start dealing with having two, or N mostly identical <whatevers>, arrays should be considered.

When you are thinking you'll need two or N arrays, you might need to think about multi-dimensional arrays.

Food for your thinking:

int trellisButtonState[2][16] = {
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, // pad 0
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, // pad 1
};

A two dimensional array holding the 16 initial values for each of two trelliae.

Don't cut/paste/edit code to do the same thing, more or less, to two mostly identical objects.

a7

No, still using only 1 trellis board
Saving loading button presses for 2 separate buttons

OK sry confused.

Now here's where you'll have to help by drawing a block diagram of your device and saying a few words about how it is to operate.

I can only guess you have one trellis, and are storing/recalling which of N patterns that you have entered, so as to display/operate/modify a currently loaded pattern, and (at will? anytime?) switch out one pattern for another, or the next, pattern.

Assuming so, what is the means for selecting or moving amongst the stored patterns? Can we switch meaningfully in the middle of a step?

The patterns can be stored in a two-dimensional array, this will make more sense when you want to increase the number of patterns if I am guessing right.

So at least part of my self-misguided response (I'll blmae it on the tiny window) would still obtain:

const NPATTERNS  = 7; 

int trellisButtonState[NPATTERNS][16] = {
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, 
}

I might change the name, as you have one trellis but N stored trellis patterns. Or whatever you call them.

BTW if the initial contents of all patterns is to be zero; you can just

const NPATTERNS  = 7;

int trellisButtonState[NPATTERNS][16]; 

Such an array is zeroed at the very beginning of the sketch automatically.

One day you will put the patterns into EEPROM and restore them when you turn the machine on again. You would still wake up to all zeroes, but grab back the last saved data for all patterns. The user interface for that is up to you. EEPROM does have a limited life cycle, so you'll want to be careful about how often you store. It could be on explicit command of the user (as could be save them all), or part of a soft shutdown, where turning the whole thing off means save and then turn yourslef off.

HTH

a7

1 Like

-MAIN PAGE-

-Start/Stop Pink LED Sequence (Global)

-Tap Tempo (Global)

Momentary LEDs & Buttons

PlayFlash 16 sounds One for each pad

Long press to that pads pattern page

β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”

-PATTERN PAGE-

-16 patterns for each pad

-Start/Stop Pink LED Sequence (Global)

-Tap Tempo (Global)

Latching LEDs & Buttons

Play selected sample on all pads when latched on blue

Exit to main page, long press any pad, eeprom save here?

CURRENT PROBLEMS

Save/load separate patterns to array

I've had too much of one thing and not enough of another to make any sense of your additional description. Or vice versa. :expressionless:

The video is equally helpful.

I imagine that by reading all your code and using common sense I could deduce what this thing is and what it does and how you do it and why you'd even want to.

If the entire device is shown in the video, one trellis two pushbuttons two LEDs (?) I don't know what you mean by pages.

In the code I see that only some of any mention of a second pattern is commented out. I see some serial printing, but a crap-ton more to check on the values of key variables and confirm the flow through your code would not hurt.

Pretend you never saw the LeetTrellis, and a friend said hey, you gotta have one of these, and handed you one and…

write the little manual you'd need (friend went off to the beach, no quick here's how it works) [congratulations on the wisdom of your purchase] to thoroughly understand what it is, what it does and how anythings you can press or see are to be pressed or seen so you can use your new toy.

Like how it hooks up to whatever it hooks up to, and a block diagram showing the modes you can get in and out of, and I hope you get the idea. A user manual.

Otherwise, all I can say is you have some error in your logic somewhere. Which of course you do. Finding it (or them) will be a matter of pretending you are the processor and put your finger on the code, and assisted by the copious printing you might install, see why the code is doing exactly what you wrote, even if it it not yet doing what you want.

HTH

a7

A member of the Umbrella Academy showed me #4 above, she's got more to bring to this just now. TBH she always brings it, good thing trust me.

That does help a bit. You are def gonna want to not write out the (essentially) same code 16 times.

I hope to know when and why it goes automatic and when it is per trellis button and do forth.

So it should be that getting two working will practically automatically turn into sixteen working by change one occurrence (!) of '2', somewhere up top, to '16'.

a7

Main page with sounds for reference

basic sketch showing how pad presses trigger samples

/* NeoTrellis 16 Sample RAW Player
 *  Plays Samples From Audio Board Flash Chip

*/

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

#include "Adafruit_NeoTrellis.h"



// Create the Audio components.  These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
AudioPlaySerialflashRaw  playFlashRaw1;
AudioPlaySerialflashRaw  playFlashRaw2;
AudioPlaySerialflashRaw  playFlashRaw3;
AudioPlaySerialflashRaw  playFlashRaw4;
AudioPlaySerialflashRaw  playFlashRaw5;
AudioPlaySerialflashRaw  playFlashRaw6;
AudioPlaySerialflashRaw  playFlashRaw7;
AudioPlaySerialflashRaw  playFlashRaw8;
AudioPlaySerialflashRaw  playFlashRaw9;
AudioPlaySerialflashRaw  playFlashRaw10;
AudioPlaySerialflashRaw  playFlashRaw11;
AudioPlaySerialflashRaw  playFlashRaw12;
AudioPlaySerialflashRaw  playFlashRaw13;
AudioPlaySerialflashRaw  playFlashRaw14;
AudioPlaySerialflashRaw  playFlashRaw15;
AudioPlaySerialflashRaw  playFlashRaw16;

AudioMixer4              mix1;
AudioMixer4              mix2;
AudioMixer4              mix3;
AudioMixer4              mix4;
AudioMixer4              mix5;
AudioOutputAnalog        dac;
AudioOutputI2S           headphones;

// Create Audio connections between the components
//
AudioConnection          patchCord1(playFlashRaw1, 0, mix1, 0);
AudioConnection          patchCord2(playFlashRaw2, 0, mix1, 1);
AudioConnection          patchCord3(playFlashRaw3, 0, mix1, 2);
AudioConnection          patchCord4(playFlashRaw4, 0, mix1, 3);
AudioConnection          patchCord5(playFlashRaw5, 0, mix2, 0);
AudioConnection          patchCord6(playFlashRaw6, 0, mix2, 1);
AudioConnection          patchCord7(playFlashRaw7, 0, mix2, 2);
AudioConnection          patchCord8(playFlashRaw8, 0, mix2, 3);
AudioConnection          patchCord9(playFlashRaw9, 0, mix3, 0);
AudioConnection          patchCord10(playFlashRaw10, 0, mix3, 1);
AudioConnection          patchCord11(playFlashRaw11, 0, mix3, 2);
AudioConnection          patchCord12(playFlashRaw12, 0, mix3, 3);
AudioConnection          patchCord13(playFlashRaw13, 0, mix4, 0);
AudioConnection          patchCord14(playFlashRaw14, 0, mix4, 1);
AudioConnection          patchCord15(playFlashRaw15, 0, mix4, 2);
AudioConnection          patchCord16(playFlashRaw16, 0, mix4, 3);
AudioConnection          patchCord17(mix1, 0, mix5, 0);
AudioConnection          patchCord18(mix2, 0, mix5, 1);
AudioConnection          patchCord19(mix3, 0, mix5, 2);
AudioConnection          patchCord20(mix4, 0, mix5, 3);
AudioConnection          patchCord21(mix5, dac);
AudioConnection          patchCord22(mix5, 0, headphones, 0);
AudioConnection          patchCord23(mix5, 0, headphones, 1);


// Create an object to control the audio shield.
//
AudioControlSGTL5000 audioShield;

const int FlashChipSelect = 6; // digital pin for flash chip CS pin

Adafruit_NeoTrellis trellis;

//define a callback for key presses
TrellisCallback blink(keyEvent evt) {
  // Check is the pad pressed?
  if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING) {
    //Serial.println(evt.bit.NUM);
    //Serial.println(AudioMemoryUsageMax()); = 4
    if (evt.bit.NUM == 0) {
      playFlashRaw1.play("808.RAW");
    }

    if (evt.bit.NUM == 1) {
      playFlashRaw2.play("KICK2.RAW");
    }

    if (evt.bit.NUM == 2) {
      playFlashRaw3.play("SNARE1.RAW");
    }

    if (evt.bit.NUM == 3) {
      playFlashRaw4.play("SNARE2.RAW");
    }

    if (evt.bit.NUM == 4) {
      playFlashRaw5.play("SNARE3.RAW");
    }

    if (evt.bit.NUM == 5) {
      playFlashRaw6.play("SNARE4.RAW");
    }

    if (evt.bit.NUM == 6) {
      playFlashRaw7.play("HAT1.RAW");
    }

    if (evt.bit.NUM == 7) {
      playFlashRaw8.play("HAT2.RAW");
    }

    if (evt.bit.NUM == 8) {
      playFlashRaw9.play("CLAP.RAW");
    }

    if (evt.bit.NUM == 9) {
      playFlashRaw10.play("CRASH.RAW");
    }

    if (evt.bit.NUM == 10) {
      playFlashRaw11.play("VOX2.RAW");
    }

    if (evt.bit.NUM == 11) {
      playFlashRaw12.play("PERCS2.RAW");
    }

    if (evt.bit.NUM == 12) {
      playFlashRaw13.play("PERCS3.RAW");
    }

    if (evt.bit.NUM == 13) {
      playFlashRaw14.play("PERCS4.RAW");
    }

    if (evt.bit.NUM == 14) {
      playFlashRaw15.play("FX.RAW");
    }

    if (evt.bit.NUM == 15) {
      playFlashRaw16.play("VOX1.RAW");
    }


    //Serial.println("NeoPixel Pressed");
    trellis.pixels.setPixelColor(evt.bit.NUM, Wheel(map(evt.bit.NUM, 0, trellis.pixels.numPixels(), 0, 255))); //on rising
  } else if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_FALLING) {
    // or is the pad released?
    //Serial.println("NeoPixel Released");
    trellis.pixels.setPixelColor(evt.bit.NUM, 0); //off falling
  }

  // Turn on/off the neopixels!
  trellis.pixels.show();

  return 0;
}

void setup() {


  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(6);

  // turn on the output
  audioShield.enable();
  audioShield.volume(0.5);

  // by default the Teensy 3.1 DAC uses 3.3Vp-p output
  // if your 3.3V power has noise, switching to the
  // internal 1.2V reference can give you a clean signal
  //dac.analogReference(INTERNAL);

  // reduce the gain on mixer channels, so more than 1
  // sound can play simultaneously without clipping
  mix1.gain(0, 0.4);
  mix1.gain(1, 0.4);
  mix1.gain(2, 0.4);
  mix1.gain(3, 0.4);
  mix2.gain(0, 0.4);
  mix2.gain(1, 0.4);
  mix2.gain(2, 0.8);
  mix2.gain(3, 1);
  mix3.gain(0, 0.4);
  mix3.gain(1, 0.8);
  mix3.gain(2, 0.4);
  mix3.gain(3, 0.4);
  mix4.gain(0, 0.8);
  mix4.gain(1, 0.4);
  mix4.gain(2, 0.4);
  mix4.gain(3, 0.4);

  mix5.gain(1, 1);
  mix5.gain(2, 1);

  SPI.setSCK(14);  // Audio shield has SCK on pin 14
  SPI.setMOSI(7);  // Audio shield has MOSI on pin 7

  SerialFlash.begin(FlashChipSelect);

  if (!trellis.begin()) {
    Serial.println("Could not start trellis, check wiring?");
    while (1) delay(1);
  } else {
    Serial.println("NeoPixel Trellis started");
  }

  //activate all keys and set callbacks
  for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) {
    trellis.activateKey(i, SEESAW_KEYPAD_EDGE_RISING);
    trellis.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING);
    trellis.registerCallback(i, blink);
  }
}

void loop() {
  
  trellis.read();  // interrupt management does all the work! :)

  delay(10); //the trellis has a resolution of around 60hz
}


/******************************************/


// Input a value 0 to 255 to get a color value.
// The colors are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if (WheelPos < 85) {
    return trellis.pixels.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if (WheelPos < 170) {
    WheelPos -= 85;
    return trellis.pixels.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
    WheelPos -= 170;
    return trellis.pixels.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  return 0;
}

Block diagram / operation flow chart

fixed last problem by adding currentPad=

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    if (currentPad == 0 && trellisButtonState1[evt.bit.NUM] == 0) { // if off latch on              // array 1
      trellis.pixels.setPixelColor(evt.bit.NUM, 0x0000FF); //blue led on
      trellisButtonState1[evt.bit.NUM] = 1; // save state to array 1
    }

    else if (currentPad == 0 && trellisButtonState1[evt.bit.NUM] == 1) {  // if on turn off
      trellis.pixels.setPixelColor(evt.bit.NUM, 0); //turn off led
      trellisButtonState1[evt.bit.NUM] = 0; // save state to array 1
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    else if (currentPad == 1 && trellisButtonState2[evt.bit.NUM] == 0) { // if off latch on             // array 2
      trellis.pixels.setPixelColor(evt.bit.NUM, 0x0000FF); //blue on rising
      trellisButtonState2[evt.bit.NUM] = 1; // save state to array 2
    }

    else if (currentPad == 1 && trellisButtonState2[evt.bit.NUM] == 1) {  // if on turn off
      trellis.pixels.setPixelColor(evt.bit.NUM, 0); //turn off on rising
      trellisButtonState2[evt.bit.NUM] = 0; // save state to array 2
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

next problem, only current pad 1 correctly re lights any latched on leds blue again after pink traveller passes, other turns off any latched on blue leds

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  if (currentPad == 0 && trellisButtonState1[pad] == 1 && latchingMode == true) { // array 1
    // code to make LED Blue again after pink traveller passes
    trellis.pixels.setPixelColor(pad, 0x0000ff); // this doesnt work correctly
  }
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  if (currentPad == 1 && trellisButtonState2[pad] == 1 && latchingMode == true) { // array 2
    // code to make LED Blue again after pink traveller passes
    trellis.pixels.setPixelColor(pad, 0x0000ff); // this works correctly
  }
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

latest

#include "Adafruit_NeoTrellis.h"

// Array works, need to add more

// ----------------------------------------FSW NEOPIXELS---------------------------------------------
#include <Adafruit_NeoPixel.h>
#define PIXEL_PIN  5 // (Led Pin)
#define PIXEL_COUNT   2
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);
// ----------------------------------------FSW NEOPIXELS---------------------------------------------

Adafruit_NeoTrellis trellis;

int FSW2 = 2;  // Start/stop footswitch
int FSW2State = HIGH;
int button1 = 3; // tap footswitch
int pad = -1;
int ssLedState = -1; //this variable tracks the startStop LED, negative if off, positive if on
int ttLedState = LOW; // ledState used to set the tempo LED
int lastTapState = LOW;  //the last tap button state
int activeButton = 0;
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 500;    // the debounce time; increase if the output flickers
unsigned long currentTimer[2] = { 750, 750 };  // array of most recent tap counts
unsigned long timeoutTime = 0;   //this is when the timer will trigger next
unsigned long timeoutTime2 = 0;   //this is when the timer will trigger next
unsigned long lastTap = 0; //this vairable determines the time of the most recent tap, used when recording data when button is pressed
unsigned long indicatorTimeout; //variable used to determine the duration of the led blinks on state
unsigned long indicatorTimeout2; //variable used to determine the duration of the pad X4 blinks on state



int trellisButtonState1[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; // pad 0
int trellisButtonState2[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; // pad 1

elapsedMillis button_hold_counter = 0;
int8_t held_button_id = -1;

int latchingMode = false;

int currentPad;

void setup() {
  Serial.begin(9600);
  trellis.begin();
  trellis.pixels.setBrightness(50);
  pixels.begin(); // for start/stop and tempo LEDs
  pixels.setBrightness(50);
  pixels.setPixelColor(1, 220, 20, 60);
  pixels.show();
  pinMode( button1, INPUT_PULLUP );   //tempo button input
  pinMode(FSW2, INPUT_PULLUP);
  Serial.println("NeoPixel Trellis started");


  //activate momentary pads and set callbacks
  for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) {
    trellis.activateKey(i, SEESAW_KEYPAD_EDGE_RISING);
    trellis.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING);
    trellis.registerCallback(i, momentary);
  }

  //do a little animation to show we're on
  for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
    trellis.pixels.setPixelColor(i, Wheel(map(i, 0, trellis.pixels.numPixels(), 0, 255)));
    trellis.pixels.show();
    delay(25);
  }
  for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
    trellis.pixels.setPixelColor(i, 0x000000);
    trellis.pixels.show();
    delay(25);
  }
}


void loop() {
  trellis.read();  // interrupt management does all the work! :)
  startStopFSW();
  if (ssLedState > 0) {
    runSeq();
  }
}




void runSeq() {
  int tapState = digitalRead( button1 );   //button is first pressed down

  if ( tapState == LOW && tapState != lastTapState ) {
    currentTimer[1] = currentTimer[0];    //this takes the tap time done before it (measured in millis), sets it one back in the array,
    currentTimer[0] = millis() - lastTap;     //and then sets the other slot of the array to the length of the most recent tap time
    lastTap = millis();
    timeoutTime = 0;
    timeoutTime2 = 0;
  }

  lastTapState = tapState; //variable used to determine the most recent state of the button

  if ( millis() >= timeoutTime )  {     //checks to see if it is time to turn on the tap fsw LED
    indicatorTimeout = millis() + 150;  //determines the length of time that the LED is on
    //this will set the new tempo
    timeoutTime = millis() + ((currentTimer[0] + currentTimer[1]) / 2);
  }

  if ( millis() >= timeoutTime2 )  {    //checks to see if it is time to turn on the trellis LEDs
    indicatorTimeout2 = millis() + 10;  //determines the length of time that the trellis LED is on
    //this will set the new tempo
    timeoutTime2 = millis() + ((currentTimer[0] + currentTimer[1]) / 8); // Divide by 8 to make it 4 times faster
    pad++;
    pad %= 16;
  }

  //turns on tempo led for the length of indicatorTimeout and then turns it off
  if ( millis() < indicatorTimeout ) {
    pixels.setPixelColor(0, 0, 255, 255); // Tempo LED on
    pixels.show();
  }

  pixels.setPixelColor(0, 0, 0, 0);  // Tempo LED off
  pixels.show();


  //turns on trellis led for the length of indicatorTimeout, then turns it off, then moves to next led
  if ( millis() < indicatorTimeout2 ) {
    trellis.pixels.setPixelColor(pad, 220, 20, 60); // The Pink Traveller
    trellis.pixels.show();
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  if (currentPad == 0 && trellisButtonState1[pad] == 1 && latchingMode == true) { // array 1
    // code to make LED Blue again after pink traveller passes
    trellis.pixels.setPixelColor(pad, 0x0000ff); // this doesnt work correctly
  }
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  if (currentPad == 1 && trellisButtonState2[pad] == 1 && latchingMode == true) { // array 2
    // code to make LED Blue again after pink traveller passes
    trellis.pixels.setPixelColor(pad, 0x0000ff); // this works correctly
  }
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////


  else {
    //   code to turn LED Off
    trellis.pixels.setPixelColor(pad, 0);
  }
}





void startStopFSW() {
  FSW2State = digitalRead(FSW2);   //sample the state of the button - is it pressed or not?

  if ( (millis() - lastDebounceTime) > debounceDelay) {   //filter out any noise by setting a time buffer
    if ( (FSW2State == LOW) && (ssLedState < 0) ) {     //if the button has been pressed, lets toggle the LED from "off to on" or "on to off"
      pixels.setPixelColor(1, 124, 252, 0);       // start LED lawn green:
      pixels.show();
      // runSeq();  // START runSeq FUNCTION
      Serial.println("Start");
      pad = -1;
      ssLedState = -ssLedState; //now the LED is on, we need to change the state
      lastDebounceTime = millis(); //set the current time
    }

    else if ( (FSW2State == LOW) && (ssLedState > 0) ) {
      pixels.setPixelColor(1, 220, 20, 60);       // stop LED crimson
      pixels.show();
      Serial.println("Stop");
      for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
        trellis.pixels.setPixelColor(i, 0); // stray pink traveller off
        trellis.pixels.show();
      }
      ssLedState = -ssLedState; //
      lastDebounceTime = millis(); //set the current time
    }
  }//close if
}


void clearTrellis() {

  for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
    trellis.pixels.setPixelColor(i, 0); // trellis leds off
  }
}




TrellisCallback momentary(keyEvent evt) {  // Trellis Momentary Mode, Main Page, Start Page

  if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING) {  // Check is the pad pressed?
    Serial.println("NeoPixel Pressed");
    trellis.pixels.setPixelColor(evt.bit.NUM, Wheel(map(evt.bit.NUM, 0, trellis.pixels.numPixels(), 0, 255))); //on rising

    //reset timer for detecting a long press, which will take us to latching pattern page if held for 0.8secs
    button_hold_counter = 0;
    held_button_id = evt.bit.NUM;
  }
  else if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_FALLING) {  // or is the pad released?
    //Serial.println("NeoPixel Released");
    trellis.pixels.setPixelColor(evt.bit.NUM, 0); //off falling

    if (held_button_id == evt.bit.NUM) {  // if the held button has been released,
      if (button_hold_counter >= 800) { // if the button was held for 800 ms
        Serial.println("Long press detected");   // *Load pattern input page for pressed pad HERE*

        for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) {  // Activate latching trellis buttons
          trellis.registerCallback(i, latching);
        }
        latchingMode = true;
        loadPattern();
      }
      else {
        held_button_id = -1; // Resetting the held button id means we are no longer tracking that long press.
      }
    }
  }


  // Turn on/off the neopixels!
  trellis.pixels.show();

  return 0;
}


void loadPattern() {
  currentPad = held_button_id;
  if (held_button_id == 0) { // if held pad is pad 0
    for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { // load stored pattern // array 1
      if (trellisButtonState1[i] == 1) {
        trellis.pixels.setPixelColor(i, 0x0000FF); // stored blue leds on
      }
    }
    // highlight pressed pad
    trellis.pixels.setPixelColor(held_button_id, Wheel(map(held_button_id, 0, trellis.pixels.numPixels(), 0, 255)));
    //currentPad = held_button_id;
  }

  if (held_button_id == 1) { // if held pad is pad 1
    for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { // load stored pattern // array 2
      if (trellisButtonState2[i] == 1) {
        trellis.pixels.setPixelColor(i, 0x0000FF); //blue on rising
      }
    }
    // highlight pressed pad
    trellis.pixels.setPixelColor(held_button_id, Wheel(map(held_button_id, 0, trellis.pixels.numPixels(), 0, 255)));
    //currentPad = held_button_id;
  }
}



TrellisCallback latching(keyEvent evt) {  // Trellis Latching Mode
  if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING) { // if any pad button is pressed

    //reset timer for detecting a long press, which will take us to momentary main page if held for 0.8secs
    button_hold_counter = 0;
    held_button_id = evt.bit.NUM;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    if (currentPad == 0 && trellisButtonState1[evt.bit.NUM] == 0) { // if off latch on              // array 1
      trellis.pixels.setPixelColor(evt.bit.NUM, 0x0000FF); //blue led on
      trellisButtonState1[evt.bit.NUM] = 1; // save state to array 1
    }

    else if (currentPad == 0 && trellisButtonState1[evt.bit.NUM] == 1) {  // if on turn off
      trellis.pixels.setPixelColor(evt.bit.NUM, 0); //turn off led
      trellisButtonState1[evt.bit.NUM] = 0; // save state to array 1
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    else if (currentPad == 1 && trellisButtonState2[evt.bit.NUM] == 0) { // if off latch on             // array 2
      trellis.pixels.setPixelColor(evt.bit.NUM, 0x0000FF); //blue on rising
      trellisButtonState2[evt.bit.NUM] = 1; // save state to array 2
    }

    else if (currentPad == 1 && trellisButtonState2[evt.bit.NUM] == 1) {  // if on turn off
      trellis.pixels.setPixelColor(evt.bit.NUM, 0); //turn off on rising
      trellisButtonState2[evt.bit.NUM] = 0; // save state to array 2
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    trellis.pixels.show();

  }

  else

    //if the held button has been released, Resetting the held button id means we are no longer tracking that long press.
    if (held_button_id == evt.bit.NUM) {
      //
      if (button_hold_counter >= 800) {
        Serial.println("Long press detected");

        // press any pad to exit pattern mode without changing current pattern
        if (trellisButtonState1[evt.bit.NUM] == 1) {
          trellis.pixels.setPixelColor(evt.bit.NUM, 0); //turn off on rising
          trellisButtonState1[evt.bit.NUM] = 0;
        }
        else if (trellisButtonState1[evt.bit.NUM] == 0) {  // if on turn off
          trellis.pixels.setPixelColor(evt.bit.NUM, 0); //turn off on rising
          trellisButtonState1[evt.bit.NUM] = 1;
        }  // press any pad to exit pattern mode without changing current pattern

        for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) {  // Activate momentary trellis buttons
          trellis.registerCallback(i, momentary);
        }
        clearTrellis();
        latchingMode = false;
      }

      else {
        held_button_id = -1;
      }
    }

  trellis.pixels.show(); // works without, leaving for now
  return 0;
}




/******************************************/

// Input a value 0 to 255 to get a color value.
// The colors are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if (WheelPos < 85) {
    return trellis.pixels.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if (WheelPos < 170) {
    WheelPos -= 85;
    return trellis.pixels.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
    WheelPos -= 170;
    return trellis.pixels.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  return 0;
}

Why are the trellis buttons different colours?

Your flow diagram doesn't, but I get the idea. Probably maybe. I am sure you have seen better descriptions and diagrams for other f/x boxes or writ watches or GoPro cameras that while sucking not just because the UI sucks, at least let you see what a button does at a point in the flow of modes.

Leave it for later, but please it should be done if you want to see this copied and used, or purchased or whatever.

What happens if a button is short pressed when the traveller is running? Does start/stop just stop and start the automatic sequencing? When is the tap tempo button respected or used to determine the traveller speed?

In single press mode, a long press on any 1 of 16 trellis buttons loads that pattern for editing.
In edit mode, any long press on any button returns to single press mode.

AmIRight?

So… obvsly it's not that it doesn't work right, it's because that line of code doesn't get execute.

Put a print statement in there and see it never. Print.

Print out the values of the variables that comprise the logical expression right before the if statement. See what makes the total expression is false, why that code line that "doesn't work" isn't even being performed.


You are on the verge of copy/pasting code fifteen more times in two different places.

I think you could have the sixteen patterns in one two dimensional array, and use another one dimensional array called, say, currentPattern.

Anything needing the current pattern reads it from the current pattern buffer. Other processes transfer between the operating pattern and the stored patterns.

Separating it that way will be a big step to writing one block that would otherwise be sixteen slightly different blocks.

a7

In image pads are different colors just for fun,
In code pad colors are a rainbow color from color wheel at bottom of code, only in momentary main page mode,

Momentary pads play 16 different samples in momentary main page mode, if pink traveller is running or not running,

Yes, β€œIn single press mode, a long press on any 1 of 16 trellis buttons loads that pattern for editing.
In edit mode, any long press on any button returns to single press mode”

Can it play two things at once? Does the short pressed button launch a sound on the beat or just whenever the press gets detected?

a7

Someone just noticed that you have, or will have, many variables stored as int that are never anything that would overflow a byte.

byte someArray[16];

takes half the storage and allows 0 to 255.

int8_t someArray[16];

also half the storage, this time a signed integer -128 to 127.

You decide. 16 * 16 is too many things not to worry about the size of each.

If you ever ran out of RAM even with one byte numbers, if you could say no such number wou,d ever be greater than some X, there are tricks to make the memory use more efficient.

a7

yes pads can play many samples at once, at any time, hopefully

added else if, seems to have fixed it for now

  //turns on trellis led for the length of indicatorTimeout, then turns it off, then moves to next led
  if ( millis() < indicatorTimeout2 ) {
    trellis.pixels.setPixelColor(pad, 220, 20, 60); // The Pink Traveller
    trellis.pixels.show();
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  if (currentPad == 0 && trellisButtonState1[pad] == 1 && latchingMode == true) { // array 1
    // code to make LED Blue again after pink traveller passes
    trellis.pixels.setPixelColor(pad, 0x0000ff); // this doesnt work correctly
    Serial.println("pad 0 blue after traveller");
  }
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  else if (currentPad == 1 && trellisButtonState2[pad] == 1 && latchingMode == true) { // array 2
    // code to make LED Blue again after pink traveller passes
    trellis.pixels.setPixelColor(pad, 0x0000ff); // this works correctly
    Serial.println("pad 1 blue after traveller");
  }
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////


  else {
    //   code to turn LED Off
    trellis.pixels.setPixelColor(pad, 0);
  }