Button Array an improvement? Resolved

reviewed and researched but can't find answer. Is an array for my 4 buttons and five functions going to be any better functioning than this currently functioning straight forward code? Probably at least make it look neater.....

+3.3v input to trigger button 0-3, current remains ON (not momentary) to keep the function looping(which is fine for my project).

Thoughts, corrections, improvements?

#define SPASMABUTTON 0
#define RASMABUTTON 1
#define GRASMABUTTON 2
#define BLASMABUTTON 3


int SPASMABUTTONState = 0;
int RASMABUTTONState = 0;
int GRASMABUTTONState = 0;
int BLASMABUTTONState = 0;

setup(){
 pinMode(SPASMABUTTON, INPUT_PULLDOWN);
  pinMode(RASMABUTTON, INPUT_PULLDOWN);
  pinMode(GRASMABUTTON, INPUT_PULLDOWN);
  pinMode(BLASMABUTTON, INPUT_PULLDOWN);
}

void loop() {
  SPASMABUTTONState = digitalRead(SPASMABUTTON);
  RASMABUTTONState = digitalRead(RASMABUTTON);
  GRASMABUTTONState = digitalRead(GRASMABUTTON);
  BLASMABUTTONState = digitalRead(BLASMABUTTON);

  if (SPASMABUTTONState == 1) {
    myPalette();
  }
  if (RASMABUTTONState == 1) {
    myPalette1();
  }
  if (GRASMABUTTONState == 1) {
    myPalette2();
  }
  if (BLASMABUTTONState == 1) {
    myPalette3();
  }
  if ((SPASMABUTTONState == 0) && (RASMABUTTONState == 0) && (GRASMABUTTONState == 0) && (BLASMABUTTONState == 0)) {
    myPalette4();
  }
}

I don't think it will do anything performance wise. I do have a suggestion however. Since digital read returns HIGH or LOW, then that is what you should put in your comparisons instead of 0 and 1.

Also, how are you intending on handling multiple button presses?

Any time you have numbers in your variable names, you should consider an array. You have distinct names for all the buttons, so no, you don't need an array.

However...

XRAD: myPalette(); myPalette1(); myPalette2(); myPalette3(); myPalette4();

I have to ask, what is so special about the first one that it doesn't even get a number? This code is screaming out for an array, or maybe a class.

My approach will be to use a struct that combines a pin with a function. You will need to read up on structs as well as on function pointers and function prototypes.

Add this to the beginning of your code;

// function prototypes
void myPalette();
void myPalette1();
void myPalette2();
void myPalette3();
void myPalette4();

Next define a structure that associates a pin with a function

struct BTN
{
  const byte pin;   // button pin
  void (*func)();   // function to execute when button is pressed (function pointer)
};

Next create an array of BTN

BTN buttons[] =
{
  {2, myPalette},
  {3, myPalette1},
  {4, myPalette2},
  {5, myPalette3},
};

In setup(), you can loop through the array and set the input mode

void setup() {
  for (uint8_t btnCount = 0; btnCount < sizeof(buttons) / sizeof(buttons[0]); btnCount++)
  {
    pinMode(buttons[btnCount].pin, INPUT_PULLDOWN);
  }
}

sizeof(buttons) / sizeof(buttons[0]) calculates the number of elements in the array at compile time; using this makes that you can add / remove elements from the array without having to adjust anything else in the code. Usually you put this in a macro but I was too lazy :wink:

And in loop(), you can loop through the array to test the button state and execute the associated function.

void loop()
{
  bool done = false;

  for (uint8_t btnCount = 0; btnCount < sizeof(buttons) / sizeof(buttons[0]); btnCount++)
  {
    if (digitalRead(buttons[btnCount].pin) == HIGH)
    {
      buttons[btnCount].func();
      done = true;
    }
  }

  // default if no buttons pressed
  if (done == false)
  {
    myPalette();
  }
}

Notes:

  1. pin numbers don’t match yours
  2. code not tested
  3. variations are possible depending on needs; above loop() matches your original code

Thank you to everyone for your help and suggestions!

MorganS: I gave ‘myPallete’ a number ‘myPalette0’ ; I did initially try HIGH LOW button read, but it caused some unusual pixel display

Metallor: Maybe not noticeable to most, but there was actually a slight visual improvement when switching from some of the palletes to another. Slight pixel hesitation no longer present. Also I saved 1% of memory.

sterretje: I very much appreciate all your help! Really helped me understand some of the struct and pointer basics. Thank you!!

The code is cleaner and functions extremely well. Mini clean dither with easy button control.

short vid cycling through buttons:

/*****XRAD'S Modified code for button press to
   choose color pattern. Original base code from
   https://github.com/FastLED/FastLED/blob/master/examples/NoisePlusPalette/NoisePlusPalette.ino
   with fantastic help from "sterretje" and others on Arduino
   forums: https://forum.arduino.cc/index.php?topic=601506.0
   For project;  TrinketM0 pin inputs=color patterns:
   SPASMABUTTON 0, RASMABUTTON 1, GRASMABUTTON 2, BLASMABUTTON 3
*/

#include <FastLED.h>

#define LED_PIN     4
#define BRIGHTNESS  50
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB

const uint8_t kMatrixWidth  = 14;
const uint8_t kMatrixHeight = 3;
const bool    kMatrixSerpentineLayout = true;

#define NUM_LEDS (kMatrixWidth * kMatrixHeight)
#define MAX_DIMENSION ((kMatrixWidth>kMatrixHeight) ? kMatrixWidth : kMatrixHeight)

CRGB leds[kMatrixWidth * kMatrixHeight];

//coordinates
static uint16_t x;
static uint16_t y;
static uint16_t z;

// We're using the x/y dimensions to map to the x/y pixels on the matrix.  We'll
// use the z-axis for "time". 1 for slow, higher number = faster
uint16_t speed = 100; // speed is set dynamically once we've started up

// Scale determines how far apart the pixels in our noise matrix are.    A value
// of 1 will be so zoomed in, you'll mostly see solid colors.
uint16_t scale = 100; // scale is set dynamically once we've started up

// This is the array that we keep our computed noise values in
uint8_t noise[MAX_DIMENSION][MAX_DIMENSION];

CRGBPalette16 currentPalette( RainbowColors_p );
uint8_t       colorLoop = 1;

//color palletes 0-3 match button pins
void myPalette0() {
  currentPalette = RainbowStripeColors_p;
  speed = 2; scale = 30; colorLoop = 0;//adjust to needs
}

void myPalette1() {
  currentPalette = LavaColors_p;
  speed = 2; scale = 30; colorLoop = 0;
}

void myPalette2() {
  currentPalette = ForestColors_p;
  speed = 2; scale = 30; colorLoop = 0;
}

void myPalette3() {
  currentPalette = CloudColors_p;
  speed = 3; scale = 30; colorLoop = 0;
}

void myPalette4() {
  currentPalette = RainbowColors_p;
  speed = 2; scale = 30; colorLoop = 0;
}

struct BTN
{
  const byte pin;   // button pin
  void (*func)();   // * execute when button is pressed 
};

BTN buttons[] =
{
  {0, myPalette0},
  {1, myPalette1},
  {2, myPalette2},
  {3, myPalette3},
};


void setup() {
  delay(3000);
  LEDS.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  LEDS.setBrightness(BRIGHTNESS);

  // Initialize our coordinates to some random values
  x = random16();
  y = random16();
  z = random16();

  for (uint8_t btnCount = 0; btnCount < sizeof(buttons) / sizeof(buttons[0]); btnCount++)
  {
    pinMode(buttons[btnCount].pin, INPUT_PULLDOWN);
  }
}

void loop()
{
  bool done = false;

  for (uint8_t btnCount = 0; btnCount < sizeof(buttons) / sizeof(buttons[0]); btnCount++)
  {
    if (digitalRead(buttons[btnCount].pin) == HIGH)
    {
      buttons[btnCount].func();
      done = true;
    }
  }
  // default if no buttons pressed
  if (done == false)
  {
    myPalette4();
  }
  fillnoise8(); mapNoiseToLEDsUsingPalette(); LEDS.show();
}


// Fill the x/y array of 8-bit noise values using the inoise8 function.
void fillnoise8() {
  // If we're runing at a low "speed", some 8-bit artifacts become visible
  // from frame-to-frame.  In order to reduce this, we can do some fast data-smoothing.
  // The amount of data smoothing we're doing depends on "speed".
  uint8_t dataSmoothing = 0;
  if ( speed < 50) {
    dataSmoothing = 200 - (speed * 4);
  }

  for (int i = 0; i < MAX_DIMENSION; i++) {
    int ioffset = scale * i;
    for (int j = 0; j < MAX_DIMENSION; j++) {
      int joffset = scale * j;
      uint8_t data = inoise8(x + ioffset, y + joffset, z);
      // The range of the inoise8 function is roughly 16-238.
      // These two operations expand those values out to roughly 0..255
      // You can comment them out if you want the raw noise data.
      data = qsub8(data, 16);
      data = qadd8(data, scale8(data, 39));

      if ( dataSmoothing ) {
        uint8_t olddata = noise[i][j];
        uint8_t newdata = scale8( olddata, dataSmoothing) + scale8( data, 256 - dataSmoothing);
        data = newdata;
      }
      noise[i][j] = data;
    }
  }
  z += speed;
  // apply slow drift to X and Y, just for visual variation.
  x += speed / 8;
  y -= speed / 16;
}

void mapNoiseToLEDsUsingPalette()
{
  static uint8_t ihue = 0;
  for (int i = 0; i < kMatrixWidth; i++) {
    for (int j = 0; j < kMatrixHeight; j++) {
      // We use the value at the (i,j) coordinate in the noise
      // array for our brightness, and the flipped value from (j,i)
      // for our pixel's index into the color palette.
      uint8_t index = noise[j][i];
      uint8_t bri =   noise[i][j];
      // if this palette is a 'loop', add a slowly-changing base value
      if (colorLoop) {
        index += ihue;
      }
      // brighten up, as the color palette itself often contains the
      // light/dark dynamic range desired
      if ( bri > 127 ) {
        bri = 255;
      } else {
        bri = dim8_raw( bri * 2);
      }
      CRGB color = ColorFromPalette( currentPalette, index, bri);
      leds[XY(i, j)] = color;
    }
  }
  ihue += 1;
}

// Mark's xy coordinate mapping code.  See the XYMatrix for more information on it.
//
uint16_t XY( uint8_t x, uint8_t y)
{
  uint16_t i;
  if ( kMatrixSerpentineLayout == false) {
    i = (y * kMatrixWidth) + x;
  }
  if ( kMatrixSerpentineLayout == true) {
    if ( y & 0x01) {
      // Odd rows run backwards
      uint8_t reverseX = (kMatrixWidth - 1) - x;
      i = (y * kMatrixWidth) + reverseX;
    } else {
      // Even rows run forwards
      i = (y * kMatrixWidth) + x;
    }
  }
  return i;
}