animated gif sdcard spiffs
/* ESP32 Dev Module
* Upload speed: 921600
* CPU fr: 240Mhz
* Flash fr: QIO
* Flash size "4MB (32MB)
* Partition: Defult 4MB with spiffs (1.2MB APP/1.5 SB SPIFFS)
* Debug: none
* PSRAM: Disabled
* Arduin Runs on "Core 1"
* Events Run on: Core 1"
* Erase All Flash: Disabled
* JTAG: Disabled
* Zigbee : Disabled
*/
#include <SD.h>
// GPIO26 = pin 7 = button
// GPIO27 = pin 6 = switch (down = 3.3v vcc, up = gnd)
#include <SPI.h>
#include <FS.h>
#include <SPIFFS.h>
#include <TFT_eSPI.h> // Install this library with the Arduino Library Manager
// Don't forget to configure the driver for the display!
#include <AnimatedGIF.h> // Install this library with the Arduino Library Manager
#define SD_CS_PIN 5 // changed by MSC 12 // Chip Select Pin (CS) for SD card Reader
AnimatedGIF gif;
File gifFile; // Global File object for the GIF file
TFT_eSPI tft = TFT_eSPI();
const char *filename[2][7] = {
{ "/one.gif", "/two.gif", "/three.gif", "/four.gif", "/five.gif", "/six.gif", "/seven.gif" }, // Change to load other gif files in images/GIF
{ "/sub/one.gif", "/sub/two.gif", "/sub/three.gif", "/sub/four.gif", "/sub/five.gif", "/sub/six.gif", "/sub/seven.gif" }
};
char file_index=0;
int no_gifs = 7;
const uint8_t buttonPin = 26;
const uint8_t switchPin = 27;
volatile bool not_pressed = true;
// interrupt routine
void ARDUINO_ISR_ATTR buttonISR() {
not_pressed = false;
}
void setup() {
#ifdef DEBUG_MODE
Serial.begin(115200);
#endif
pinMode(switchPin, INPUT);
pinMode(buttonPin, INPUT_PULLUP);
attachInterrupt(buttonPin, buttonISR, RISING);
tft.begin();
tft.setRotation(2); // Adjust Rotation of your screen (0-3)
tft.fillScreen(TFT_BLACK);
// Initialize SD card
if (!SD.begin(SD_CS_PIN,tft.getSPIinstance())) {
#ifdef DEBUG_MODE
Serial.println("SD card initialization failed!");
#endif
return;
}
// Initialize SPIFFS
if (!SPIFFS.begin(true)) {
#ifdef DEBUG_MODE
Serial.println("SPIFFS initialization failed!");
#endif
}
// Reformmating the SPIFFS to have space if a large GIF is loaded
// You could run out of SPIFFS storage space if you load more than one image or a large GIF
// You can also increase the SPIFFS storage space by changing the partition of the ESP32
//
#ifdef DEBUG_MODE
Serial.println("Formatting SPIFFS...");
#endif
SPIFFS.format(); // This will erase all the files, change as needed, SPIFFs is non-volatile memory
#ifdef DEBUG_MODE
Serial.println("SPIFFS formatted successfully.");
#endif
if (digitalRead(switchPin) == HIGH) file_index = 1;
else file_index = 0;
} // setup
void loop() {
// Open GIF file from SD card
for (int i=0; i < no_gifs; i++) {
File sdFile = SD.open(filename[file_index][i]);
if (!sdFile) {
#ifdef DEBUG_MODE
Serial.println("Failed to open GIF file from SD card!");
#endif
return; // restarts loop()
}
// Create a file in SPIFFS to store the GIF
File spiffsFile = SPIFFS.open(filename[file_index][i], FILE_WRITE, true);
if (!spiffsFile) {
#ifdef DEBUG_MODE
Serial.println("Failed to copy GIF in SPIFFS!");
#endif
return;
}
// Read the GIF from SD card and write to SPIFFS
#ifdef DEBUG_MODE
Serial.println("Copy GIF in SPIFFS...");
#endif
byte buffer[4096]; // block size sd card
while (sdFile.available()) {
int bytesRead = sdFile.read(buffer, sizeof(buffer));
spiffsFile.write(buffer, bytesRead);
}
spiffsFile.close();
sdFile.close();
// Initialize the GIF
#ifdef DEBUG_MODE
Serial.println("Starting animation...");
#endif
gif.begin(BIG_ENDIAN_PIXELS);
// this is the gif display loop
// int times = 0;
while (not_pressed) {
if (gif.open(filename[file_index][i], fileOpen, fileClose, fileRead, fileSeek, GIFDraw)) {
tft.startWrite(); // The TFT chip select is locked low
while (gif.playFrame(true, NULL)) {
}
gif.close();
tft.endWrite(); // Release TFT chip select for the SD Card Reader
}
// times++;
}
tft.fillScreen(TFT_BLACK);
SPIFFS.remove(filename[file_index][i]);
not_pressed = true;
} // for filename[] loop
} // loop
// Callback for file operations for the Animated GIF Lobrary
void *fileOpen(const char *filename, int32_t *pFileSize) {
gifFile = SPIFFS.open(filename, FILE_READ);
*pFileSize = gifFile.size();
if (!gifFile) {
#ifdef DEBUG_MODE
Serial.println("Failed to open GIF file from SPIFFS!");
#endif
}
return &gifFile;
}
// Callback for file operations for the Animated GIF Library
void fileClose(void *pHandle){
gifFile.close();
}
// Callback for file operations for the Animated GIF Library
int32_t fileRead(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen) {
int32_t iBytesRead;
iBytesRead = iLen;
if ((pFile->iSize - pFile->iPos) < iLen)
iBytesRead = pFile->iSize - pFile->iPos;
if (iBytesRead <= 0)
return 0;
gifFile.seek(pFile->iPos);
int32_t bytesRead = gifFile.read(pBuf, iLen);
pFile->iPos += iBytesRead;
return bytesRead;
}
// Callback for file operations for the Animated GIF Library
int32_t fileSeek(GIFFILE *pFile, int32_t iPosition) {
if (iPosition < 0)
iPosition = 0;
else if (iPosition >= pFile->iSize)
iPosition = pFile->iSize - 1;
pFile->iPos = iPosition;
gifFile.seek(pFile->iPos);
return iPosition;
}
---------------GIFDraw.ino-------------------------------
// GIFDraw is called by AnimatedGIF library frame to screen
#define DISPLAY_WIDTH tft.width()
#define DISPLAY_HEIGHT tft.height()
#define BUFFER_SIZE 256 // Optimum is >= GIF width or integral division of width
#ifdef USE_DMA
uint16_t usTemp[2][BUFFER_SIZE]; // Global to support DMA use
#else
uint16_t usTemp[1][BUFFER_SIZE]; // Global to support DMA use
#endif
bool dmaBuf = 0;
// Draw a line of image directly on the LCD
void GIFDraw(GIFDRAW *pDraw)
{
uint8_t *s;
uint16_t *d, *usPalette;
int x, y, iWidth, iCount;
// Display bounds check and cropping
iWidth = pDraw->iWidth;
if (iWidth + pDraw->iX > DISPLAY_WIDTH)
iWidth = DISPLAY_WIDTH - pDraw->iX;
usPalette = pDraw->pPalette;
y = pDraw->iY + pDraw->y; // current line
if (y >= DISPLAY_HEIGHT || pDraw->iX >= DISPLAY_WIDTH || iWidth < 1)
return;
// Old image disposal
s = pDraw->pPixels;
if (pDraw->ucDisposalMethod == 2) // restore to background color
{
for (x = 0; x < iWidth; x++)
{
if (s[x] == pDraw->ucTransparent)
s[x] = pDraw->ucBackground;
}
pDraw->ucHasTransparency = 0;
}
// Apply the new pixels to the main image
if (pDraw->ucHasTransparency) // if transparency used
{
uint8_t *pEnd, c, ucTransparent = pDraw->ucTransparent;
pEnd = s + iWidth;
x = 0;
iCount = 0; // count non-transparent pixels
while (x < iWidth)
{
c = ucTransparent - 1;
d = &usTemp[0][0];
while (c != ucTransparent && s < pEnd && iCount < BUFFER_SIZE )
{
c = *s++;
if (c == ucTransparent) // done, stop
{
s--; // back up to treat it like transparent
}
else // opaque
{
*d++ = usPalette[c];
iCount++;
}
} // while looking for opaque pixels
if (iCount) // any opaque pixels?
{
// DMA would degrtade performance here due to short line segments
tft.setAddrWindow(pDraw->iX + x, y, iCount, 1);
tft.pushPixels(usTemp, iCount);
x += iCount;
iCount = 0;
}
// no, look for a run of transparent pixels
c = ucTransparent;
while (c == ucTransparent && s < pEnd)
{
c = *s++;
if (c == ucTransparent)
x++;
else
s--;
}
}
}
else
{
s = pDraw->pPixels;
// Unroll the first pass to boost DMA performance
// Translate the 8-bit pixels through the RGB565 palette (already byte reversed)
if (iWidth <= BUFFER_SIZE)
for (iCount = 0; iCount < iWidth; iCount++) usTemp[dmaBuf][iCount] = usPalette[*s++];
else
for (iCount = 0; iCount < BUFFER_SIZE; iCount++) usTemp[dmaBuf][iCount] = usPalette[*s++];
#ifdef USE_DMA // 71.6 fps (ST7796 84.5 fps)
tft.dmaWait();
tft.setAddrWindow(pDraw->iX, y, iWidth, 1);
tft.pushPixelsDMA(&usTemp[dmaBuf][0], iCount);
dmaBuf = !dmaBuf;
#else // 57.0 fps
tft.setAddrWindow(pDraw->iX, y, iWidth, 1);
tft.pushPixels(&usTemp[0][0], iCount);
#endif
iWidth -= iCount;
// Loop if pixel buffer smaller than width
while (iWidth > 0)
{
// Translate the 8-bit pixels through the RGB565 palette (already byte reversed)
if (iWidth <= BUFFER_SIZE)
for (iCount = 0; iCount < iWidth; iCount++) usTemp[dmaBuf][iCount] = usPalette[*s++];
else
for (iCount = 0; iCount < BUFFER_SIZE; iCount++) usTemp[dmaBuf][iCount] = usPalette[*s++];
#ifdef USE_DMA
tft.dmaWait();
tft.pushPixelsDMA(&usTemp[dmaBuf][0], iCount);
dmaBuf = !dmaBuf;
#else
tft.pushPixels(&usTemp[0][0], iCount);
#endif
iWidth -= iCount;
}
}
} /* GIFDraw() */