Go Down

Topic: Serial and WiFi Causes LED Strip Instability (Read 1 time) previous topic - next topic

tjohnson_

Hello All,

I'm attempting to use ESP32 in order to create a WiFi MQTT controlled LED Strip. At this point in time, I've successfully got all of the pieces working and can control the light strip via my home automation platform, OpenHAB. However, I'm running into an issue with consistent performance.

The High Level: When ESP32 is connected to WiFi or I use Serial commands, my LED Strip commands doesn't behave consistently. For example, if I send a command to every LED to be off, 1 or 2 might turn on. If I send the exact command again, 1 or 2 different ones may turn on (and the others off). This occurs even when turning LEDs on. If I turn the first 5 on, a couple other ones down the line may turn on.

If I comment out all of my WiFi and Serial and invoke the LED commands manually everything works perfectly.

Is there something special I need to do to prevent this type of behavior?

The Detail:

My Hardware
Espressif ESP32 - https://www.amazon.com/gp/product/B01N0SB08Q/
LED Strip: WS2812B - 16ft 30leds/m - https://www.amazon.com/gp/product/B019HAAVLQ/
Power Supply for LED Strip: https://www.amazon.com/gp/product/B01M0KLECZ/

My Software
Arduino 1.8.3
Adafruit NeoPixel Library

My Code
This is my first project with Arduino, so most of this is just a combination of online tutorials for the various pieces I'm trying to get working. Also, I'm using placeholders for some of the parameters. All my connections and such are working properly.

Code: [Select]

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif
#include <ArduinoJson.h>
#include <WiFi.h>
#include <PubSubClient.h>

#define DATA_PIN 12
#define NUM_LEDS 150

//Wifi and MQTT Setup
const char* ssid = "SSID";
const char* password = "PASSWORD";

const char* mqttServer = "HOST";
const int mqttPort = 1883;

const char* mqttUser = "USER";
const char* mqttPassword = "PASSWORD";

WiFiClient espClient;
PubSubClient client(espClient);

//LED Strip setup
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_LEDS, DATA_PIN, NEO_GRB + NEO_KHZ800);
int delayval = 100;

void callback(char* topic, byte* payload, unsigned int length) {

 
  Serial.print("Message arrived in topic: ");
  Serial.println(topic);
 
  Serial.print("Message:");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
 
  Serial.println();
  Serial.println("-----------------------");

 
  StaticJsonBuffer<300> JSONBuffer;                         //Memory pool

  JsonObject& parsed = JSONBuffer.parseObject(payload); //Parse message
  if (!parsed.success()) {   //Check for errors in parsing
 
    Serial.println("Parsing failed");
    delay(5000);
    return;
 
  }

  const char* functionType = parsed["function"];

  Serial.print("Function Requested: ");
  Serial.println(functionType);

  if((String)functionType == "single") {
    LEDSingle(parsed);
   
  } else if((String)functionType == "zone") {
    LEDZone(parsed);
  } else if((String)functionType == "total") {
    LEDTotal(parsed);
  }
}
 
void setup() {

  // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
  #if defined (__AVR_ATtiny85__)
    if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
  #endif
  // End of trinket special code

  pixels.begin(); // This initializes the NeoPixel library.
 
  Serial.begin(115200);
 
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("Connecting to WiFi..");
  }
  Serial.println("Connected to the WiFi network");

  client.setServer(mqttServer, mqttPort);
  client.setCallback(callback);
 
  while (!client.connected()) {
    //Serial.println("Connecting to MQTT...");
 
    if (client.connect("ESP32Client", mqttUser, mqttPassword )) {
 
      //Serial.println("connected"); 
 
    } else {
 
      //Serial.print("failed with state ");
      //Serial.print(client.state());
      delay(2000);
 
    }
  }
 
  client.subscribe("home/dinning/table/ledstrip");
  //client.publish("esp/test", "Hello from RSP32");
 
}
 
void loop() {
  client.loop();
}

void LEDSingle(JsonObject& parsed) {
    int ledTarget = parsed["target"];
    int red = parsed["red"];
    int green = parsed["green"];
    int blue = parsed["blue"];

    Serial.print("Single function call to LED: ");
    Serial.println(ledTarget);

    pixels.setPixelColor(ledTarget, pixels.Color(red,green,blue));
    pixels.show();
}

void LEDZone(JsonObject& parsed) {
    int ledTargetStart = parsed["targetStart"];
    int ledTargetEnd = parsed["targetEnd"];
    int red = parsed["red"];
    int green = parsed["green"];
    int blue = parsed["blue"];

    if(ledTargetStart < ledTargetEnd && ledTargetEnd <= (NUM_LEDS-1)) {
      for(int i = ledTargetStart; i <= ledTargetEnd; i++) {
        pixels.setPixelColor(i, pixels.Color(red,green,blue));
      }
      pixels.show();
    } else {
      Serial.println("Invalid values passed to Zone function");
    }
}

void LEDTotal(JsonObject& parsed) {
  const char* ledTarget = parsed["target"];

  if((String)ledTarget == "ON") {
    Serial.println("MQTT Request: Total ON");
    for(int i = 0; i < NUM_LEDS; i++) {
      pixels.setPixelColor(i, pixels.Color(10,10,10));
    }
  } else {
    Serial.println("MQTT Request: Total OFF");
    for(int i = 0; i < NUM_LEDS; i++) {
      pixels.setPixelColor(i, pixels.Color(0,0,0));
    }
  }

  pixels.show();
}


Now the code below will work perfectly. The Serial commands are commented out, but if those are enabled I get random LEDs acting funky again. Also, if you add in just the WiFi connection line it will act strange.

Code: [Select]

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif
#include <ArduinoJson.h>
#include <WiFi.h>
#include <PubSubClient.h>

#define DATA_PIN 12
#define NUM_LEDS 150

//Wifi and MQTT Setup
const char* ssid = "SSID";
const char* password = "PASSWORD";

const char* mqttServer = "HOST";
const int mqttPort = 1883;

const char* mqttUser = "USER";
const char* mqttPassword = "PASSWORD";

WiFiClient espClient;
PubSubClient client(espClient);

//LED Strip setup
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_LEDS, DATA_PIN, NEO_GRB + NEO_KHZ800);
int delayval = 100;


 
void setup() {

  // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
  #if defined (__AVR_ATtiny85__)
    if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
  #endif
  // End of trinket special code

  pixels.begin(); // This initializes the NeoPixel library.
 
  //Serial.begin(115200);
 //WiFi.begin(ssid, password);
 
  //Serial.println("Connected to the WiFi network");
 
}
 
void loop() {
 
  //Serial.println("Starting LEDs Run");
  for(int i=0;i<NUM_LEDS;i++){

    // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
    pixels.setPixelColor(i, pixels.Color(0,50,0)); // Moderately bright green color.

    pixels.show(); // This sends the updated pixel color to the hardware.

    delay(delayval); // Delay for a period of time (in milliseconds).
   
    pixels.setPixelColor(i, pixels.Color(0,0,0)); // Moderately bright green color.


  }

}



Any thoughts or direction would be greatly appreciated!

Grumpy_Mike

Thoughts.
Are you running out of static ram memory? A trinket does not have much.

A Trinket has no serial hardware so you have to do serial in software, I would have thought that this would interfere with the timing of the Neopixel signal.

I would try it on a Uno first and see if you get the same problems.

PieterP

Thoughts.
Are you running out of static ram memory? A trinket does not have much.

A Trinket has no serial hardware so you have to do serial in software, I would have thought that this would interfere with the timing of the Neopixel signal.

I would try it on a Uno first and see if you get the same problems.
I don't think he's using a Trinket, he clearly says that he's using an ESP32. The Trinket code is probably just copied from one of the Neopixel examples.

WS2812B LEDs need very precise timing, when using Serial, there are probably interrupts firing, interrupting the WS2812B code, messing up the timing.
Same goes for WiFi, when the TCP/IP stack needs a lot of CPU time, the WS2812B code gets less resources.

If I'm not mistaken, the ESP32 Arduino core is built on top of a RTOS. This is different from a normal Arduino, where your own loop function is pretty much the only thing that's running.

A solution would be to use a hardware peripheral with DMA, so you don't need CPU time to write out data from a buffer.
On the ESP8266, people used the I²S bus for WS2812B LEDs: https://github.com/cnlohr/esp8266ws2812i2s
On the ESP32, it seems you can use the IR remote control "RMT" peripheral: https://github.com/MartyMacGyver/ESP32-Digital-RGB-LED-Drivers


Pieter

Go Up