LED flicker and e-ink display slideshow on loop issues using millis()

hello, i am having trouble integrating in the loop function both a flickering LED (meant to simulate a candle flickering) and a slideshow of images on an e-ink display that cycles through a new image every 30 seconds.

i am using this display from adafruit and harvested some of the code from their example for displaying images on the display. i am also using the nano 33 iot.

what i know works: the flickering LED code works on its own. the slideshow (using only two photos for this test) works on its own in the loop function too, but the the cycle is faster than what i thought i coded for.

because i want these two functions to run simultaneously, i thought using the millis() function for both would make sense, but it’s not really working.

as the code stands right now the LED flicker functions works for about 16 seconds, then stays at a constant random brightness. while that is happening, the display does nothing. after the light stops flickering, there is about a 3 seconds pause and then the display begins its cycle but with only about 10 seconds in between the images (from beginning of refresh cycle to final image showing) instead of the 30 seconds i thought i had set.

what am i not understanding?

any help is appreciated, thank you in advance!

// SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT

// Adafruit_ImageReader test for Adafruit E-Ink Breakouts.
// Demonstrates loading images from SD card or flash memory to the screen,
// to RAM, and how to query image file dimensions.
// Requires BMP file in root directory of QSPI Flash:
// blinka.bmp.

#include <Adafruit_GFX.h>  // Core graphics library
#include "Adafruit_ThinkInk.h"
#include <SdFat_Adafruit_Fork.h>       // SD card & FAT filesystem library
#include <Adafruit_SPIFlash.h>         // SPI / QSPI flash library
#include <Adafruit_ImageReader_EPD.h>  // Image-reading functions

//CANDLE FLICKER VARIABLES
const int ledPin = A3;             
unsigned long previousMillis = 0;  
const long interval = 50;

//E-INK DISPLAY VARIABLES
const long einkInterval = 30 * 1000;  // 30 seconds for image
unsigned long lastEinkUpdate = 0;

// Comment out the next line to load from SPI/QSPI flash instead of SD card:
#define USE_SD_CARD

#define EPD_DC 10
#define EPD_CS 9
#define EPD_BUSY 7  // can set to -1 to not use a pin (will wait a fixed delay)
#define SRAM_CS 6
#define EPD_RESET 8   // can set to -1 and share with microcontroller Reset!
#define EPD_SPI &SPI  // primary SPI
#define SD_CS 5       // SD card chip select

// 2.13" Quadcolor EPD with JD79661 chipset
ThinkInk_213_Quadcolor_AJHE5 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY,
                                     EPD_SPI);

#if defined(USE_SD_CARD)
SdFat SD;                             // SD card filesystem
Adafruit_ImageReader_EPD reader(SD);  // Image-reader object, pass in SD filesys
#else

// SPI or QSPI flash filesystem (i.e. CIRCUITPY drive)
#if defined(__SAMD51__) || defined(NRF52840_XXAA)
Adafruit_FlashTransport_QSPI flashTransport(PIN_QSPI_SCK, PIN_QSPI_CS,
                                            PIN_QSPI_IO0, PIN_QSPI_IO1, PIN_QSPI_IO2, PIN_QSPI_IO3);
#else
#if (SPI_INTERFACES_COUNT == 1 || defined(ADAFRUIT_CIRCUITPLAYGROUND_M0))
Adafruit_FlashTransport_SPI flashTransport(SS, &SPI);
#else
Adafruit_FlashTransport_SPI flashTransport(SS1, &SPI1);
#endif
#endif
Adafruit_SPIFlash flash(&flashTransport);
FatVolume filesys;
Adafruit_ImageReader_EPD reader(filesys);  // Image-reader, pass in flash filesys
#endif

Adafruit_Image_EPD img;  // An image loaded into RAM
int32_t width = 0,       // BMP image dimensions
  height = 0;

void setup(void) {
  //CANDLE PIN
  pinMode(ledPin, OUTPUT);


  Serial.begin(115200);
  while (!Serial)
    ;  // Wait for Serial Monitor before continuing

  display.begin();
  display.setRotation(3);

  // The Adafruit_ImageReader constructor call (above, before setup())
  // accepts an uninitialized SdFat or FatVolume object. This MUST
  // BE INITIALIZED before using any of the image reader functions!
  Serial.print(F("Initializing filesystem..."));
  // SPI or QSPI flash requires two steps, one to access the bare flash
  // memory itself, then the second to access the filesystem within...
#if defined(USE_SD_CARD)
  // SD card is pretty straightforward, a single call...
  if (!SD.begin(SD_CS, SD_SCK_MHZ(10))) {  // Breakouts require 10 MHz limit due to longer wires
    Serial.println(F("SD begin() failed"));
    for (;;)
      ;  // Fatal error, do not continue
  }
#else
  // SPI or QSPI flash requires two steps, one to access the bare flash
  // memory itself, then the second to access the filesystem within...
  if (!flash.begin()) {
    Serial.println(F("flash begin() failed"));
    for (;;)
      ;
  }
  if (!filesys.begin(&flash)) {
    Serial.println(F("filesys begin() failed"));
    for (;;)
      ;
  }
#endif
  Serial.println(F("OK!"));

//E-INK SETUP

  // Query the dimensions of image WITHOUT loading to screen:
  Serial.print(F("Querying image size..."));
  stat = reader.bmpDimensions("image1.bmp", &width, &height);
  reader.printStatus(stat);   // How'd we do?
  if(stat == IMAGE_SUCCESS) { // If it worked, print image size...
    Serial.print(F("Image dimensions: "));
    Serial.print(width);
    Serial.write('x');
    Serial.println(height);
  }


}

void loop() {

  unsigned long currentMillis = millis();


//E-INK DISPLAY LOOP
  if (currentMillis - lastEinkUpdate >= einkInterval) {
    lastEinkUpdate = currentMillis;

  //IMAGE ARRAY
  const char* images[] = {"image1.bmp", "image2.bmp"};
  int numImages = sizeof(images) / sizeof(images[0]);

  for (int i = 0; i < numImages; i++) {

  loadImages(images[i]);

  }

  }

  //CANDLE FLICKER
    if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    int flicker = random(120, 255);
    analogWrite(ledPin, flicker);
  }
}


void loadImages(const char *filename) {
  // Load full-screen BMP file at position (0,0) (top left).
  Serial.print(F("Loading image to canvas..."));
  ImageReturnCode stat = reader.drawBMP((char *)filename, display, 0, 0);
  // reader.printStatus(stat); 
  display.display();
}

This is where your program stops the LED from flickering... while it loads all the images...

Replace the "for() loop" with a counter, something like...

if (i > numImages) // constrain "i" to numImages
  i = 0: // ... or reset the count to zero
loadImages[i]; // show "i"th image
i++: // increment image counter

That seems a bit fast for a e-ink display, most of Adafruit's displays have a warning not to update the display more often that about 180 seconds.

I don't have an e-ink display to do any testing, but I suspect that it takes longer than 50mS to transfer an image to the display, which would severely affect the code for the flickering LED. You could try putting the LED code in an interrupt service routine run off a timer interrupt.

After you use the IDE Tools/Auto Format and remove unneeded vertical white space (blank lines) maybe some of the obvious errors will show themselves to you. HINT scope.