前回は静止画(文字や図形)の表示で今回はESP32のフラッシュ(SPIFFS)に保存したGIF動画の再生方法です。
部品と配線
前回のブログ記事と同じです。
プログラム
ESP32-HUB75-MatrixPanel-I2S-DMAライブラリのサンプルプログラムで問題なく書き込めるのですが、SPIFFSにファイルを書き込むツールがArduino IDE 2.xでは使えないことが判明…!
代わりにlittelfsというのを使うと良いらしい。littlefsの使い方は↓を参照。
使い方参照:https://hatakekara.com/littlefs/
※Gif動画は”data”という名前のフォルダの中に入れてください(複数入れてOK)
※ファイルの書き込み時はシリアルモニタを閉じておかないとerror 2が出て書き込めないので注意!
プログラムもSPIFFSからlittlefsを使うように変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
// Example sketch which shows how to display a 64x32 animated GIF image stored in FLASH memory // on a 64x32 LED matrix // // Credits: https://github.com/bitbank2/AnimatedGIF/tree/master/examples/ESP32_LEDMatrix_I2S // /* INSTRUCTIONS * * 1. First Run the 'ESP32 Sketch Data Upload Tool' in Arduino from the 'Tools' Menu. * - If you don't know what this is or see it as an option, then read this: * https://github.com/me-no-dev/arduino-esp32fs-plugin * - This tool will upload the contents of the data/ directory in the sketch folder onto * the ESP32 itself. * * 2. You can drop any animated GIF you want in there, but keep it to the resolution of the * MATRIX you're displaying to. To resize a gif, use this online website: https://ezgif.com/ * * 3. Have fun. */ //#define FILESYSTEM SPIFFS//削除 //#include <SPIFFS.h>//削除 #include <LittleFS.h>//追加 #include <AnimatedGIF.h> #include <ESP32-HUB75-MatrixPanel-I2S-DMA.h> // ---------------------------- /* * Below is an is the 'legacy' way of initialising the MatrixPanel_I2S_DMA class. * i.e. MATRIX_WIDTH and MATRIX_HEIGHT are modified by compile-time directives. * By default the library assumes a single 64x32 pixel panel is connected. * * Refer to the example '2_PatternPlasma' on the new / correct way to setup this library * for different resolutions / panel chain lengths within the sketch 'setup()'. * */ #define PANEL_RES_X 64 // Number of pixels wide of each INDIVIDUAL panel module. #define PANEL_RES_Y 64 // Number of pixels tall of each INDIVIDUAL panel module. #define PANEL_CHAIN 1 // Total number of panels chained one to another #define A_PIN 33 // remap esp32 library default from 23 to 33 #define B_PIN 21 // remap esp32 library default from 19 to 21 #define C_PIN 22 // remap esp32 library defaultfrom 5 to 22 #define E_PIN 32 //MatrixPanel_I2S_DMA dma_display; MatrixPanel_I2S_DMA *dma_display = nullptr; uint16_t myBLACK = dma_display->color565(0, 0, 0); uint16_t myWHITE = dma_display->color565(255, 255, 255); uint16_t myRED = dma_display->color565(255, 0, 0); uint16_t myGREEN = dma_display->color565(0, 255, 0); uint16_t myBLUE = dma_display->color565(0, 0, 255); AnimatedGIF gif; File f; int x_offset, y_offset; // Draw a line of image directly on the LED Matrix void GIFDraw(GIFDRAW *pDraw) { uint8_t *s; uint16_t *d, *usPalette, usTemp[320]; int x, y, iWidth; iWidth = pDraw->iWidth; if (iWidth > MATRIX_WIDTH) iWidth = MATRIX_WIDTH; 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 + pDraw->iWidth; x = 0; iCount = 0; // count non-transparent pixels while(x < pDraw->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 // does not have transparency { s = pDraw->pPixels; // Translate the 8-bit pixels through the RGB565 palette (already byte reversed) for (x=0; x<pDraw->iWidth; x++) { dma_display->drawPixel(x, y, usPalette[*s++]); // color 565 } } } /* GIFDraw() */ void * GIFOpenFile(const char *fname, int32_t *pSize) { Serial.print("Playing gif: "); Serial.println(fname); //f = FILESYSTEM.open(fname);//削除 f = LittleFS.open(fname);//追加 if (f) { *pSize = f.size(); return (void *)&f; } return NULL; } /* GIFOpenFile() */ void GIFCloseFile(void *pHandle) { File *f = static_cast<File *>(pHandle); if (f != NULL) f->close(); } /* GIFCloseFile() */ 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; } /* GIFReadFile() */ 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; // Serial.printf("Seek time = %d us\n", i); return pFile->iPos; } /* GIFSeekFile() */ unsigned long start_tick = 0; void ShowGIF(char *name) { start_tick = millis(); if (gif.open(name, GIFOpenFile, GIFCloseFile, GIFReadFile, GIFSeekFile, GIFDraw)) { x_offset = (MATRIX_WIDTH - gif.getCanvasWidth())/2; if (x_offset < 0) x_offset = 0; y_offset = (MATRIX_HEIGHT - gif.getCanvasHeight())/2; if (y_offset < 0) y_offset = 0; Serial.printf("Successfully opened GIF; Canvas size = %d x %d\n", gif.getCanvasWidth(), gif.getCanvasHeight()); Serial.flush(); while (gif.playFrame(true, NULL)) { if ( (millis() - start_tick) > 8000) { // we'll get bored after about 8 seconds of the same looping gif break; } } gif.close(); } } /* ShowGIF() */ /************************* Arduino Sketch Setup and Loop() *******************************/ void setup() { Serial.begin(115200); delay(1000); HUB75_I2S_CFG mxconfig( PANEL_RES_X, // module width PANEL_RES_Y, // module height PANEL_CHAIN // Chain length ); mxconfig.gpio.a = A_PIN; mxconfig.gpio.b = B_PIN; mxconfig.gpio.c = C_PIN; mxconfig.gpio.e = E_PIN; // mxconfig.clkphase = false; //mxconfig.driver = HUB75_I2S_CFG::FM6126A; // Display Setup dma_display = new MatrixPanel_I2S_DMA(mxconfig); dma_display->begin(); dma_display->setBrightness8(128); //0-255 dma_display->clearScreen(); dma_display->fillScreen(myWHITE); Serial.println("Starting AnimatedGIFs Sketch"); // Start filesystem Serial.println(" * Loading SPIFFS"); //if(!SPIFFS.begin()){//削除 if(!LittleFS.begin()){//追加 Serial.println("SPIFFS Mount Failed"); } dma_display->begin(); /* all other pixel drawing functions can only be called after .begin() */ dma_display->fillScreen(dma_display->color565(0, 0, 0)); gif.begin(LITTLE_ENDIAN_PIXELS); } String gifDir = "/gifs"; // play all GIFs in this directory on the SD card char filePath[256] = { 0 }; File root, gifFile; void loop() { while (1) // run forever { //root = FILESYSTEM.open(gifDir);//削除 root = LittleFS.open(gifDir);//追加 if (root) { gifFile = root.openNextFile(); while (gifFile) { if (!gifFile.isDirectory()) // play it { // C-strings... urghh... memset(filePath, 0x0, sizeof(filePath)); strcpy(filePath, gifFile.path()); // Show it. ShowGIF(filePath); } gifFile.close(); gifFile = root.openNextFile(); } root.close(); } // root delay(1000); // pause before restarting } // while } |
うまくいくと保存したGIF動画が順々に再生されていきます。
64x64LEDディスプレイ+ESP32でGIF動画再生テスト pic.twitter.com/A0FoUziK5A
— やまたい@ものつくり⚙️ (@yamatai_mk) May 16, 2025
コメント