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();
}