FastLED and WiFiServers on ESP8266

I have two codes. One working just fine without wifi with FastLED on my ESP8266, and the same code with the addition of a WifiServer to get word inputs from my PC. The wifi code works just fine in Serial, and the FastLED code works just fine without Wifi. When I mix the two my LEDs do not behave properly. My suspicion is the D4 pin has something to do with Wifi and adds noise to my LED signal. I'm pretty sure this is possible since I used the same ESP8266 with Wled before which is the same thing I'm trying to do but with my script instead of flashing Wled.

My goal is to create lights like in stranger things. So the message is displayed by the LEDs in sequence of blinking the letters A-0, B-1, C-2, etc. The message is sent over Wifi or in the no wifi version just entered into the code.

What would cause the LEDs to not work when Wifi is enabled here?

Working Code without Wifi:

#include <FastLED.h>

bool displayingMsg = true;
// LED strip settings
#define LED_PIN 2  //  D4 is GPIO2
#define NUM_LEDS 26
#define BRIGHTNESS 200
#define CHIPSET WS2811
CRGB leds[NUM_LEDS];

void setup() {
  // put your setup code here, to run once:


  Serial.begin(115200);
  delay(1000);
  Serial.println("Starting");

  // Setup LED strip
  FastLED.addLeds<CHIPSET, LED_PIN, RGB>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();
  FastLED.show();
  Serial.println("LED setup complete.");
}

void loop() {
  // put your main code here, to run repeatedly:
  displayingMsg = true;
  //Serial.println(message);
  while (displayingMsg) {
    displayMessage("Led test"); //led test replaces the message that would be sent by Wifi
  }
  delay(500000); // long delay since it would loop otherwise
}

void displayMessage(const char* message) {
  Serial.print("the message ");
  Serial.println(message);
  for (int i = 0; message[i] != '\0'; i++) {
    displayLetter(message[i]);
    FastLED.show();
    delay(1000);
    FastLED.clear();
    FastLED.show();
    delay(1000);
  }
  displayingMsg = false;
  FastLED.clear();
  FastLED.show();
}

void displayLetter(char letter) {
  Serial.print("Display Letter ");
  Serial.println(letter);
  int ledIndex = getLEDIndexForLetter(letter);
  if (ledIndex != -1) {
    leds[ledIndex] = CRGB::White;
    Serial.println(leds[ledIndex].r);
  }
}

int getLEDIndexForLetter(char letter) {
  Serial.print("getting index ");
  letter = toupper(letter);
  if (letter < 'A' || letter > 'Z') {
    return -1;
  }
  int n = letter - 'A';
  Serial.println(n);
  return n;
}

Same code with WifiServer added:

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <FastLED.h>

// LED strip settings
#define LED_PIN 2  // D4 is GPIO2
#define NUM_LEDS 26
#define BRIGHTNESS 200
#define CHIPSET WS2811
CRGB leds[NUM_LEDS];

// Wi-Fi credentials
const char* ssid = "WiFi";
const char* password = "Password";

// Web server on port 80
ESP8266WebServer server(80);

// Global variable to store the last message entered
char lastMessage[256] = "";  // Allows for up to 255 characters + null terminator
bool displayingMsg = false; //tracking if message playing

// Function to handle the root page and display the input form
void handleRoot() {
  String html = "<html><head><title>ESP8266 String Input</title></head><body>";
  html += "<h1>Enter a Message</h1>";
  html += "<form action='/setMessage' method='GET'>";
  html += "Message: <input type='text' name='message' maxlength='255'>";  // Accept up to 255 characters
  html += "<input type='submit' value='Submit'>";
  html += "</form>";
  
  // Show the last entered message
  html += "<p>Last message entered: <strong>";
  html += String(lastMessage);
  html += "</strong></p>";
  html += "</body></html>";

  server.send(200, "text/html", html);
}

// Function to handle the /setMessage request
void handleSetMessage() {
  if (server.hasArg("message")) {
    String messageInput = server.arg("message");
    messageInput.toCharArray(lastMessage, 256);  // Convert the String to a char array and store it
    displayingMsg = true;
  }

  // Redirect to the root after processing input to allow for new input
  server.sendHeader("Location", "/");  // This redirects the user to the root page ("/")
  server.send(302);  // Send the 302 status code for redirection
}

void setup() {
  // Initialize serial communication for debugging
  Serial.begin(115200);
  
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  Serial.println();
  Serial.print("Connecting to WiFi");
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  
  Serial.println();
  Serial.print("Connected to WiFi! IP address: ");
  Serial.println(WiFi.localIP());

  // Set up web server routes
  server.on("/", handleRoot);            // Root page to display the form and last message
  server.on("/setMessage", handleSetMessage);  // Handle message submission

  // Start the server
  server.begin();
  Serial.println("Web server started.");

  // Setup LED strip
  FastLED.addLeds<CHIPSET, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();
  FastLED.show();
  Serial.println("LED setup complete.");
  FastLED.clear();
  FastLED.show();
}

void loop() {
  // Handle client requests
  server.handleClient();
  delay(3000);
  Serial.println("Inside Loop");
  Serial.println(lastMessage);
  if (displayingMsg) {
    displayMessage(lastMessage);
  }
}

void displayMessage(const char* message) {
  Serial.print("the message ");
  Serial.println(message);
  for (int i = 0; message[i] != '\0'; i++) {
    displayLetter(message[i]);
    FastLED.show();
    delay(1000);
    FastLED.clear();
    FastLED.show();
    delay(1000);
  }
  displayingMsg = false;
  FastLED.clear();
  FastLED.show();
}

void displayLetter(char letter) {
  Serial.print("Display Letter ");
  Serial.println(letter);
  int ledIndex = getLEDIndexForLetter(letter);
  if (ledIndex != -1) {
    leds[ledIndex] = CRGB::Blue;
  }
}

int getLEDIndexForLetter(char letter) {
  Serial.print("getting index ");
  letter = toupper(letter);
  if (letter < 'A' || letter > 'Z') {
    return -1;
  }
  int n = letter - 'A';
  Serial.println(n);
  return n;
}

What lights?

To work one sketch into another sketch, start with including the library. If the old part of the sketch is working, and the new library did not crash the sketch, initialize the device or start an object and run again. Then, perform a minimal command to prove the new part of the sketch still works. Continue until it behaves badly.

Change the BRIGHTNESS value to something a lot less until you find the issue. Something like 63 or 127.

Post a wiring diagram.

Maybe the lack of official support for esp8266 fastled library?

For ESP8266 i use Makuna Neopixelbus and i use the default I2S method on GPIO3 (rx) since that really does the transmission in the background. UART method is a bit more CPU intensive, bit-bang method is not recommended icw WiFi I think FastLED uses a bit-bang method but with interrupts still turned on which may cause timing issues.

I’m not trying to make one sketch use another. The no wifi sketch was the last time it worked as I slowly added code until it breaks. It breaks when I add the wifi server portion of the code. (The “with wifi” code) I don’t know how to add less of the wifi code so technically it is a big chunk added at once but it was just one feature of the code added and it all breaks.

I can try this. I will replace the use of FastLED with the NeoPixle library you linked too.

Also make sure that the 3.3v logic level of the ESP8266 is increased to 5v, you can do this by simply adding a 74HCT04 9powered with 5v) to the signal line and running the signal thru 2 of the gates (inverting it twice)

The 3.3v logic level is just not quite enough to properly trigger the WS281x chips, at times it may work without level shifting but for reliable use you should shift it to 5v.

Keep in mind that you should call begin() in the ledstrip after you have called begin() on the Serial object or the GPIO pin (3 / RX) will not be in the correct mode.

Can you explain this a bit more? I have 3.3V working just fine for the lights on the no wifi code and this same ESP works just fine with WLED in the same lights too without the need to include 5V anywhere.

3.3v is marginal. You got lucky and your luck may not hold across wider use conditions. Using a 3.3v -> 5V buffer as suggested is best practice.

I have the LEDs working off 5V now. I still have the same issues as before but the lights are brighter. I also lowered the brightness in code to 60.

Is the data line from the ESP board to the LED string also buffered up to 5V logic level?

I don't have the 74HCT04. I did some google on the logic behind led data lines and I moved one first LED right at the pin so it can act as a signal booster / sacrificial LED to send the data to the rest. I think that should work since the LEDs have 5V for sure on the + line (checked with Volt Meter)

Not a very reliable method. If the signal is corrupted on the first LED it will be corrupted on the next.

Any other 74HCTxx ?

ok..

I dont want to sound stubborn, but I dont think how my lights are wired is the issue. I flashed Wled to my circuit without changing anything physical and I can even add 3 lengths of lights to the end of my current string (150 pixels total) and get each lights working just fine before voltage drop lowers my true white tone on the fourth strip (200 pixels). To me that validates my hardware is fine and my issue is the introduction of my Wifi Server somehow. Wled works over Wifi so the ESP is still using all the same functionality I want to get out of it in my test. Again, without changing anything I removed Wled and put my "no Wifi" Script back on and that worked too (27 pixels). I added the Wifi server portion and I am back to having erratic light behavior. Something about how I'm using Wifi is messing with D4 or FastLED. I'm going to focus on trying neopixle tonight after work.

You need to be more specific. And you need to adopt a more methodical debug strategy than just guessing. Engineers don't depend solely on guessing. Under what conditions does it "not behave properly"?

Just by adding the WiFi code?

What happened when you went back to your original (autonomously LED running) code and just added the connection to WiFi but didn’t create a Server?

What happened when you took the above code and created the server (with no callbacks) but didn’t call server.handleClient()?

What happened when you took the above code and called server.handleClient() in loop?

What happened when you took the above code, added server callbacks, called server.handleClient() in loop but didn’t actually send any messages from the PC?

What happened when you put a logic analyzer (under $100 on Amazon with free software available) on the LED pin (D4), grabbed the data, and decoded it to the WS2811 protocol? How did the decoded data differ between the autonomous-running case and the server-triggered case? WS2811 devices have very specific timing requirements on their input data. Did WiFi and/or server operations change the pulse timing?

What happened with all of the above tests when you switched from D4 to a different pin?

Why do you think changing from FastLED will make a difference?

webservering and fastleding, 2812ing, can be 'intensive' on their own - together they may not mesh well, poorly if at all.
(I'm speculating.)

D4 is GPIO 2 which in some situations can be used as TX0 and can be used as TX1. There is the potential debugging output. Still i don't know how FastLED generates the signal for WS281x, Is it UART or can you use any pin ? If it is just bit-banged then a timing issues is most likely at fault.

It may not be your only issue. Even though you have eliminated it as possible cause through a method, does not mean it is not a possible issue. You can say that once you've increased the logic level to 5v but the issues persists.

A not optimal signal in combination with a not optimal signal strength might be the cause. I think you should do both increase the logic level and ditch fastLED (not a fan of it anyway)

I use FastLED a lot and the use of WiFi (outside of WLED) is new to me. I was not sure how much of the wifi code is reliant on each other. My debug strategy is much more elaborate than I have added to this thread in the effort to not pre-impose thoughts to those willing to help me. I'm very new to Wifi so if I say I tried "X" and someone reads that and therefore doesn't suggest I try "X" I dont want to miss out on trying "X" when in reality I tried "(small)x" which is not "(big)X" because I didn't know the difference.

Anyway, I will try to break up the Wifi Code into the smaller chunks you described and see when the LEDS act up.

The LEDs are "supposed to" blink one at a time in sequence with the message being sent. When I add the Wifi code all at once the LEDs behave as follows:

  • During bootup and setup of the ESP the first 13 lights or so all turn on some shade of while or yellow. The first LED is sometimes green or red.
  • After the first set of FastLED.clear() the lights dont actually clear which is why I have it twice.
  • When a message is being displayed the first LED is always on red.
  • when a message is being displayed index 0 activates LED 2, index 1 activates LED 3, and so on. Sometimes Index 1 activates LED 3 and 4.
  • The color of each light is either white, red, or green (or mix).

I am thinking of trying Neopixle since it was suggested in Post #4.

Update on adding the Wifi code slowly.

This following code works as intended displaying "abcd" on the first 4 LEDS in blue:

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <FastLED.h>

// WiFi credentials
const char* ssid = "Wifi";
const char* password = "Password";

// LED strip settings
#define LED_PIN 2  // Change to a valid ESP8266 pin, D4 is GPIO2
#define NUM_LEDS 26
#define BRIGHTNESS 200
#define CHIPSET WS2811
CRGB leds[NUM_LEDS];

// Web server on port 80
//ESP8266WebServer server(80);

// Store the last message
String lastMessage = ""; //store the last message
bool displayingMsg = false; //bool to show when message is being displayed currently

void setup() {
  // Initialize serial and wait for port to open
  Serial.begin(115200);
  delay(1000);
  Serial.println("Starting setup...");
/*
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.print("Connected to WiFi. IP address: ");
  Serial.println(WiFi.localIP());
*/
  // Setup LED strip
  FastLED.addLeds<CHIPSET, LED_PIN, RGB>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();
  FastLED.show();
  Serial.println("LED setup complete.");
/*
  // Root page: form for message input + display previous message
  server.on("/", HTTP_GET, []() {
    String html = "<html><head><title>LED Message Board</title></head><body>";
    html += "<h2>LED Message Display</h2>";
    html += "<form action=\"/show\" method=\"GET\">";
    html += "Message: <input type=\"text\" name=\"msg\">";
    html += "<input type=\"submit\" value=\"Send\">";
    html += "</form>";

    // Display previous message if one exists
    if (lastMessage.length() > 0) {
      html += "<p>Previous message: <strong>" + lastMessage + "</strong></p>";
    }

    html += "</body></html>";
    server.send(200, "text/html", html);
  });

  // Show the message
  server.on("/show", HTTP_GET, []() {
    String message = server.arg("msg");

    // Display the message if it's valid
    if (message.length() > 0) {
      Serial.println("Recieved message: " + message);
      displayingMsg = true;
      lastMessage = message;  // Store the message in the global variable
    }

    // Send a 302 redirect to the root ("/")
    server.sendHeader("Location", "/");  // Redirect to the form
    server.send(302, "text/plain", "Redirecting...");
  });
*/

  // Start the server
  //server.begin();
  FastLED.clear();
  FastLED.show();
  Serial.println("Server started.");

  //debug message without wifi comment out when not used
  displayingMsg = true;
}

void loop() {
  // Handle client requests;
  //server.handleClient();
  while (displayingMsg) {
    //displayMessage(lastMessage.c_str());
    displayMessage("abcd");
    Serial.println("Displayed Message");
  }
}

void displayMessage(const char* message) {
  Serial.print("the message ");
  Serial.println(message);
  for (int i = 0; message[i] != '\0'; i++) {
    displayLetter(message[i]);
    FastLED.show();
    delay(1000);
    FastLED.clear();
    FastLED.show();
    delay(1000);
  }
  displayingMsg = false;
  Serial.println("End of message");
  FastLED.clear();
  FastLED.show();
}

void displayLetter(char letter) {
  Serial.print("Display Letter ");
  Serial.println(letter);
  int ledIndex = getLEDIndexForLetter(letter);
  if (ledIndex != -1) {
    leds[ledIndex] = CRGB::Blue;
  }
}

int getLEDIndexForLetter(char letter) {
  Serial.print("getting index ");
  letter = toupper(letter);
  if (letter < 'A' || letter > 'Z') {
    return -1;
  }
  int n = letter - 'A';
  Serial.println(n);
  return n;
}

This by adding only connection to my WiFi with no Server does not work

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <FastLED.h>

// WiFi credentials
const char* ssid = "Wifi";
const char* password = "Password";

// LED strip settings
#define LED_PIN 2  // Change to a valid ESP8266 pin, D4 is GPIO2
#define NUM_LEDS 26
#define BRIGHTNESS 200
#define CHIPSET WS2811
CRGB leds[NUM_LEDS];

// Web server on port 80
//ESP8266WebServer server(80);

// Store the last message
String lastMessage = ""; //store the last message
bool displayingMsg = false; //bool to show when message is being displayed currently

void setup() {
  // Initialize serial and wait for port to open
  Serial.begin(115200);
  delay(1000);
  Serial.println("Starting setup...");

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.print("Connected to WiFi. IP address: ");
  Serial.println(WiFi.localIP());

  // Setup LED strip
  FastLED.addLeds<CHIPSET, LED_PIN, RGB>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();
  FastLED.show();
  Serial.println("LED setup complete.");
/*
  // Root page: form for message input + display previous message
  server.on("/", HTTP_GET, []() {
    String html = "<html><head><title>LED Message Board</title></head><body>";
    html += "<h2>LED Message Display</h2>";
    html += "<form action=\"/show\" method=\"GET\">";
    html += "Message: <input type=\"text\" name=\"msg\">";
    html += "<input type=\"submit\" value=\"Send\">";
    html += "</form>";

    // Display previous message if one exists
    if (lastMessage.length() > 0) {
      html += "<p>Previous message: <strong>" + lastMessage + "</strong></p>";
    }

    html += "</body></html>";
    server.send(200, "text/html", html);
  });

  // Show the message
  server.on("/show", HTTP_GET, []() {
    String message = server.arg("msg");

    // Display the message if it's valid
    if (message.length() > 0) {
      Serial.println("Recieved message: " + message);
      displayingMsg = true;
      lastMessage = message;  // Store the message in the global variable
    }

    // Send a 302 redirect to the root ("/")
    server.sendHeader("Location", "/");  // Redirect to the form
    server.send(302, "text/plain", "Redirecting...");
  });
*/

  // Start the server
  //server.begin();
  FastLED.clear();
  FastLED.show();
  Serial.println("Server started.");

  //debug message without wifi comment out when not used
  displayingMsg = true;
}

void loop() {
  // Handle client requests;
  //server.handleClient();
  while (displayingMsg) {
    //displayMessage(lastMessage.c_str());
    displayMessage("abcd");
    Serial.println("Displayed Message");
  }
}

void displayMessage(const char* message) {
  Serial.print("the message ");
  Serial.println(message);
  for (int i = 0; message[i] != '\0'; i++) {
    displayLetter(message[i]);
    FastLED.show();
    delay(1000);
    FastLED.clear();
    FastLED.show();
    delay(1000);
  }
  displayingMsg = false;
  Serial.println("End of message");
  FastLED.clear();
  FastLED.show();
}

void displayLetter(char letter) {
  Serial.print("Display Letter ");
  Serial.println(letter);
  int ledIndex = getLEDIndexForLetter(letter);
  if (ledIndex != -1) {
    leds[ledIndex] = CRGB::Blue;
  }
}

int getLEDIndexForLetter(char letter) {
  Serial.print("getting index ");
  letter = toupper(letter);
  if (letter < 'A' || letter > 'Z') {
    return -1;
  }
  int n = letter - 'A';
  Serial.println(n);
  return n;
}

I have no way to boost the signal of the Data to 5V other than sacrificial LED. I dont have any 74HCTxx. I do have an arduino Board. I may try making the code on the arduino and using the ESp8266 just for its WiFi capabilities and have the 8266 send the ardunio the Message string somehow.

I use NeoPixelBus with I2S DMA and it works fine. I don't know if the other libraries support DMA mode. It has a problem in that is uses GPIO3 (hard wired) for the output. This means you cannot run the led strip and the base serial port at the same time. After programming you have to remove the serial connection before you can try it. I solved this by using OTA update, so I only need the serial connection for the initial programming . Here is the (probably more than you care to look at) code. It moves colors in from the opposite sides of the strip and shows the combined color on the central LEDs. It is more impressive in operation.

The new lines in the HTML are for humans so that is more readable when doing a display source in the browser. The browser doesn't care.

#include <NetworkInfo.h>
#include <TransmissionResult.h>

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
ESP8266WebServer server(80);   // web server on standard port
ESP8266HTTPUpdateServer httpUpdater;

#include <DNSServer.h>
const byte DNS_PORT = 53;     // name server on standard port
IPAddress apIP(192, 168, 6, 1);   // IP address to use in non-routed range
DNSServer dnsServer;

#include <FS.h>

#include <user_interface.h>

#include <string.h>
#include <Streaming.h>   //  from "Streaming by Mikal Hart" in library manager

const char* ap_ssid     = "Colors"; //  AP ssid 
const char* ap_password = "lightupleds";   // AP Password
const char* host = "www.leds.edu";       //  DNS host name
 

#include <NeoPixelBus.h>

const uint16_t pixelCount = 144; 

#define colorSaturation 127

// three element pixels, in different order and speeds
// For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.  
NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(pixelCount);



RgbColor red(colorSaturation, 0, 0);
RgbColor green(0, colorSaturation, 0);
RgbColor blue(0, 0, colorSaturation);
RgbColor white(colorSaturation);
RgbColor black(0);

RgbColor leftColor = red;
RgbColor rightColor = green;
bool firstTime = true;
unsigned long oldms,newms,deltams;
byte rate = 20;

void handleRoot(){
  
  String htmlPage;
  htmlPage.reserve(3800); // reserve space for HTML page 
  
  if (server.hasArg("lred")) {
    // we have input data - assume they are all set 
    leftColor = RgbColor(server.arg("lred").toInt(),server.arg("lgreen").toInt(),server.arg("lblue").toInt());
    rightColor = RgbColor(server.arg("rred").toInt(),server.arg("rgreen").toInt(),server.arg("rblue").toInt());
    rate = server.arg("rate").toInt();
    firstTime = true;
  }  
  
   // Fill in static part of page
   htmlPage = 
  " <!DOCTYPE HTML>\n\
<html lang='en'>\n\
 <head>\n\
    <meta name='viewport' content='width=device-width, initial-scale=1.0'>\n\
    <title>Color Collider</title>\n\
</head>\n\
\n\
\n\
<body onload='changeValues()'>\n\
    <h1 style='text-align: center;'>Color Collider color selector</h1>\n\
    <br>\n\
    <h2 id='collide'>Pick colors to collide</h2>\n\
    <form>\n\
      <p id='lside'> Left Side </p>\n\
      <div>\n\
      <label for='lred' id='lrlab'>Red Value</label><br>\n\
      <input type='range' id='lred' name='lred' min='0' max='127'  onchange='changeValues()'\n\
       value=";
 htmlPage += leftColor.R ; 
 htmlPage +=      
    "   \n\
      >\n\
      </div>\n\
      <div>\n\
      <label for='lgreen' id='lglab'>Green Value</label><br>\n\
      <input type='range' id='lgreen' name='lgreen' min='0' max='127' onchange='changeValues()'\n\
      value=";
 htmlPage += leftColor.G ; 
 htmlPage +=
    "   > \n\
      </div>\n\
      <div>\n\
      <label for='lblue' id='lblab'>Blue Value</label><br>\n\
      <input type='range' id='lblue' name='lblue' min='0' max='127' onchange='changeValues()' \n\
      value= ";
 htmlPage += leftColor.B ; 
 htmlPage +=     
      " >\n\
       </div>\n\
      <p id='rside'> Right Side </p>\n\
      <div>\n\
      <label for='rred' id='rrlab'>Red Value</label><br>\n\
      <input type='range' id='rred' name='rred' min='0' max='127'  onchange='changeValues()' \n\
      value=";     
 htmlPage += rightColor.R ; 
 htmlPage += 
      " >\n\
      </div>\n\
      <div>\n\
      <label for='rgreen' id='rglab'>Green Value</label><br>\n\
      <input type='range' id='rgreen' name='rgreen' min='0' max='127' onchange='changeValues()'\n\
       value = ";
     htmlPage += rightColor.G ; 
 htmlPage += 
     " > \n\
      </div>\n\
      <div>\n\
      <label for='rblue' id='rblab'>Blue Value</label><br>\n\
      <input type='range' id='rblue' name='rblue' min='0' max='127' onchange='changeValues()'\n\
      value=";
 htmlPage += rightColor.B ; 
  htmlPage += 
     " > \n\
      </div>\n\
      <div>\n\
      <label for='rate' id='ratelab'>Updates per Second</label><br>\n\
      <input type='range' id='rate' name='rate' min='1' max='30' onchange='changeValues()'\n\
      value=";
 htmlPage += rate ; 
 htmlPage += 
   "  >\n\
      </div>\n\
      <div>\n\
      <input type='submit' value='Set Values'>\n\
      </div>\n\
    </form> \n\
    <script>\n\
      \n\
      function changeValues() {\n\
        var lcol, lr, lg, lb ,lscale,lmax;\n\
	    	var rcolor,rr,rg,rb,rsale,rmax;\n\
	    	var collidecolor,cr,cg,cb,cscale,cmax;\n\
        var rate;\n\
        lr = document.getElementById('lred').value;\n\
        lg = document.getElementById('lgreen').value;\n\
        lb = document.getElementById('lblue').value; \n\
	    	lmax = Math.max(lr,lg,lb);\n\
        if (lmax == 0) {\n\
          lscale = 0;\n\
        }\n\
        else {\n\
          lscale = 254/lmax;\n\
        }\n\
        lcolor = 'rgb('+ (Number(lr)*lscale) + ','+ (Number(lg)*lscale)+ ','+ (Number(lb)*lscale)+')';\n\
        rr = document.getElementById('rred').value;\n\
        rg = document.getElementById('rgreen').value;\n\
        rb = document.getElementById('rblue').value; \n\
	    	rmax = Math.max(rr,rg,rb);\n\
        if (rmax == 0) {\n\
          rscale = 0;\n\
        }\n\
        else {\n\
          rscale = 254/rmax;\n\
        }\n\
        rcolor = 'rgb('+ (Number(rr)*rscale) + ','+ (Number(rg)*rscale)+ ','+ (Number(rb)*rscale)+')';\n\
        document.getElementById('lrlab').innerHTML = 'Red value '+ lr;\n\
        document.getElementById('lglab').innerHTML = 'Green value '+lg;\n\
        document.getElementById('lblab').innerHTML = 'Blue value '+lb;\n\
        document.getElementById('lside').style.backgroundColor = lcolor;\n\
   //     document.getElementById('lside').innerHTML = lcolor;\n\
        document.getElementById('rrlab').innerHTML = 'Red value '+ rr;\n\
        document.getElementById('rglab').innerHTML = 'Green value '+rg;\n\
        document.getElementById('rblab').innerHTML = 'Blue value '+rb;\n\
        document.getElementById('rside').style.backgroundColor = rcolor;\n\
        cr = Number(rr)+Number(lr);\n\
        cg = Number(rg)+Number(lg);\n\
        cb = Number(rb)+Number(lb);\n\
        cmax = Math.max(cr,cg,cb);\n\
        if (cmax == 0) {\n\
          cscale = 0;\n\
        }\n\
        else {\n\
          cscale = 254/cmax;\n\
        }\n\
        collidecolor = 'rgb(' + (cr*cscale) +',' +(cg*cscale) +',' +(cb*cscale) +')';\n\
        document.getElementById('collide').style.backgroundColor = collidecolor;\n\
      //  document.getElementById('collide').innerHTML = collidecolor;\n\
        rate = document.getElementById('rate').value;\n\
        document.getElementById('ratelab').innerHTML = 'Updates per Second '+ rate;\n\
      }\n\
    </script>\n\
 </body>\n\
</html>\n";
 server.send(200, "text/html",htmlPage);
  
}  

bool PixelEqual( RgbColor color1,RgbColor color2){
  if((color1.R == color2.R) &&
     (color1.G == color2.G) &&
     (color1.B == color2.B)){
      return true; 
  }
  else {
    return false;
  }   
}

void setup()
{
  Serial.begin(115200);
  // Set up WiFi network
  WiFi.mode(WIFI_AP);
  WiFi.softAPConfig(apIP, apIP,IPAddress(255, 255, 255, 0));
  
  if (!WiFi.softAP(ap_ssid, ap_password)) {
     Serial.println("wifi connection  fail");
    
   //  while (true) {};    // hang until wdt forces a reset to retry
  }
  Serial.println("wifi connected");
 // DNS 
 // modify TTL associated  with the domain name (in seconds)
  // default is 60 seconds
  dnsServer.setTTL(300);
  // set which return code will be used for all other domains (e.g. sending
  // ServerFailure instead of NonExistentDomain will reduce number of queries
  // sent by clients)
  // default is DNSReplyCode::NonExistentDomain
  dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure);

  // start DNS server for a specific domain name
  dnsServer.start(DNS_PORT,host, apIP);   

  // set web page handlers
  server.on("/", handleRoot);

  
  // Start the server
  httpUpdater.setup(&server);
  server.begin();
  
  
  // this resets all the neopixels to an off state
  strip.Begin();
  strip.Show();
 
}


void loop()
{
  RgbColor merge; // combined color value
  
  dnsServer.processNextRequest();    // check for DNS request
  server.handleClient();             // check for connection request 

  if (firstTime){
    // set the colors
    strip.ClearTo(black);
    for (int i =0; i<(pixelCount)/2;i+=6) {
      for(int j=0;j<4;j++){
        strip.SetPixelColor(i+j, leftColor );
        strip.SetPixelColor(pixelCount-(i+j+1), rightColor);
      }
    }
    // set up timing
    oldms = millis();
    deltams = 1000/rate;
    firstTime = false;
  }
  newms = millis();
  if((newms - oldms) > deltams){ 
    oldms = newms;      // set next time
    strip.Show();       // show previous set up
    // shift colors toward middle
    strip.RotateLeft(1,pixelCount/2,pixelCount-1);
    strip.RotateRight(1,0,(pixelCount/2)-1);
    // combine colors four away from middle and set them in pattern - the color may be black
    merge = RgbColor::LinearBlend(strip.GetPixelColor(68), strip.GetPixelColor(75), 0.50f);
    strip.SetPixelColor(68, merge);
    strip.SetPixelColor(75, merge);
    // if the end colors are not black correct for merged color after rotate
    if(!PixelEqual(strip.GetPixelColor(0), black)){
       strip.SetPixelColor(0,leftColor);
       strip.SetPixelColor(pixelCount-1,rightColor);
    }
  }
 }