Adding OTA to Code That Uses FastLED Causes Timing Issues

Hey All -

I've got some code which mainly drives a WS2812 strip and currently runs on a Nano which I want to port over to an ESP8266 so that I may imply have the ability to update/change it OTA. I added the basic Arduino OTA code to my existing code, uploaded it to a NodeMCU, and powered on to test. Unfortunately, the animation stuttered and was sporadic.

I read that FastLED and ArduinoOTA aren't really compatible due to how FastLED works, but haven't found a solution yet. Does anyone have any ideas or suggestions? Current code with ArduinoOTA injected is below. Thanks!

Note: I tried taking "ArduinoOTA.handle();" out of loop, but nogo there...

#include <FastLED.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

#ifndef STASSID
#define STASSID "sid"
#define STAPSK "pass"
#endif

const char* ssid = STASSID;
const char* password = STAPSK;

// Init LED Strip
#define LED_PIN     D2
// #define NUM_LEDS    36
#define NUM_LEDS    41
#define BRIGHTNESS  254

//Init Flux LEDs
#define FLUX_1 D4
#define FLUX_2 D6
#define FLUX_3 D8
#define FLUX_4 D1

// Init Misc LEDs

#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

#define UPDATES_PER_SECOND 100

void setup() {
  delay( 3000 ); // power-up safety delay
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  FastLED.setBrightness(  BRIGHTNESS );
    
  pinMode(FLUX_1, OUTPUT);
  pinMode(FLUX_2, OUTPUT);

  Serial.begin(115200);
  Serial.println("Booting");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }
  ArduinoOTA.setHostname("Delorean");
  ArduinoOTA.setPassword("Gateway12");
  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH) {
      type = "sketch";
    } else {  // U_FS
      type = "filesystem";
    }

    // NOTE: if updating FS this would be the place to unmount FS using FS.end()
    Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) {
      Serial.println("Auth Failed");
    } else if (error == OTA_BEGIN_ERROR) {
      Serial.println("Begin Failed");
    } else if (error == OTA_CONNECT_ERROR) {
      Serial.println("Connect Failed");
    } else if (error == OTA_RECEIVE_ERROR) {
      Serial.println("Receive Failed");
    } else if (error == OTA_END_ERROR) {
      Serial.println("End Failed");
    }
  });
  ArduinoOTA.begin();
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void blink(int iMode){

  int delayTime = 100;
  int fade = 100;
  int arraysize = 12;
  int devider = 3;

  #define saturation 130
  CHSV color1 (160, saturation, 255); 
  CHSV color2 (160, saturation, 190); 
  CHSV color3 (160, saturation, 130); 
  CHSV color4 (160, saturation, 60); 
  CHSV color5 (160, saturation, 0); 
  CRGB off = CRGB::Black;
    
  //int r[] = {24, 25, 26, 27,  0,  1,  2,  3,  4,  5, 6, 7};
  int r[] = {29, 30, 31, 32,  0,  1,  2,  3,  4,  5, 6, 7};
  //int l[] = {24, 23, 22, 21, 15, 14, 13, 12, 11, 10, 9, 8};
  int l[] = {29, 28, 27, 26, 15, 14, 13, 12, 11, 10, 9, 8};
  // int tires[] = {20, 19, 18, 17};
  int tires[] = {20, 19, 18, 17};
  // int misc[] = {28, 29, 30, 31, 32, 33, 34, 35};
  int misc[] = {25, 24, 23, 22, 21, 33, 34, 35, 36, 37, 38, 39, 40};

  int iModulo = arraysize;


  leds[16] = CRGB::White;

  // *****************************************************************************
  for(int i = 0; i < arraysize; i++){

    int a = i;
    int b = (i+1)%arraysize;
    int c = (i+2)%arraysize;
    int d = (i+3)%arraysize;
    int e = (i+4)%arraysize;

    int aa = (i+6)%arraysize;
    int bb = (i+1+6)%arraysize;
    int cc = (i+2+6)%arraysize;
    int dd = (i+3+6)%arraysize;
    int ee = (i+4+6)%arraysize;


    // ************************************************************
    // ** Tires **************************************************
    for(int j = 0; j < 4; j++){
      leds[tires[j]] = color1;      
    }

    // ************************************************************
    // ** Tires **************************************************
    for(int j = 0; j < 13; j++){
      leds[misc[j]] = color1;      
    }
  
    // ************************************************************
    // ** Fluxkompensator *****************************************
    if(iMode == 1){
      iModulo *= 2;
      delayTime*2;
      devider *= 2;
    }
      
    if(i%iModulo < arraysize/devider){          
      digitalWrite(FLUX_1, 0);
      digitalWrite(FLUX_2, 0);
      digitalWrite(FLUX_3, 0);
      digitalWrite(FLUX_4, 0);
    } else if (i%iModulo < 2*arraysize/devider){      
      digitalWrite(FLUX_1, 0);   
      digitalWrite(FLUX_2, 1);
      digitalWrite(FLUX_3, 1);
      digitalWrite(FLUX_4, 1);
              
    } else {              
      digitalWrite(FLUX_1, 1);
      digitalWrite(FLUX_2, 0);
      digitalWrite(FLUX_3, 0);
      digitalWrite(FLUX_4, 0);

    }

    // ************************************************************
    // ** Strip **************************************************        
    // Left                           Right
    leds[l[a]] = color5;  leds[r[a]] = color5;
    leds[l[b]] = color4;  leds[r[b]] = color4;
    leds[l[c]] = color3;  leds[r[c]] = color3;
    leds[l[d]] = color2;  leds[r[d]] = color2;
    leds[l[e]] = color1;  leds[r[e]] = color1; 

    if(iMode == 1){
      leds[l[aa]] = color5;  leds[r[aa]] = color5;
      leds[l[bb]] = color4;  leds[r[bb]] = color4;
      leds[l[cc]] = color3;  leds[r[cc]] = color3;
      leds[l[dd]] = color2;  leds[r[dd]] = color2;
      leds[l[ee]] = color1;  leds[r[ee]] = color1; 
    }
    FastLED.show(); delay(delayTime); 
        
   }
  // *****************************************************************************
  
}

void loop()
{
   ArduinoOTA.handle();
   blink(0);
}

Hi,

I faced with the same issue, and in my case helped copmpletlely get rid of 'delay' function (either arduino wrapper delay or FastLed.delay).
It can works with delay, but OTA will not works (I checked :smiley: )

It is part of my loop function:

void loop() {
  static unsigned long prevMillsLedStrip = 0;

  ArduinoOTA.handle();
  
  const unsigned long currentMillis = millis();
  if (currentMillis - prevMillsLedStrip >= updateInterval) {
      // update your led strip state here
  }
  
  // ...

}

Another solution: enable OTA in setup(), wait for 5-10 seconds (a delay() should work great here), and then disable OTA.

This way you can use OTA by rebooting your project, it will be in OTA mode for a few seconds during which you can do your OTA. Just a short window at startup is all you need to get an OTA upload started, if needs be. After that, it'll be disabled and your project runs without interruption by OTA checks.

So frequently calling .handle() isn't necessary?

Also I found another solution: use second core of my ESP32 for accepting sketch update request.
I use P9813 as led driver. Here is my sketch:

#include <WiFi.h>
#include <ArduinoOTA.h>

#include <FastLED.h>

#define DEBUG

const char* ssid = "********";
const char* password = "********";

const int embLed = 2; // ESP32 Pin to which onboard LED is connected
int embLedState = LOW;  // ledState used to set the LED

// How many leds in your strip?
#define NUM_LEDS 2

CRGB leds[NUM_LEDS];

TaskHandle_t TaskUpdateSketch;

void setup() {
  pinMode(embLed, OUTPUT);
  
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    // Serial.println("Connection Failed! Rebooting...");
    delay(3000);
    ESP.restart();
  }

  xTaskCreatePinnedToCore(
                  updateSketch,           /* Task function. */
                  "updateSketch",         /* name of task. */
                  10000,                  /* Stack size of task */
                  NULL,                   /* parameter of the task */
                  0,                      /* priority of the task */
                  &TaskUpdateSketch,      /* Task handle to keep track of created task */
                  0);                     /* pin task to core 0 */

  FastLED.addLeds<P9813, SDA, SCL, GRB>(leds, NUM_LEDS);  // BGR ordering is typical
#ifdef DEBUG  
  leds[0] = CRGB::White;
  leds[1] = CRGB::White;
  FastLED.show();
  delay(3000);
#endif
}

void updateSketch(void * pvParameters) {
  ArduinoOTA
    .onStart([]() {
      String type;
      if (ArduinoOTA.getCommand() == U_FLASH)
        type = "sketch";
      else // U_SPIFFS
        type = "filesystem";
    });

  ArduinoOTA.begin();

  while (true) {
    ArduinoOTA.handle();
    delay(0); // ?
  }
}

void loop() {
  static uint8_t hue = 0;
  static int delay = 300;

  fill_rainbow(&(leds[0]), 1 /*led count*/, hue++ /*starting hue*/);
  fill_rainbow(&(leds[1]), 1 /*led count*/, hue - 127 /*starting hue*/);

  FastLED.delay(delay);

  embLedState = not(embLedState);
  digitalWrite(embLed, embLedState);
}

It seems I'm a bit rusty in this :slight_smile: OTA indeed needs to be called.

Just use a millis() based while loop or so. I should have looked up my old code for this.
After setting up all your OTA stuff, do this - still in setup():

  uint32_t startTime = millis();
  while (millis() - startTime < 5000) {
    ArduinoOTA.handle();
    yield();
  }

After those five seconds no more OTA possible, but also no interference from it.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.