Creating A Remote Control for Multi-Mode Light System

I want to make a remote to control a light array. I want 6 modes: pattern 1, pattern 2, pattern 3, accelerometer controlled, always on, and off. I am using two HC-05 Bluetooth modules to control the system. I want to be able to send a signal with the remote, the remote turns off after I've sent the message to save power, and be able to switch modes at any time (ie, if it's in the middle of the first pattern, I can still switch to accelerometer). I have tested every mode and bluetooth, and they all work independently. Bluetooth also tested with most modes. However, when I put it all together, it seems to have issues. My Bluetooth, the on in the light system, repeats the signal continuously in the serial monitor during debugging, even though this did not happen before. I can select one mode but can't switch until I turn off the system to fix it. I'm not sure if the mode switches are possible, because I know I'm trying to access the BTSerial data in the middle of the loop. I tried to fix this with a while loop, but after trying several different variations, I can't seem to get it to switch when I press a new button. I am pretty new to Arduino, so I'm not sure how to write "continue mode if number is not #2, #3, etc." into the code, or if a while loop is what should be used in this situation. I was running the mode changes off of this tutorial: The correct way to use buttons with FastLed library, Arduino and WS2812 NEOPIXEL - YouTube
I think a big difference that's causing issues is the fact that my functions are running continuously, while the videos I think ends the loop even while the lights continue, which is why I switched the code around a bit.

Here are the fritzing diagrams of the light and remote systems, and both their codes. I have a 2K resistor on the HC-05 voltage splitter section, but couldn't find 2K in fritzing, so a 2.2K resistor is noted. I also plan to expand light patterns 2 and 3, so those are just place holders until I can make sure the system works.


Morpeko Microphone Remote Fritzing

Light System Code:


```cpp
#include <Adafruit_Sensor.h>
#include <Adafruit_MMA8451.h>
#include <Wire.h>
#include <FastLED_NeoPixel.h>
#include <SoftwareSerial.h>

//Setup Bluetooth
SoftwareSerial BTSerial(11, 12);  //RX,TX
int micdata = 0;

//Accelerometer
Adafruit_MMA8451 mma = Adafruit_MMA8451();
#define MAX_G 3.0
#define MAX_G_RESULTANT 3.0
#define EVENTS_LIMIT 3
#define SAMPLE_RATE 5000
#define CHK_DEADTIME 100
#define LOGGING_RATE 100
#define LED_ONTIME 5000ul
#define LED_IDLE 0
#define LED_TIME 1

char
  szStr[50];

float
  xg_f,
  yg_f,
  zg_f,
  xg_sum,
  yg_sum,
  zg_sum;

unsigned long
  sample_count;

bool
  bGLimitExceeded;


//Setup Reedswitch
const int reed = 5;

//Neopixels
const int LAPin = 6;
const int RAPin = 7;
const int LLPin = 8;
const int RLPin = 9;

const int LALeds = 30;
const int RALeds = 30;
const int LLLeds = 30;
const int RLLeds = 30;

FastLED_NeoPixel<LALeds, LAPin, NEO_GRB> LAstrip;
FastLED_NeoPixel<RALeds, RAPin, NEO_GRB> RAstrip;
FastLED_NeoPixel<LLLeds, LLPin, NEO_GRB> LLstrip;
FastLED_NeoPixel<RLLeds, RLPin, NEO_GRB> RLstrip;

String but1 = "Oran";
String but2 = "Pecha";
String but3 = "Chesto";
String but4 = "Cheri";
String but5 = "Razz";
String but6 = "Pinap";

void setup() {
  pinMode(reed, INPUT);
  LAstrip.begin();
  RAstrip.begin();
  LLstrip.begin();
  RLstrip.begin();
  BTSerial.begin(38400);
  Serial.begin(9600);
  mma.setRange(MMA8451_RANGE_4_G);

  bGLimitExceeded = false;
  xg_sum = 0.0;
  yg_sum = 0.0;
  zg_sum = 0.0;
  sample_count = 0;
}

void loop() {
  if (BTSerial.available() > 0) {  // Checks whether data is comming from the serial port
    micdata = BTSerial.read();     // Reads the data from the serial port and store it in dataFromMaster variable
  }
  // Controlling the led
  if (micdata == '1') {
    LightPat1();
    Serial.print(but1);
    delay(150);  // LED ON
    while (micdata == !('2') || !('3') || !('4') || !('5') || !('6'))
      ;
  } else if (micdata == '2') {
    LightPat2();
    Serial.print(but2);
    delay(150);
    while (micdata == !('1') || !('3') || !('4') || !('5') || !('6'))
      ;
  } else if (micdata == '3') {
    LightPat3();
    Serial.print(but3);
    delay(150);
    while (micdata == !('2') || !('1') || !('4') || !('5') || !('6'))
      ;
  } else if (micdata == '4') {
    TakeSamples();
    strike();
    Serial.print(but4);
    delay(150);
    while (micdata == !('2') || !('3') || !('1') || !('5') || !('6'))
      ;
  } else if (micdata == '5') {
    flow();
    Serial.print(but5);
    delay(150);
    while (micdata == !('2') || !('3') || !('4') || !('1') || !('6'))
      ;
  } else if (micdata == '6') {
    darkencheck();
    Serial.print(but6);
    delay(150);
    while (micdata == !('2') || !('3') || !('4') || !('5') || !('1'))
      ;
  }
}

void clear() {
  LAstrip.clear();
  RAstrip.clear();
  LLstrip.clear();
  RLstrip.clear();
  LAstrip.show();
  RAstrip.show();
  LLstrip.show();
  RLstrip.show();
}

void flash() {
  flashcheck();
  delay(125);
  darkencheck();
}

void flashcheck() {
  if (digitalRead(reed) == HIGH) {
    LAstrip.fill(CRGB::White, 0, LALeds);
    RAstrip.fill(CRGB::White, 0, RALeds);
    LLstrip.fill(CRGB::White, 0, LLLeds);
    RLstrip.fill(CRGB::White, 0, RLLeds);
    LAstrip.show();
    RAstrip.show();
    LLstrip.show();
    RLstrip.show();
  } else {
    LAstrip.fill(CRGB::Purple, 0, LALeds);
    RAstrip.fill(CRGB::Purple, 0, RALeds);
    LLstrip.fill(CRGB::Purple, 0, LLLeds);
    RLstrip.fill(CRGB::Purple, 0, RLLeds);
    LAstrip.show();
    RAstrip.show();
    LLstrip.show();
    RLstrip.show();
  }
}
void flow() {
  if (digitalRead(reed) == HIGH) {
    colorWipe(LAstrip.Color(CRGB::White), RAstrip.Color(CRGB::White), LLstrip.Color(CRGB::White), RLstrip.Color(CRGB::White), 20);
  } else {
    colorWipe(LAstrip.Color(CRGB::Purple), RAstrip.Color(CRGB::Purple), LLstrip.Color(CRGB::Purple), RLstrip.Color(CRGB::Purple), 20);
  }
  delay(50);
}

void LightPat1() {
  flash();
  delay(1100);
  flow();
  darkencheck();
  delay(1600);
  flow();
  darkencheck();
  delay(1650);
  flash();
  delay(1050);
  flow();
  darkencheck();
  delay(1550);
  flow();
  darkencheck();
  delay(1350);
  flow();
  delay(1400);
  darkencheck();
  delay(500);
  flash();
  delay(500);
  flow();
  darkencheck();
  delay(300);
  flash();
  delay(500);
  flash();
  delay(500);
  flow();
  darkencheck();
  delay(500);
  flash();
  delay(300);
  flow();
  delay(1500);
  darkencheck();
  delay(600);
}

void LightPat2() {
  flow();
  darkencheck();
  delay(100);
}

void LightPat3() {
  flow();
  darkencheck();
  delay(2000);
}

void colorWipe(uint32_t color1, uint32_t color2, uint32_t color3, uint32_t color4, int wait) {
  int numPixels = LAstrip.numPixels();
  if (RAstrip.numPixels() > numPixels) numPixels = RAstrip.numPixels();
  if (LLstrip.numPixels() > numPixels) numPixels = LLstrip.numPixels();
  if (RLstrip.numPixels() > numPixels) numPixels = RLstrip.numPixels();
  for (int i = 0; i < numPixels; i++) {
    if (i < LAstrip.numPixels()) LAstrip.setPixelColor(i, color1);
    if (i < RAstrip.numPixels()) RAstrip.setPixelColor(i, color2);
    if (i < LLstrip.numPixels()) LLstrip.setPixelColor(i, color3);
    if (i < RLstrip.numPixels()) RLstrip.setPixelColor(i, color4);
    LAstrip.show();
    RAstrip.show();
    LLstrip.show();
    RLstrip.show();
    delay(wait);
  }
}

void strikecheck() {
  flow();
  darkencheck();
  delay(50);
}

void darkencheck() {
  if (digitalRead(reed) == HIGH) {
    darkenwhite();
  } else {
    darkenpurple();
  }
}

void darkenwhite() {
  uint16_t i, j;
  int numPixels = LAstrip.numPixels();
  if (RAstrip.numPixels() > numPixels) numPixels = RAstrip.numPixels();
  if (LLstrip.numPixels() > numPixels) numPixels = LLstrip.numPixels();
  if (RLstrip.numPixels() > numPixels) numPixels = RLstrip.numPixels();
  for (j = 255; j > 0; j--) {
    for (i = 0; i < LAstrip.numPixels(); i++) {
      LAstrip.setPixelColor(i, j, j, j);
    }
  }
  for (j = 255; j > 0; j--) {
    for (i = 0; i < RAstrip.numPixels(); i++) {
      RAstrip.setPixelColor(i, j, j, j);
    }
  }
  for (j = 255; j > 0; j--) {
    for (i = 0; i < LLstrip.numPixels(); i++) {
      LLstrip.setPixelColor(i, j, j, j);
    }
  }
  for (j = 255; j > 0; j--) {
    for (i = 0; i < RLstrip.numPixels(); i++) {
      RLstrip.setPixelColor(i, j, j, j);
    }
  }
  LAstrip.show();
  RAstrip.show();
  LLstrip.show();
  RLstrip.show();
}

void darkenpurple() {
  uint16_t i, j;
  int numPixels = LAstrip.numPixels();
  if (RAstrip.numPixels() > numPixels) numPixels = RAstrip.numPixels();
  if (LLstrip.numPixels() > numPixels) numPixels = LLstrip.numPixels();
  if (RLstrip.numPixels() > numPixels) numPixels = RLstrip.numPixels();
  for (j = 255; j > 0; j--) {
    for (i = 0; i < LAstrip.numPixels(); i++) {
      LAstrip.setPixelColor(i, j, i, j);
    }
  }
  for (j = 255; j > 0; j--) {
    for (i = 0; i < RAstrip.numPixels(); i++) {
      RAstrip.setPixelColor(i, j, i, j);
    }
  }
  for (j = 255; j > 0; j--) {
    for (i = 0; i < LLstrip.numPixels(); i++) {
      LLstrip.setPixelColor(i, j, i, j);
    }
  }
  for (j = 255; j > 0; j--) {
    for (i = 0; i < RLstrip.numPixels(); i++) {
      RLstrip.setPixelColor(i, j, i, j);
    }
  }
  LAstrip.show();
  RAstrip.show();
  LLstrip.show();
  RLstrip.show();
}

void TakeSamples() {
  static byte
    gLimitDeadTime = 0;
  float
    intermediate,
    resultant;
  static unsigned long
    timeSample = 0;
  unsigned long
    timeNow;

  timeNow = micros();
  if ((timeNow - timeSample) < SAMPLE_RATE)
    return;
  timeSample = timeNow;

  mma.read();
  xg_f = (float)mma.x / 2048.0;
  yg_f = (float)mma.y / 2048.0;
  zg_f = (float)mma.z / 2048.0;

  xg_sum = xg_sum + xg_f;
  yg_sum = yg_sum + yg_f;
  zg_sum = zg_sum + zg_f;
  sample_count = sample_count + 1;

#ifndef TEST_RESULTANT
  resultant = 0.0;
#else
  resultant = sqrt(pow(xg_f, 2) + pow(yg_f, 2) + pow(zg_f, 2));
#endif
  if (gLimitDeadTime)
    gLimitDeadTime--;

  if (gLimitDeadTime == 0) {
    if ((abs(xg_f) > MAX_G) || (abs(yg_f) > MAX_G) || (abs(zg_f) > MAX_G) || resultant > MAX_G_RESULTANT) {
      {
        bGLimitExceeded = true;
        gLimitDeadTime = CHK_DEADTIME;
      }
    }
  }
}

void strike() {
  static int
    nEvents = 0;
  static byte
    stateLED = LED_IDLE;
  static unsigned long
    timeLED;
  unsigned long
    timeNow;

  switch (stateLED) {
    case LED_IDLE:
      if (bGLimitExceeded) {
        timeNow = millis();
        bGLimitExceeded = false;
        nEvents++;
        if (nEvents >= EVENTS_LIMIT)
          strikecheck();
        timeLED = millis();
        stateLED = LED_TIME;
      }
      break;
  }
}


Remote Code:
//Include library
#include <SoftwareSerial.h>

//Set up button pin
const int oran = 12;
const int pecha = 11;
const int chesto = 10;
const int cheri = 9;
const int razz = 8;
const int pinap = 7;
int status = false;

String but1 = "Oran";
String but2 = "Pecha";
String but3 = "Chesto";
String but4 = "Cheri";
String but5 = "Razz";
String but6 = "Pinap";

//Define Bluetooth
SoftwareSerial BTSerial(3, 2);  //RX, TX
int dataFromComputer = 0;

void setup() {
  //Set buttons to input
  pinMode(oran, INPUT);
  pinMode(pecha, INPUT);
  pinMode(chesto, INPUT);
  pinMode(cheri, INPUT);
  pinMode(razz, INPUT);
  pinMode(pinap, INPUT);
  //Start Bluetooth
  BTSerial.begin(38400);
  Serial.begin(9600);
}

void loop() {
  //Begin The Bluetooth
  if (Serial.available() > 0) {
    dataFromComputer = Serial.read();
  }

  //Prepare Button Read
  int orval = digitalRead(oran);
  int peval = digitalRead(pecha);
  int chesval = digitalRead(chesto);
  int cherval = digitalRead(cheri);
  int raval = digitalRead(razz);
  int pival = digitalRead(pinap);

  //Each button transmits a different number
  if (orval == HIGH) {
    status = !status;
    BTSerial.write('1');
    Serial.print(but1);
    delay(10);
    status = status;
    while (status = !status);
  } else if (peval == HIGH) {
    status = !status;
    BTSerial.write('2');
    Serial.print(but2);
    delay(10);
    status = status;
    while (status = !status);
  } else if (chesval == HIGH) {
    status = !status;
    BTSerial.write('3');
    Serial.print(but3);
    delay(10);
    status = status;
    while (status = !status);
  } else if (cherval == HIGH) {
    status = !status;
    BTSerial.write('4');
    Serial.print(but4);
    delay(10);
    status = status;
    while (status = !status);
  } else if (raval == HIGH) {
    status = !status;
    BTSerial.write('5');
    Serial.print(but5);
    delay(10);
    status = status;
    while (status = !status);
  } else if (pival == HIGH) {
    status = !status;
    BTSerial.write('6');
    Serial.print(but6);
    delay(10);
    status = status;
    while (status = !status);
  }
}

In the remote code, I think that this will not do what you expect it to do

  1. '=' is an assignment, '==' is for compare.
  2. Even if you use '==', comparing status with the opposite of status will always be false.

Please explain what you expect from that line?

For your light system:

  1. Did you test it completely without Bluetooth? If not, I suggest that you use Serial Monitor to send the commands instead of your remote. That will indicate of the remote is part of the problem or not.
  2. The system will be slow due to the use of delay(); so while e.g. LightPat1 is being executed, there will not be reaction on the remote.

Possibly not a cause of the problems that you describe but what is the power supply that you're using for the light system? For full white on all strips at the same time, you need 120 x 60 mA = 7.2 A. Your diagram seems to indicate a 2A power supply.

Note:
I did not load your codes in the IDE to have a proper look.

@slighthuman Further to that, if you're really using a breadboard to fan out more than an amp of current(or less), expect a meltdown. The amount of contact surface in a breadboard-to-wire connection is totally inadequate, you'll either damage the breadboard pretty quick.

Better to simply use wire nuts for that fanout.
I know, it's not related to your question, but it's hard not to recommend fixes that are clearly going to bite you sooner than later.

Interrupts are disabled by the FastLED show() function. SoftwareSerial uses interrupts. Serial data that arrives while the show() function is executing can be ignored or corrupted.

I used while (status = !status) because I set status = !status as soon as the button is pressed, then to status = status at the end of the loop. So my thought was I set the value to false when initiating, and to true at the end, it should end it. This was hopefully to stop the remote from continuously broadcasting the number and wasting power. I should change it to "==" though, thank you!

I tested everything with and without Bluetooth for each individual function. I didn't have the issue of the serial monitor saying that the light system Bluetooth is continuously reading "1" for example, while the remote serial monitor stated that it transmitted once and then ended. I can rewrite everything to run straight from the buttons, but that will take a second to set up.

If I get rid of the delay, would that mean that I can change the mode even while it is still executing LightPat1?

I'm not sure exactly how many LEDS I'm going to have per strip right now. It's for a costume, and I'm not exactly sure how long I'm going to run it. My actual converter is for 3A, but I can see an issue occurring. I have enough power banks that I can add a secondary on for two of the strips. I figured that might be a problem, but also figured I'd test the code and make sure it works before introducing the full led range, so I've only been testing with one LED strip. I did try to run everything off the 5v of the arduino initially, but that had issues, so I transferred the LED to a usb power bank. It can all turn on and start, but I can't change while in a loop.

That makes a lot of sense. Can it cause issues with my HC-05 if I tried to run one LED strip, the bluetooth, and the accelerometer? I pulled the USB to another power supply after this, but I feel like my HC-05 really started acting up after that.

Welp, I hadn't heard of interrupts before. Is there a way to enable interrupts? For the YouTube tutorial that I was looking at (link in the description), they are using Fast LED and buttons to change the aspects of the function. In their code, see that the FastLed.show() is at the end of the void loop, outside of the if statements. Can changing to something like this help? But with my function trying to run several strips, I feel like this would cause issues.

As it should be. During an animation (sequence) one must call the show() function as often as possible.

One way to deal with the interrupt problem is to, periodically (between animations), suspend calling show(), send a message (handshake0 to the sender to request data, read the data and resume with show().

Or, use a hardware serial port. Uno/Nano, you have to learn to do without Serial Monitor (there are tricks you can play, but that's mostly necessary). If you have extra pins, you could add an I2C LCD to give you back some debugging messages, if you want.

Are you sure? I, honestly, don't know if that would work.
Hardware serial is still using interrupts and ISRs disable all interrupts, not just pin change interrupts.

That would simplify my banner project. If I have time tomorrow I will modify the banner board and try it.

Sort of. I was thinking of one or two char commands, because the UART has an internal buffer that will retain, IIRC, two chars. So while interrupts aren't working, a command could still be received successfully, and I think the serial interrupt will be asserted as soon as enabled. So context and design will be important.
Thing is, the UART hardware does it's thing for incoming characters without any interrupts. It's only when a full char is available that an interrupt is used to move that char from the internal register to the software buffer. Additionally, if you read the datasheet, a second char can be received while the UART is waiting to have the first char removed from the internal register, hence my "two char" comment.
Contrast that with software serial, which, as I understand it, requires interrupt capability for per-bit 'recording' of the incoming bit sense. Much more interrupt-sensitive.

Okay that makes sense. This project was for a deadline in October, but that fell through. I haven't had time to work on the coding, as I was focusing on the other parts of the project. I think I vaguely understand the char commands that camsysca suggested. I'll do more research, and update everything, because I do want to make the final working code available. However, I won't be able to work on it for a while due to work obligations. Thank you everyone!

I take it you are unaware there are 6 channel remote control relays out there for around $10...????

I know of them yes, thank you. However, as far as I know, it won't work for my application. This is a very specific setup. Space, power, and controller setup are very specific.