Add SDCard AnimatedGIF example

This commit is contained in:
mrfaptastic 2023-03-13 00:09:40 +00:00
parent efad4c1dfd
commit 326c9a0de7
12 changed files with 517 additions and 0 deletions

View file

@ -0,0 +1,268 @@
/*********************************************************************
* AnimatedGif LED Matrix Panel example where the GIFs are
* stored on a SD card connected to the ESP32 using the
* standard GPIO pins used for SD card acces via. SPI.
*
* Put the gifs into a directory called 'gifs' (case sensitive) on
* a FAT32 formatted SDcard.
********************************************************************/
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
#include <AnimatedGIF.h>
/********************************************************************
* Pin mapping below is for LOLIN D32 (ESP 32)
*
* Default pin mapping used by this library is NOT compatable with the use of the
* ESP32-Arduino 'SD' card library (there is overlap). As such, some of the pins
* used for the HUB75 panel need to be shifted.
*
* 'SD' card library requires GPIO 23, 18 and 19
* https://github.com/espressif/arduino-esp32/tree/master/libraries/SD
*
*/
/*
* Connect the SD card to the following pins:
*
* SD Card | ESP32
* D2 -
* D3 SS
* CMD MOSI
* VSS GND
* VDD 3.3V
* CLK SCK
* VSS GND
* D0 MISO
* D1 -
*/
/**** SD Card GPIO mappings ****/
#define SS_PIN 5
//#define MOSI_PIN 23
//#define MISO_PIN 19
//#define CLK_PIN 18
/**** HUB75 GPIO mapping ****/
// GPIO 34+ are on the ESP32 are input only!!
// https://randomnerdtutorials.com/esp32-pinout-reference-gpios/
#define A_PIN 33 // remap esp32 library default from 23 to 33
#define B_PIN 32 // remap esp32 library default from 19 to 32
#define C_PIN 22 // remap esp32 library defaultfrom 5 to 22
//#define R1_PIN 25 // library default for the esp32, unchanged
//#define G1_PIN 26 // library default for the esp32, unchanged
//#define B1_PIN 27 // library default for the esp32, unchanged
//#define R2_PIN 14 // library default for the esp32, unchanged
//#define G2_PIN 12 // library default for the esp32, unchanged
//#define B2_PIN 13 // library default for the esp32, unchanged
//#define D_PIN 17 // library default for the esp32, unchanged
//#define E_PIN -1 // IMPORTANT: Change to a valid pin if using a 64x64px panel.
//#define LAT_PIN 4 // library default for the esp32, unchanged
//#define OE_PIN 15 // library default for the esp32, unchanged
//#define CLK_PIN 16 // library default for the esp32, unchanged
/***************************************************************
* HUB 75 LED DMA Matrix Panel Configuration
**************************************************************/
#define PANEL_RES_X 64 // Number of pixels wide of each INDIVIDUAL panel module.
#define PANEL_RES_Y 32 // Number of pixels tall of each INDIVIDUAL panel module.
#define PANEL_CHAIN 1 // Total number of panels chained one to another
/**************************************************************/
AnimatedGIF gif;
MatrixPanel_I2S_DMA *dma_display = nullptr;
static int totalFiles = 0; // GIF files count
static File FSGifFile; // temp gif file holder
static File GifRootFolder; // directory listing
std::vector<std::string> GifFiles; // GIF files path
const int maxGifDuration = 30000; // ms, max GIF duration
#include "gif_functions.hpp"
#include "sdcard_functions.hpp"
/**************************************************************/
void draw_test_patterns();
int gifPlay( const char* gifPath )
{ // 0=infinite
if( ! gif.open( gifPath, GIFOpenFile, GIFCloseFile, GIFReadFile, GIFSeekFile, GIFDraw ) ) {
log_n("Could not open gif %s", gifPath );
}
Serial.print("Playing: "); Serial.println(gifPath);
int frameDelay = 0; // store delay for the last frame
int then = 0; // store overall delay
while (gif.playFrame(true, &frameDelay)) {
then += frameDelay;
if( then > maxGifDuration ) { // avoid being trapped in infinite GIF's
//log_w("Broke the GIF loop, max duration exceeded");
break;
}
}
gif.close();
return then;
}
void setup()
{
Serial.begin(115200);
// **************************** Setup SD Card access via SPI ****************************
if(!SD.begin(SS_PIN)){
// bool begin(uint8_t ssPin=SS, SPIClass &spi=SPI, uint32_t frequency=4000000, const char * mountpoint="/sd", uint8_t max_files=5, bool format_if_empty=false);
Serial.println("Card Mount Failed");
return;
}
uint8_t cardType = SD.cardType();
if(cardType == CARD_NONE){
Serial.println("No SD card attached");
return;
}
Serial.print("SD Card Type: ");
if(cardType == CARD_MMC){
Serial.println("MMC");
} else if(cardType == CARD_SD){
Serial.println("SDSC");
} else if(cardType == CARD_SDHC){
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);
//listDir(SD, "/", 1, false);
Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
// **************************** Setup DMA Matrix ****************************
HUB75_I2S_CFG mxconfig(
PANEL_RES_X, // module width
PANEL_RES_Y, // module height
PANEL_CHAIN // Chain length
);
// Need to remap these HUB75 DMA pins because the SPI SDCard is using them.
// Otherwise the SD Card will not work.
mxconfig.gpio.a = A_PIN;
mxconfig.gpio.b = B_PIN;
mxconfig.gpio.c = C_PIN;
// mxconfig.gpio.d = D_PIN;
//mxconfig.clkphase = false;
//mxconfig.driver = HUB75_I2S_CFG::FM6126A;
// Display Setup
dma_display = new MatrixPanel_I2S_DMA(mxconfig);
// Allocate memory and start DMA display
if( not dma_display->begin() )
Serial.println("****** !KABOOM! HUB75 memory allocation failed ***********");
dma_display->setBrightness8(128); //0-255
dma_display->clearScreen();
// **************************** Setup Sketch ****************************
Serial.println("Starting AnimatedGIFs Sketch");
// SD CARD STOPS WORKING WITH DMA DISPLAY ENABLED>...
File root = SD.open("/gifs");
if(!root){
Serial.println("Failed to open directory");
return;
}
File file = root.openNextFile();
while(file){
if(!file.isDirectory())
{
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.println(file.size());
std::string filename = "/gifs/" + std::string(file.name());
Serial.println(filename.c_str());
GifFiles.push_back( filename );
// Serial.println("Adding to gif list:" + String(filename));
totalFiles++;
}
file = root.openNextFile();
}
file.close();
Serial.printf("Found %d GIFs to play.", totalFiles);
//totalFiles = getGifInventory("/gifs");
// This is important - Set the right endianness.
gif.begin(LITTLE_ENDIAN_PIXELS);
}
void loop(){
// Iterate over a vector using range based for loop
for(auto & elem : GifFiles)
{
gifPlay( elem.c_str() );
gif.reset();
delay(500);
}
}
void draw_test_patterns()
{
// fix the screen with green
dma_display->fillRect(0, 0, dma_display->width(), dma_display->height(), dma_display->color444(0, 15, 0));
delay(500);
// draw a box in yellow
dma_display->drawRect(0, 0, dma_display->width(), dma_display->height(), dma_display->color444(15, 15, 0));
delay(500);
// draw an 'X' in red
dma_display->drawLine(0, 0, dma_display->width()-1, dma_display->height()-1, dma_display->color444(15, 0, 0));
dma_display->drawLine(dma_display->width()-1, 0, 0, dma_display->height()-1, dma_display->color444(15, 0, 0));
delay(500);
// draw a blue circle
dma_display->drawCircle(10, 10, 10, dma_display->color444(0, 0, 15));
delay(500);
// fill a violet circle
dma_display->fillCircle(40, 21, 10, dma_display->color444(15, 0, 15));
delay(500);
delay(1000);
}

View file

@ -0,0 +1,15 @@
# ESP32-HUB75-MatrixPanel-DMA SDCard example
A very basic example using the 'Animated GIF' library by Larry Bank + the SD / File system library provided for Arduino by Espressif.
Some default HUB75 pins need to be remapped to accomodate for the SD Card.
![image](esp32_sdcard.jpg)
## How to use it?
1. Format a SD Card with FAT32 file system (default setting)
2. Create a directory called 'gifs'
3. Drop your gifs in there. The resolution of the GIFS must match that of the display.

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

View file

@ -0,0 +1,132 @@
// Code copied from AnimatedGIF examples
#ifndef M5STACK_SD
// for custom ESP32 builds
#define M5STACK_SD SD
#endif
static void * GIFOpenFile(const char *fname, int32_t *pSize)
{
//log_d("GIFOpenFile( %s )\n", fname );
FSGifFile = M5STACK_SD.open(fname);
if (FSGifFile) {
*pSize = FSGifFile.size();
return (void *)&FSGifFile;
}
return NULL;
}
static void GIFCloseFile(void *pHandle)
{
File *f = static_cast<File *>(pHandle);
if (f != NULL)
f->close();
}
static int32_t GIFReadFile(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen)
{
int32_t iBytesRead;
iBytesRead = iLen;
File *f = static_cast<File *>(pFile->fHandle);
// Note: If you read a file all the way to the last byte, seek() stops working
if ((pFile->iSize - pFile->iPos) < iLen)
iBytesRead = pFile->iSize - pFile->iPos - 1; // <-- ugly work-around
if (iBytesRead <= 0)
return 0;
iBytesRead = (int32_t)f->read(pBuf, iBytesRead);
pFile->iPos = f->position();
return iBytesRead;
}
static int32_t GIFSeekFile(GIFFILE *pFile, int32_t iPosition)
{
int i = micros();
File *f = static_cast<File *>(pFile->fHandle);
f->seek(iPosition);
pFile->iPos = (int32_t)f->position();
i = micros() - i;
//log_d("Seek time = %d us\n", i);
return pFile->iPos;
}
// Draw a line of image directly on the LCD
void GIFDraw(GIFDRAW *pDraw)
{
uint8_t *s;
uint16_t *d, *usPalette, usTemp[320];
int x, y, iWidth;
iWidth = pDraw->iWidth;
if (iWidth > PANEL_RES_X)
iWidth = PANEL_RES_X;
usPalette = pDraw->pPalette;
y = pDraw->iY + pDraw->y; // current line
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;
int x, iCount;
pEnd = s + iWidth;
x = 0;
iCount = 0; // count non-transparent pixels
while(x < iWidth) {
c = ucTransparent-1;
d = usTemp;
while (c != ucTransparent && s < pEnd) {
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?
for(int xOffset = 0; xOffset < iCount; xOffset++ ){
dma_display->drawPixel(x + xOffset, y, usTemp[xOffset]); // 565 Color Format
}
x += iCount;
iCount = 0;
}
// no, look for a run of transparent pixels
c = ucTransparent;
while (c == ucTransparent && s < pEnd) {
c = *s++;
if (c == ucTransparent)
iCount++;
else
s--;
}
if (iCount) {
x += iCount; // skip these
iCount = 0;
}
}
} else {
s = pDraw->pPixels;
// Translate the 8-bit pixels through the RGB565 palette (already byte reversed)
for (x=0; x<iWidth; x++)
dma_display->drawPixel(x, y, usPalette[*s++]); // color 565
/*
usTemp[x] = usPalette[*s++];
for (x=0; x<pDraw->iWidth; x++) {
dma_display->drawPixel(x, y, usTemp[*s++]); // color 565
} */
}
} /* GIFDraw() */

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View file

@ -0,0 +1,102 @@
/************************ SD Card Code ************************/
// As per: https://github.com/espressif/arduino-esp32/tree/master/libraries/SD/examples/SD_Test
void listDir(fs::FS &fs, const char * dirname, uint8_t levels, bool add_to_gif_list = false){
Serial.printf("Listing directory: %s\n", dirname);
File root = fs.open(dirname);
if(!root){
Serial.println("Failed to open directory");
return;
}
if(!root.isDirectory()){
Serial.println("Not a directory");
return;
}
File file = root.openNextFile();
while(file){
if(file.isDirectory()){
Serial.print(" DIR : ");
Serial.println(file.name());
if(levels){
listDir(fs, file.path(), levels -1, false);
}
} else {
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.println(file.size());
if (add_to_gif_list && levels == 0)
{
GifFiles.push_back( std::string(dirname) + file.name() );
Serial.println("Adding to gif list:" + String(dirname) +"/" + file.name());
totalFiles++;
}
}
file = root.openNextFile();
}
file.close();
}
void readFile(fs::FS &fs, const char * path){
Serial.printf("Reading file: %s\n", path);
File file = fs.open(path);
if(!file){
Serial.println("Failed to open file for reading");
return;
}
Serial.print("Read from file: ");
while(file.available()){
Serial.write(file.read());
}
file.close();
}
void testFileIO(fs::FS &fs, const char * path){
File file = fs.open(path);
static uint8_t buf[512];
size_t len = 0;
uint32_t start = millis();
uint32_t end = start;
if(file){
len = file.size();
size_t flen = len;
start = millis();
while(len){
size_t toRead = len;
if(toRead > 512){
toRead = 512;
}
file.read(buf, toRead);
len -= toRead;
}
end = millis() - start;
Serial.printf("%u bytes read for %u ms\n", flen, end);
file.close();
} else {
Serial.println("Failed to open file for reading");
}
file = fs.open(path, FILE_WRITE);
if(!file){
Serial.println("Failed to open file for writing");
return;
}
size_t i;
start = millis();
for(i=0; i<2048; i++){
file.write(buf, 512);
}
end = millis() - start;
Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
file.close();
}