Hardware components | ||||||
![]() |
| × | 1 | |||
| × | 1 | ||||
Software apps and online services | ||||||
![]() |
| |||||
| ||||||
|
This script now works with Arduino_GFX_Library version 1.2.0 & 1.2.1 (aka GFX library for Arduino, under Library Manager).
espgfxGIFBased on Arduino_GFX and gifdec, espgfxGIF is an Arduino sketch that plays animated GIF on TFT screen of some Arduino Dev modules, mainly esp32 and esp8266.
- Button A - Invert display
- Button B - Adjust Brightness
- Power button - Reboot and play the designated animated GIF, or a random GIF file in the SPIFFS if the GIF_FILENAME is not defined.
- High performance. If the GIF has no delay, most esp32 module is capable to play 70fps for a 135 x 240 animated GIF.
- M5Stack M5Stick-C (esp32 + ST7735)
- TTGO T-Display (esp32 + ST7789)
- D1 mini with TFT-2.4 shield (esp8266 + ILI9341)
- https://github.com/moononournation/Arduino_GFX version 1.2.0
- https://github.com/lecram/gifdec (https://github.com/BasementCat/arduino-tft-gif edition)
TFT_eSPI, which is the most common TFT graphic library, supports BMP, and MJPEG/JPEG files via drawBmp() and drawJpeg(). However, due to the way how GIF handles cmap with custom color palettes, drawGIF is not supported (as what I am aware of). Adafruit_GFX also lack support for animated GIF. Color corruption is a common issue.
Arduino_GFX is a rewritten library from Adafruit_GFX, TFT_eSPI to support various displays with various data bus interfaces. Using gifdec to fill the GIF frames into to display data bus, an animated GIF can be played on the TFT display.
Installations:(15 minutes setup. 1 minute GIF upload to SPIFFS. 2 minutes to compile and flash)
1. Download esp32fs to your Ardunio IDE from https://github.com/me-no-dev/arduino-esp32fs-plugin
2. Unzip the folder into %USERPROFILE%\Documents\Arduino\tools
3. Restart Arduino IDE.
4. Make sure esp32, esp8266 board info are installed.
5. Make sure M5StickC library is installed.
6. Install "GFX Library for Arduino" (aka Arduino_GFX) from Library Manager.
7. Download the sketch from my github https://github.com/tommykho/IOT-cookbook
8. Extract and create a folder "espgfxGIF" from the zip file.
9. Put your own animated GIF files on the "espgfxGIF/data" folder. Please note most esp32 DEV modules only have 1Mb of SPIFFS. Limit your total file size to 900Kb.
10. Open the "espgfxGIF.ino" with your Arduino IDE.
11. Connect your DEV module to your Arduino IDE. Select the correct COM port.
12. Edit your own settings with the Module and GIF file that you used.
// *** BEGIN editing of your settings ...
#define ARDUINO_TDISPLAY
//#define GIF_FILENAME "/suatmm_240x135.gif" /* comment out for random GIF */
// *** END editing of your settings ...
13. To enable debug screen test, uncomment line 23
#define DEBUG /* uncomment this line to start with screen test */
14. Click "Upload" to compile and upload the sketch to your DEV Module.
15. Select "Tools, ESP32 Sketch Data Upload" to upload the GIF files to the SPIFFS of your DEV Module.
*** IMPORTANT ***
If you are not using m5Stack m5StickC or TTGO T-Display, please add your own configuration to the script after line 52. You need to declare your canvas and data-bus class, MOSI, SCLK, CS, DC, RST, BL pins, as well as control button pins
in the configuration. If you need help with the configuration, please leave a comment or visit
https://github.com/moononournation/Arduino_GFX/wiki/Canvas-Class
https://github.com/moononournation/Arduino_GFX/wiki/Data-Bus-Class
For example,
#elif defined(ARDUINO_RPI-PICO)
/* Raspberry Pi Pico with GC9A01 display */
Arduino_G *output_display = new Arduino_GC9A01(bus, TFT_RST, 0 /* rotation */, true /* IPS */);
Arduino_GFX *gfx = new Arduino_Canvas(240 /* width */, 240 /* height */, output_display);
Arduino_DataBus *bus = new Arduino_RPiPicoSPI(27 /* DC */, 17 /* CS */, PIN_SPI0_SCK /* SCK */, PIN_SPI0_MOSI /* MOSI */, PIN_SPI0_MISO /* MISO */, spi0 /* spi */);
Known bugs:- Only play a single GIF file in a loop until the power cycle
- Brightness control is not working with M5Stack-C
- Minor artifact with M5Stack-C
- Multiple devices sync (playing GIF/MPEG in synchronization)
- Gesture control with IMU
- Play multiple GIF files in a loop
- MP3 or Radio playback (see my other projects)
- BLE connection for wireless GIF upload (similar to Wireless Image Transfer with Circuit Playground Bluefruit and TFT Gizmo)
- IFTTT and MQTT integration for remote power cycle or GIF change
- MJPEG support (live webcam or IP CAM)Multiple
espgfxGIF.ino
Arduino#include <SPIFFS.h>
#include <Arduino_GFX_Library.h> /* Install via Arduino Library Manager */
#include "gifdec.h"
/*
* espgfxGIF Version 2022.05B (Brightness Edition)
* Board: TTGO T-Display & M5Stack M5StickC (esp32)
* Author: tommyho510@gmail.com
* Original Author: moononournation
* Required: Arduino library Arduino_GFX 1.2.1
* Dependency: gifdec.h
*
* Please upload SPIFFS data with ESP32 Sketch Data Upload:
* https://github.com/me-no-dev/arduino-esp32fs-plugin
* GIF src: various
*/
// *** BEGIN editing of your settings ...
#define ARDUINO_TDISPLAY
//#define GIF_FILENAME "/your_file.gif" /* comment out for random GIF */
// *** END editing of your settings ...
//#define DEBUG /* uncomment this line to start with screen test */
#if defined(ARDUINO_M5STICKC)
/* M5Stack */
/* 0.96" ST7735 IPS LCD 80x160 M5Stick-C * (Rotation: 0 bottom up, 1 right, 2 top, 3 left) */
#include <M5StickC.h>
//#define TFT_MOSI 15
//#define TFT_SCLK 13
//#define TFT_CS 5
//#define TFT_DC 23
#define TFT_RST 18
#define TFT_BL 2
Arduino_DataBus *bus = new Arduino_ESP32SPI(23 /* DC */, 5 /* CS */, 13 /* SCK */, 15 /* MOSI */, -1 /* MISO */);
Arduino_ST7735 *gfx = new Arduino_ST7735(bus, TFT_RST /* RST */, 1 /* rotation */, true /* IPS */, 80 /* width */, 160 /* height */, 26 /* col offset 1 */, 1 /* row offset 1 */, 26 /* col offset 2 */, 1 /* row offset 2 */);
const int BTN_A = 37;
const int BTN_B = 39;
#elif defined(ARDUINO_TDISPLAY)
/* TTGO T-Display */
/* 1.14" ST7789 IPS LCD 135x240 TTGO T-Display (Rotation: 0 top up, 1 left, 2 bottom, 3 right) */
//#define TFT_MOSI 19
//#define TFT_SCLK 18
//#define TFT_CS 5
//#define TFT_DC 16
#define TFT_RST -1
#define TFT_BL 4
Arduino_DataBus *bus = new Arduino_ESP32SPI(16 /* DC */, 5 /* CS */, 18 /* SCK */, 19 /* MOSI */, -1 /* MISO */);
Arduino_ST7789 *gfx = new Arduino_ST7789(bus, TFT_RST /* RST */, 1 /* rotation */, true /* IPS */, 135 /* width */, 240 /* height */, 52 /* col offset 1 */, 40 /* row offset 1 */, 53 /* col offset 2 */, 40 /* row offset 2 */);
const int BTN_A = 0;
const int BTN_B = 35;
#elif defined(ARDUINO_D1MINI)
/* LOLIN D1 Mini esp8266 + TFT-2.4 Shield */
/* 2.8" ILI9341 TFT LCD 240x320 (Rotation: 0 top up, 1 left, 2 bottom, 3 right) */
#define TFT_MOSI 13
#define TFT_MISO 12
#define TFT_SCLK 14
#define TFT_CS 16
#define TFT_DC 15
#define TFT_RST 5
//#define TFT_BL NC
Arduino_DataBus *bus = new Arduino_ESP32SPI(TFT_DC /* DC */, TFT_CS /* CS */, TFT_SCLK /* SCK */, TFT_MOSI /* MOSI */, TFT_MISO /* MISO */);
Arduino_ILI9341 *gfx = new Arduino_ILI9341(bus, TFT_RST /* RST */, 0 /* rotation */, false /* IPS */);
const int BTN_A = 3;
const int BTN_B = 4;
#elif defined(ARDUINO_DEVKITV1)
/* DOIT ESP32 DEVKIT V1 + ILI9341 */
/* 2.8" ILI9341 TFT LCD 240x320 (Rotation: 0 top up, 1 left, 2 bottom, 3 right) */
#define TFT_MOSI 23
#define TFT_MISO 19
#define TFT_SCLK 18
#define TFT_CS 5
#define TFT_DC 16
#define TFT_RST 17
//#define TFT_BL 4
Arduino_DataBus *bus = new Arduino_ESP32SPI(TFT_DC /* DC */, TFT_CS /* CS */, TFT_SCLK /* SCK */, TFT_MOSI /* MOSI */, TFT_MISO /* MISO */);
Arduino_ILI9341 *gfx = new Arduino_ILI9341(bus, TFT_RST /* RST */, 0 /* rotation */, false /* IPS */);
const int BTN_A = 13;
const int BTN_B = 15;
#endif /* not selected specific hardware */
// Rotation & Brightness control
int rot[2] = {1, 3};
int backlight[5] = {10, 30, 60, 120, 240};
const int pwmFreq = 5000;
const int pwmResolution = 8;
const int pwmLedChannelTFT = 0;
byte a = 1;
byte b = 4;
unsigned long p = 0;
bool inv = 0;
int pressA = 0;
int pressB = 0;
String gifArray[30], randGIF_FILENAME, playFile;
int gifArraySize;
void listSPIFFS() {
gifArraySize = 0;
Serial.println("{ls /:[");
if (SPIFFS.begin(true)) {
File root = SPIFFS.open("/");
File file = root.openNextFile();
while(file){
gifArray[gifArraySize] = String(file.name());
Serial.println((" {#:" + String(gifArraySize) + ", name:" + String(file.name()) + ", ").substring(0,40) + "\tsize:" + String(file.size()) + "}");
file = root.openNextFile();
gifArraySize++;
}
randGIF_FILENAME = gifArray[random(0, gifArraySize)];
//p = millis();
//randGIF_FILENAME = gifArray[millis() % gifArraySize];
Serial.println(" {randomGIF:" + String(randGIF_FILENAME) +"}");
Serial.println("]}");
}
}
void eraseSPIFFS() {
if(SPIFFS.begin(true)) {
bool formatted = SPIFFS.format();
if(formatted) {
Serial.println("\n\nSuccess formatting");
listSPIFFS();
} else {
Serial.println("\n\nError formatting");
}
}
}
void adjGIF() {
if (digitalRead(BTN_A) == 0) {
if (pressA == 0) {
pressA = 1;
inv = !inv;
gfx->invertDisplay(inv);
Serial.println("{BTN_A:Pressed, Inverted:" + String(inv) + "}");
//a++;
//if (a >= 2)
// a = 0;
//gfx->setRotation(rot[a]);
//Serial.println("{BTN_A:Pressed, Rotation:" + String(rot[a]) + "}");
//Serial.println("{BTN_A:Pressed}");
listSPIFFS();
}
} else pressA = 0;
}
void adjBrightness() {
if (digitalRead(BTN_B) == 0) {
if (pressB == 0) {
pressB = 1;
b++;
//if (b > 15) b = 7;
//M5.Axp.ScreenBreath(b);
if (b >= 5) b = 0;
ledcWrite(pwmLedChannelTFT, backlight[b]);
Serial.println("{BTN_B:Pressed, Brightness:" + String(backlight[b]) + "}");
}
} else pressB = 0;
}
void gfxPlayGIF() {
// Init SPIFFS
if (!SPIFFS.begin(true)) {
Serial.println(F("ERROR: SPIFFS mount failed!"));
gfx->println(F("ERROR: SPIFFS mount failed!"));
} else {
#ifndef GIF_FILENAME
#define GIF_FILENAME
playFile = "/" + String(randGIF_FILENAME);
Serial.println("{Opening random GIF_FILENAME " + playFile + "}");
#else
playFile = GIF_FILENAME;
Serial.println("{Opening designated GIF_FILENAME " + playFile + "}");
#endif
File vFile = SPIFFS.open(playFile);
if (!vFile || vFile.isDirectory()) {
Serial.println(F("ERROR: Failed to open file for reading"));
gfx->println(F("ERROR: Failed to open file for reading"));
gfx->println(playFile);
} else {
gd_GIF *gif = gd_open_gif(&vFile);
if (!gif) {
Serial.println(F("gd_open_gif() failed!"));
} else {
int32_t s = gif->width * gif->height;
uint8_t *buf = (uint8_t *)malloc(s);
if (!buf) {
Serial.println(F("buf malloc failed!"));
} else {
Serial.println(F("{acion:play, GIF:started, Info:["));
Serial.printf(" {canvas size: %ux%u}\n", gif->width, gif->height);
Serial.printf(" {number of colors: %d}\n", gif->palette->size);
Serial.println(F("]}"));
int t_fstart, t_delay = 0, t_real_delay, res, delay_until;
int duration = 0, remain = 0;
while (1) {
gfx->setAddrWindow((gfx->width() - gif->width) / 2, (gfx->height() - gif->height) / 2, gif->width, gif->height);
t_fstart = millis();
t_delay = gif->gce.delay * 10;
res = gd_get_frame(gif, buf);
if (res < 0) {
Serial.println(F("ERROR: gd_get_frame() failed!"));
break;
} else if (res == 0) {
Serial.printf("{action:rewind, duration:%d, remain:%d (%0.1f%%)}\n", duration, remain, 100.0 * remain / duration);
duration = 0;
remain = 0;
gd_rewind(gif);
continue;
}
gfx->startWrite();
gfx->writeIndexedPixels(buf, gif->palette->colors, s);
gfx->endWrite();
t_real_delay = t_delay - (millis() - t_fstart);
duration += t_delay;
remain += t_real_delay;
delay_until = millis() + t_real_delay;
do {
delay(1);
} while (millis() < delay_until);
adjBrightness();
adjGIF();
}
Serial.println(F("action:stop, GIF:ended"));
Serial.printf("{duration: %d, remain: %d (%0.1f %%)}\n", duration, remain, 100.0 * remain / duration);
gd_close_gif(gif);
}
}
}
}
}
unsigned long testRainbow(uint8_t cIndex) {
gfx->fillScreen(BLACK);
unsigned long start = micros();
int w = gfx->width(), h = gfx->height(), s = h / 8;
uint16_t arr [] = { PINK, RED, ORANGE, YELLOW, GREEN, MAGENTA, BLUE, WHITE, PINK, RED, ORANGE, YELLOW, GREEN, MAGENTA, BLUE, WHITE };
gfx->fillRect(0, 0, w, s, arr [cIndex]);
gfx->fillRect(0, s, w, 2 * s, arr [cIndex + 1]);
gfx->fillRect(0, 2 * s, w, 3 * s, arr [cIndex + 2]);
gfx->fillRect(0, 3 * s, w, 4 * s, arr [cIndex + 3]);
gfx->fillRect(0, 4 * s, w, 5 * s, arr [cIndex + 4]);
gfx->fillRect(0, 5 * s, w, 6 * s, arr [cIndex + 5]);
gfx->fillRect(0, 6 * s, w, 7 * s, arr [cIndex + 6]);
gfx->fillRect(0, 7 * s, w, 8 * s, arr [cIndex + 7]);
return micros() - start;
}
unsigned long testChar(uint16_t colorT, uint16_t colorB) {
gfx->fillScreen(colorB);
unsigned long start = micros();
gfx->setTextColor(GREEN);
for (int x = 0; x < 16; x++){
gfx->setCursor(10 + x * 8, 2);
gfx->print(x, 16);
}
gfx->setTextColor(BLUE);
for (int y = 0; y < 16; y++){
gfx->setCursor(2, 12 + y * 10);
gfx->print(y, 16);
}
char c = 0;
for (int y = 0; y < 16; y++){
for (int x = 0; x < 16; x++){
gfx->drawChar(10 + x * 8, 12 + y * 10, c++, colorT, colorB);
}
}
return micros() - start;
}
unsigned long testFilledCircles(uint8_t radius, uint16_t color) {
gfx->fillScreen(BLACK);
unsigned long start;
int x, y, r2 = radius * 2,
w = gfx->width(), h = gfx->height();
start = micros();
for(x=radius; x<w; x+=r2) {
for(y=radius; y<h; y+=r2) {
gfx->fillCircle(x, y, radius, color);
}
}
return micros() - start;
}
unsigned long testCircles(uint8_t radius, uint16_t color) {
// gfx->fillScreen(BLACK);
// Screen is not cleared for this one -- this is
// intentional and does not affect the reported time.
unsigned long start;
int x, y, r2 = radius * 2,
w = gfx->width() + radius, h = gfx->height() + radius;
start = micros();
for(x=0; x<w; x+=r2) {
for(y=0; y<h; y+=r2) {
gfx->drawCircle(x, y, radius, color);
}
}
return micros() - start;
}
void screen_test() {
Serial.print(F("Draw Ranbow: "));
Serial.println(testRainbow(0));
delay(500);
Serial.print(F("Draw Ranbow: "));
Serial.println(testRainbow(2));
delay(500);
Serial.print(F("Draw Ranbow: "));
Serial.println(testRainbow(4));
delay(500);
Serial.print(F("Draw Ranbow: "));
Serial.println(testRainbow(6));
delay(500);
Serial.print(F("Draw Filled Circles: "));
Serial.println(testFilledCircles(10, MAGENTA));
delay(500);
Serial.print(F("Draw Circles: "));
Serial.println(testCircles(10, BLACK));
delay(500);
Serial.print(F("Draw Filled Circles: "));
Serial.println(testFilledCircles(10, YELLOW));
delay(500);
Serial.print(F("Draw Circles: "));
Serial.println(testCircles(10, BLUE));
delay(500);
Serial.print(F("Draw Filled Circles: "));
Serial.println(testFilledCircles(10, RED));
delay(500);
Serial.print(F("Draw Circles: "));
Serial.println(testCircles(10, WHITE));
delay(500);
Serial.print(F("Draw Text: "));
Serial.println(testChar(WHITE, BLACK));
delay(500);
Serial.print(F("Draw Text: "));
Serial.println(testChar(BLUE, WHITE));
delay(500);
gfx->fillScreen(BLACK);
}
void setup() {
pinMode(BTN_A, INPUT_PULLUP);
pinMode(BTN_B, INPUT);
#if defined(ARDUINO_M5STICKC)
M5.begin();
#endif
Serial.begin(115200);
delay(500);
Serial.println("{Device:Started}");
listSPIFFS();
//eraseSPIFFS();
// Init Video
gfx->begin();
gfx->fillScreen(BLACK);
// Turn on Backlight
#ifdef TFT_BL
//M5.Axp.ScreenBreath(b);
ledcSetup(pwmLedChannelTFT, pwmFreq, pwmResolution); // 5 kHz PWM, 8-bit resolution
ledcAttachPin(TFT_BL, pwmLedChannelTFT); // assign TFT_BL pin to channel
ledcWrite(pwmLedChannelTFT, backlight[b]); // brightness 0 - 255
#endif
#ifdef DEBUG
screen_test();
#endif
gfxPlayGIF();
// Turn off Backlight
#ifdef TFT_BL
delay(60000);
ledcDetachPin(TFT_BL);
#endif
// Put device to sleep
gfx->displayOff();
esp_deep_sleep_start();
}
void loop() {
}
espgfxGIF_Timer.ino
Arduino#include <SPIFFS.h>
#include <Arduino_GFX_Library.h> /* Install via Arduino Library Manager */
#include "gifdec.h"
/*
* espgfxGIF Version 2023.02A(Brightness Edition)
* Board: TTGO T-Display & M5Stack M5StickC (esp32)
* Author: tommyho510@gmail.com
* Original Author: moononournation
* Required: Arduino library Arduino_GFX 1.2.1
* Dependency: gifdec.h
*
* Please upload SPIFFS data with ESP32 Sketch Data Upload:
* https://github.com/me-no-dev/arduino-esp32fs-plugin
* GIF src: various
*/
// *** BEGIN editing of your settings ...
#define ARDUINO_TDISPLAY
//#define GIF_FILENAME "/your_file.gif" /* comment out for random GIF */
// *** END editing of your settings ...
//#define DEBUG /* uncomment this line to start with screen test */
#if defined(ARDUINO_M5STICKC)
/* M5Stack */
/* 0.96" ST7735 IPS LCD 80x160 M5Stick-C * (Rotation: 0 bottom up, 1 right, 2 top, 3 left) */
#include <M5StickC.h>
//#define TFT_MOSI 15
//#define TFT_SCLK 13
//#define TFT_CS 5
//#define TFT_DC 23
#define TFT_RST 18
#define TFT_BL 2
Arduino_DataBus *bus = new Arduino_ESP32SPI(23 /* DC */, 5 /* CS */, 13 /* SCK */, 15 /* MOSI */, -1 /* MISO */);
Arduino_ST7735 *gfx = new Arduino_ST7735(bus, TFT_RST /* RST */, 1 /* rotation */, true /* IPS */, 80 /* width */, 160 /* height */, 26 /* col offset 1 */, 1 /* row offset 1 */, 26 /* col offset 2 */, 1 /* row offset 2 */);
const int BTN_A = 37;
const int BTN_B = 39;
#elif defined(ARDUINO_TDISPLAY)
/* TTGO T-Display */
/* 1.14" ST7789 IPS LCD 135x240 TTGO T-Display (Rotation: 0 top up, 1 left, 2 bottom, 3 right) */
//#define TFT_MOSI 19
//#define TFT_SCLK 18
//#define TFT_CS 5
//#define TFT_DC 16
#define TFT_RST -1
#define TFT_BL 4
Arduino_DataBus *bus = new Arduino_ESP32SPI(16 /* DC */, 5 /* CS */, 18 /* SCK */, 19 /* MOSI */, -1 /* MISO */);
Arduino_ST7789 *gfx = new Arduino_ST7789(bus, TFT_RST /* RST */, 1 /* rotation */, true /* IPS */, 135 /* width */, 240 /* height */, 52 /* col offset 1 */, 40 /* row offset 1 */, 53 /* col offset 2 */, 40 /* row offset 2 */);
const int BTN_A = 0;
const int BTN_B = 35;
#elif defined(ARDUINO_D1MINI)
/* LOLIN D1 Mini esp8266 + TFT-2.4 Shield */
/* 2.8" ILI9341 TFT LCD 240x320 (Rotation: 0 top up, 1 left, 2 bottom, 3 right) */
#define TFT_MOSI 13
#define TFT_MISO 12
#define TFT_SCLK 14
#define TFT_CS 16
#define TFT_DC 15
#define TFT_RST 5
//#define TFT_BL NC
Arduino_DataBus *bus = new Arduino_ESP32SPI(TFT_DC /* DC */, TFT_CS /* CS */, TFT_SCLK /* SCK */, TFT_MOSI /* MOSI */, TFT_MISO /* MISO */);
Arduino_ILI9341 *gfx = new Arduino_ILI9341(bus, TFT_RST /* RST */, 0 /* rotation */, false /* IPS */);
const int BTN_A = 3;
const int BTN_B = 4;
#elif defined(ARDUINO_DEVKITV1)
/* DOIT ESP32 DEVKIT V1 + ILI9341 */
/* 2.8" ILI9341 TFT LCD 240x320 (Rotation: 0 top up, 1 left, 2 bottom, 3 right) */
#define TFT_MOSI 23
#define TFT_MISO 19
#define TFT_SCLK 18
#define TFT_CS 5
#define TFT_DC 16
#define TFT_RST 17
//#define TFT_BL 4
Arduino_DataBus *bus = new Arduino_ESP32SPI(TFT_DC /* DC */, TFT_CS /* CS */, TFT_SCLK /* SCK */, TFT_MOSI /* MOSI */, TFT_MISO /* MISO */);
Arduino_ILI9341 *gfx = new Arduino_ILI9341(bus, TFT_RST /* RST */, 0 /* rotation */, false /* IPS */);
const int BTN_A = 13;
const int BTN_B = 15;
#endif /* not selected specific hardware */
// Sleep timer
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 1 /* Time ESP32 will go to sleep (in seconds) */
RTC_DATA_ATTR int bootCount = 0;
// Rotation & Brightness control
int rot[2] = {1, 3};
int backlight[5] = {10, 30, 60, 120, 240};
const int pwmFreq = 5000;
const int pwmResolution = 8;
const int pwmLedChannelTFT = 0;
byte a = 1;
byte b = 4;
unsigned long p = 0;
bool inv = 0;
int pressA = 0;
int pressB = 0;
String gifArray[30], randGIF_FILENAME, playFile;
int gifArraySize;
void listSPIFFS() {
gifArraySize = 0;
Serial.println("{ls /:[");
if (SPIFFS.begin(true)) {
File root = SPIFFS.open("/");
File file = root.openNextFile();
while(file){
gifArray[gifArraySize] = String(file.name());
Serial.println((" {#:" + String(gifArraySize) + ", name:" + String(file.name()) + ", ").substring(0,40) + "\tsize:" + String(file.size()) + "}");
file = root.openNextFile();
gifArraySize++;
}
randGIF_FILENAME = gifArray[random(0, gifArraySize)];
//p = millis();
//randGIF_FILENAME = gifArray[millis() % gifArraySize];
Serial.println(" {randomGIF:" + String(randGIF_FILENAME) +"}");
Serial.println("]}");
}
}
void eraseSPIFFS() {
if(SPIFFS.begin(true)) {
bool formatted = SPIFFS.format();
if(formatted) {
Serial.println("\n\nSuccess formatting");
listSPIFFS();
} else {
Serial.println("\n\nError formatting");
}
}
}
void adjGIF() {
if (digitalRead(BTN_A) == 0) {
if (pressA == 0) {
pressA = 1;
inv = !inv;
gfx->invertDisplay(inv);
Serial.println("{BTN_A:Pressed, Inverted:" + String(inv) + "}");
//a++;
//if (a >= 2)
// a = 0;
//gfx->setRotation(rot[a]);
//Serial.println("{BTN_A:Pressed, Rotation:" + String(rot[a]) + "}");
//Serial.println("{BTN_A:Pressed}");
listSPIFFS();
}
} else pressA = 0;
}
void adjBrightness() {
if (digitalRead(BTN_B) == 0) {
if (pressB == 0) {
pressB = 1;
b++;
//if (b > 15) b = 7;
//M5.Axp.ScreenBreath(b);
if (b >= 5) b = 0;
ledcWrite(pwmLedChannelTFT, backlight[b]);
Serial.println("{BTN_B:Pressed, Brightness:" + String(backlight[b]) + "}");
}
} else pressB = 0;
}
void gfxPlayGIF() {
// Init SPIFFS
if (!SPIFFS.begin(true)) {
Serial.println(F("ERROR: SPIFFS mount failed!"));
gfx->println(F("ERROR: SPIFFS mount failed!"));
} else {
#ifndef GIF_FILENAME
#define GIF_FILENAME
playFile = "/" + String(randGIF_FILENAME);
Serial.println("{Opening random GIF_FILENAME " + playFile + "}");
#else
playFile = GIF_FILENAME;
Serial.println("{Opening designated GIF_FILENAME " + playFile + "}");
#endif
File vFile = SPIFFS.open(playFile);
if (!vFile || vFile.isDirectory()) {
Serial.println(F("ERROR: Failed to open file for reading"));
gfx->println(F("ERROR: Failed to open file for reading"));
gfx->println(playFile);
} else {
gd_GIF *gif = gd_open_gif(&vFile);
if (!gif) {
Serial.println(F("gd_open_gif() failed!"));
} else {
int32_t s = gif->width * gif->height;
uint8_t *buf = (uint8_t *)malloc(s);
if (!buf) {
Serial.println(F("buf malloc failed!"));
} else {
Serial.println(F("{acion:play, GIF:started, Info:["));
Serial.printf(" {canvas size: %ux%u}\n", gif->width, gif->height);
Serial.printf(" {number of colors: %d}\n", gif->palette->size);
Serial.println(F("]}"));
int t_fstart, t_delay = 0, t_real_delay, res, delay_until;
int duration = 0, remain = 0;
// while (1) {
gfx->setAddrWindow((gfx->width() - gif->width) / 2, (gfx->height() - gif->height) / 2, gif->width, gif->height);
t_fstart = millis();
t_delay = gif->gce.delay * 10;
res = gd_get_frame(gif, buf);
if (res < 0) {
Serial.println(F("ERROR: gd_get_frame() failed!"));
break;
} else if (res == 0) {
Serial.printf("{action:rewind, duration:%d, remain:%d (%0.1f%%)}\n", duration, remain, 100.0 * remain / duration);
duration = 0;
remain = 0;
gd_rewind(gif);
continue;
}
gfx->startWrite();
gfx->writeIndexedPixels(buf, gif->palette->colors, s);
gfx->endWrite();
t_real_delay = t_delay - (millis() - t_fstart);
duration += t_delay;
remain += t_real_delay;
delay_until = millis() + t_real_delay;
do {
delay(1);
} while (millis() < delay_until);
adjBrightness();
adjGIF();
// }
Serial.println(F("action:stop, GIF:ended"));
Serial.printf("{duration: %d, remain: %d (%0.1f %%)}\n", duration, remain, 100.0 * remain / duration);
gd_close_gif(gif);
}
}
}
}
}
unsigned long testRainbow(uint8_t cIndex) {
gfx->fillScreen(BLACK);
unsigned long start = micros();
int w = gfx->width(), h = gfx->height(), s = h / 8;
uint16_t arr [] = { PINK, RED, ORANGE, YELLOW, GREEN, MAGENTA, BLUE, WHITE, PINK, RED, ORANGE, YELLOW, GREEN, MAGENTA, BLUE, WHITE };
gfx->fillRect(0, 0, w, s, arr [cIndex]);
gfx->fillRect(0, s, w, 2 * s, arr [cIndex + 1]);
gfx->fillRect(0, 2 * s, w, 3 * s, arr [cIndex + 2]);
gfx->fillRect(0, 3 * s, w, 4 * s, arr [cIndex + 3]);
gfx->fillRect(0, 4 * s, w, 5 * s, arr [cIndex + 4]);
gfx->fillRect(0, 5 * s, w, 6 * s, arr [cIndex + 5]);
gfx->fillRect(0, 6 * s, w, 7 * s, arr [cIndex + 6]);
gfx->fillRect(0, 7 * s, w, 8 * s, arr [cIndex + 7]);
return micros() - start;
}
unsigned long testChar(uint16_t colorT, uint16_t colorB) {
gfx->fillScreen(colorB);
unsigned long start = micros();
gfx->setTextColor(GREEN);
for (int x = 0; x < 16; x++){
gfx->setCursor(10 + x * 8, 2);
gfx->print(x, 16);
}
gfx->setTextColor(BLUE);
for (int y = 0; y < 16; y++){
gfx->setCursor(2, 12 + y * 10);
gfx->print(y, 16);
}
char c = 0;
for (int y = 0; y < 16; y++){
for (int x = 0; x < 16; x++){
gfx->drawChar(10 + x * 8, 12 + y * 10, c++, colorT, colorB);
}
}
return micros() - start;
}
unsigned long testFilledCircles(uint8_t radius, uint16_t color) {
gfx->fillScreen(BLACK);
unsigned long start;
int x, y, r2 = radius * 2,
w = gfx->width(), h = gfx->height();
start = micros();
for(x=radius; x<w; x+=r2) {
for(y=radius; y<h; y+=r2) {
gfx->fillCircle(x, y, radius, color);
}
}
return micros() - start;
}
unsigned long testCircles(uint8_t radius, uint16_t color) {
// gfx->fillScreen(BLACK);
// Screen is not cleared for this one -- this is
// intentional and does not affect the reported time.
unsigned long start;
int x, y, r2 = radius * 2,
w = gfx->width() + radius, h = gfx->height() + radius;
start = micros();
for(x=0; x<w; x+=r2) {
for(y=0; y<h; y+=r2) {
gfx->drawCircle(x, y, radius, color);
}
}
return micros() - start;
}
void screen_test() {
Serial.print(F("Draw Ranbow: "));
Serial.println(testRainbow(0));
delay(500);
Serial.print(F("Draw Ranbow: "));
Serial.println(testRainbow(2));
delay(500);
Serial.print(F("Draw Ranbow: "));
Serial.println(testRainbow(4));
delay(500);
Serial.print(F("Draw Ranbow: "));
Serial.println(testRainbow(6));
delay(500);
Serial.print(F("Draw Filled Circles: "));
Serial.println(testFilledCircles(10, MAGENTA));
delay(500);
Serial.print(F("Draw Circles: "));
Serial.println(testCircles(10, BLACK));
delay(500);
Serial.print(F("Draw Filled Circles: "));
Serial.println(testFilledCircles(10, YELLOW));
delay(500);
Serial.print(F("Draw Circles: "));
Serial.println(testCircles(10, BLUE));
delay(500);
Serial.print(F("Draw Filled Circles: "));
Serial.println(testFilledCircles(10, RED));
delay(500);
Serial.print(F("Draw Circles: "));
Serial.println(testCircles(10, WHITE));
delay(500);
Serial.print(F("Draw Text: "));
Serial.println(testChar(WHITE, BLACK));
delay(500);
Serial.print(F("Draw Text: "));
Serial.println(testChar(BLUE, WHITE));
delay(500);
gfx->fillScreen(BLACK);
}
void setup() {
pinMode(BTN_A, INPUT_PULLUP);
pinMode(BTN_B, INPUT);
#if defined(ARDUINO_M5STICKC)
M5.begin();
#endif
Serial.begin(115200);
delay(500);
Serial.println("{Device:Started}");
listSPIFFS();
//eraseSPIFFS();
// Init Video
gfx->begin();
gfx->fillScreen(BLACK);
// Turn on Backlight
#ifdef TFT_BL
//M5.Axp.ScreenBreath(b);
ledcSetup(pwmLedChannelTFT, pwmFreq, pwmResolution); // 5 kHz PWM, 8-bit resolution
ledcAttachPin(TFT_BL, pwmLedChannelTFT); // assign TFT_BL pin to channel
ledcWrite(pwmLedChannelTFT, backlight[b]); // brightness 0 - 255
#endif
#ifdef DEBUG
screen_test();
#endif
gfxPlayGIF();
// Turn off Backlight
#ifdef TFT_BL
delay(60000);
ledcDetachPin(TFT_BL);
#endif
// Put device to sleep
gfx->displayOff();
++bootCount;
Serial.println("Boot number: " + String(bootCount));
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Serial.flush();
esp_deep_sleep_start();
}
void loop() {
}
/*
* rewrite from: https://github.com/BasementCat/arduino-tft-gif
*/
#include "gifdec.h"
#include <sys/types.h>
#include <FS.h>
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define MAX(A, B) ((A) > (B) ? (A) : (B))
int gif_buf_last_idx, gif_buf_idx, file_pos;
uint8_t gif_buf[GIF_BUF_SIZE];
static bool gif_buf_seek(File *fd, int len)
{
if (len > (gif_buf_last_idx - gif_buf_idx))
{
fd->seek(len - (gif_buf_last_idx - gif_buf_idx), SeekCur);
gif_buf_idx = gif_buf_last_idx;
}
else
{
gif_buf_idx += len;
}
return true;
}
static int gif_buf_read(File *fd, uint8_t *dest, int len)
{
while (len--)
{
if (gif_buf_idx == gif_buf_last_idx)
{
gif_buf_last_idx = fd->read(gif_buf, GIF_BUF_SIZE);
gif_buf_idx = 0;
}
file_pos++;
*(dest++) = gif_buf[gif_buf_idx++];
}
return len;
}
static uint8_t gif_buf_read(File *fd)
{
if (gif_buf_idx == gif_buf_last_idx)
{
gif_buf_last_idx = fd->read(gif_buf, GIF_BUF_SIZE);
gif_buf_idx = 0;
}
file_pos++;
return gif_buf[gif_buf_idx++];
}
static uint16_t gif_buf_read16(File *fd)
{
return gif_buf_read(fd) + (((uint16_t)gif_buf_read(fd)) << 8);
}
static void read_palette(File *fd, gd_Palette *dest, int32_t num_colors)
{
uint8_t r, g, b;
dest->size = num_colors;
for (int32_t i = 0; i < num_colors; i++)
{
r = gif_buf_read(fd);
g = gif_buf_read(fd);
b = gif_buf_read(fd);
dest->colors[i] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3);
}
}
gd_GIF *gd_open_gif(File *fd)
{
uint8_t sigver[3];
uint16_t width, height, depth;
uint8_t fdsz, bgidx, aspect;
int32_t gct_sz;
gd_GIF *gif;
// init global variables
gif_buf_last_idx = GIF_BUF_SIZE;
gif_buf_idx = gif_buf_last_idx; // no buffer yet
file_pos = 0;
/* Header */
gif_buf_read(fd, sigver, 3);
if (memcmp(sigver, "GIF", 3) != 0)
{
Serial.println(F("invalid signature"));
return NULL;
}
/* Version */
gif_buf_read(fd, sigver, 3);
if (memcmp(sigver, "89a", 3) != 0)
{
Serial.println(F("invalid version"));
return NULL;
}
/* Width x Height */
width = gif_buf_read16(fd);
height = gif_buf_read16(fd);
/* FDSZ */
gif_buf_read(fd, &fdsz, 1);
/* Presence of GCT */
if (!(fdsz & 0x80))
{
Serial.println(F("no global color table"));
return NULL;
}
/* Color Space's Depth */
depth = ((fdsz >> 4) & 7) + 1;
/* Ignore Sort Flag. */
/* GCT Size */
gct_sz = 1 << ((fdsz & 0x07) + 1);
/* Background Color Index */
gif_buf_read(fd, &bgidx, 1);
/* Aspect Ratio */
gif_buf_read(fd, &aspect, 1);
/* Create gd_GIF Structure. */
gif = (gd_GIF *)calloc(1, sizeof(*gif));
gif->fd = fd;
gif->width = width;
gif->height = height;
gif->depth = depth;
/* Read GCT */
read_palette(fd, &gif->gct, gct_sz);
gif->palette = &gif->gct;
gif->bgindex = bgidx;
gif->anim_start = file_pos; // fd->position();
gif->table = new_table();
return gif;
}
static void discard_sub_blocks(gd_GIF *gif)
{
uint8_t size;
do
{
gif_buf_read(gif->fd, &size, 1);
gif_buf_seek(gif->fd, size);
} while (size);
}
static void read_plain_text_ext(gd_GIF *gif)
{
if (gif->plain_text)
{
uint16_t tx, ty, tw, th;
uint8_t cw, ch, fg, bg;
off_t sub_block;
gif_buf_seek(gif->fd, 1); /* block size = 12 */
tx = gif_buf_read16(gif->fd);
ty = gif_buf_read16(gif->fd);
tw = gif_buf_read16(gif->fd);
th = gif_buf_read16(gif->fd);
cw = gif_buf_read(gif->fd);
ch = gif_buf_read(gif->fd);
fg = gif_buf_read(gif->fd);
bg = gif_buf_read(gif->fd);
gif->plain_text(gif, tx, ty, tw, th, cw, ch, fg, bg);
}
else
{
/* Discard plain text metadata. */
gif_buf_seek(gif->fd, 13);
}
/* Discard plain text sub-blocks. */
discard_sub_blocks(gif);
}
static void read_graphic_control_ext(gd_GIF *gif)
{
uint8_t rdit;
/* Discard block size (always 0x04). */
gif_buf_seek(gif->fd, 1);
gif_buf_read(gif->fd, &rdit, 1);
gif->gce.disposal = (rdit >> 2) & 3;
gif->gce.input = rdit & 2;
gif->gce.transparency = rdit & 1;
gif->gce.delay = gif_buf_read16(gif->fd);
gif_buf_read(gif->fd, &gif->gce.tindex, 1);
/* Skip block terminator. */
gif_buf_seek(gif->fd, 1);
}
static void read_comment_ext(gd_GIF *gif)
{
if (gif->comment)
{
gif->comment(gif);
}
/* Discard comment sub-blocks. */
discard_sub_blocks(gif);
}
static void read_application_ext(gd_GIF *gif)
{
char app_id[8];
char app_auth_code[3];
/* Discard block size (always 0x0B). */
gif_buf_seek(gif->fd, 1);
/* Application Identifier. */
gif_buf_read(gif->fd, (uint8_t *)app_id, 8);
/* Application Authentication Code. */
gif_buf_read(gif->fd, (uint8_t *)app_auth_code, 3);
if (!strncmp(app_id, "NETSCAPE", sizeof(app_id)))
{
/* Discard block size (0x03) and constant byte (0x01). */
gif_buf_seek(gif->fd, 2);
gif->loop_count = gif_buf_read16(gif->fd);
/* Skip block terminator. */
gif_buf_seek(gif->fd, 1);
}
else if (gif->application)
{
gif->application(gif, app_id, app_auth_code);
discard_sub_blocks(gif);
}
else
{
discard_sub_blocks(gif);
}
}
static void read_ext(gd_GIF *gif)
{
uint8_t label;
gif_buf_read(gif->fd, &label, 1);
switch (label)
{
case 0x01:
read_plain_text_ext(gif);
break;
case 0xF9:
read_graphic_control_ext(gif);
break;
case 0xFE:
read_comment_ext(gif);
break;
case 0xFF:
read_application_ext(gif);
break;
default:
Serial.print("unknown extension: ");
Serial.println(label, HEX);
}
}
static gd_Table *new_table()
{
// int key;
// int init_bulk = MAX(1 << (key_size + 1), 0x100);
// Table *table = (Table*) malloc(sizeof(*table) + sizeof(Entry) * init_bulk);
// if (table) {
// table->bulk = init_bulk;
// table->nentries = (1 << key_size) + 2;
// table->entries = (Entry *) &table[1];
// for (key = 0; key < (1 << key_size); key++)
// table->entries[key] = (Entry) {1, 0xFFF, key};
// }
// return table;
int s = sizeof(gd_Table) + (sizeof(gd_Entry) * 4096);
gd_Table *table = (gd_Table *)malloc(s);
if (table)
{
Serial.printf("new_table() malloc %d.\n", s);
}
else
{
Serial.printf("new_table() malloc %d failed!\n", s);
}
table->entries = (gd_Entry *)&table[1];
return table;
}
static void reset_table(gd_Table *table, int32_t key_size)
{
table->nentries = (1 << key_size) + 2;
for (int32_t key = 0; key < (1 << key_size); key++)
{
table->entries[key] = (gd_Entry){1, 0xFFF, key};
}
}
/* Add table entry. Return value:
* 0 on success
* +1 if key size must be incremented after this addition
* -1 if could not realloc table */
static int32_t add_entry(gd_Table *table, int32_t length, uint16_t prefix, uint8_t suffix)
{
// Table *table = *tablep;
// if (table->nentries == table->bulk) {
// table->bulk *= 2;
// table = (Table*) realloc(table, sizeof(*table) + sizeof(Entry) * table->bulk);
// if (!table) return -1;
// table->entries = (Entry *) &table[1];
// *tablep = table;
// }
table->entries[table->nentries] = (gd_Entry){length, prefix, suffix};
table->nentries++;
if ((table->nentries & (table->nentries - 1)) == 0)
return 1;
return 0;
}
static uint16_t get_key(gd_GIF *gif, int key_size, uint8_t *sub_len, uint8_t *shift, uint8_t *byte)
{
int bits_read;
int rpad;
int frag_size;
uint16_t key;
key = 0;
for (bits_read = 0; bits_read < key_size; bits_read += frag_size)
{
rpad = (*shift + bits_read) % 8;
if (rpad == 0)
{
/* Update byte. */
if (*sub_len == 0)
gif_buf_read(gif->fd, sub_len, 1); /* Must be nonzero! */
gif_buf_read(gif->fd, byte, 1);
(*sub_len)--;
}
frag_size = MIN(key_size - bits_read, 8 - rpad);
key |= ((uint16_t)((*byte) >> rpad)) << bits_read;
}
/* Clear extra bits to the left. */
key &= (1 << key_size) - 1;
*shift = (*shift + key_size) % 8;
return key;
}
/* Compute output index of y-th input line, in frame of height h. */
static int interlaced_line_index(int h, int y)
{
int p; /* number of lines in current pass */
p = (h - 1) / 8 + 1;
if (y < p) /* pass 1 */
return y * 8;
y -= p;
p = (h - 5) / 8 + 1;
if (y < p) /* pass 2 */
return y * 8 + 4;
y -= p;
p = (h - 3) / 4 + 1;
if (y < p) /* pass 3 */
return y * 4 + 2;
y -= p;
/* pass 4 */
return y * 2 + 1;
}
/* Decompress image pixels.
* Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */
static int32_t read_image_data(gd_GIF *gif, int interlace, uint8_t *frame)
{
uint8_t sub_len, shift, byte;
int init_key_size, key_size, table_is_full;
int frm_off, str_len, p, x, y;
uint16_t key, clear, stop;
int32_t ret;
gd_Entry entry;
off_t start, end;
// Serial.println("Read key size");
gif_buf_read(gif->fd, &byte, 1);
key_size = (int)byte;
// Serial.println("Set pos, discard sub blocks");
// start = gif->fd->position();
// discard_sub_blocks(gif);
// end = gif->fd->position();
// gif_buf_seek(gif->fd, start, SeekSet);
clear = 1 << key_size;
stop = clear + 1;
// Serial.println("New LZW table");
// table = new_table(key_size);
reset_table(gif->table, key_size);
key_size++;
init_key_size = key_size;
sub_len = shift = 0;
// Serial.println("Get init key");
key = get_key(gif, key_size, &sub_len, &shift, &byte); /* clear code */
frm_off = 0;
ret = 0;
while (1)
{
if (key == clear)
{
// Serial.println("Clear key, reset nentries");
key_size = init_key_size;
gif->table->nentries = (1 << (key_size - 1)) + 2;
table_is_full = 0;
}
else if (!table_is_full)
{
// Serial.println("Add entry to table");
ret = add_entry(gif->table, str_len + 1, key, entry.suffix);
// if (ret == -1) {
// // Serial.println("Table entry add failure");
// free(table);
// return -1;
// }
if (gif->table->nentries == 0x1000)
{
// Serial.println("Table is full");
ret = 0;
table_is_full = 1;
}
}
// Serial.println("Get key");
key = get_key(gif, key_size, &sub_len, &shift, &byte);
if (key == clear)
continue;
if (key == stop)
break;
if (ret == 1)
key_size++;
entry = gif->table->entries[key];
str_len = entry.length;
uint8_t tindex = gif->gce.tindex;
// Serial.println("Interpret key");
while (1)
{
p = frm_off + entry.length - 1;
x = p % gif->fw;
y = p / gif->fw;
if (interlace)
{
y = interlaced_line_index((int)gif->fh, y);
}
if (tindex != entry.suffix)
{
frame[(gif->fy + y) * gif->width + gif->fx + x] = entry.suffix;
}
if (entry.prefix == 0xFFF)
break;
else
entry = gif->table->entries[entry.prefix];
}
frm_off += str_len;
if (key < gif->table->nentries - 1 && !table_is_full)
gif->table->entries[gif->table->nentries - 1].suffix = entry.suffix;
}
// Serial.println("Done w/ img data, free table and seek to end");
// free(table);
gif_buf_read(gif->fd, &sub_len, 1); /* Must be zero! */
// gif_buf_seek(gif->fd, end, SeekSet);
return 0;
}
/* Read image.
* Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */
static int32_t read_image(gd_GIF *gif, uint8_t *frame)
{
uint8_t fisrz;
int interlace;
/* Image Descriptor. */
// Serial.println("Read image descriptor");
gif->fx = gif_buf_read16(gif->fd);
gif->fy = gif_buf_read16(gif->fd);
gif->fw = gif_buf_read16(gif->fd);
gif->fh = gif_buf_read16(gif->fd);
// Serial.println("Read fisrz?");
gif_buf_read(gif->fd, &fisrz, 1);
interlace = fisrz & 0x40;
/* Ignore Sort Flag. */
/* Local Color Table? */
if (fisrz & 0x80)
{
/* Read LCT */
// Serial.println("Read LCT");
read_palette(gif->fd, &gif->lct, 1 << ((fisrz & 0x07) + 1));
gif->palette = &gif->lct;
}
else
{
gif->palette = &gif->gct;
}
/* Image Data. */
// Serial.println("Read image data");
return read_image_data(gif, interlace, frame);
}
static void render_frame_rect(gd_GIF *gif, uint16_t *buffer, uint8_t *frame)
{
int i, j, k;
uint8_t index, *color;
i = gif->fy * gif->width + gif->fx;
for (j = 0; j < gif->fh; j++)
{
for (k = 0; k < gif->fw; k++)
{
index = frame[(gif->fy + j) * gif->width + gif->fx + k];
// color = &gif->palette->colors[index*2];
if (!gif->gce.transparency || index != gif->gce.tindex)
buffer[(i + k)] = gif->palette->colors[index];
// memcpy(&buffer[(i+k)*2], color, 2);
}
i += gif->width;
}
}
/* Return 1 if got a frame; 0 if got GIF trailer; -1 if error. */
int32_t gd_get_frame(gd_GIF *gif, uint8_t *frame)
{
char sep;
while (1)
{
gif_buf_read(gif->fd, (uint8_t *)&sep, 1);
if (sep == 0)
{
gif_buf_read(gif->fd, (uint8_t *)&sep, 1);
}
if (sep == ',')
{
break;
}
if (sep == ';')
{
return 0;
}
if (sep == '!')
{
read_ext(gif);
}
else
{
Serial.printf("Read sep: [%d].\n", sep);
return -1;
}
}
// Serial.println("Do read image");
if (read_image(gif, frame) == -1)
return -1;
return 1;
}
void gd_rewind(gd_GIF *gif)
{
gif->fd->seek(gif->anim_start, SeekSet);
file_pos = gif->anim_start;
gif_buf_idx = gif_buf_last_idx; // reset buffer
}
void gd_close_gif(gd_GIF *gif)
{
gif->fd->close();
free(gif->table);
free(gif);
}
/*
* rewrite from: https://github.com/BasementCat/arduino-tft-gif
*/
#ifndef _GIFDEC_H_
#define _GIFDEC_H_
#include <FS.h>
#define GIF_BUF_SIZE 1024
typedef struct gd_Palette {
int size;
uint16_t colors[256];
} gd_Palette;
typedef struct gd_GCE {
uint16_t delay;
uint8_t tindex;
uint8_t disposal;
int input;
int transparency;
} gd_GCE;
typedef struct gd_Entry {
int32_t length;
uint16_t prefix;
uint8_t suffix;
} gd_Entry;
typedef struct gd_Table {
int bulk;
int nentries;
gd_Entry *entries;
} gd_Table;
typedef struct gd_GIF {
File* fd;
off_t anim_start;
uint16_t width, height;
uint16_t depth;
uint16_t loop_count;
gd_GCE gce;
gd_Palette *palette;
gd_Palette lct, gct;
void (*plain_text)(
struct gd_GIF *gif, uint16_t tx, uint16_t ty,
uint16_t tw, uint16_t th, uint8_t cw, uint8_t ch,
uint8_t fg, uint8_t bg
);
void (*comment)(struct gd_GIF *gif);
void (*application)(struct gd_GIF *gif, char id[8], char auth[3]);
uint16_t fx, fy, fw, fh;
uint8_t bgindex;
gd_Table* table;
} gd_GIF;
gd_GIF *gd_open_gif(File* fd);
static gd_Table * new_table();
static void reset_table(gd_Table* table, int key_size);
// int32_t add_entry(gd_Table* table, int32_t length, uint16_t prefix, uint8_t suffix)
int32_t gd_get_frame(gd_GIF *gif, uint8_t *frame);
void gd_rewind(gd_GIF *gif);
void gd_close_gif(gd_GIF *gif);
#endif /* _GIFDEC_H_ */
LibraryDebugger.ino
Arduino/*
* Arduino_GFX_Library Library Debugger
* Author: tommyho510@gmail.com
* Background: Arduino_ESP32SPI_DMA class obsoleted on Apr 4, 2021. Some users may have issue compiling espgfxGIF.ino
* because of mixing up different codes and library.
* Usage: Compile this file to see if the installed libraries are compatible with the main script
*
* Adafruit GFX Library 1.10.12
* Arduino_GFX_Library 1.0.6 - passed OBS
* Arduino_GFX_Library 1.1.2 - passed
* Arduino_GFX_Library 1.1.3 - passed
* Arduino_GFX_Library 1.1.4 - failed
* Arduino_GFX_Library 1.1.5 - failed
* Arduino_GFX_Library 1.1.6 - failed
*
*/
/* PICK YOU DEBUG BOARD */
//#define ARDUNIO_GENERIC_HW
//#define ARDUINO_M5CORE2
#define ARDUINO_M5STICKC
//#define ARDUINO_TDISPLAY
//#define ARDUINO_M5STICKC_OBS
//#define ARDUINO_TDISPLAY_OBS
/* DO NOT EDIT BELOW */
#include <Arduino_GFX_Library.h>
/* M5Stack Core2
* 2.00" ILI9342 TFT LCD 320x240 Core2 * (Rotation: 0 bottom up, 1 right, 2 top, 3 left
*/
#if defined(ARDUINO_GENERIC_HW)
#define TFT_BL 2
static Arduino_DataBus *bus = new Arduino_HWSPI(27 /* DC */, 5 /* CS */);
static Arduino_GFX *gfx = new Arduino_ST7735(bus, 18 /* RST */, 1 /* rotation */, true /* IPS */, 80 /* width */, 160 /* height */, 26 /* col offset 1 */, 1 /* row offset 1 */, 26 /* col offset 2 */, 1 /* row offset 2 */);
/* M5Stack Core2
* 2.00" ILI9342 TFT LCD 320x240 Core2 * (Rotation: 0 bottom up, 1 right, 2 top, 3 left
*/
#elif defined(ARDUINO_M5CORE2)
#include <M5Core2.h>
#define TFT_BL 32
static Arduino_DataBus *bus = new Arduino_ESP32SPI(27 /* DC */, 14 /* CS */, SCK, MOSI, MISO);
static Arduino_GFX *gfx = new Arduino_ILI9342(bus, 33 /* RST */, 1 /* rotation */);
/* M5Stack M5StickC
* 0.96" ST7735 IPS LCD 80x160 M5Stick-C * (Rotation: 0 bottom up, 1 right, 2 top, 3 left)
*/
#elif defined(ARDUINO_M5STICKC)
#include <M5StickC.h>
#define TFT_BL 2
static Arduino_DataBus *bus = new Arduino_ESP32SPI(27 /* DC */, 5 /* CS */, 13 /* SCK */, 15 /* MOSI */, -1 /* MISO */);
static Arduino_GFX *gfx = new Arduino_ST7735(bus, 18 /* RST */, 1 /* rotation */, true /* IPS */, 80 /* width */, 160 /* height */, 26 /* col offset 1 */, 1 /* row offset 1 */, 26 /* col offset 2 */, 1 /* row offset 2 */);
/* TTGO T-DISPLAY
* 1.14" ST7789 IPS LCD 135x240 TTGO T-Display (Rotation: 0 top up, 1 left, 2 bottom, 3 right)
*/
#elif defined(ARDUINO_TDISPLAY)
#define TFT_BL 4
static Arduino_DataBus *bus = new Arduino_ESP32SPI(16 /* DC */, 5 /* CS */, 18 /* SCK */, 19 /* MOSI */, -1 /* MISO */);
static Arduino_GFX *gfx = new Arduino_ST7789(bus, 23 /* RST */, 2 /* rotation */, true /* IPS */, 135 /* width */, 240 /* height */, 53 /* col offset 1 */, 40 /* row offset 1 */, 52 /* col offset 2 */, 40 /* row offset 2 */);
/* M5Stack M5StickC with OBS GFX Lib V1.0.6
* 0.96" ST7735 IPS LCD 80x160 M5Stick-C * (Rotation: 0 bottom up, 1 right, 2 top, 3 left)
*/
#elif defined(ARDUINO_M5STICKC_OBS)
#include <M5StickC.h>
#define TFT_BL 2
Arduino_ESP32SPI_DMA *bus = new Arduino_ESP32SPI_DMA(23 /* DC */, 5 /* CS */, 13 /* SCK */, 15 /* MOSI */, -1 /* MISO */);
Arduino_ST7735 *gfx = new Arduino_ST7735(bus, 18 /* RST */, 1 /* rotation */, true /* IPS */, 80 /* width */, 160 /* height */, 26 /* col offset 1 */, 1 /* row offset 1 */, 26 /* col offset 2 */, 1 /* row offset 2 */);
/* TTGO T-DISPLAY with OBS GFX Lib V1.0.6
* 0.96" ST7735 IPS LCD 80x160 M5Stick-C * (Rotation: 0 bottom up, 1 right, 2 top, 3 left)
*/
#elif defined(ARDUINO_TDISPLAY_OBS)
#define TFT_BL 4
Arduino_ESP32SPI_DMA *bus = new Arduino_ESP32SPI_DMA(16 /* DC */, 5 /* CS */, 18 /* SCK */, 19 /* MOSI */, -1 /* MISO */);
Arduino_ST7789 *gfx = new Arduino_ST7789(bus, 23 /* RST */, 2 /* rotation */, true /* IPS */, 135 /* width */, 240 /* height */, 53 /* col offset 1 */, 40 /* row offset 1 */, 52 /* col offset 2 */, 40 /* row offset 2 */);
#endif
void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
}
Comments