Need advice for proposed project approach (led light show)

This is the first question I need answered in order to start on my led light show project:
Can the commands in this sketch be executed on multiple devices via wireless rather than a push button on the device? If not, is there another sketch or project that can do that?

In a nut shell, can a wireless controller execute the same command on 25 different devices?

Hardware:
WS2812B LED Strip DC 5V
Arduino mini or nano
NRF24L01+ 2.4GHz radio or ESP8266 radio

(I would use a strip rather than a color wheel.)

// This is a demonstration on how to use an input device to trigger changes on your neo pixels.
// You should wire a momentary push button to connect from ground to a digital IO pin.  When you
// press the button it will change to a new pixel animation.  Note that you need to press the
// button once to start the first animation!

#include <Adafruit_NeoPixel.h>

#define BUTTON_PIN   5    // Digital IO pin connected to the button.  This will be
                          // driven with a pull-up resistor so the switch should
                          // pull the pin to ground momentarily.  On a high -> low
                          // transition the button press logic will execute.

#define PIXEL_PIN    4    // Digital IO pin connected to the NeoPixels.

#define PIXEL_COUNT 16

// Parameter 1 = number of pixels in strip,  neopixel stick has 8
// Parameter 2 = pin number (most are valid)
// Parameter 4 = pixel type flags, add together as needed:
//   NEO_RGB     Pixels are wired for RGB bitstream
//   NEO_GRB     Pixels are wired for GRB bitstream, correct for neopixel stick
//   NEO_KHZ400  400 KHz bitstream (e.g. FLORA pixels)
//   NEO_KHZ800  800 KHz bitstream (e.g. High Density LED strip), correct for neopixel stick
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);

bool oldState = HIGH;
int showType = 0;

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  digitalWrite(13, HIGH);
}

void loop() {
  // Get current button state.
  bool newState = digitalRead(BUTTON_PIN);

  // Check if state changed from high to low (button press).
  if (newState == LOW && oldState == HIGH) {
    // Short delay to debounce button.
    delay(20);
    // Check if button is still low after debounce.
    newState = digitalRead(BUTTON_PIN);
    if (newState == LOW) {
      showType++;
      if (showType > 4)
        showType=0;
      startShow(showType);
    }
  }

  // Set the last button state to the old state.
  oldState = newState;
}

void startShow(int i) {
  switch(i){
    case 0: colorWipe(strip.Color(0, 0, 0), 50);    // Black/off
            break;
    case 1: colorWipe(strip.Color(0, 43, 0), 50);  // Green
            break;
    case 2: colorWipe(strip.Color(34, 34, 0), 50);  // Yellow
            break;
    case 3: colorWipe(strip.Color(43, 0, 0), 50);  // Red
            break;
    case 4:
          theaterChase(strip.Color(127,   0,   0), 450); // Red
          break;
    
  }
}

// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}

void rainbow(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
    for(i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
  for (int j=0; j<10; j++) {  //do 10 cycles of chasing
    for (int q=0; q < 3; q++) {
      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, c);    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
  for (int j=0; j < 256; j++) {     // cycle all 256 colors in the wheel
    for (int q=0; q < 3; q++) {
      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, Wheel( (i+j) % 255));    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

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

In a nut shell, can a wireless controller execute the same command on 25 different devices?

"In a nut shell" - yes

Reality might be more complicated. You can use any number of wireless methods to send data. It appears you want to send a command packet which should be relatively easy with any wireless module; the NRF24L01+ 2.4GHz radio might be easier because every Arduino can receive the command at once. The trickier part I think you might find is synchronising the Arudinos. What happens if every Arduino in your display is out of sync by +- 5ms, maybe 10ms maybe more ms? Does that effect your result?

If not then try wireless. If it does matter to you then I would suggest using a wired bus approach that will allow you to send the data to each Arduino and then toggle a synchronised input that changes at the same time for all of them. This should in theory synchronise your display.

Once you've decided what you are using then the first step is to confirm that you can send/receive your command data. This will be dependant on your choice in communication method. I suggest as a very starting point, join 2 Arduinos together via i2c and see if one can tell the other what to do. Then build from there. You'll find many tutorials on this - https://www.arduino.cc/en/Tutorial/MasterWriter

Good Luck!

I suspect that you have to be very careful.

Addressable led strips are timing sensitive and the library might disable interrupts (not sure about the adafruit one, fastled does) to achieve uninterrupted transfer of the led data through the strip.

My experience with fastled and serial comms is that you can not send long messages in one go. You have to send one byte to the receiver, wait till it is echoed back by the receiver to the sender and only then send the next byte.

Thank you for your replies!! Your answers got me pointed in the right direction to find the answers I am looking for. I found immediate success!! Although, short of what I need, this tutorial implemented exactly what I want to accomplish. I just need to figure out how to push the changes out to 25 devices (arduino nano, NRF24L01+ 2.4GHz radio, and WS2812B LED Strip). I need to have about 10-15 light scenes that will display across the 25 violin bows during a youth strings concert.

https://www.brainy-bits.com/how-to-use-the-nrf24l01-2-4ghz-wireless-module/

Thanks again!

That sounds like an awesome application and I hope you get it working! If you get the opportunity then please post the results.

sterretje:
My experience with fastled and serial comms is that you can not send long messages in one go. You have to send one byte to the receiver, wait till it is echoed back by the receiver to the sender and only then send the next byte.

I thought the hardware buffer of an ATmega328p is a bit bigger than that...?

Anyway, a single byte is enough for 256 unique commands, where one command can be for a complex pre-programmed sequence.

With interrupts disabled, data does not get moved from the RX register to the software RX buffer. So when the next byte arrives, it overwrites the previously received byte.

I have dinged around with things, off-and-on for a week now and have achieved some results near what I am hoping to accomplish for the led light show. The neopixel-receive sketch below utilizes FastLed and helped me get a basic understanding of wireless control of an LED strip. However, it is very basic and does not suit my needs

The neopixel-receive2 sketch is closer to what I want to achieve (for now) but I want each sequential press of the button to launch the next scene (up to 12-15 scenes). (Press once = all blue, press twice = theaterChase, press third time = colorWipe). This would be prototype 1. After successfully getting this working, I want to, next, enter a numeric number on a wireless controller to choose any of the 12-15 scenes, but that can wait for now.

I searched quite a bit but could not find a tutorial to accomplish what I am asking for (via wireless). I am able to accomplish my first request with a simple push button on the same control board attached to the LEDS.

Any guidance is greatly appreciated.

Neopixel-transmitter:

/*
Created by Yvan / https://Brainy-Bits.com

This code is in the public domain...
You can: copy it, use it, modify it, share it or just plain ignore it!
Thx!

*/

// NRF24L01 Module Tutorial - Code for Transmitter using Arduino NANO

//Include needed Libraries at beginning
#include "nRF24L01.h" //NRF24L01 library created by TMRh20 https://github.com/TMRh20/RF24
#include "RF24.h"
#include "SPI.h"
#include <Adafruit_NeoPixel.h>

#define SwitchPin 8 // Arcade switch is connected to Pin 8 on NANO
int SentMessage[1] = {000}; // Used to store value before being sent through the NRF24L01

RF24 radio(9,10); // NRF24L01 used SPI pins + Pin 9 and 10 on the NANO

const uint64_t pipe = 0xE6E6E6E6E6E6; // Needs to be the same for communicating between 2 NRF24L01 


void setup(void){

pinMode(SwitchPin, INPUT_PULLUP); // Define the arcade switch NANO pin as an Input using Internal Pullups
digitalWrite(SwitchPin,HIGH); // Set Pin to HIGH at beginning

radio.begin(); // Start the NRF24L01
radio.openWritingPipe(pipe); // Get NRF24L01 ready to transmit
}

void loop(void){

if (digitalRead(SwitchPin) == LOW){ // If Switch is Activated
SentMessage[0] = 111;
radio.write(SentMessage, 1); // Send value through NRF24L01
}
else {
SentMessage[0] = 000;
radio.write(SentMessage, 1);
}
}

Neopixel-receiver:

/* 
Created by Yvan / https://Brainy-Bits.com

This code is in the public domain...
You can: copy it, use it, modify it, share it or just plain ignore it!
Thx!

*/


// NRF24L01 Module Tutorial - Code for Receiver using Arduino UNO

//Include needed Libraries at beginning
#include "nRF24L01.h" // NRF24L01 library created by TMRh20 https://github.com/TMRh20/RF24
#include "RF24.h"
#include "SPI.h"
#include "FastLED.h" // FastLED library for WS2812 RGB Stick http://fastled.io/

#define NUM_LEDS 16 // Number of leds on stick
#define LED_PIN 8 // Digital In (DI) of RGB Stick connected to pin 8 of the UNO

CRGB leds[NUM_LEDS]; // FastLED Library Init

int ReceivedMessage[1] = {000}; // Used to store value received by the NRF24L01

RF24 radio(9,10); // NRF24L01 used SPI pins + Pin 9 and 10 on the UNO

const uint64_t pipe = 0xE6E6E6E6E6E6; // Needs to be the same for communicating between 2 NRF24L01 


void setup(void){

FastLED.addLeds<NEOPIXEL,LED_PIN>(leds, NUM_LEDS); // Setup FastLED Library
FastLED.clear(); // Clear the RGB Stick LEDs

// Light up starting LED's
for(int x = 0; x != NUM_LEDS; x++) {
leds[x] = CRGB::Red;
}

FastLED.setBrightness(50);
FastLED.show();

radio.begin(); // Start the NRF24L01

radio.openReadingPipe(1,pipe); // Get NRF24L01 ready to receive

radio.startListening(); // Listen to see if information received

pinMode(LED_PIN, OUTPUT); // Set RGB Stick UNO pin to an OUTPUT
}

void loop(void){

while (radio.available())
{
radio.read(ReceivedMessage, 1); // Read information from the NRF24L01

if (ReceivedMessage[0] == 111) // Indicates switch is pressed
{
for(int x = 0; x != NUM_LEDS; x++)
{
leds[x] = CRGB::Red;
FastLED.show();
}
}
else
{
for(int x = 0; x != NUM_LEDS; x++) 
{
leds[x] = CRGB::Green;
FastLED.show();
}
}
delay(10);
}
}

Neopixel-receiver2

#define PIXEL_PIN    8    // Digital IO pin connected to the NeoPixels.

#define PIXEL_COUNT 16
#include <Arduino.h>
#include <Servo.h>    //the library which helps us to control the servo motor
#include <SPI.h>      //the communication interface with the modem
#include "RF24.h"     //the library which helps us to control the radio modem
//#include "strandtest.h"
#include "Adafruit_NeoPixel.h"
RF24 radio(9, 10);     /*This object represents a modem connected to the Arduino.
                      Arguments 9 and 10 are a digital pin numbers to which signals
                      CE and CSN are connected.*/
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);

const byte address[6] = "00001";

int randomNum;
int lastRandomNum = -1;

void setup() {
  // These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
  // Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
  clock_prescale_set(clock_div_1);
#endif
  // END of Trinket-specific code.

  strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.show();            // Turn OFF all pixels ASAP
  strip.setBrightness(15); // Set BRIGHTNESS

  Serial.begin(9600);
  radio.begin();
  radio.openReadingPipe(0, address);
  radio.setPALevel(RF24_PA_MIN);
  radio.startListening();

  randomSeed(analogRead(0));
}
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}
void theaterChase(uint32_t c, uint8_t wait) {
  for (int j=0; j<10; j++) {  //do 10 cycles of chasing
    for (int q=0; q < 3; q++) {
      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, c);    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}
void loop() {

  while (randomNum == lastRandomNum)
  {
    randomNum = random(3);
  }
  if (radio.available()) {
    char text[32] = "";
    radio.read(&text, sizeof(text));
   
    if (randomNum == 0)
    {
   
      theaterChase(strip.Color(127,   0,   0), 450); // Red
    }
  
    else if (randomNum == 1)
    {
   
      theaterChase((255,100,100), 50);
    }
else if (randomNum == 2)
    {
   
      colorWipe(strip.Color(43, 0, 0), 50);  // Red
    }
  }
  
  lastRandomNum = randomNum;
}

I would suggest simplifying your issues first. Have you been successful in getting the 2 boards to communicate wireless? Can you send "1" from 1 board to the other? If this isn't working then the led code is totally irrelevant to the issues at hand.

If you have 1 single led and 1 single button with 2 arduinos and 2 wireless units then can you get a button press on 1 arduino to get the LED to turn on on the other?

Once you have mastered this then try something more complex like 3 button presses = flashing led on second arduino.

Sketch 1 with sketch 2 work for me, via wireless, with the press of a button. However, the change only lasts as long as the button is pressed. So, yes, I am able to get the two boards to talk to each other. Now, I am looking for guidance (docs, examples, etc) that show me how to make permanent changes with the press of a button, followed by another change with a second push of the button, and so-on.

Thank you for your reply.

Take a look at this tutorial for "state change" - https://www.arduino.cc/en/Tutorial/StateChangeDetection

You can capture the number of presses of the button and send this via your radio link to your other arduino. The receiving code would then activate a different state depending on the current value.

You'll also find this useful for the receiving arduino - it changes the led based on the message it receives;