Independent control of three pixel strips with Serial Data using FastLED lib

Greetings!

I'll start off by saying that yes, I realize this topic has been covered ad nauseam all over these and other forums. Despite this, I'm struggling to figure out how to write the code that will provide the additional functionality that I am looking for.

The primary motivation behind this project is for me to learn how to program better. So while other people may have done this already, I would really like to understand how they did it, or rather how their code works or how to modify my code to make it work in a similar fashion.

Anyway, here goes, I'll try to be as clear as possible.

Currently, my code is able to:

  1. Drive three strips of WS2811 neopixels, with 50 leds in each string, and each strip connected to its own output pin on an Arduino Uno.

  2. Control each strip by command via serial monitor - limited to solid color on/off essentially, using single byte commands that are defined individually for each strip in the readData() function used to read and interpret the serial data

  3. each strip can be turned on or off independently of the others

Desired functionality to add:

  1. allow turning on or off simple animations for each strip, independently of the others
  2. write the code that defines commands and their actions more efficiently, ideally not being limited to crude single letter commands

Eventually this project will be part of a small network of devices that will be controlled via local flask webserver running on a raspberry pi. Not sure that matters for the purposes of this discussion, but included nonetheless..

I've tried reading in more than one byte of serial data at a time in previous versions of the included code, however I was never able to get it to light up more than one strip at a time, and the code was always blocking at some level it seemed.

Reading the FastLED documentation, it seems that even though using an Arduino to do this is not ideal, it can be done. And i really, really, want to understand how to go about writing non blocking code to accomplish this. Any nudges in the right direction etc would be tremendously appreciated!!

// sketch using FastLED library to control three strips of WS2811 leds via Serial communication using three pins on an Arduino Uno
// currently, the sketch reads a single byte of data from the serial monitor, turns on the specified strip to a given color based on definitions in readData() function
// goals:
// *** to be able to read more than one byte of data at a time (ie instead of writing out a specific action for 'A', to be able to send A=123 and that would define the strip, the color, the animation etc
// *** to be able to run simple animations SIMULTANEOUSLY on each strip of leds independently - as in figure out a way to write non blocking code that allows strip1 to run chaser dot animation, while also being able to send command to start strip2 rainbow color cycle

#include <FastLED.h>

#define LED_TYPE WS2811 // pixel  type
#define NUM_STRIPS 3 // how many independent pixel strips/strings there are
#define NUM_LEDS 50 // number of leds per strip
#define STRIP_1 6 //strip1 pin
#define STRIP_2 5 //strip2 pin
#define STRIP_3 3 //strip3 pin
#define COLOR_ORDER RGB // pixel color order
#define BRIGHTNESS 255

//CRBGset for each strip
CRGBArray<NUM_LEDS>strip1; 
CRGBArray<NUM_LEDS>strip2;
CRGBArray<NUM_LEDS>strip3;

int incomingByte; // assign variable that will hold incoming serial data

int half = NUM_LEDS/2; // included to make typing out the range for a half strip of leds easier
int total = NUM_LEDS; // same as above

void setup() {

  Serial.begin(9600); //start serial communication
  FastLED.addLeds<LED_TYPE,STRIP_1,COLOR_ORDER>(strip1,NUM_LEDS); // tell sketch which pin strip1 is at etc
  FastLED.addLeds<LED_TYPE,STRIP_2,COLOR_ORDER>(strip2,NUM_LEDS); // same as above for strip2
  FastLED.addLeds<LED_TYPE,STRIP_3,COLOR_ORDER>(strip3,NUM_LEDS); // same as above for strip3
  FastLED.setBrightness(BRIGHTNESS);
  testPixels(); // startup sequence
  Serial.println("Ready."); // print "Ready" to serial monitor so we know setup is complete and we can send commands via serial monitor now
}

void loop() {
    readData(); // function for reading incoming byte of serial data, and the definitions for what to do based on what is sent
}

void testPixels() { // basic chaser dot startup sequence that goes through each strip sequentially
    for (int y=0;y<NUM_LEDS;y++) {
      strip1[y] = CRGB::Red; // red dot chaser for strip1
      FastLED.show();
      strip1[y] = CRGB::Black;
      delay(25);
    }
    for (int z=0;z<NUM_LEDS;z++) {
      strip2[z] = CRGB::Green; //green dot chaser for strip2
      FastLED.show();
      strip2[z] = CRGB::Black;
      delay(25);
    }
    for (int x=0;x<NUM_LEDS;x++) {
      strip3[x] = CRGB::Blue; //blue dot chaser for strip3
      FastLED.show();
      strip3[x] = CRGB::Black;
      delay(25);
    }
  FastLED.clear(true); //turn off each strip once sequence is complete
}

void readData() { 
  if (Serial.available() > 0) { // check if serial data is available
    incomingByte = Serial.read(); // set variable incomingByte to hold one byte of incoming serial data
    if (incomingByte == 'a') {  
      strip1.fill_solid(0x000000); //turn off strip1
    }
    if (incomingByte == 'b') {
      strip2.fill_solid(0x000000);  //turn off strip2
    }
    if (incomingByte == 'c') { 
      strip3.fill_solid(0x000000); //turn off strip3
    }
    if (incomingByte == 'A') { 
      strip1.fill_solid(0xFF0000); //strip1 red     
    }
    if (incomingByte == 'B') { 
      strip2.fill_solid(0xFF0000); //strip2 red
    }
    if (incomingByte == 'C') {
      strip3.fill_solid(0xFF0000); //strip3 red
    }
    if (incomingByte == 'D') {
      strip1.fill_solid(0x0033FF); //strip1 blue
    }
    if (incomingByte == 'E') {
      strip2.fill_solid(0x0033FF); //strip2 blue
    }
    if (incomingByte == 'F') {
      strip3.fill_solid(0x0033FF); //strip3 blue
    }
    if (incomingByte == 'G') {
      strip1(0,half-1).fill_solid(0x0033FF);      // first half of strip1 blue
      strip1(half,total-1).fill_solid(0xFF0011);  // second half of strip1 pink
    }
    if (incomingByte == 'H') {
      strip2(0,half-1).fill_solid(0x0033FF);      // first half of strip2 blue
      strip2(half,total-1).fill_solid(0xFF0011);  // second half of strip2 pink
    }
    if (incomingByte == 'I') {
      strip3(0,half-1).fill_solid(0x0033FF);      // first half of strip3 blue
      strip3(half,total-1).fill_solid(0xFF0011);  // second half of strip3 pink
    }
    if (incomingByte == 'J') {
      strip1(0,half-1).fill_solid(0xFF7700);      // first half strip1 orange
      strip1(half,total-1).fill_solid(0xFF0000);  // second half strip1 red
    }
    if (incomingByte == 'K') {
      strip2(0,half-1).fill_solid(0xFF7700);      // first half strip2 orange
      strip2(half,total-1).fill_solid(0xFF0000);  // second half strip2 red
    }
    if (incomingByte == 'L') {
      strip3(0,half-1).fill_solid(0xFF7700);      // first half strip3 orange
      strip3(half,total-1).fill_solid(0xFF0000);  // second half strip3 red
    }
    FastLED.show();
  }
}

Additional notes:

  • Each pixel strip is connected to a power supply with way more than enough juice, so power is not an issue (grounds all tied together incl arduino etc.)
  • The arduino is connected to PC via serial USB, no additional power being supplied to it.

Happy to provide further information. Thanks!