Neopixel and WS2812B - unable to control with higher led count

Hello,

Disclaimer: this is my first project using a controller with LED strip and C++ so please bare with me.

The plan is to have a long WS2812B addressable LED strip split into two parts using Adafruit_NeoPixel library.
It is controlled via Wemos D1 controller, which runs a webserver allowing me to control the lights via a web requests.

Here's a schematic of the wiring

Here's the code that my controller runs:

// load led lib
#include <Adafruit_NeoPixel.h>
// Load Wi-Fi library
#include <ESP8266WiFi.h>
// load filesystem lib 
#include <FS.h>
// define web server
#include <ESP8266WebServer.h>

// Define led strip
#define LED_COUNT1 75
#define LED_COUNT2 75
#define PIN D4
Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(LED_COUNT1, PIN, NEO_GRB | NEO_KHZ800);
Adafruit_NeoPixel strip2 = Adafruit_NeoPixel(LED_COUNT2, PIN, NEO_GRB | NEO_KHZ800);

// wifi info
const char* ssid     = "my_wifi_name";
const char* password = "my_wifi_password";

// start webserver I guess?
ESP8266WebServer server;

// STATE
// strip 1
bool state_on = false;
uint16_t state_hue = 65536/6;
uint8_t state_saturation = 255;
uint8_t state_value = 255;
// strip 2
bool state_on2 = false;
uint16_t state_hue2 = 65536/6;
uint8_t state_saturation2 = 255;
uint8_t state_value2 = 255;

String boolToString(bool val) {
  if (val == false) {
    return "false";
  } else {
    return "true";
  }
}

bool stringToBool(String val) {
  if (val == "false") {
    return false;
  } else {
    return true;
  }
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH); // TODO set off by default
  Serial.begin(115200);
  SPIFFS.begin();
  
  // setup strip 1
  strip1.begin();
  strip1.setBrightness(255);
  strip1.fill(strip1.Color(0, 0, 0), 0);
  strip1.show();

  // setup strip 2
  strip2.begin();
  strip2.setBrightness(255);
  strip2.fill(strip2.Color(0, 0, 0), 0);
  strip2.show();
  
  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // SERVER ROUTES
  server.on("/", HTTP_GET, serveIndexFile);
  server.on("/getLightState", HTTP_GET, getLightState);
  server.on("/light/switch", HTTP_GET, setLightOnState);
  server.on("/light/color", HTTP_POST, setStripColor);
  server.begin();
}

  // RETURN JSON
void getLightState()
{
  String state = "{\"on\":" + boolToString(state_on) + ", \"hue\": " + state_hue + ", \"saturation\": " + state_saturation + ", \"value\": " + state_value 
  + ", \"on2\":" + boolToString(state_on2) + ", \"hue2\": " + state_hue2 + ", \"saturation2\": " + state_saturation2 + ", \"value2\": " + state_value2 + "}";
  server.send(200,"text/plain", state);
}

void serveIndexFile()
{
  File file = SPIFFS.open("/index.html","r");
  server.streamFile(file, "text/html");
  file.close();
}

void fillAndShowStrips () {
  strip1.fill(strip1.gamma32(strip1.ColorHSV(state_hue, state_saturation, state_on ? state_value : 0)));
  strip1.show();
  
  strip2.fill(strip2.gamma32(strip2.ColorHSV(state_hue2, state_saturation2, state_on2 ? state_value2 : 0)));
  strip2.show();
}

void setLightOnState() {
  bool firstOn = stringToBool((server.arg("on").c_str()));
  bool secondOn = stringToBool((server.arg("on2").c_str()));
  state_on = firstOn;
  state_on2 = secondOn;

  fillAndShowStrips();
  server.send(200);
}

void setStripColor () {
  // set strip 1
  int newHue = std::atoi (server.arg("hue").c_str());
  int newSaturation = std::atoi (server.arg("saturation").c_str());
  int newValue = std::atoi (server.arg("value").c_str());
  state_hue = newHue;
  state_saturation = newSaturation;
  state_value = newValue;

  // set strip 2
  int newHue2 = std::atoi (server.arg("hue2").c_str());
  int newSaturation2 = std::atoi (server.arg("saturation2").c_str());
  int newValue2 = std::atoi (server.arg("value2").c_str());
  state_hue2 = newHue2;
  state_saturation2 = newSaturation2;
  state_value2 = newValue2;

  fillAndShowStrips();
  server.send(200);
}

void loop(){
  server.handleClient();
}

The problem:
For development I've used shorter 1m LED strip and everything worked flawlessly.
For the final product, I've connected the 5m LED strip (from a different brand) and set LED strip1/strip2 to be 75 LEDs each. In the code that is:

#define LED_COUNT1 75
#define LED_COUNT2 75

Unfortunately, only strip2 reacts to changes and it is displayed from 1st to 75th LED. Leaving the other half of the LED strip off.

If I set strip1/strip2 to be 1/149 LEDs, then again, first 149 light up and react to strip2 changes, and the last LED in the strip remains off.

Here's the interesting part - if I set the strip1/strip2 to be a low amount of LEDs, let's say 20/20, then it all works completely fine.

Since everything works completely fine with "small" strip1/strip2, I would guess that the problem is something with the hardware? Damaged data input?

But then if I set strips to 1/149 I can controll all but the last LED just fine. That kinda makes it sound like the data input is fine?

You seem to be using the same pin for each strip? If you are driving two strips each one needs its own data pin, these need to be different pins.

If you want to use one long strip and split it up into two sections you don't go about it like that. You define one strip in the software and separate the two displays by using a different set of LED numbers in the software. So for a 150 LED strip, you use LED numbers 0 to 74 for the first virtual strip of your display and 75 to 149 for the second.

Your schematic shows only one strip?

I would guess that the problem is something with the hardware? Damaged data input?

You don't use any data inputs. Do you mean outputs?

Grumpy_Mike:
You seem to be using the same pin for each strip? If you are driving two strips each one needs its own data pin, these need to be different pins.

If you want to use one long strip and split it up into two sections you don't go about it like that. You define one strip in the software and separate the two displays by using a different set of LED numbers in the software. So for a 150 LED strip, you use LED numbers 0 to 74 for the first virtual strip of your display and 75 to 149 for the second.

Your schematic shows only one strip?
You don't use any data inputs. Do you mean outputs?

You're correct, I have a single physical strip that I'm dividing into two sections. Sorry if I was not clear.
And the physical strip is driven with a single data pin.

So then the following syntax is for two separate physical strips driven by two data pins?

Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(LED_COUNT1, PIN, NEO_GRB | NEO_KHZ800);
Adafruit_NeoPixel strip2 = Adafruit_NeoPixel(LED_COUNT2, PIN, NEO_GRB | NEO_KHZ800);

It's weird that this setup works with LED_COUNT1 and LED_COUNT2 set to something low in my setup with single data pin and single physical strip.

I'll try addressing the sections of the strip in some other way rather than using Adafruit_NeoPixel function. Thanks!

Will update with the results.

Yes, that solved the issue!

I defined the whole strip as

Adafruit_NeoPixel ledStrip = Adafruit_NeoPixel(150, PIN, NEO_GRB | NEO_KHZ800);

And then in the update function I update the two sections separately

void fillAndShowStrips () {
  for (int i=0; i < LED_COUNT1; i=i+1) {
    ledStrip.setPixelColor(i, ledStrip.gamma32(ledStrip.ColorHSV(state_hue, state_saturation, state_on ? state_value : 0)));
  }
  for (int i=LED_COUNT1; i < LED_COUNT2 + LED_COUNT1; i=i+1) {
    ledStrip.setPixelColor(i, ledStrip.gamma32(ledStrip.ColorHSV(state_hue2, state_saturation2, state_on2 ? state_value2 : 0)));
  }
  ledStrip.show();
}

Thanks again for the help!

Glad you got it going, well done.

It's weird that this setup works with LED_COUNT1 and LED_COUNT2 set to something low in my setup with single data pin and single physical strip.

I think if you did a bit more rigorous testing you would have found out that in fact it didn’t work. I think your tests were not sufficient to show any problems.

Weirdly enough, it did.
I have a simple web app made to make requests to the server which in turn sets the colors and on/off state of the strips.

Using this I was able to set different colors and turn on/off the sections of the strip separately. And I played with it quite a bit before connecting the 5m strip which started causing problems.
It's super weird that my old setup worked at all, since it obviously was incorrect.

don’t want to turd on your parade, but i highly recommend checking out fastled web server. It comes with several built in patterned and you can edit the code over the web.

Weirdly enough, it did.

The problem is that you have too much software in that chain of control that is in the form of libraries or code that you did not write. Therefore you don't know exactly what it is doing.

It is possible that some software, somewhere along the line, saw that the same pin was assigned for each strip and decided to be "smart" and treat them as one strip split up into two regions.

It was not so "smart" however because, as you found, out it fails at longer strip lengths.