Removing delay()

Blinking an LED i'm trying to get rid of the delay,
doesnt seem to blink the way it does with the delay
any ideas?

    if (patternBank[0][pad] == 1) // if pad is latched on in array
    {
      playSound(0);
      trellis.pixels.setPixelColor(0, Wheel(map(0, 0, trellis.pixels.numPixels(), 0, 255))); // LED on
      trellis.pixels.show();
      delay(50);
      trellis.pixels.setPixelColor(0, 0);  //  LED off
      trellis.pixels.show();
    }
float prevblink = 0;
float blinkint = 50;

    if (patternBank[0][pad] == 1) // if pad is latched on in array
    {
      playSound(0);
      trellis.pixels.setPixelColor(0, Wheel(map(0, 0, trellis.pixels.numPixels(), 0, 255))); // LED on
      trellis.pixels.show();

      unsigned long currentblink = millis();
      if (currentblink - prevblink >= blinkint) {
        prevblink = currentblink;

        trellis.pixels.setPixelColor(0, 0);  //  Turn off blink
        trellis.pixels.show();
      }
    }

Look at the blink with out delay example.

Assuming this is in the loop (Don't post snippets (Snippets R Us!))

In the version with the delay you do

  • playsound
  • led on
  • delay
  • led off

In the version without delay you do

  • playsound
  • led on
  • playsound
  • led on
  • playsound
  • led on
  • playsound
  • led on
  • playsound
  • led on
  • playsound
  • led on
  • playsound
  • led on
  • playsound
  • led on
  • playsound
  • led on
  • playsound
  • led on
    •••
    and then 50ms later
  • led off

➜ it's not the same thing

This is calculating the time since the last time you turned the led off. I think you want to calculate the time since you turned it on. Record the time when you first turn the led on, and then subtract that from the current millis.

Blink WIthout Delay isn't a magic set of lines that you just copy together into a program. The point is to realize that when you subtract two times then the result is the length of time between them.

Don't you want:

No the one that needs to survive the loop is prevblink.

1 Like

Your code seems to be missing the logic to toggle the LED state between ON and OFF. The if condition inside the loop() checks whether enough time has passed to turn off the LED, but it never turns it back on.

To fix this, you need to introduce another variable to track the state of the LED (ON or OFF). Then, within the if condition, you can check this state variable to decide whether to turn the LED on or off. Here's how you could modify your code:

bool ledState = false; // Variable to hold the LED state

if (patternBank[0][pad] == 1) // if pad is latched on in array
{
 playSound(0);
 unsigned long currentblink = millis();
 if (currentblink - prevblink >= blinkint) {
    prevblink = currentblink;
    ledState = !ledState; // Toggle the LED state
    trellis.pixels.setPixelColor(0, ledState ? Wheel(map(0, 0, trellis.pixels.numPixels(), 0, 255)) : 0); // Set LED color based on state
    trellis.pixels.show();
 }
}

In this modification, ledState is a boolean variable that keeps track of whether the LED is currently on (true) or off (false). Whenever the blink interval elapses, ledState is toggled, and the LED color is set accordingly. If ledState is true, the LED is turned on with the desired color; otherwise, it's turned off.

its inside a for loop i think, inside the rescheduleTimer() function

// Step Sequencer Sample Player
// Teensy 4.0 + audio board + adafruit neotrellis 4x4 rgb
// uses adafruit seesaw library https://github.com/adafruit/Adafruit_Seesaw
// Neotrellis addr (0x2E)

// to do: verify midi clock output works correctly
// to do: blink latched pads on main page
// to do: remove pink traveller on main page
// to do: activate tap LED on main page
// to do:

#include <SD.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SerialFlash.h>
#include "Adafruit_NeoTrellis.h"
#include <MIDI.h>


// ----------------------------------------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---------------------------------------------


// Create the Audio components.  These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
AudioPlaySerialflashRaw  playFlashRaw0;
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;

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

// Create Audio connections between the components
//
AudioConnection          patchCord1(playFlashRaw0, 0, mix1, 0);
AudioConnection          patchCord2(playFlashRaw1, 0, mix1, 1);
AudioConnection          patchCord3(playFlashRaw2, 0, mix1, 2);
AudioConnection          patchCord4(playFlashRaw3, 0, mix1, 3);
AudioConnection          patchCord5(playFlashRaw4, 0, mix2, 0);
AudioConnection          patchCord6(playFlashRaw5, 0, mix2, 1);
AudioConnection          patchCord7(playFlashRaw6, 0, mix2, 2);
AudioConnection          patchCord8(playFlashRaw7, 0, mix2, 3);
AudioConnection          patchCord9(playFlashRaw8, 0, mix3, 0);
AudioConnection          patchCord10(playFlashRaw9, 0, mix3, 1);
AudioConnection          patchCord11(playFlashRaw10, 0, mix3, 2);
AudioConnection          patchCord12(playFlashRaw11, 0, mix3, 3);
AudioConnection          patchCord13(playFlashRaw12, 0, mix4, 0);
AudioConnection          patchCord14(playFlashRaw13, 0, mix4, 1);
AudioConnection          patchCord15(playFlashRaw14, 0, mix4, 2);
AudioConnection          patchCord16(playFlashRaw15, 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
const int SDchipSelect = 10;    // Audio Shield has SD card CS on pin 10


Adafruit_NeoTrellis trellis;



unsigned long previousMillis = 0; // will store last time LED was updated
const int FSW1 = 3;  // tap tempo footswitch
const int FSW2 = 2;  // Start/stop footswitch
int ledState = LOW; // ledState used to set the tempo LED
int FSW1State = 0;         // variable for reading the pushbutton status
int FSW2State = HIGH;
int ledState2 = -1; //this variable tracks the state of the LED, negative if off, positive if on
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 500;    // the debounce time; increase if the output flickers
int lastTapState = LOW;  /* the last tap button state */
unsigned long currentTimer[2] = { 1000, 1000 };  /* 60 bpm */
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 downTime = 0;     /* for our delayed press reset */
unsigned long indicatorTimeout; /* for our fancy "blink" tempo indicator */
unsigned long indicatorTimeout2; //variable used to determine the duration of the pad X4 blinks on state
boolean pressTimeout = false;
unsigned long lastTap = 0; /* when the last tap happened */
elapsedMillis button_hold_counter = 0;
int8_t held_button_id = -1;
int latchingMode = false;
int kitLoadPage = false;
int loadKitNum;

const byte NPATTERNS = 16;

byte patternBank[NPATTERNS][16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

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

MIDI_CREATE_DEFAULT_INSTANCE();

//long bpm = 60.0;
//long tempo = 1000.0 / (bpm / 60.0); // = 1000ms

float prevmillis = 0;
float interval = currentTimer[0] / 24; //interval is the number of milliseconds defined by tempo formula.

float prevblink = 0;
float blinkint = 50;

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

void setup() {
  MIDI.begin(MIDI_CHANNEL_OMNI);
  Serial.begin(115200);
  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(FSW1, INPUT_PULLUP);
  pinMode(FSW2, INPUT_PULLUP);
  AudioMemory(40);
  audioShield.enable();
  audioShield.volume(0.9);

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

  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);
  SD.begin(SDchipSelect);

  //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);
  }
}

int pad = -1;



/****************************************************************************************************************/
void copyFiles() {

  delay(100);
  Serial.println(F("Copy all files from SD Card to SPI Flash"));


  File rootdir;

  if (loadKitNum == 0) {
    rootdir = SD.open("0"); // "mySamples = 0" "shortSamples = 1" "brushSamples = 2"  "amb samples = 3"  "/" = root
  }

  if (loadKitNum == 1) {
    rootdir = SD.open("1"); // "mySamples = 0" "shortSamples = 1" "brushSamples = 2"  "amb samples = 3"  "/" = root
  }

  if (loadKitNum == 2) {
    rootdir = SD.open("2"); // "mySamples = 0" "shortSamples = 1" "brushSamples = 2"  "amb samples = 3"  "/" = root
  }

  if (loadKitNum == 3) {
    rootdir = SD.open("3"); // "mySamples = 0" "shortSamples = 1" "brushSamples = 2"  "amb samples = 3"  "/" = root
  }

  if (loadKitNum == 4) {
    rootdir = SD.open("4"); // "mySamples = 0" "shortSamples = 1" "brushSamples = 2"  "amb samples = 3"  "/" = root
  }

  if (loadKitNum == 5) {
    rootdir = SD.open("5"); // "mySamples = 0" "shortSamples = 1" "brushSamples = 2"  "amb samples = 3"  "/" = root
  }

  if (loadKitNum == 6) {
    rootdir = SD.open("6"); // "mySamples = 0" "shortSamples = 1" "brushSamples = 2"  "amb samples = 3"  "/" = root
  }

  while (1) {
    // open a file from the SD card
    Serial.println();
    File f = rootdir.openNextFile();
    if (!f) break;
    const char *filename = f.name();
    Serial.print(filename);
    Serial.print(F("    "));
    unsigned long length = f.size();
    Serial.println(length);

    // check if this file is already on the Flash chip
    if (SerialFlash.exists(filename)) {
      // delete the copy on the Flash chip, if different
      Serial.println(F("  delete file from Flash chip"));
      SerialFlash.remove(filename);
    }

    // create the file on the Flash chip and copy data
    if (SerialFlash.create(filename, length)) {
      SerialFlashFile ff = SerialFlash.open(filename);
      if (ff) {
        Serial.print(F("  copying"));
        // copy data loop
        unsigned long count = 0;
        unsigned char dotcount = 9;
        while (count < length) {
          char buf[256];
          unsigned int n;
          n = f.read(buf, 256);
          ff.write(buf, n);
          count = count + n;
          Serial.print(".");
          if (++dotcount > 100) {
            Serial.println();
            dotcount = 0;
          }
        }
        ff.close();
        if (dotcount > 0) Serial.println();
      } else {
        Serial.println(F("  error opening freshly created file!"));
      }
    } else {
      Serial.println(F("  unable to create file"));
    }
    f.close();
  }

  rootdir.close();
  delay(10);
  Serial.println(F("Finished All Files"));
  kitLoadPage = false;
  //clearTrellis();

  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 clockOut() {
  unsigned long currentMillis = millis();
  if (currentMillis - prevmillis >= interval) {
    prevmillis = currentMillis;
    MIDI.sendClock();
  }
}
/****************************************************************************************************************/

void loop() {
  trellis.read();
  startStopFSW();
  runSeq();
  clockOut();
}


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

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

  //filter out any noise by setting a time buffer
  if ( (millis() - lastDebounceTime) > debounceDelay) {

    //if the button has been pressed, lets toggle the LED from "off to on" or "on to off"
    if ( (FSW2State == LOW) && (ledState2 < 0) ) {

      // turn LED lawn green:
      pixels.setPixelColor(1, 124, 252, 0);
      pixels.show();
      Serial.println("Start");
      //MIDI.sendStart();
      pad = -1;
      ledState2 = -ledState2; //now the LED is on, we need to change the state
      lastDebounceTime = millis(); //set the current time
    }
    else if ( (FSW2State == LOW) && (ledState2 > 0) ) {

      // LED crimson
      pixels.setPixelColor(1, 220, 20, 60);
      pixels.show();
      Serial.println("Stop");
      //MIDI.sendStop();
      ledState2 = -ledState2; //
      lastDebounceTime = millis(); //set the current time
    }
  }
}



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

void loadKitPage() { // Green K landing page

  ledState2 = -1;
  kitLoadPage = true;
  pixels.setPixelColor(1, 220, 20, 60); // start LED off (red)
  pixels.setPixelColor(0, 20, 55, 155); // kit LED (blue)
  pixels.show();
  clearTrellis();
  trellis.pixels.setPixelColor(0, 0, 255, 0); // letter "K" (green) Load Kit Page
  trellis.pixels.setPixelColor(2, 0, 255, 0);
  trellis.pixels.setPixelColor(3, 0, 255, 0);
  trellis.pixels.setPixelColor(4, 0, 255, 0);
  trellis.pixels.setPixelColor(5, 0, 255, 0);
  trellis.pixels.setPixelColor(8, 0, 255, 0);
  trellis.pixels.setPixelColor(10, 0, 255, 0);
  trellis.pixels.setPixelColor(12, 0, 255, 0);
  trellis.pixels.setPixelColor(15, 0, 255, 0);
  trellis.pixels.show();

}

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


void runSeq() {
  int tapState = digitalRead(FSW1);
  if ( tapState == LOW && tapState != lastTapState ) /* button up->down */
  {
    tap(); /* we got a HIGH-LOW transition, call our tap() function */
    downTime = millis();
    pressTimeout = false;
  }



  /* press-and-hold detection start */
  if ( tapState == LOW ) /* button is down... */
  {
    /* if we have not triggered before, and the down time is more than 2 seconds... */
    /* 2000 = 2000ms = 2 seconds */
    if ( !pressTimeout && ((millis() - downTime ) > 1000 ))
    {


      /* Load Kit Here */

      loadKitPage();

      /* Load Kit Here */


      currentTimer[0] = currentTimer[1]; /* dont let long press change current tempo */
      rescheduleTimer(); /* start the timer */
      pressTimeout = true; /* make sure we don't get called again during this press */
    }
  }
  /* press-and-hold detection end */



  lastTapState = tapState; /* keep track of the last state, for transition detection */

  if (ledState2 > 0) // if footswitch start
  {
    /* check for timer timeout */
    if ( millis() >= timeoutTime2 ) // timeoutTime2 is 4x faster than timeoutTime
    {
      /* timeout happens */

      indicatorTimeout = millis() + 20;  /* this sets the time when the LED goes off */
      rescheduleTimer();            /* and reschedule the timer to keep the pace */
    }



    /* display the tap blink on Trellis */
    if ( millis() < indicatorTimeout)
    {

      if (pad == held_button_id && latchingMode == true)
      {
        trellis.pixels.setPixelColor(held_button_id, 0, 255, 0);  //  Make held trellis LED green as pink traveller passes
        trellis.pixels.show();
      }
      else
      {
        trellis.pixels.setPixelColor(pad, 220, 20, 60); // The Pink Traveller
        trellis.pixels.show();
      }
    }



    if (currentPattern[pad] == 1 && latchingMode == true) // if pad is latched on in array
    {
      trellis.pixels.setPixelColor(pad, 0x0000ff); // make latched LEDs Blue again after pink traveller passes
    }
    else {
      trellis.pixels.setPixelColor(pad, 0);  //  Turn off pink traveller after it passes
    }

  } // if footswitch start






  if (ledState2 < 0) // if footswitch stop
  {
    /* check for timer timeout */
    if ( millis() >= timeoutTime ) // timeoutTime2 is 4x faster than timeoutTime
    {
      /* timeout happens */
      indicatorTimeout = millis() + 20;  /* this sets the time when the LED goes off */
      rescheduleTimer();            /* and reschedule the timer to keep the pace */
    }



    /* display the tap blink on LED  */
    if ( millis() < indicatorTimeout)
    {
      pixels.setPixelColor(0, 0, 255, 255); // Tempo LED on aqua
      pixels.show();
    }

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


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


void tap()
{
  /* we keep two of these around to average together later */
  currentTimer[1] = currentTimer[0];
  currentTimer[0] = millis() - lastTap; /* current = duration since last tap */
  lastTap = millis();
  timeoutTime = 0; /* force the trigger to happen immediately - sync and blink! */
  timeoutTime2 = 0; /* force the trigger to happen immediately - sync and blink! */
}


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

void rescheduleTimer()
{
  /* set the timer to go off again when the time reaches the
     timeout.  The timeout is all of the "currentTimer" values averaged
     together, then added onto the current time.  When that time has been
     reached, the next tick will happen...
  */
  timeoutTime = millis() + ((currentTimer[0] + currentTimer[1]) / 2);  // Neopixel LED speed
  timeoutTime2 = millis() + ((currentTimer[0] + currentTimer[1]) / 8);  // Neotrellis speed x4 faster
  interval = currentTimer[0] / 24;
  if (ledState2 > 0) // if footswitch start
  {
    pad++;
    pad %= 16;

    if (patternBank[0][pad] == 1) // if pad is latched on in array
    {
      playSound(0);
      trellis.pixels.setPixelColor(0, Wheel(map(0, 0, trellis.pixels.numPixels(), 0, 255))); // LED on
      trellis.pixels.show();
      unsigned long currentblink = millis();
      if (currentblink - prevblink >= blinkint) {
        prevblink = currentblink;
        trellis.pixels.setPixelColor(0, 0);  //  Turn off LED
        trellis.pixels.show();
      }
    }

    if (patternBank[1][pad] == 1) // if pad is latched on in array
    {
      playSound(1);
    }

    if (patternBank[2][pad] == 1) // if pad is latched on in array
    {
      playSound(2);
    }

    if (patternBank[3][pad] == 1) // if pad is latched on in array
    {
      playSound(3);
    }

    if (patternBank[4][pad] == 1) // if pad is latched on in array
    {
      playSound(4);
    }

    if (patternBank[5][pad] == 1) // if pad is latched on in array
    {
      playSound(5);
    }

    if (patternBank[6][pad] == 1) // if pad is latched on in array
    {
      playSound(6);
    }

    if (patternBank[7][pad] == 1) // if pad is latched on in array
    {
      playSound(7);
    }

    if (patternBank[8][pad] == 1) // if pad is latched on in array
    {
      playSound(8);
    }

    if (patternBank[9][pad] == 1) // if pad is latched on in array
    {
      playSound(9);
    }

    if (patternBank[10][pad] == 1) // if pad is latched on in array
    {
      playSound(10);
    }

    if (patternBank[11][pad] == 1) // if pad is latched on in array
    {
      playSound(11);
    }

    if (patternBank[12][pad] == 1) // if pad is latched on in array
    {
      playSound(12);
    }

    if (patternBank[13][pad] == 1) // if pad is latched on in array
    {
      playSound(13);
    }

    if (patternBank[14][pad] == 1) // if pad is latched on in array
    {
      playSound(14);
    }

    if (patternBank[15][pad] == 1) // if pad is latched on in array
    {
      playSound(15);
    }
    //    trellis.pixels.setPixelColor(0, 0);  //  Turn off LED
    //    trellis.pixels.show();
  }
}

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


void clearTrellis() {

  for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
    trellis.pixels.setPixelColor(i, 0); // all 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");

    if (kitLoadPage == false) {
      if (evt.bit.NUM >= 0 && evt.bit.NUM <= 15) { // push pads = sounds
        playSound(evt.bit.NUM);
        delay(10); // freezes without
      }
    }

    if (kitLoadPage == true) {
      if (evt.bit.NUM >= 0 && evt.bit.NUM <= 15) { // push pads = sounds
        loadKitNum = evt.bit.NUM;
        Serial.println(loadKitNum);
        copyFiles();
      }
    }


    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(held_button_id);
      }
      else {
        held_button_id = -1; // Resetting the held button id means we are no longer tracking that long press.
      }
    }
  }
  trellis.pixels.show();  // Turn on/off the neopixels
  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;
    {
      if (currentPattern[evt.bit.NUM] == 0) {
        trellis.pixels.setPixelColor(evt.bit.NUM, 0x0000FF); // if off latch on
        currentPattern[evt.bit.NUM] = 1; // save state to array
        savePattern(held_button_id); // save array to array bank
      }
      else if (currentPattern[evt.bit.NUM] == 1) {
        trellis.pixels.setPixelColor(evt.bit.NUM, 0); // if on turn off
        currentPattern[evt.bit.NUM] = 0; // save state to array
        savePattern(held_button_id); // save array to array bank
      }
      trellis.pixels.show();
    }
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // long press any pad to exit pattern mode without changing current pattern
  if (button_hold_counter >= 800) {
    Serial.println("Long press detected, loading main page");

    if (currentPattern[evt.bit.NUM] == 1) {
      currentPattern[evt.bit.NUM] = 0;
      savePattern(held_button_id); // save array to array bank
    }
    else if (currentPattern[evt.bit.NUM] == 0) {  // if on turn off
      currentPattern[evt.bit.NUM] = 1;
      savePattern(held_button_id); // save array to array bank
    }

    // long 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;
  }
  trellis.pixels.show();
  return 0;
}



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


void playSound(int num) {
  char fileName[5];
  sprintf(fileName, "%d.raw", num);

  switch (num) {
    case 0: playFlashRaw0.play(fileName); break;
    case 1: playFlashRaw1.play(fileName); break;
    case 2: playFlashRaw2.play(fileName); break;
    case 3: playFlashRaw3.play(fileName); break;
    case 4: playFlashRaw4.play(fileName); break;
    case 5: playFlashRaw5.play(fileName); break;
    case 6: playFlashRaw6.play(fileName); break;
    case 7: playFlashRaw7.play(fileName); break;
    case 8: playFlashRaw8.play(fileName); break;
    case 9: playFlashRaw9.play(fileName); break;
    case 10: playFlashRaw10.play(fileName); break;
    case 11: playFlashRaw11.play(fileName); break;
    case 12: playFlashRaw12.play(fileName); break;
    case 13: playFlashRaw12.play(fileName); break;
    case 14: playFlashRaw14.play(fileName); break;
    case 15: playFlashRaw15.play(fileName); break;
  }
}



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


void loadPattern(byte patternToLoad)
{
  Serial.print("loading pattern "); Serial.println(patternToLoad);
  for (byte ii = 0; ii < 16; ii++)
    currentPattern[ii] = patternBank[patternToLoad][ii];
  for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { // load stored pattern
    if (currentPattern[i] == 1) {
      trellis.pixels.setPixelColor(i, 0x0000FF); // stored blue leds on
    }
  }
  trellis.pixels.setPixelColor(held_button_id, 0, 255, 0); // held button green
}

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


void savePattern(byte patternToSave)
{
  Serial.print("saving pattern "); Serial.println(patternToSave);
  Serial.print("held button id "); Serial.println(held_button_id);
  for (byte ii = 0; ii < 16; ii++)
    patternBank[patternToSave][ii] = currentPattern[ii];
}

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

// Input a value 0 to 255 to get a rainbow 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;
}

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

@Leetut - whatever else is going on correctly or incorrectly, I see huge opportunities to simplify your code in one sense by making it just a bit more complicated in another.

Any time you find yourself making new variables by appending a differentiating character like a digit or two to the name of one you've worked with, it is time to think about making an array of them.

Then you could refer to any of N of them by number, instead of copy/paste/editing the code.

Anytime you you find yourself adding code by copy/paste/editing it, it is time to think about writing a function.

Then when you act to perfect, modify, enhance or fix such code, doing it once will do it for all the formerly copy/paste/edited code.

  if (loadKitNum == 4) {
    rootdir = SD.open("4");
  }

  if (loadKitNum == 5) {
    rootdir = SD.open("5");
  }

That's two patterns yanked from the seven in your code that could be one or two statements. I deleted the comments, at a glance it looks like they just went along for the ride and are now either needlessly repeated or meaningless ink. You will find when reading code, even your own, the less ink the better. Fewer things that need ignoring, even subconsciously, means more attention that can be paid to things what actually matter.

Same same

    if (patternBank[4][pad] == 1) // if pad is latched on in array
    {
      playSound(4);
    }

    if (patternBank[5][pad] == 1) // if pad is latched on in array
    {
      playSound(5);
    }

if placed in a loop.

And

  trellis.pixels.setPixelColor(3, 0, 255, 0);
  trellis.pixels.setPixelColor(4, 0, 255, 0);
  trellis.pixels.setPixelColor(5, 0, 255, 0);
  trellis.pixels.setPixelColor(8, 0, 255, 0);
  trellis.pixels.setPixelColor(10, 0, 255, 0);

could be in a loop, with the pixel numbers you want to to make green listed as the elements of an array.

a7

It appears to be rather difficult

you might benefit from studying state machines. Here is a small introduction to the topic: Yet another Finite State Machine introduction

tried again, ended up with exact same code :laughing:

play sound;
LED on;
remember time;
when time has passed LED off;

float prevblink = 0;
float blinkint = 50;

    if (patternBank[0][pad] == 1) // if pad is latched on in array
    {
      playSound(0);
      trellis.pixels.setPixelColor(0, Wheel(map(0, 0, trellis.pixels.numPixels(), 0, 255))); // LED on
      trellis.pixels.show();

      unsigned long currentblink = millis();
      if (currentblink - prevblink >= blinkint) {
        prevblink = currentblink;

        trellis.pixels.setPixelColor(0, 0);  //  Turn off blink
        trellis.pixels.show();
      }
    }

Don't post snippets (Snippets R Us!)

we have no clue how your code fits within the loop, so can't tell much

1 Like

You have to put this part somewhere that it will run on every loop. It does't necessarily need to be anywhere near the place where you turn the LED on.

The code goes into an if statement if (patternBank[0][pad] == 1) to turn the led on. But it will not be going back into that later to turn it off. So you need to put the code that turns the led back off somewhere that it will be actually running all the time and constantly checking to see if the led is on and how long it has been on.

doesnt work here

    pad++;
    pad %= 16;
    
      unsigned long currentblink = millis();
      if (currentblink - prevblink >= blinkint) {
        prevblink = currentblink;
        trellis.pixels.setPixelColor(0, 0);  //  Turn off LED
        trellis.pixels.show();
      }

    if (patternBank[0][pad] == 1) // if pad is latched on in array
    {
      playSound(0);
      trellis.pixels.setPixelColor(0, Wheel(map(0, 0, trellis.pixels.numPixels(), 0, 255))); // LED on
      trellis.pixels.show();
}

dont need to go "back into that later to turn it off"
because im still in that

if (patternBank[0][pad] == 1) // if pad is latched on in array
    {
// do these 3 things>
play sound;
turn led on
turn led off 20 milliseconds later
}
// next time around do it all again

the code is complicated to read - I don't see clear states that would be usable for a finite state machine approach

In one section, when you turn on the LED, start the timer.

In another separate section, if the timer is expired, turn off the LED.

Since (I assume) you are looping, the order does not matter. Here we check to see if it is time to turn off the LED before seeing if it shoukd be turned on.

    unsigned long now =millis();

    if (now - prevblink >= blinkint)
    {
        trellis.pixels.setPixelColor(0, 0);  //  Turn off LED
        trellis.pixels.show();
    }

    if (patternBank[0][pad] == 1) 
    {
      playSound(0);
      trellis.pixels.setPixelColor(0, Wheel(map(0, 0, trellis.pixels.numPixels(), 0, 255))); // LED on
      trellis.pixels.show();

      prevblink = now;  // here start the timer
    }

But that still won't blink. The LED will be on for blonkint milliseconds after the pad is cleared.

You'll have to introduce some new variable to track the state of the blinker.

The pad is active all the time you want to be blinking, so there needs to be something that says "I was off, it's time to turn on" and vice versa. The on/off flag timer are the variables that will inform the decision to illuminate or extinguish the LED.

I changed the name of your grab at millis() to now. In a smoothly running sketch, now coukd be a global variable and everyone would use it, not millis(), for all timing


I could swear we've helped you do this kinda thing before to this very sketch or its progenitor. Which means our help is actually detrimental.

a7

No you're not.

This code executes the playSound line, then the setPixelColor line and show to turn the led on. Then it saves the time in currentblink. Then it checks to see how long it has been. Since the led just now got turned on, it hasn't been 20ms yet, so the if statmement is false and it gets skipped. Then this block exits. The execution of the code doesn't sit there and wait for that if statement to become true. The code won't enter this if statement again until patternBank[0][pad] is 1 again. As long as that is 0, this if statement isn't true so this block isn't entered so the if statement is not checked again.

That part needs to move outside of the if statement that is turning the led on unless you only want the led to only turn off if that patternbank[0][pad] variable is still a 1.

The condition for turning on the LED is

    patternBank[0][pad] == 1

The only sense I can make so far is that the condition becoming true or remaining true should mean that the LED blinks.

So…

When patternBank[0][pad] == 1 becomes true, set some flag that sez you blinking, and start the timer.

Respect and update the LED using the timer as long as patternBank[0][pad] == 1 remains true. When it goes false, clear the flag that says you wanna be blinking. Turn the LED off, too.

So as someone said, you'll need a flag variable and the timer you already have. If this is in a loop handling multiple pads… you'll need multiple flags, at least, and multiple timers unless you want or would be OK with all the blinking being synchronized. It's a choice you can make. If I was voting I'd have them not synchronized to create more cray-crat chaotic seizures inducing visual noise.

But this is all guessing, based on what I've been able to forget about last time. :expressionless:

Please post the code that does what you want as far as the LED goes, but is to be changed so as not to do using delay().

That might make your goal less unclear.

a7