Video als BMP-Sequenz von SD-Karte auf Neopixel Stripe abspielen

Hallo zusammen,

ich stehe vor einem neuen Kunstprojekt, bei dem ich verschiedene Röhren im Inneren mit LED-Stripes ausstatte und so eine interaktive Licht-Installation kreiere.

Nun möchte ich nicht nur Animationen "coden", sondern auch schon existierende Videos abspielen.
Dazu war meine Idee, das Video als BMP-Sequenz abzuspielen. Diese liegt auf einer SD-Karte in genau der Auflösung meines LED-Stripes (60x1) und in einer Formatierung mit der ich umgehen kann. Soll heißen, ein einzelnes Bild kann ich anzeigen. Mein Problem liegt beim Abspielen mehrerer Bilder hintereinander.

Ich nutze folgende Libs: FastLED und SDfat. Hier ein Beispielcode:

//LED-STRIPE
#include "FastLED.h"
FASTLED_USING_NAMESPACE
CRGB stripe[60] = {0};

//SD-KARTE---------------------------------------------------------------------
#include "SdFat.h"
const uint8_t SD_CS_PIN = 46;
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
#define SPI_CLOCK SD_SCK_MHZ(50)
// Try to select the best SD card configuration.
#if HAS_SDIO_CLASS
#define SD_CONFIG SdioConfig(FIFO_SDIO)
#elif ENABLE_DEDICATED_SPI
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
#else  // HAS_SDIO_CLASS
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
#endif  // HAS_SDIO_CLASS
SdFat32 sd;
File32 dir;
File32 file;
//------------------------------------------------------------------------------
// Store error strings in flash to save RAM.
#define error(s) sd.errorHalt(&Serial, F(s))
//------------------------------------------------------------------------------

void setup() {
  delay(3000);
  Serial.begin(9600);
  FastLED.addLeds < WS2812B, 2, GRB > (stripe, 60).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(200);

  if (!sd.begin(SD_CONFIG)) {
    sd.initErrorHalt(&Serial);
  }
  if (!dir.open("/TestVideo")) {
    error("dir.open failed");
  }
}

void loop() {
  if(!file.openNext(&dir, O_RDONLY)){
    error("file.open failed");
  } else {
    Serial.println("file opened");
    showBMP(file);
    file.close();
  } 
  FastLED.show();
  delay(1000/30) ;
}

void showBMP(File32 &file) {
  file.seek(54);
  for (int i = 0; i < 40; i++) {
    byte b = file.read();
    byte g = file.read();
    byte r = file.read();
    file.read();
    stripe[i] = CRGB(r, g, b);
  }
}


Bei diesem Code kommt folgender Output:

file opened
error: file.open failed
SdError: 0XC,0XFF

Das erste Bild wird auch auf dem Stripe angezeigt.

Ich habe bisher rausgefunden, dass wohl die Zeile mit "FastLED.show();" das Problem ist. Wenn ich sie auskommentiere, öffnet das Programm alle Dateien im Ordner TestVideo.
Aber ohne "FastLED.show();" zeigt mir der Stripe nix an.
Gibt es eine Alternative, um die LEDs zum Show zu bringen?
Eine andere Lib würde ich ungern nutzen, da ich später Funktionen wie Overlay o.ä. nutzen möchte.

Ich bin nun mit meinem Latein am Ende.
Habt Ihr eine Idee, wie man das Problem lösen kann?

Viele Grüße und schonmal danke für Eure Antworten,
Nougat

Auf welcher Hardware soll das laufen?
Da es ohne FastLED geht, fällt der Verdacht auf einen zu knappen RAM-Bereich.

Ah, stimmt, hab ich vergessen: Ich nutze aktuell einen Arduino Mega 2560.
Insgesamt werde ich max. 10 Röhren mit jeweils bis zu max. 80 Pixeln bespielen.
An jeder Röhre hängt ein Ultraschall-Sensor für die Interaktivität.
Je nach dem, welcher Sensor betätigt wird, soll ein oder mehrere Videos bzw. eine Animation abgespielt werden.

Ich bin auch offen gegenüber besserer Hardware (kenne mich nur mit sonst nix aus).

Was lustigerweise geht, ist wenn ich dem Strip vor dem FastLED.show(); eine andere Farbe gebe:

void loop() {
  if(!file.openNext(&dir, O_RDONLY)){
    error("file.open failed");
  } else {
    Serial.println("file opened");
    showBMP(file);
    file.close();
  }  
  fill_solid(stripe, 60, CRGB(200, 0, 0));
  FastLED.show();
  delay(1000/30);
}
FastLED.show();

braucht es unbedingt, da nur diese Funktion die Daten an die LEDs schickt. Während der Datenübertragung ist der interrupt gesperrt und darum kann nichts anderes während dieser Zeit gemacht werden.

Also sind das irgendwas um 700 Pixel.
Da jedes Pixel 3 Byte braucht sind das ca 2100 Byte für den Speicher der LED-Werte.

Grüße Uwe

Ja, aber dennoch verstehe ich nicht, warum es klappt, wenn ich den Stripe vor dem FastLED.show(); mit einer Farbe fülle...

Jetzt verstehe ich Dich nicht.
Grüße Uwe

Damit meine ich, dass bei folgendem Code nach dem Öffnen der ersten Datei ein Fehler kommt. Dann, wenn eigentlich die zweite Datei geöffnet werden soll, geht es nicht und der error "file.open failed" wird angezeigt.

void loop() {
  if(!file.openNext(&dir, O_RDONLY)){
    error("file.open failed");
  } else {
    Serial.println("file opened");
    showBMP(file);
    file.close();
  } 
  FastLED.show();
  delay(1000/30) ;
}

.
.

Bei diesem Code (unten) funktioniert das Öffnen der nächsten Dateien auf der SD-Karte. Ich verstehe nicht, warum das "fill_solid()" einen Unterschied macht.

void loop() {
  if(!file.openNext(&dir, O_RDONLY)){
    error("file.open failed");
  } else {
    Serial.println("file opened");
    showBMP(file);
    file.close();
  }  
  fill_solid(stripe, 60, CRGB(200, 0, 0));
  FastLED.show();
  delay(1000/30);
}

Hallo,

Lass Dir doch mal den Dateinamen anzeigen. ich vermute da ist was faul deshalb kann die Datei nicht geöffnet werden.

Du bekommst jedenfalls kein (bool)file==false .

Aber dass sich die Variablen stripe, file, dir nicht irgendwie verheddern, siehst du auch nicht.

Ich auch nicht, ehrlich gesagt.

Warum ist FastLED.show(); nicht Teil von showBMP , wie es der Funktionsname andeutet?

Ok, ich habe den Fehler gefunden ...
Im späteren kompletten Set-Up werden die Stripes über ein externes Netzteil mit Strom versorgt.
Für meine kleinen Funktionstests nutze ich meistens aber die Stromversorgung über den Arduino, da das bisher immer funktioniert hat.
So habe ich das hier auch gemacht. Wahrscheinlich hat der Test-Stripe so viel Strom gezogen, dass es nicht mehr für den SD-Kartenleser gereicht hat.

Auf jeden Fall funktioniert der erste Code nun mit einer externen Stromquelle für den Stripe.

Das habe ich nicht gemacht, weil im späteren Set-Up verschiedene Funktionen und verschiedene Videos aufgerufen werden. Bei jedem Video muss ich separat die Helligkeitswerte anpassen. Die showBMP() soll lediglich das Bild ins Array des Stripes schreiben. Natürlich könnte ich die showBMP() umbenennen in writeBMP() oder so, aber das war für mich in dem Moment am intuitivsten. =)

Schon einmal vielen Dank für Eure Hilfe. Es könnte passieren, dass ich in den nächsten 2 Monaten hier nochmal was poste, wenn ich am verzweifeln bin. XD