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
#if defined ESP8266
#include
#include
#include
#endif
#include
#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
FastLED.addLeds
FastLED.addLeds
FastLED.addLeds
// 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
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:
gPatterns[gCurrentPatternNumber]();// 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:
gPatterns[gCurrentPatternNumber]();// 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:
gPatterns[gCurrentPatternNumber]();// 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:
gPatterns[gCurrentPatternNumber]();// 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
}