From bbfb90ce844d7e61126f032f941cb1cc764cac69 Mon Sep 17 00:00:00 2001 From: Keith Henrickson Date: Thu, 21 Sep 2023 22:00:36 -0500 Subject: [PATCH 1/3] Wifi support and animated GIF support for the TechDungeon LED display This needs a patched version of the AnimatedGIF library, to work around a couple of issues/limitations with it. --- led_display.ino | 593 ++++++++++++++++++++++++++++++++++++++++++++++++ leds-sample.ino | 131 ----------- sprites.h | 106 +++++---- 3 files changed, 651 insertions(+), 179 deletions(-) create mode 100644 led_display.ino delete mode 100644 leds-sample.ino diff --git a/led_display.ino b/led_display.ino new file mode 100644 index 0000000..969e74c --- /dev/null +++ b/led_display.ino @@ -0,0 +1,593 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// LED 16x16 Display Module +// Copyright 2023 The Tech Dungeon +// +// This code is licensed under the GPL +// +// We believe in open development. The GPL has rights and obligations. +// You must abide by the terms of the GPL v3 license as noted in the LICENSE file +// If you modify this code you must make your source code available to end users upon request. +// +// Please read the LICENSE file as it defines your specific rights regarding the use of this +// source code, as well as condiditions under which the rights are given to you +////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include + +#include "sprites.h" + +int gifPlaying; +int gifPlayingStartMillis; +int gifPlayingDelay; +uint8_t showed_image = 0; +WiFiManager wifiManager; +ESP8266WebServer webServer(80); +AnimatedGIF gif; + +#define LED_PIN 2 // Data pin for controlling LED's +#define LED_XWIDTH 16 // X Width of Pixels, 16 in our case +#define LED_YWIDTH 16 // Y Width of Pixels, 16 in our case +#define NUM_LEDS LED_XWIDTH * LED_YWIDTH // Total number of pixels +#define BRIGHTNESS 32 // Brightness setting. We keep this sane to reduce current draw +#define PAUSETIME 5000 // Pause time between sprites in milliseconds +#define TEMPERATURE_1 Tungsten100W // Color Temperature to use with FastLED library + +CRGB leds[NUM_LEDS]; // Define the array for our LED's + +sprites cur_sprite; // Current sprite being displayed +File uploadFile; +File playbackFile; +String rootStr = "/"; +const char* serverIndex = "TechDungeon LED Display
\n" +""; + +Dir rootDirectory; +unsigned long fileMillis; + +////////////////////////////////////////////////////////////////////////////////////////////// +// colors is an array of color values for our pixels. We have 10 defined. You can +// tweak the color values to get a "better blue" or whatever. If you add more colors it is +// recommended to start them after the white. So start at 0x0a and go from there. +////////////////////////////////////////////////////////////////////////////////////////////// + +CRGB colors[]={ + CHSV(0x00,0x00,0x00), // 0x00 BLACK + CHSV(0x00,0xff,0xFF), // 0x01 Red + CHSV(0x60,0xff,0xFF), // 0x02 Green + CHSV(0xA0,0xFF,0xFF), // 0x03 Blue + CHSV(0x80,0xff,0xFF), // 0x04 Cyan + CHSV(0x40,0xff,0xFF), // 0x05 Yellow + CHSV(0xc0,0xff,0xFF), // 0x06 Purple + CHSV(0x20,0xff,0xFF), // 0x07 Orange + CHSV(0x00,0x00,0x46), // 0x08 Gray + CHSV(0x00,0x00,0xFF) // 0x09 White +}; + +////////////////////////////////////////////////////////////////////////////////////////////// +// Initial Setup of FastLED library +// Counting of total sprites +// Clearing the display +////////////////////////////////////////////////////////////////////////////////////////////// + +void setup() { + WiFi.mode(WIFI_STA); + + Serial.begin(115200); + + FastLED.addLeds(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); + + FastLED.setBrightness( BRIGHTNESS ); + FastLED.setTemperature( TEMPERATURE_1 ); // first temperature + + ////////////////////////////////////////////////////////////////////////////////////////////// + // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + // + // DO NOT CHANGE THE VALUES IN THE LINE BELOW THIS COMMENT UNLESS YOU KNOW WHAT YOU ARE DOING. + // THE CURRENT VALUES SET THE FASTLED LIBRARY TO LIMIT TOTAL AMPS USED BY THE 256 LED'S TO + // 1000 MILLIAMPS AT 5V. CHANGING THESE VALUES COULD DAMAGE THE MICROCONTROLLER OR YOUR + // PC'S USB PORT, YOUR MOTHERBOARD, YOUR USB-C POWER DEVICE, ETC. + // + // WE CAN NOT BE RESPONSIBLE FOR DAMAGE YOU MIGHT DO TO THE DISPLAY OR YOUR PC IF YOU MODIFY + // THE CODE AND IN PARTICULAR THE FastLED.setMaxPowerInVoltsAndMilliamps(5,1000) line!!!!!! + // + // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + ////////////////////////////////////////////////////////////////////////////////////////////// + + FastLED.setMaxPowerInVoltsAndMilliamps(5,1000); + + // Clear display + FastLED.clear(true); + FastLED.show(); + + show_system_sprite(0); + FastLED.delay(500); + + if (!LittleFS.begin()) { + Serial.println("No filesystem, formatting."); + LittleFS.format(); + if (!LittleFS.begin()) { + Serial.println("Unable to format."); + return; + } + } + + wifiManager.setDebugOutput(true); + wifiManager.setAPCallback(apCallback); + wifiManager.setConnectTimeout(15); + wifiManager.setConnectRetries(2); + wifiManager.setConfigPortalTimeout(180); + if (wifiManager.autoConnect("TechDungeon")) { + Serial.println("Connected to wifi..."); + handleWifiConnection(); + } else { + Serial.println("Unable to connect to wifi."); + } + + fileMillis = 0; + gifPlaying = 0; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// Main program loop. This goes through all sprites and displays one and then pauses the +// PAUSETIME before moving on to the next sprite. +////////////////////////////////////////////////////////////////////////////////////////////// + +void loop() { + Dir rootDirectory = LittleFS.openDir("/"); + for(;;) { + int newchar = Serial.read(); + if (newchar >= 0) { + Serial.write(newchar); + if (newchar == 'r') { + wifiManager.resetSettings(); + wifiManager.reboot(); + } + if (newchar == 'b') { + wifiManager.reboot(); + } + } + + if (gifPlaying) { + if (millis() - gifPlayingStartMillis > gifPlayingDelay) { + gifPlayingStartMillis = millis(); + int frameStatus = gifPlayFrame(&gifPlayingDelay); + if (1 != frameStatus) { + if (millis() - fileMillis > 5000) { + fileMillis = millis(); + selectNextGif(); + } else { + if (-1 == frameStatus) { + selectNextGif(); + } else if (0 == frameStatus) { + if (gif.getLastError() == GIF_EMPTY_FRAME) { + gif.reset(); + } else if (gif.getLastError() != GIF_SUCCESS) { + selectNextGif(); + } + } + } + } + } + } else { + if (millis() - fileMillis > 5000) { + fileMillis = millis(); + selectNextGif(); + } + } + MDNS.update(); + webServer.handleClient(); + FastLED.show(); + } +} +////////////////////////////////////////////////////////////////////////////////////////////// +// WifiManager: Asking to be added to Wifi +////////////////////////////////////////////////////////////////////////////////////////////// +void selectNextGif(void) { + bool ready = true; + if (!rootDirectory.next()) { + Serial.println("Restarting directory."); + if (!showed_image) { + show_system_sprite(2); + } + rootDirectory = LittleFS.openDir("/"); + ready = rootDirectory.next(); + showed_image = 0; + } + if (ready) { + if (gifPlaying) { + gifPlayEnd(); + } + if (rootDirectory.fileName().endsWith(".gif")) { + gifPlayBegin(rootDirectory.fileName().c_str()); + gifPlayingStartMillis = millis(); + gifPlayingDelay = 0; + showed_image = 1; + } else { + Serial.printf("Skipping: %s\r\n", rootDirectory.fileName().c_str()); + } + } else { + Serial.println("Unable to find any file."); + show_system_sprite(2); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// WifiManager: Asking to be added to Wifi +////////////////////////////////////////////////////////////////////////////////////////////// +void apCallback(WiFiManager *manager) { + show_system_sprite(1); + FastLED.delay(500); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// ESP is on the Wifi and ready to go +////////////////////////////////////////////////////////////////////////////////////////////// +void handleWifiConnection(void) { + if (MDNS.begin("techdungeon")) { + Serial.println("MDNS started..."); + } + + webServer.on("/", HTTP_GET, handleRootPage); + webServer.on("/list", HTTP_GET, handleFileList); + webServer.on("/edit", HTTP_DELETE, handleFileDelete); + webServer.on("/edit", HTTP_POST, handlePostPage, handlePostUpload); + webServer.onNotFound(handleNotFound); + + webServer.begin(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// WebServer: Deliver root page +////////////////////////////////////////////////////////////////////////////////////////////// +void handleRootPage(void) { + webServer.sendHeader("Connection", "close"); + webServer.send(200, "text/html", serverIndex); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// WebServer: Deliver image, if one exists +////////////////////////////////////////////////////////////////////////////////////////////// +void handleNotFound(void) { + if(!handleFileRead(webServer.uri())) { + webServer.send(404, "text/plain", "FileNotFound"); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// WebServer: Unescape percent encoding +////////////////////////////////////////////////////////////////////////////////////////////// +unsigned char h2int(char c) +{ + if (c >= '0' && c <='9'){ + return((unsigned char)c - '0'); + } + if (c >= 'a' && c <='f'){ + return((unsigned char)c - 'a' + 10); + } + if (c >= 'A' && c <='F'){ + return((unsigned char)c - 'A' + 10); + } + return(0); +} + +String urldecode(String str) +{ + String encodedString=""; + char c; + char code0; + char code1; + for (int i =0; i < str.length(); i++){ + c=str.charAt(i); + if (c == '+'){ + encodedString+=' '; + }else if (c == '%') { + i++; + code0=str.charAt(i); + i++; + code1=str.charAt(i); + c = (h2int(code0) << 4) | h2int(code1); + encodedString+=c; + } else{ + + encodedString+=c; + } + + yield(); + } + + return encodedString; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// WebServer: When transferring file, send as an image, or an octet stream when it's being downloaded +////////////////////////////////////////////////////////////////////////////////////////////// +String getContentType(String filename){ + if(webServer.hasArg("download")) return "application/octet-stream"; + else if(filename.endsWith(".gif")) return "image/gif"; + return "text/plain"; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// WebServer: Read files from LittleFS +////////////////////////////////////////////////////////////////////////////////////////////// +bool handleFileRead(String path){ + String decoded_url = urldecode(path); + String contentType = getContentType(decoded_url); + Serial.printf("read: %s\r\n", decoded_url.c_str()); + if(LittleFS.exists(decoded_url)){ + File file = LittleFS.open(decoded_url, "r"); + size_t sent = webServer.streamFile(file, contentType); + file.close(); + return true; + } + return false; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// WebServer: List files on LittleFS partition, send as JSON stream +////////////////////////////////////////////////////////////////////////////////////////////// +void handleFileList() { + if(!webServer.hasArg("dir")) {webServer.send(500, "text/plain", "BAD ARGS"); return;} + + String path = webServer.arg("dir"); + Dir dir = LittleFS.openDir(path); + path = String(); + + String output = "["; + while(dir.next()){ + if (output != "[") output += ','; + bool isDir = false; + output += "{\"type\":\""; + output += (isDir)?"dir":"file"; + output += "\",\"name\":\""; + output += dir.fileName(); + output += "\"}"; + } + + output += "]"; + webServer.send(200, "text/json", output); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// WebServer: Delete a file from the LittleFS partition +////////////////////////////////////////////////////////////////////////////////////////////// +void handleFileDelete(){ + if(webServer.args() == 0) return webServer.send(500, "text/plain", "BAD ARGS"); + String path = webServer.arg(0); + if(path == "/") + return webServer.send(500, "text/plain", "BAD PATH"); + if(!LittleFS.exists(path)) + return webServer.send(404, "text/plain", "FileNotFound"); + LittleFS.remove(path); + webServer.send(200, "text/plain", ""); + path = String(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// WebServer: Respond to the POST requests +////////////////////////////////////////////////////////////////////////////////////////////// +void handlePostPage(void) { + webServer.sendHeader("Connection", "close"); + webServer.send(200, "text/plain", "OK"); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// WebServer: Injest the new .gif file and save to LittleFS +////////////////////////////////////////////////////////////////////////////////////////////// +void handlePostUpload(void) { + HTTPUpload& upload = webServer.upload(); + if (upload.status == UPLOAD_FILE_START) { + Serial.printf("Getting image: %s -- Expect %d bytes.\r\n", upload.filename.c_str(), upload.contentLength); + uploadFile = LittleFS.open((rootStr + upload.filename).c_str(), "w"); + if (!uploadFile) { + Serial.println("Unable to open file for write."); + } + } else if (upload.status == UPLOAD_FILE_WRITE) { + if (!uploadFile.write(upload.buf, upload.currentSize)) { + Serial.println("Unable to write to file."); + } else { + Serial.printf("Accepted %d image bytes.\r\n", upload.currentSize); + } + } else if (upload.status == UPLOAD_FILE_END) { + Serial.println("Image accepted."); + uploadFile.close(); + } + yield(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// GIF player: Hook the animation player to LittleFS +////////////////////////////////////////////////////////////////////////////////////////////// +static void * GIFOpenFile(const char *fname, int32_t *pSize) +{ + playbackFile = LittleFS.open(fname, "r"); + if (playbackFile) { + *pSize = playbackFile.size(); + return (void *)&playbackFile; + } + return NULL; +} + +static void GIFCloseFile(void *pHandle) +{ + File *f = static_cast(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(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(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; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// GIF Player: RGB565 to 24 bit color for FastLED, and position correctly +////////////////////////////////////////////////////////////////////////////////////////////// +void LEDDrawPixel(int x, int y, uint16_t color) { + uint16_t unswapped_color = __builtin_bswap16(color); + uint8_t red = (unswapped_color & 0xf800) >> 8; + uint8_t green = (unswapped_color & 0x07e0) >> 3; + uint8_t blue = (unswapped_color & 0x001f) << 3; + leds[order[(y * 16) + x]] = CRGB(red, green, blue); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// GIF Player: Handle drawing call given that transparent pixels may exist +////////////////////////////////////////////////////////////////////////////////////////////// +void GIFDraw(GIFDRAW *pDraw) +{ + uint8_t *s; + uint16_t *d, *usPalette, usTemp[320]; + int x, y, iWidth; + + 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++) + { + LEDDrawPixel(x, y, usTemp[xOffset]); + } + 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 < pDraw->iWidth; x++) + { + LEDDrawPixel(x, y, usPalette[*s++]); + } + } +} /* GIFDraw() */ + +////////////////////////////////////////////////////////////////////////////////////////////// +// GIF Player: Call the GIF animation player, and play a file from LittleFS +////////////////////////////////////////////////////////////////////////////////////////////// +void gifPlayBegin( const char* gifPath ) +{ + + gif.begin(BIG_ENDIAN_PIXELS); + + if( ! gif.open( gifPath, GIFOpenFile, GIFCloseFile, GIFReadFile, GIFSeekFile, GIFDraw ) ) { + Serial.printf("Could not open gif %s:%d\r\n", gifPath, gif.getLastError()); + } + + Serial.printf("Playing %s\r\n", gifPath); + gifPlaying = 1; +} + +int gifPlayFrame(int *inFrameDelay) { + int frameStatus; + long lTime = millis(); + int frameDelay = 0; // store delay for the last frame + if (frameStatus = gif.playFrame(false, &frameDelay)) { + lTime = millis() - lTime; + if (lTime < frameDelay) { + *inFrameDelay = frameDelay - lTime; + } else { + *inFrameDelay = 0; + } + } + return frameStatus; +} + +void gifPlayEnd(void) { + gifPlaying = 0; + gif.close(); + return; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// show_system_sprite loads a system sprite into the LEDs array +////////////////////////////////////////////////////////////////////////////////////////////// +void show_system_sprite(int spriteNum) { + // Run through all LED's and set their color to the given data for that frame + for(int x=0;x -#include "sprites.h" - -#define LED_PIN 2 // Data pin for controlling LED's -#define LED_XWIDTH 16 // X Width of Pixels, 16 in our case -#define LED_YWIDTH 16 // Y Width of Pixels, 16 in our case -#define NUM_LEDS LED_XWIDTH * LED_YWIDTH // Total number of pixels -#define BRIGHTNESS 32 // Brightness setting. We keep this sane to reduce current draw -#define PAUSETIME 5000 // Pause time between sprites in milliseconds -#define TEMPERATURE_1 Tungsten100W // Color Temperature to use with FastLED library - -CRGB leds[NUM_LEDS]; // Define the array for our LED's - -int total_sprites = 0; // Used to store total number of sprites in our array -sprites cur_sprite; // Current sprite being displayed - -////////////////////////////////////////////////////////////////////////////////////////////// -// colors is an array of color values for our pixels. We have 10 defined. You can -// tweak the color values to get a "better blue" or whatever. If you add more colors it is -// recommended to start them after the white. So start at 0x0a and go from there. -////////////////////////////////////////////////////////////////////////////////////////////// - -CRGB colors[]={ - CHSV(0x00,0x00,0x00), // 0x00 BLACK - CHSV(0x00,0xff,0xFF), // 0x01 Red - CHSV(0x60,0xff,0xFF), // 0x02 Green - CHSV(0xA0,0xFF,0xFF), // 0x03 Blue - CHSV(0x80,0xff,0xFF), // 0x04 Cyan - CHSV(0x40,0xff,0xFF), // 0x05 Yellow - CHSV(0xc0,0xff,0xFF), // 0x06 Purple - CHSV(0x20,0xff,0xFF), // 0x07 Orange - CHSV(0x00,0x00,0x46), // 0x08 Gray - CHSV(0x00,0x00,0xFF) // 0x09 White -}; - -////////////////////////////////////////////////////////////////////////////////////////////// -// Initial Setup of FastLED library -// Counting of total sprites -// Clearing the display -////////////////////////////////////////////////////////////////////////////////////////////// - -void setup() { - FastLED.addLeds(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); - - FastLED.setBrightness( BRIGHTNESS ); - FastLED.setTemperature( TEMPERATURE_1 ); // first temperature - - ////////////////////////////////////////////////////////////////////////////////////////////// - // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING - // - // DO NOT CHANGE THE VALUES IN THE LINE BELOW THIS COMMENT UNLESS YOU KNOW WHAT YOU ARE DOING. - // THE CURRENT VALUES SET THE FASTLED LIBRARY TO LIMIT TOTAL AMPS USED BY THE 256 LED'S TO - // 1000 MILLIAMPS AT 5V. CHANGING THESE VALUES COULD DAMAGE THE MICROCONTROLLER OR YOUR - // PC'S USB PORT, YOUR MOTHERBOARD, YOUR USB-C POWER DEVICE, ETC. - // - // WE CAN NOT BE RESPONSIBLE FOR DAMAGE YOU MIGHT DO TO THE DISPLAY OR YOUR PC IF YOU MODIFY - // THE CODE AND IN PARTICULAR THE FastLED.setMaxPowerInVoltsAndMilliamps(5,1000) line!!!!!! - // - // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING - ////////////////////////////////////////////////////////////////////////////////////////////// - - FastLED.setMaxPowerInVoltsAndMilliamps(5,1000); - - // Clear display - FastLED.clear(); - FastLED.show(); - - total_sprites = count_sprites(); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -// count_sprites loops through array checking for NULL to denote the end -////////////////////////////////////////////////////////////////////////////////////////////// - -int count_sprites() { - int x; - for(x=0;spritelist[x].type!=0;x++); - return(x); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -// We call this function to translate a location on the 16x16 grid from the top/down -// left/right we expect, to the actual pattern the LED's are wired in on the board. -////////////////////////////////////////////////////////////////////////////////////////////// - -unsigned char getOrder(unsigned char pos) { - return(order[pos]); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -// Main program loop. This goes through all sprites and displays one and then pauses the -// PAUSETIME before moving on to the next sprite. -////////////////////////////////////////////////////////////////////////////////////////////// - -void loop() { - for(;;) { - for(int x=0;x Date: Thu, 21 Sep 2023 22:12:23 -0500 Subject: [PATCH 2/3] Fix the naming --- led_display.ino => LED-DISPLAY.ino | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename led_display.ino => LED-DISPLAY.ino (100%) diff --git a/led_display.ino b/LED-DISPLAY.ino similarity index 100% rename from led_display.ino rename to LED-DISPLAY.ino From 8620a1f6595e2eb63841f39f04f88f40c2e53a63 Mon Sep 17 00:00:00 2001 From: Keith Henrickson Date: Sun, 24 Sep 2023 16:27:49 -0500 Subject: [PATCH 3/3] When previewing a GIF file on the website, also preview it on the LED display --- LED-DISPLAY.ino | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/LED-DISPLAY.ino b/LED-DISPLAY.ino index 969e74c..7a71163 100644 --- a/LED-DISPLAY.ino +++ b/LED-DISPLAY.ino @@ -22,7 +22,7 @@ #include "sprites.h" int gifPlaying; -int gifPlayingStartMillis; +int gifFrameMillis; int gifPlayingDelay; uint8_t showed_image = 0; WiFiManager wifiManager; @@ -155,8 +155,8 @@ void loop() { } if (gifPlaying) { - if (millis() - gifPlayingStartMillis > gifPlayingDelay) { - gifPlayingStartMillis = millis(); + if (millis() - gifFrameMillis > gifPlayingDelay) { + gifFrameMillis = millis(); int frameStatus = gifPlayFrame(&gifPlayingDelay); if (1 != frameStatus) { if (millis() - fileMillis > 5000) { @@ -186,8 +186,22 @@ void loop() { FastLED.show(); } } + ////////////////////////////////////////////////////////////////////////////////////////////// -// WifiManager: Asking to be added to Wifi +// Play a gif file +////////////////////////////////////////////////////////////////////////////////////////////// +void setupAndPlayGif(const char *filename) { + if (gifPlaying) { + gifPlayEnd(); + } + gifPlayBegin(filename); + gifFrameMillis = millis(); + fileMillis = millis(); + gifPlayingDelay = 0; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// Select and play next gif ////////////////////////////////////////////////////////////////////////////////////////////// void selectNextGif(void) { bool ready = true; @@ -201,13 +215,8 @@ void selectNextGif(void) { showed_image = 0; } if (ready) { - if (gifPlaying) { - gifPlayEnd(); - } if (rootDirectory.fileName().endsWith(".gif")) { - gifPlayBegin(rootDirectory.fileName().c_str()); - gifPlayingStartMillis = millis(); - gifPlayingDelay = 0; + setupAndPlayGif(rootDirectory.fileName().c_str()); showed_image = 1; } else { Serial.printf("Skipping: %s\r\n", rootDirectory.fileName().c_str()); @@ -325,6 +334,9 @@ bool handleFileRead(String path){ File file = LittleFS.open(decoded_url, "r"); size_t sent = webServer.streamFile(file, contentType); file.close(); + if(!webServer.hasArg("download")) { + setupAndPlayGif(decoded_url.c_str()); + } return true; } return false;