Electronic – ESP32 ws2812fx BLE leds flicker and flash different colors

bluetoothbluetooth low energyesp32ws2811ws2812b

I am creating a device to use a bluetooth app to control the effects in the ws2812fx library on a esp32 thing from Sparkfun. It works great using the arduino serial monitor to update. But whenever I add the BLE code the led signal becomes unstable and starts to make the leds flicker and change colors.

I get the same result with multithreading or not.

I have tried running this with the leds being powered from the chip and on their own power supply.

I have tried the following wiring configurations with a 16led ws2812 ring (I have also tried these with different led fixtures including a ws2811 string):
1. esp32.usb > led.5v, esp32.gnd > led.gnd, esp32.gpio16(I have tried on other gpio's) > led.din
a. esp32.gpio > 470ohm resistor > led.din
2. I also tried this configuration with a 3.3v-5v logic level converter in between. Placing the resistor before and after the logic level converter.

I have tried this with 2 different Sparkfun esp32 thing's. I also tried this on an Adafruit esp32 Feather. All with the same results.

Here is the code:
main.cpp

#include <Arduino.h>
#include <Streaming.h>
#include <comm.h>
#include <led.h>

TaskHandle_t taskComm, taskLED;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  delay(500);

  xTaskCreatePinnedToCore(
   TaskComm,                  /* pvTaskCode */
   "Communication",            /* pcName */
   2048,                   /* usStackDepth */
   NULL,                   /* pvParameters */
   1,                      /* uxPriority */
   &taskComm,                 /* pxCreatedTask */
   0);                     /* xCoreID */

  Serial.println("Start Comm process on Core 0");

  xTaskCreatePinnedToCore(
   TaskLED,                  /* pvTaskCode */
   "LED",            /* pcName */
   1024,                   /* usStackDepth */
   NULL,                   /* pvParameters */
   1,                      /* uxPriority */
   &taskLED,                 /* pxCreatedTask */
   1);                     /* xCoreID */

  Serial.println("Start LED process on Core 1");
}

void loop() {
  // put your main code here, to run repeatedly:
  if(cmd_complete) { //from comm.h
    ProcessCommand(); //from led.h
  }
}

comm.h

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

#define SERVICE_UUID        "71ae1439-4160-4532-b952-74fa9ccbd337"
#define CHARACTERISTIC_UUID "0fffbd21-d7ca-4869-88d3-53005b6192a7"

#define MAX_NUM_CHARS 16 // maximum number of characters read from the Serial Monitor

char cmd[MAX_NUM_CHARS];       // char[] to store incoming serial commands
boolean cmd_complete = false;  // whether the command string is complete

class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string value = pCharacteristic->getValue();

      int index;
      if (value.length() > 0) {
        for (int i = 0; i < value.length(); i++) {
          cmd[i] = value[i];
          index = i + 1;
        }
        cmd[index] = '\0'; // terminate the string
        Serial << "BLE received '" << cmd << "'" << endl;
        cmd_complete = true;
      }
    }
};

void recvChar(void) {
    static byte index = 0;
    while (Serial.available() > 0 && cmd_complete == false) {
        char rc = Serial.read();
        if (rc != '\n') {
            if(index < MAX_NUM_CHARS) cmd[index++] = rc;
        } else {
            cmd[index] = '\0'; // terminate the string
            index = 0;
            Serial << "received '" << cmd << "'" << endl;
            cmd_complete = true;
        }
    }
}

void TaskComm( void * parameter ) {
    //Setup
    //Bluetooth Setup
    char chipName[20];
    uint64_t chipid=ESP.getEfuseMac();
    snprintf(chipName, 20, "board-%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
    Serial << "Chip Name: " << chipName << endl;
    BLEDevice::init(chipName);

    BLEServer *pServer = BLEDevice::createServer();

    BLEService *pService = pServer->createService(SERVICE_UUID);

    BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                          CHARACTERISTIC_UUID,
                                          BLECharacteristic::PROPERTY_READ |
                                          BLECharacteristic::PROPERTY_WRITE
                                        );

    pCharacteristic->setCallbacks(new MyCallbacks());

    pCharacteristic->setValue("Isomia LED Controller");
    pService->start();

    BLEAdvertising *pAdvertising = pServer->getAdvertising();
    pAdvertising->start();

    for (;;) {
        //Loop
        recvChar(); // read serial comm

        delay(10);
    }
}

led.h

#include <WS2812FX.h>

#define LED_COUNT 16
#define LED_PIN 16

// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

void ProcessCommand() {
    if (strncmp(cmd,"b ",2) == 0) {
      uint8_t b = (uint8_t)atoi(cmd + 2);
      ws2812fx.setBrightness(b);
      Serial << "Set brightness to: " << ws2812fx.getBrightness() << endl;
    }
    if (strncmp(cmd,"s ",2) == 0) {
      uint16_t s = (uint16_t)atoi(cmd + 2);
      ws2812fx.setSpeed(s); 
      Serial << "Set speed to: " << ws2812fx.getSpeed() << endl;
    }
    if (strncmp(cmd,"m ",2) == 0) {
      uint8_t m = (uint8_t)atoi(cmd + 2);
      ws2812fx.setMode(m);
      Serial << "Set mode to: " << ws2812fx.getMode() << " - " << ws2812fx.getModeName(ws2812fx.getMode()) << endl;
    }
    if (strncmp(cmd,"c ",2) == 0) {
      uint32_t c = (uint32_t)strtoul(cmd + 2, NULL, 16);
      ws2812fx.setColor(c);
      Serial << "Set color to: 0x" << String(ws2812fx.getColor(), HEX) << endl;
    }

    cmd[0] = '\0';         // reset the commandstring
    cmd_complete = false;  // reset command complete
}

void TaskLED( void * parameter ) {
    //Setup
    ws2812fx.init();
    ws2812fx.setBrightness(1);
    ws2812fx.setSpeed(1000);
    ws2812fx.setColor(0x007BFF);
    ws2812fx.setMode(1);
    ws2812fx.start();

    for (;;) {
        //Loop
        ws2812fx.service();
    }
}
```

Best Answer

Using RMT on the esp32 chip solved my problem. I even tested on a board without a 470ohm resistor or logic level converter. I took out the multithreading as well. Here is the source code. You need to add the ESP32_RMT_Driver.h from the ws2812fx_esp32 example

#include <Arduino.h>
#include <WS2812FX.h>
#include <Streaming.h>
#include "ESP32_RMT_Driver.h"
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

#define SERVICE_UUID        "71ae1439-4160-4532-b952-74fa9ccbd337"
#define CHARACTERISTIC_UUID "0fffbd21-d7ca-4869-88d3-53005b6192a7"

#define LED_COUNT 16
#define LED_PIN 16

#define MAX_NUM_CHARS 16 // maximum number of characters read from the Serial Monitor

char cmd[MAX_NUM_CHARS];       // char[] to store incoming serial commands
boolean cmd_complete = false;  // whether the command string is complete

// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB  + NEO_KHZ800);

class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string value = pCharacteristic->getValue();

      int index;
      if (value.length() > 0) {
        for (int i = 0; i < value.length(); i++) {
          cmd[i] = value[i];
          index = i + 1;
        }
        cmd[index] = '\0'; // terminate the string
        Serial << "BLE received '" << cmd << "'" << endl;
        cmd_complete = true;
      }
    }
};

// Custom show functions which will use the RMT hardware to drive the LEDs.
void myCustomShow1(void) {
  uint8_t *pixels = ws2812fx.getPixels();
  // numBytes is one more then the size of the ws2812fx's *pixels array.
  // the extra byte is used by the driver to insert the LED reset pulse at the end.
  uint16_t numBytes = ws2812fx.getNumBytes() + 1;
  rmt_write_sample(RMT_CHANNEL_0, pixels, numBytes, false); // channel 0
}

void recvChar(void) {
    static byte index = 0;
    while (Serial.available() > 0 && cmd_complete == false) {
        char rc = Serial.read();
        if (rc != '\n') {
            if(index < MAX_NUM_CHARS) cmd[index++] = rc;
        } else {
            cmd[index] = '\0'; // terminate the string
            index = 0;
            Serial << "received '" << cmd << "'" << endl;
            cmd_complete = true;
        }
    }
}

void ProcessCommand() {
    if (strncmp(cmd,"b ",2) == 0) {
      uint8_t b = (uint8_t)atoi(cmd + 2);
      ws2812fx.setBrightness(b);
      Serial << "Set brightness to: " << ws2812fx.getBrightness() << endl;
    }
    if (strncmp(cmd,"s ",2) == 0) {
      uint16_t s = (uint16_t)atoi(cmd + 2);
      ws2812fx.setSpeed(s); 
      Serial << "Set speed to: " << ws2812fx.getSpeed() << endl;
    }
    if (strncmp(cmd,"m ",2) == 0) {
      uint8_t m = (uint8_t)atoi(cmd + 2);
      ws2812fx.setMode(m);
      Serial << "Set mode to: " << ws2812fx.getMode() << " - " << ws2812fx.getModeName(ws2812fx.getMode()) << endl;
    }
    if (strncmp(cmd,"c ",2) == 0) {
      uint32_t c = (uint32_t)strtoul(cmd + 2, NULL, 16);
      ws2812fx.setColor(c);
      Serial << "Set color to: 0x" << String(ws2812fx.getColor(), HEX) << endl;
    }

    cmd[0] = '\0';         // reset the commandstring
    cmd_complete = false;  // reset command complete
}

void setup() {
  Serial.begin(115200);
  delay(500);

  //Bluetooth Setup
  char chipName[20];
  uint64_t chipid=ESP.getEfuseMac();
  snprintf(chipName, 20, "chip-%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
  Serial << "Chip Name: " << chipName << endl;
  BLEDevice::init(chipName);
  BLEServer *pServer = BLEDevice::createServer();
  BLEService *pService = pServer->createService(SERVICE_UUID);
  BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                        CHARACTERISTIC_UUID,
                                        BLECharacteristic::PROPERTY_READ |
                                        BLECharacteristic::PROPERTY_WRITE
                                      );
  pCharacteristic->setCallbacks(new MyCallbacks());
  pCharacteristic->setValue("Isomia LED Controller");
  pService->start();
  BLEAdvertising *pAdvertising = pServer->getAdvertising();
  pAdvertising->start();

  //LED Setup
  ws2812fx.init();
  ws2812fx.setBrightness(10);
  rmt_tx_int(RMT_CHANNEL_0, ws2812fx.getPin()); // assign ws2812fx1 to RMT channel 0
  ws2812fx.setCustomShow(myCustomShow1); // set the custom show function to forgo the NeoPixel
  // parameters: seg_index, start, stop, mode, color, speed, options
  ws2812fx.setSegment(0, 0, LED_COUNT-1, FX_MODE_COMET, GREEN, 1000, NO_OPTIONS);
  ws2812fx.start(); // start'em up
}

void loop() {
  ws2812fx.service(); // service each ws2812fx instance

  recvChar(); // read serial comm
  if (cmd_complete) {
    ProcessCommand();
  }
}
```
Related Topic