Friends, I am stuck! And I would owe a favor or a dinner or

fastled-support
gplus
(Chris Iannello) #1

Friends, I am stuck!

And I would owe a favor or a dinner or my eternal gratitude if you an solve it without me having to use 4 separate esp8266s which will solve the problem but will make it harder to synchronize the light show.

Summary:
Issue is will multiple strings on a single ESP8266 with Asyncwebserver and IOTAppStory vs a single string of the same number of LEDS on the same ESP8266. Something to do with multiple strings running same time. Symptom is flickering performance problems toward end of string. As bad or worse on short strings (short in terms of numbers of wires but there are cat5 cables used to extend ground, clock, and data). If I shut off all but one string by commenting out the FastLED.addleds lines for the other strings and the subsequent references in the loop)…when I do this problem solved on any string no matter how long. I can command it to color with fill_solid by web interface and animations work although frame rate of course faster on short strings. Is this some EMI issue or a FastLED code base issue?

Details:

I am building a home theaters which brings 4 series strings of APA102C to an ESP8266 NodeMCU board (2160 LEDS in ceiling coffers, 1090 on ceiling perimeter, 288 in vumeter strips on either side of screen, and 200 more as spot lights on various speakers in room. I run these are 4 long serial chains into 4 pins on a single esp8266 one at a time along with clock and ground and its rock solid. Have them all on the esp8266 at same time and end leds flicker with odd colors.

Power is not an issue and is fed into both end of the string with more than 1kw of total 5V power available.

Again, I can run any string individually but running 4 strings on 4 pins and sharing clock becomes and issue. 5 wires total…4 data lines on separate pins, a shared clock on one esp pin, and the esp8266 GND pin.

Problem there at 32 for global brightness but seems a less pronounced (very little flicker) at 255 and never an issue when I send all LEDS to solid blue.

Also, I am trying to show a single string a solid color then turn off show to that string but that doesnt seem to work, that string still flickers so I am not doing that right. I dont need to animate all strings at once so if I can just manage it so only one line at a time is bit banging that would work but cant seem to solve it that way either.

Before anyone asks, I dont have a logic level converter but that doesnt seem to bother system when single strings on esp8266. A simplified version of the code is below. Help!!!

#include <IOTAppStory.h>
#if defined ESP8266
#include <ESPAsyncTCP.h> // https://github.com/me-no-dev/ESPAsyncTCP #elif defined ESP32
#include <AsyncTCP.h> // https://github.com/me-no-dev/AsyncTCP
#include <SPIFFS.h>
#endif
#include <ESPAsyncWebServer.h> // https://github.com/me-no-dev/ESPAsyncWebServer #include <FS.h>
#include “FastLED.h”

#define COMPDATE DATE TIME
#define MODEBUTTON 0 // Button pin on the esp for selecting modes. D3 for the Wemos!
#define FASTLED_ESP8266_NODEMCU_PIN_ORDER

FASTLED_USING_NAMESPACE

#define DATA_PIN_COFFERS 7
#define DATA_PIN_PERIMETER 1
#define DATA_PIN_SPEAKERS 8
#define DATA_PIN_VUMETER 6
#define CLK_PIN 5
#define LED_TYPE APA102
#define COLOR_ORDER BGR

#define NUM_LEDS_COFFERS 2160
#define NUM_LEDS_PERIMETER 1296
#define NUM_LEDS_SPEAKERS 288
#define NUM_LEDS_VUMETER 288
#define BRIGHTNESS 64
#define FRAMES_PER_SECOND 30
CRGB leds_coffers[NUM_LEDS_COFFERS];
CRGB leds_perimeter[NUM_LEDS_PERIMETER];
CRGB leds_speakers[NUM_LEDS_SPEAKERS];
CRGB leds_vumeter[NUM_LEDS_VUMETER];
bool gReverseDirection = false;

// IotAppStory.com library
IOTAppStory IAS(COMPDATE, MODEBUTTON); // Initialize IotAppStory
AsyncWebServer server(80); // Initialize AsyncWebServer
const char* PARAM_MESSAGE = “message”;
const char* PARAM_COFFERSTATE = “cofferstate”;
const char* PARAM_PERIMETERSTATE = “perimeterstate”;
const char* PARAM_SPEAKERSSTATE = “speakersstate”;
const char* PARAM_VUMETERSTATE = “vumeterstate”;
const char* PARAM_BRIGHTNESS = “brightness”;
//called when the url is not defined here return 404

void notFound(AsyncWebServerRequest *request) {
request->send(404, “text/plain”, “Not found”);
}

// ================================================ EXAMPLE VARS =========================================
// List of patterns to cycle through. Each is defined as a separate function below.
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = {rainbow,rainbowWithGlitter,sinelon,red,breatheclearbluesky,};
uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current
uint8_t gHue = 0; // rotating “base color” used by many of the patterns
int cofferstate=0;
int perimeterstate=0;
int speakersstate=0;
int vumeterstate=0;
int brightness=0;
int disableshow=0;

// ================================================ SETUP ================================================
void setup() {

IAS.preSetDeviceName(“hometheater”); // preset deviceName this is also your MDNS responder: http://virginsoil.local

IAS.preSetWifi(“pezziannello”,“pezziannello”); // preset Wifi

IAS.onModeButtonShortPress( {
Serial.println(F(" If mode button is released, I will enter in firmware update mode."));
Serial.println(F("-----------------------------------------------------------------------"));
});

IAS.onModeButtonLongPress( {
Serial.println(F(" If mode button is released, I will enter in configuration mode."));
Serial.println(F("-----------------------------------------------------------------------"));
});

IAS.onModeButtonVeryLongPress( {
Serial.println(F(" If mode button is released, I won’t do anything unless you program me to."));
Serial.println(F("-----------------------------------------------------------------------"));

});

/*
IAS.onModeButtonNoPress( {
Serial.println(F(" Mode Button is not pressed."));
Serial.println(F("-----------------------------------------------------------------------"));
});

IAS.onFirstBoot( {
Serial.println(F(" Run or display something on the first time this app boots"));
Serial.println(F("-----------------------------------------------------------------------"));
});

IAS.onFirmwareUpdateCheck( {
Serial.println(F(" Checking if there is a firmware update available."));
Serial.println(F("-----------------------------------------------------------------------"));
});

IAS.onFirmwareUpdateDownload( {
Serial.println(F(" Downloading and Installing firmware update."));
Serial.println(F("-----------------------------------------------------------------------"));
});

IAS.onFirmwareUpdateError( {
Serial.println(F(" Update failed…Check your logs"));
Serial.println(F("-----------------------------------------------------------------------"));
});

IAS.onConfigMode( {
Serial.println(F(" Starting configuration mode. Search for my WiFi and connect to 192.168.4.1."));
Serial.println(F("-----------------------------------------------------------------------"));
});
*/

IAS.begin(‘P’); // Optional parameter: What to do with EEPROM on First boot of the app? ‘F’ Fully erase | ‘P’ Partial erase(default) | ‘L’ Leave intact

IAS.setCallHome(false); // Set to true to enable calling home frequently (disabled by default)
// IAS.setCallHomeInterval(60); // Call home interval in seconds, use 60s only for development. Please change it to at least 2 hours in production

//------ Your Setup starts from here -------------
FastLED.addLeds<LED_TYPE,DATA_PIN_COFFERS,CLK_PIN,COLOR_ORDER,DATA_RATE_MHZ(2)>(leds_coffers, NUM_LEDS_COFFERS).setCorrection(TypicalSMD5050);
FastLED.addLeds<LED_TYPE,DATA_PIN_PERIMETER,CLK_PIN,COLOR_ORDER,DATA_RATE_MHZ(2)>(leds_perimeter, NUM_LEDS_PERIMETER).setCorrection(TypicalSMD5050);
FastLED.addLeds<LED_TYPE,DATA_PIN_SPEAKERS,CLK_PIN,COLOR_ORDER,DATA_RATE_MHZ(2)>(leds_speakers, NUM_LEDS_SPEAKERS).setCorrection(TypicalSMD5050);
FastLED.addLeds<LED_TYPE,DATA_PIN_VUMETER,CLK_PIN,COLOR_ORDER>(leds_vumeter, NUM_LEDS_VUMETER).setCorrection(TypicalSMD5050);
// set master brightness control
FastLED.setBrightness(BRIGHTNESS);
//FastLED.setDither( 0 );

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){

    request->send(200, "text/plain", "Hello, world1");
    nextPattern(); 
});



 server.on("/disableshow", HTTP_GET, [] (AsyncWebServerRequest *request) {
    disableshow=1;
    if(disableshow) {request->send(200, "text/plain", "Show Disabled");}
});

 server.on("/enableshow", HTTP_GET, [] (AsyncWebServerRequest *request) {
    disableshow=0;
    request->send(200, "text/plain", "Show Enabled");
});

// Send a GET request to /?message=
server.on("/getx", HTTP_GET, [] (AsyncWebServerRequest *request) {

    if (request->hasParam(PARAM_COFFERSTATE)) {
        
        AsyncWebParameter* p = request->getParam(PARAM_COFFERSTATE);
         Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
        
         cofferstate=p->value().toInt();
    } 
    
    if (request->hasParam(PARAM_BRIGHTNESS)) {
       
        AsyncWebParameter* p = request->getParam(PARAM_BRIGHTNESS);
         Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
         brightness=p->value().toInt();
         FastLED.setBrightness(brightness);
    }     
     if (request->hasParam(PARAM_PERIMETERSTATE)) {
         AsyncWebParameter* p = request->getParam(PARAM_PERIMETERSTATE);
         Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
         perimeterstate=p->value().toInt();
         
    }     

    if (request->hasParam(PARAM_VUMETERSTATE)) {
         AsyncWebParameter* p = request->getParam(PARAM_VUMETERSTATE);
         Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
         vumeterstate=p->value().toInt();
         
    }     

   if (request->hasParam(PARAM_SPEAKERSSTATE)) {
         AsyncWebParameter* p = request->getParam(PARAM_SPEAKERSSTATE);
         Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
         speakersstate=p->value().toInt();
         
    }     
    request->send(200, "text/plain", "Hello, GETx: " );
});

server.onNotFound(notFound);

server.begin();

}

// ================================================ LOOP =================================================
void loop() {
IAS.loop(); // this routine handles the calling home functionality and reaction of the MODEBUTTON pin. If short press (<4 sec): update of sketch, long press (>7 sec): Configuration

//------ Your Sketch starts from here -------------
// Call the current pattern function once, updating the ‘leds’ array

//fill_solid( leds_coffers, 500, CRGB::RED);

switch (cofferstate) {
case 0:
fill_solid( leds_coffers, NUM_LEDS_COFFERS, CRGB(ClearBlueSky));// statements
break;
case 1:
fill_solid( leds_coffers, NUM_LEDS_COFFERS,CRGB(Tungsten100W));// statements
break;
case 2:
fill_solid( leds_coffers, NUM_LEDS_COFFERS,CRGB(Candle));// statements
break;
case 7:
fill_solid( leds_perimeter, NUM_LEDS_PERIMETER, CRGB::Black);// statements
break;
case 8:
fadeToBlackBy( leds_coffers, NUM_LEDS_COFFERS, 10);
break;
case 9:
gPatternsgCurrentPatternNumber;// statements
break;
default:
// statements
break;
}
// Serial.println(perimeterstate);

switch (perimeterstate) {
case 0:
fill_solid( leds_perimeter, NUM_LEDS_PERIMETER, CRGB(ClearBlueSky));// statements
break;
case 1:
fill_solid( leds_perimeter, NUM_LEDS_PERIMETER,CRGB(Tungsten100W));// statements
break;
case 2:
fill_solid( leds_perimeter, NUM_LEDS_PERIMETER,CRGB(Candle));// statements
break;
case 7:
fill_solid( leds_perimeter, NUM_LEDS_PERIMETER, CRGB::Black);// statements
break;
case 8:
fadeToBlackBy( leds_perimeter, NUM_LEDS_PERIMETER, 10);
break;
case 9:
gPatternsgCurrentPatternNumber;// statements
break;
}

switch (vumeterstate) {
case 0:
fill_solid( leds_vumeter, NUM_LEDS_VUMETER, CRGB(ClearBlueSky));// statements
break;
case 1:
fill_solid( leds_vumeter, NUM_LEDS_VUMETER,CRGB(Tungsten100W));// statements
break;
case 2:
fill_solid( leds_vumeter, NUM_LEDS_VUMETER,CRGB(Candle));// statements
break;
case 6:

random16_add_entropy( rand());
Fire2012(); // run simulation frame
//FastLED[3].showLeds(brightness);
break;

case 7:
fill_solid( leds_vumeter, NUM_LEDS_VUMETER, CRGB::Black);// statements
break;
case 8:
fadeToBlackBy( leds_vumeter, NUM_LEDS_VUMETER, 10);
break;
case 9:
gPatternsgCurrentPatternNumber;// statements
break;
}

switch (speakersstate) {
case 0:
fill_solid( leds_speakers, NUM_LEDS_SPEAKERS, CRGB(ClearBlueSky));// statements
break;
case 1:
fill_solid( leds_speakers, NUM_LEDS_SPEAKERS,CRGB(Tungsten100W));// statements
break;
case 2:
fill_solid( leds_speakers, NUM_LEDS_SPEAKERS,CRGB(Candle));// statements
break;
case 7:
fill_solid( leds_perimeter, NUM_LEDS_PERIMETER, CRGB::Black);// statements
break;
case 8:
fadeToBlackBy( leds_speakers, NUM_LEDS_SPEAKERS, 10);
break;
case 9:
gPatternsgCurrentPatternNumber;// statements
break;
}

// send the ‘leds’ array out to the actual LED strip
if(!disableshow){
FastLED.show();
}

if(disableshow){
FastLED[3].showLeds(32);
}

//insert a delay to keep the framerate modest
FastLED.delay(1000/FRAMES_PER_SECOND);

// do some periodic updates
//EVERY_N_MILLISECONDS( 20 ) { gHue++; } // slowly cycle the “base color” through the rainbow
//EVERY_N_SECONDS( 10 ) { nextPattern(); } // change patterns periodically

}

(Yves BAZIN) #2

@Chris_Iannello before #include ‘fasted.h’
Add this line
#define FASTLED_ALLOW_INTERRUPTS 0
This line
#define FASTLED_ESP8266_NODEMCU_PIN_ORDER should be before #include ‘fastled.h’
I hope this will help

(Jeremy Spencer) #3

I’ve used 4 strings of APA102Cs on an ESP8266 set up like this

FastLED.addLeds<APA102,5,7>(leds[0],NUM_LEDS);
FastLED.addLeds<APA102,5,8>(leds[1],NUM_LEDS);
FastLED.addLeds<APA102,6,7>(leds[2],NUM_LEDS);
FastLED.addLeds<APA102,6,8>(leds[3],NUM_LEDS);

(Chris Iannello) #4

@Yves_BAZIN Thanks for his idea, I will try tonight. But question…since I use the Async webserver library, will turning interrupts off disable it? thats how the home theater controls the lights. Does that value “0” only shut off interrupts during a show portion of the loop?

(Chris Iannello) #5

@Jeremy_Spencer Thanks I ill try that controller setup line instead…mine is like this…so I think its about the same but I’ll use the array notation to see if it helps.

FastLED.addLeds<LED_TYPE,DATA_PIN_COFFERS,CLK_PIN,COLOR_ORDER,DATA_RATE_MHZ(2)>(leds_coffers, NUM_LEDS_COFFERS).setCorrection(TypicalSMD5050);

(Jeremy Spencer) #6

@Chris_Iannello it’s not the setup line, it’s the pins I used to drive the strips. Some of the esp8266 pins have unexpected results…

(Chris Iannello) #7

@Jeremy_Spencer Ahh I see and I see you have 2 pins dedicated as clock where I only have one for all 4…this will help for sure…didnt realize I could define more than one clock. If all else fails I can only show the string that is running and that will turn off clocks to other strings I bet.