ESP32, Tasks, WS2812b und WiFi

Hallo zusammen,

ich entwickle aktuell ein Projekt mit dem ESP32, welcher LEDs vom Typ WS2812B steuern soll. Das klappt auch im Großen und Ganzen gut, solange WiFi abgeschaltet ist.

Schalte ich WiFi an, werden immer wieder mal LEDs falsch angesteuert, aber halt auch immer nur, wenn die Daten übertragen werden ("FastLED.show()", ich nutze die aktuelle FastLED-Bibliothek). Das Problem ist wohl bekannt.
Was mich hier noch wundert, dass die Framerate leicht steigt und schwankt, wenn WiFi aktiviert ist, also eine Connection besteht. Trenne ich die Verbindung ("WiFi.diconnect()"), dann sinkt die Rate wieder auf den konstanten Wert.

Die Steuerung der LEDs habe ich in einem Task auf Core 0, die restlichen Teile des Codes laufen auf Core 1. Ein Tauschen der Cores ändert aber nichts.

Jetzt wollte ich mal schauen, welche Tasks durch FreeRTOS generiert werden und gleichzeitig laufen, aber vTaskList läßt sich nicht ohne größeren Aufwand auf dem ESP32 mit der Arduino-IDE nutzen.

Im Netz habe ich zwar eine Seite gefunden, auf welcher man sieht, dass ein "IDLE"-Task und ein "Tmr Svc"-Task läuft. Leider sieht man hier nicht die Kerne, auf welchen die laufen, bzgl. WiFi habe ich gar nichts gefunden.

Der Code ist mittlerweile schon etwas groß, aber ich muss mal schauen, ob ich da auf ein Minimum reduzieren kann. Aber ich fürchte, das wird nicht viel helfen.

Meine Befürchtung ist halt, dass das Timing für die Übertragung an die WS2812b durch Aktionen von WiFi kurz gestört werden, so dass hier der Bitstrom Fehler bekommt.

Gibt es noch generelle Ideen, was man ändern kann?

Gruß
Nils

Wifi braucht ziemlich viel Strom. Wie sieht die Versorgung aus?

Wie viele?

Schon über WS2815 nachgedacht?

Einen Typ mit Daten und Takt getrennt wie APA102 könnte besser sein.

Eines meiner Netzteile für die LEDs und USB verträgt sich nicht gut. Wenn ich auch den ESP32 mit dem Netzteil versorge und OTA verwende, ist es besser.

Ich verwende FastLED in der Arduino-IDE, ohne mich um die Tasks zu kümmern, funktioniert eigentlich ganz gut. Allerdings habe ich nur spärlichen Funkverkehr zur Animationsauswahl und deren Konfiguration mit JSON. Mehrere ESP32 hängen an meinem Router.

Hallo,

ich habe einen Streifen mit 144 LEDs, die Helligkeit habe ich mit setBrightness(15) aktuell stark gedrosselt, reicht auch vollkommen aus. Aktiv sind nur 32, wobei hier auch nicht alle auf "voller" Helligkeit laufen. Ich habe einen USB-Strommesser zwischen Notebook und ESP hängen, der misst ca. 200mA... wobei ich mir nicht sicher bin, ob ich dem Ding richtig trauen kann, vor allem, wenn der Strom von den LEDs gepulst verarbeitet werden würde. Aktuell habe ich leider kein Netzteil, welches genug Leistung hat, um alles LEDs unter voller Last betreiben zu können, das wären dann etwas unter 50W, also bei 5V 10A.

Rein rechnerisch käme ich nur mit den paar LEDs bei ca. 6% der Leistung auf ca. 115mA (=32 * 0,06A * 6%). Ich gehe aber von keiner linearen Stromaufnahme aus. Trotzdem sollte auch das USB-Steckernetzteil, welches ich als Vergleich nutze (ebenfalls mit dem Strommesser), bei 5v und 500mA nicht so stark einbrechen (das Netzteil könnte mehr liefern, aber ich denke, über den USB2-Anschluß wird keine höhere Leistung ausgehandelt).

Ich muss mal schauen, dass ich eine stärkere Stromquelle bekomme, um hier auf der sicheren Seite zu sein. Ein Oszi steht im Schrank, das müsste ich auch mal anschließen.

Wie gesagt, mich wundert, dass sich bei verbundenem WLAN die FPS erhöht, aber unregelmäßig wird.

Danke und Gruß
Nils

Update: ich habe jetzt eine zweite Stromversorgung für die LEDs angeschlossen, also Strom für LEDs über Netzteil, Masse der LEDs und die Steuerleitung an ESP, ESP über USB wegen seriellem Monitor. Somit haben die LEDs mit dem ESP gemeinsam eine Masseleitung. Die +5v vom ESP-WROOM-32-Board zu den LEDs ist getrennt. Leider funktioniert die Schaltung nicht mehr.

Die ESP-WROOM-32-Platine (vom Layout her DEVKITC) hat zwei nutzbare GND-Anschlüsse, aber beide führen nicht zum Erfolg. Ach ja, das Signal vom ESP geht über einen Level-Shifter zur Steuerleitung der LEDs.

Einen Level-Shifter verwende ich normal nicht, was für einen verwendest Du?

Sonst liest sich das OK.

Vergleiche mal mit Adafruit NeoPixel Überguide, die haben noch einen Kondensator drin, den ich allerdings auch nicht verwende.

Ohne Werbung machen zu wollen, aber der Level Shifter / Level Converter ist solch einer:

Gibt es von vielen Anbietern. Nach einem Blick mit dem Oszi waren die 5v des Netzteils sehr flattrig, ein Kondensator hat hier geholfen.

Jetzt teste ich mal, ob WiFi und LEDs gleichzeitig auch stabil laufen.

Update:
klappt leider nicht. Die Störungen sind zwar weniger, aber es blitzen immer noch LEDs unkontrolliert auf. Wird WiFi per "disconnect" abgeschaltet, gibt es keine Probleme. Ist WiFi nur verbunden, treten gelegentlich Störungen auf. Ist der Web-Server aktiv (ESPAsyncWebServer), sind die Störungen häufiger, werden Daten übertragen (Webseite neu geladen), dann sehe ich ein Feuerwerk.

Das ist einer für I²C, eher nicht so ideal, weil ein FET nach 5V fehlt. Ein einfaches IC mit je einem FET nach 5V und GND wäre besser. Ich nutze keinen Level Shifter.

Versorge bitte mal zum Testen den ESP32 vom Netzteil aus, bei mir hilft das. Ich mache Debug-Ausgaben auf ein OLED.

Es könnte auch helfen (anstelle des Levelshifters), die erste LED über eine Diode mit einer etwas niedrigeren Spannung zu betreiben, damit der HIGH-Pegel des ESP für sie ausreicht. Dereren höherer Ausgangspegel genügt dann für die nächste LED.

Gruß Tommy

Naja, ohne aktiviertes WiFi klappt ja alles. Aber sobald WiFi eingeschaltet ist, gehen die Störungen los.

Jetzt ist eben die Frage, ob die Störungen durch einen Einbruch der Stromversorgung auftreten (sollte ich jetzt ausgeschlossen haben, die zusätzliche Stromversorgung habe ich hinbekommen) oder ob es ein Problem durch die WiFi-Bibliothek und die genutzten Hardware-Komponenten im ESP auftreten. Ich habe schon viele Seiten zu diesem Thema durchsucht, auch das Abschalten der Interrupts oder Änderungen am Retry-Count habe ich probiert:
#define FASTLED_ALLOW_INTERRUPTS 0
#define FASTLED_INTERRUPT_RETRY_COUNT 0
Wobei mit der Abschalten der Interrupts über den in der Bibliothek genutzten Mechanismus wohl auch nur eine bestimmte Kategorie verzögert wird. Daher suche ich halt nach Hinweisen, ob das mit dem ESP überhaupt parallel klappt. Von den ESP her gibt es ja auch genügend Upgrades und der von mit genutzte (ist halt einfach vorhanden) ist nicht sonderlich neu. Evtl. müsste ich das auch mal mit einem anderen testen.

Aber grundlegend wäre es interessant, wie FreeRTOS und der ESP32 zusammen arbeiten. Zwei leistungsfähige Kerne, ein Low-Power-Kern, den man so wohl nicht direkt nutzen kann. Und dann die Frage, wie die Interrupts laufen, sprich, legt ein Interrupt kurzfristig beide Kerne lahm, oder einen beliebigen? Kann man die Interrupts auf einen Kern festlegen?

Naja, ich suche mal weiter.

Gruß
Nils

Hast Du die Spannungsversorgung an den LED mit Elkos gepuffert und genügend Einspeisepunkte für GND und 5V?

Gruß Tommy

Ich sehe nur, was Du zeigst, aber ein Testprogramm gehört bislang nicht dazu. Mein ESP32 und verschiedene LED-Streifentypen warten auf ihren Einsatz.

Noch'n Spruch: Manchmal verbirgt sich der Fehler genau da, wo man nicht sucht :slightly_smiling_face:

Hallo zusammen,

ich habe mal das Projekt reduziert, um verschiedene Punkte auszuschließen.

Natürlich gibt es, da der Webserver genutzt wird, noch eine "index.html" und eine "style.css". Darin steht jetzt aber nichts wichtiges, die index.html habe ich sinnlos auf 70kb aufgefüllt, damit beim Abruf über den Browser auch nennenswert Daten übertragen werden. Aber es ist letztlich nur Text.

// Try to avoid random color pixels
// https://github.com/FastLED/FastLED/wiki/Interrupt-problems
#define FASTLED_ALLOW_INTERRUPTS 0
#define FASTLED_INTERRUPT_RETRY_COUNT 0

#include <FastLED.h>
#include <WiFi.h>

// Include WPS library for ESP
#include "esp_wps.h"

// Use library for web server
// from https://github.com/me-no-dev/ESPAsyncWebServer
#include <ESPAsyncWebServer.h>
#include <AsyncTCP.h>

// Include support for SPI File System
#include "SPIFFS.h"

// Task handle
TaskHandle_t handleLEDLoop;
TaskHandle_t handleMainLoop;

// ##########################################
// Data pin for LED strip
#define DATA_PIN 18

// LED strip definition
#define COLUMNS 16
#define ROWS 9
#define NUM_LEDS (COLUMNS * ROWS)
#define BRIGHTNESSDEFAULT 20
uint8_t brightness = BRIGHTNESSDEFAULT;

// Fading steps, bigger steps result in less smoothing but faster handling
#define FADERSTEPSDEFAULT 1
uint8_t fadersteps = FADERSTEPSDEFAULT;
TickType_t leddelay;

// White factor definitions, simulating a warm white light
#define REDFACTOR 1.0
#define GREENFACTOR 0.7
#define BLUEFACTOR 0.3

// LED array
CRGB leds[ NUM_LEDS ];

// ##########################################
WiFiUDP wifiUdp;

#define ESP_WPS_MODE      WPS_TYPE_PBC
#define ESP_MANUFACTURER  "ESPRESSIF"
#define ESP_MODEL_NUMBER  "ESP32"
#define ESP_MODEL_NAME    "ESPRESSIF IOT"
#define ESP_DEVICE_NAME   "LEDTEST"

static esp_wps_config_t config;

// Network name
const char* hostname = ESP_DEVICE_NAME;

// ##########################################
// Timer definitions
volatile bool timerInterrupt = false;

// ##########################################
// Web server on port 80 / http default
AsyncWebServer server(80);

void setup()
{
  Serial.begin( 115200 );
  while( !Serial )
    vTaskDelay( 100 / portTICK_PERIOD_MS );

  Serial.println( "setup(): Initializing LED strip" );
  fill_solid( leds, NUM_LEDS, CRGB::Black );
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>( leds, NUM_LEDS );
  FastLED.setBrightness( 0 );
  FastLED.show();

  // Initialize SPIFFS
  if( !SPIFFS.begin( true ) )
  {
    Serial.println( "setup(): An Error has occurred while mounting SPIFFS" );
    return;
  }

  // Just deliver index.html, do not check for parameters
  server.on( "/", HTTP_GET, []( AsyncWebServerRequest *request )
    {
      Serial.print("server.on(): Executing on core " );
      Serial.println( xPortGetCoreID() );
      request->send( SPIFFS, "/index.html", String(), false, processor );
    }
  );
  
  // Route to load style.css file
  server.on( "/style.css", HTTP_GET, []( AsyncWebServerRequest *request )
    {
      request->send( SPIFFS, "/style.css", "text/css" );
    }
  );

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

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

void loop()
{
  vTaskDelete( NULL );
}


// ##########################################
// Main loop
void MainLoop( void * parameter )
{
  uint32_t waitingTime = 20000;
  Serial.print("MainLoop(): Executing on core " );
  Serial.println( xPortGetCoreID() );
  vTaskDelay( waitingTime / portTICK_PERIOD_MS );

  for(;;)
  {
    Serial.println( "MainLoop(): Connecting to WiFi." );
    ConnectToWifi();
    vTaskDelay( waitingTime / portTICK_PERIOD_MS );
    Serial.println( "MainLoop(): Starting WEB Server." );
    server.begin();
    vTaskDelay( waitingTime / portTICK_PERIOD_MS );
    Serial.println( "MainLoop(): Stopping WEB Server." );
    server.end();
    vTaskDelay( waitingTime / portTICK_PERIOD_MS );
    Serial.println( "MainLoop(): Disconnecting to WiFi." );
    WiFi.disconnect();
    vTaskDelay( waitingTime / portTICK_PERIOD_MS );
  }
}

void LEDLoop( void * parameter )
{
  Serial.print("LEDLoop(): Executing on core " );
  Serial.println( xPortGetCoreID() );

  // Initialize variables
  uint8_t column = 2; // Current processed main column
  int16_t cell;       // Current single LED
  uint8_t bright;     // Brightness for fading out
  uint8_t r, g, b;    // RGB values calculated with current correction factor for warm white simulation
  uint64_t fpssum, counter;
  uint32_t timebefore, timeafter, durationsum;
  
  // Turn off all LEDs, which means to set each LED to black
  fill_solid( leds, NUM_LEDS, CRGB::Black );
  FastLED.setBrightness( brightness );
  FastLED.show();

  // Calculate delay
  leddelay = 3 * fadersteps / portTICK_PERIOD_MS;

  // Set two columns to white
  for( uint8_t i = 0; i < ROWS; i++ )
  {
    r = (uint8_t)( 255 * REDFACTOR );
    g = (uint8_t)( 255 * GREENFACTOR );
    b = (uint8_t)( 255 * BLUEFACTOR );
    // Set the current column to white
    leds[ COLUMNS * i + column ] = CRGB( r , g, b );
    // Set the next column to white, check for overflow
    cell = COLUMNS * i + column  + 1;
    if( cell > NUM_LEDS )
      cell = 0;
    leds[ cell ] = CRGB( r , g, b );
  }
  FastLED.show();

  for(;;)
  {
    fpssum = 0;
    counter = 0;
    durationsum = 0;
    // Fade in right and out left
    for( uint8_t j = 0; j < ( 256 - fadersteps ); j += fadersteps )
    {
      r = (uint8_t)( j * REDFACTOR );
      g = (uint8_t)( j * GREENFACTOR );
      b = (uint8_t)( j * BLUEFACTOR );
 
      // Fade in right
      for( uint8_t i = 0; i < ROWS; i++ )
      {
        cell = COLUMNS * i + column + 2;
        if( cell >= NUM_LEDS )
          cell -= NUM_LEDS;
        if( cell >= NUM_LEDS ) Serial.println( "LEDLoop(): a cell: " + String( cell ) );
        leds[ cell ] = CRGB( r, g, b );
      }

      // Fade out left
      r = (uint8_t)(( 255 - j ) * REDFACTOR );
      g = (uint8_t)(( 255 - j ) * GREENFACTOR );
      b = (uint8_t)(( 255 - j ) * BLUEFACTOR );
      if( r <= fadersteps ) r = 0;
      if( g <= fadersteps ) g = 0;
      if( b <= fadersteps ) b = 0;
      for( uint8_t i = 0; i < ROWS ; i++ )
      {
        cell = COLUMNS * i + column - 1;
        if( column == 0 )
          cell += COLUMNS;
        if( column < 0 ) Serial.println( "LEDLoop(): b cell: " + String( cell ) );
        leds[ cell ] = CRGB( r, g, b );
      }

      timebefore = micros();
      FastLED.show();
      timeafter = micros();
      durationsum += timeafter - timebefore;
      
      fpssum += FastLED.getFPS();
      counter++;
      vTaskDelay( leddelay );
    }
    Serial.print( "LEDLoop(): FPS: " );
    Serial.println( fpssum / counter );
    Serial.print( "LEDLoop(): Duration sum: " );
    Serial.println( durationsum );

    // Go to next column
    column++;
    if( column >= COLUMNS )
      column = 0;
  }
}


// ##########################################
void wpsInitConfig()
{
  config.crypto_funcs = &g_wifi_default_wps_crypto_funcs;
  config.wps_type = ESP_WPS_MODE;
  strcpy( config.factory_info.manufacturer, ESP_MANUFACTURER );
  strcpy( config.factory_info.model_number, ESP_MODEL_NUMBER );
  strcpy( config.factory_info.model_name, ESP_MODEL_NAME );
  strcpy( config.factory_info.device_name, ESP_DEVICE_NAME );
}


// ##########################################
// Handle WiFi events
void WiFiEvent( WiFiEvent_t event, system_event_info_t info )
{
  switch( event )
  {
    case SYSTEM_EVENT_STA_START:
      break;
    case SYSTEM_EVENT_STA_GOT_IP:
      Serial.println( "WiFiEvent(): Connected to :" + String( WiFi.SSID() ) );
      Serial.print( "WiFiEvent(): Got IP: " );
      Serial.println( WiFi.localIP() );
      break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
      Serial.println( "WiFiEvent(): Disconnected from station, attempting reconnection" );
      WiFi.reconnect();
      break;
    case SYSTEM_EVENT_STA_WPS_ER_SUCCESS:
      Serial.println( "WiFiEvent(): WPS successfull, stopping WPS and connecting to: " + String( WiFi.SSID() ) );
      esp_wifi_wps_disable();
      delay( 10 );
      WiFi.begin();
      break;
    case SYSTEM_EVENT_STA_WPS_ER_FAILED:
      Serial.println( "WiFiEvent(): WPS failed, retrying" );
      esp_wifi_wps_disable();
      esp_wifi_wps_enable( &config );
      esp_wifi_wps_start( 0 );
      break;
    case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT:
      Serial.println( "WiFiEvent(): WPS timed out, retrying" );
      esp_wifi_wps_disable();
      esp_wifi_wps_enable( &config );
      esp_wifi_wps_start( 0 );
      break;
    case SYSTEM_EVENT_STA_WPS_ER_PIN:
      break;
    default:
      break;
  }
}

// Connect to WiFi
void ConnectToWifi()
{
  uint8_t counter = 30;
  WiFi.disconnect();
  WiFi.config( INADDR_NONE, INADDR_NONE, INADDR_NONE );
  WiFi.setHostname( hostname );

  WiFi.begin();
  while( WiFi.status() != WL_CONNECTED )
  {
    if( counter > 0 )
    {
      counter--;
      Serial.print( "." );
      vTaskDelay( 500 / portTICK_PERIOD_MS );
    }
    else
    {
      Serial.println( "." );
      break;
    }
  }

  // Check if connection was successfull
  if( WiFi.status() != WL_CONNECTED )
  {
    // No connection esablished
    WiFi.onEvent( WiFiEvent );
    WiFi.mode( WIFI_MODE_STA );
    Serial.println( "Starting WPS - wpsInitConfig();" );
    wpsInitConfig();
    esp_wifi_wps_enable( &config );
    esp_wifi_wps_start( 0 );
    while (WiFi.status() != WL_CONNECTED )
    {
       Serial.print( "." );
       delay( 500 );
    }
    Serial.println( "Starting WPS - done" );
  }
  else
  {
    Serial.print( "ConnectToWifi(): WiFi status: " );
    Serial.println( WiFi.status() );
    Serial.print( "ConnectToWifi(): WiFi IP address: " );
    Serial.println( WiFi.localIP() );
    Serial.print( "ConnectToWifi(): WiFi signal strength: " );
    Serial.println( WiFi.RSSI() );
  }
}

// HTML processor for variable replacement
String processor( const String& var )
{
  if( var == "xxx" )
    return "XxXxXxX";

  return String();
} 

In der Funktion LEDLoop wird ein Lauflicht mit ein- und ausblenden simuliert. Nach jedem Schritt lasse ich die Frames per Second als Schnitt ausgeben, weiterhin wird über das komplette Ein- und Ausblenden die Dauer von "FastLED.show()" addiert und ebenfalls ausgegeben.
Damit sehe ich schon, ohne dass LEDs angeschlossen sind, wie diese Zeit beim Aktivieren von WiFi oder Abrufen der index.html über den Web-Server doch deutlich länger wird. Ohne jetzt auch die LEDs überhaupt am Strom zu haben.

Aber ja, ein Elko ist angeschlossen, Strom kommt am Anfang und am Ende hinzu. Und da ich den LED-Streifen aktuell als Spirale um eine Papprolle gewickelt habe, ist die Zuleitung an beiden Seiten kurz.

Gruß
Nils

Um Fehler in Hard/Software ausschliesen oder bestätigen zu können, kann man weit verbreitete WS2812b Programme nutzen.

WLED erstellt auch eine Webseite, wo man alles steuern und einstellen kann. Aircoookie/WLED: Control WS2812B and many more types of digital RGB LEDs with an ESP8266 or ESP32 over WiFi! (github.com)

Wenn der Fehler dort auch auftritt, könnte das Problem eher an der Hardware liegen. Wenn das Problem dann nicht mehr auftritt, dann liegt es eher an der Software.

Gute Idee, denn sie zeigt, daß ich von Äpfeln rede und Du von Birnen. Die Dateien/Bibliotheken esp_wps.h, ESPAsyncWebServer.h, AsyncTCP.h verwende ich nicht. Meine Erfahrungen sind daher für Dich leider belanglos.

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