ESP32学习,驱动LED点阵屏,SD卡播放GIF动图Webserver上传 调试完成
文章目录
- ESP32学习,驱动LED点阵屏,SD卡播放GIF动图Webserver上传 调试完成
- 前言
- 水平有限,慢慢学习,勉强整了一个小程序 功能比较简单,Led屏循环播放Gif动图,Webserver管理动图,循环播放;需要连接WIFI。新WIFI用手机连接192.168.4.1 配置一下即可。如果不需要随时联网,做一个超时即可;也可以直接用softAP随时访问。 ![在这里插入图片描述](https://s2.51cto.com/images/blog/202406/26122924_667b99246626330944.jpg?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=) 电路板是手工简单焊接的
- 一、VScode环境platformIO
- 二、使用步骤
- 1.main.cpp
- 2.sdcard_functions.hpp
- 2.gif_functions.hpp
- 总结
前言
水平有限,慢慢学习,勉强整了一个小程序
功能比较简单,Led屏循环播放Gif动图,Webserver管理动图,循环播放;需要连接WIFI。新WIFI用手机连接192.168.4.1 配置一下即可。如果不需要随时联网,做一个超时即可;也可以直接用softAP随时访问。
电路板是手工简单焊接的
一、VScode环境platformIO
VScode的代码,直接可用
二、使用步骤
1.main.cpp
代码如下(示例):
#include <Arduino.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
#include <AnimatedGIF.h>
#include <WiFi.h>
#include <WiFiManager.h>
//#include <ESPAsyncWebServer.h>
#include <WebServer.h>
#include <U8g2_for_Adafruit_GFX.h>
/*
* 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 E_PIN 21 //-1 // IMPORTANT: Change to a valid pin if using a 64x64px panel.
#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 2 // Total number of panels chained one to another
/**************************************************************/
//AsyncWebServer server(80);
WebServer server(80);
AnimatedGIF gif;
MatrixPanel_I2S_DMA *dma_display = nullptr;
U8G2_FOR_ADAFRUIT_GFX u8g2Fonts;
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
String GifFileName[20];
bool GifPlayFlag=true;
const int maxGifDuration = 30000; // ms, max GIF duration
unsigned long NowTime=0;
int RunTimes=0;
#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 );
//gif.close();
return 6;
}
//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;
}
}
//Serial.println("Play Gif frames : " + then );
gif.close();
return then;
}
void initSD_Gif(){
// **************************** 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;
}
GifFiles.clear() ;totalFiles=0;
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());
GifFileName[totalFiles]=String(filename.c_str());
Serial.println("Adding to gif list: "+String(totalFiles)+": " + GifFileName[totalFiles]);
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. \n", totalFiles);
//totalFiles = getGifInventory("/gifs");
// This is important - Set the right endianness.
gif.begin(LITTLE_ENDIAN_PIXELS);
GifPlayFlag=true;
}
String listDir2Web(fs::FS &fs, const char * dirname, uint8_t levels)
{
Serial.printf("Listing directory: %s\n", dirname);
String message="";
message=String(dirname)+"<br/>";
//dirname=message.c_str();
File root = fs.open(dirname);
if(!root){
message += "Failed to open directory <br />";
return message;
}
if(!root.isDirectory()){
message += "Not a directory <br />";
return message;
}
File file = root.openNextFile();
while(file){
if(file.isDirectory()){
message +=" DIR : ";
message += String(file.name())+String("<br />");
if(levels){
message += listDir2Web(fs, file.name(), levels -1);
}
} else {
message += String("---FILE: ");
message += String(file.name())+String(" ")+String("<a href='/deletefile?filename="+String(file.name())+"'><button> Del</button></a>");
message += String(" SIZE: ");
message += formatBytes(file.size())+String("<br />");
}
file = root.openNextFile();
}
return message;
}
//网页查看目录
void handleListDir(){ //AsyncWebServerRequest *request) {
String header = "<html><body>";
String message= header + "<h2>List the file in the SD Card:</h2><br><hr><br>";
message += listDir2Web(SD,"/gifs",0);
message += "<br><hr><br>";
message += "<a href='/'>GIF List Success !Back!</a>";
server.send(200,"text/html",message);
}
//单个删除gif文件
void handleDeleteFile(String filename){// AsyncWebServerRequest *request,String filename) {
gif.close();
GifPlayFlag=false;
filename="/gifs/"+filename;
deleteFile(SD, filename.c_str());
Serial.println(filename);
server.send(200,"text/html","<a href='/'>"+ filename+String(" deleted...Click Back!</a>"));
initSD_Gif();
}
//批量删除gif文件
void handleDelete(){//AsyncWebServerRequest *request) { //AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final
GifPlayFlag=false;
gif.close();
Serial.println("GIFs to delete... ");
int i=0;
if(totalFiles>=3){
for(i=1;i<totalFiles-1;i++)
{
deleteFile(SD, GifFileName[i].c_str());
Serial.println("Found GIFs to delete... "+ GifFileName[i]);
delay(500);
}
}
for(i=0;i<totalFiles;i++){
Serial.println("List File ... "+ GifFileName[i]);
}
server.send(200, "text/html", "<a href='/'>GIF Delete Success !Back!</a>");
initSD_Gif();
//GifPlayFlag=true;
}
//上传gif文件
void handleUpload(){ //AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
static File uploadFile;
GifPlayFlag=false;
gif.close();
HTTPUpload& upload = server.upload();
String fileName = upload.filename;
if (upload.status == UPLOAD_FILE_START) {
if (SD.exists((char *)upload.filename.c_str())) {
SD.remove((char *)upload.filename.c_str());
}
fileName = "/gifs/"+upload.filename.substring(upload.filename.lastIndexOf('\\')+1);
//Serial.print("fileName1: ");Serial.println(fileName);
uploadFile = SD.open(fileName, FILE_WRITE);
//if(uploadFile!=NULL) Serial.println("File exsist!");
Serial.print("Upload: START, filename: "); Serial.println(upload.filename);
} else if (upload.status == UPLOAD_FILE_WRITE) {
if (uploadFile) {
if(uploadFile.write(upload.buf, upload.currentSize))
//Serial.print("write ");
Serial.print("write Bytes: "); Serial.println(upload.currentSize);
}
} else if (upload.status == UPLOAD_FILE_END) {
uploadFile.close();
server.send(200, "text/html", "<a href='/'>GIF Upload Success !Back!</a>");
Serial.print("Upload: END! Size: "); Serial.println(upload.totalSize);
initSD_Gif();
}else{
if (uploadFile) {
if(uploadFile.write(upload.buf, upload.currentSize))
Serial.print("Upload else, Bytes: "); Serial.println(upload.currentSize);
}
Serial.println("Upload:Donot Know ??? "); //Serial.println(upload.totalSize);
uploadFile.close();
server.send(200, "text/html", "<a href='/'>GIF Upload Success !Back!</a>");
Serial.print("Upload: END! Size: "); Serial.println(upload.totalSize);
initSD_Gif();
}
}
void updateServer() {
//开启web文件服务
GifPlayFlag=true;
server.on("/", HTTP_GET, [](){//AsyncWebServerRequest *request){
GifPlayFlag=false;
server.send(200, "text/html;charset=utf-8", "<head><title>上传GIF文件</title></head><br><br><a >1.选择要上传的文件 2.点击Upload进行上传</a><br><br><form method='POST' action='/upload' enctype='multipart/form-data'><input type='file' name='image'><input type='submit' value='Upload'></form><br><form method='GET' action='/delete'><a>3.删除已上传GIF文件 </a><input type='submit' value='Delete'></form><br><hr><br><br><form method='POST' action='/listwebdir'><a>查看文件列表...</a><input type='submit' value='List GIF'></form>"); //<a href='/delete'> 按钮重复 <button>List GIF</button></a>
});
server.on("/delete", HTTP_GET, handleDelete);
server.on("/deletefile", HTTP_GET,[] (){//AsyncWebServerRequest *request){
String inputMessage1;
if (server.hasArg("filename")) {
inputMessage1 = server.arg("filename");//->value();
Serial.println(inputMessage1);
}
else {
inputMessage1 = "No message sent";
Serial.println(inputMessage1);
}
handleDeleteFile(inputMessage1);
server.send(200, "text/html", "<a href='/'>Delete Success!Back!</a>");
});
server.on("/listwebdir", HTTP_POST, handleListDir);
server.on("/upload", HTTP_POST, [](){ //AsyncWebServerRequest *request){
//AsyncWebServerResponse *response = request->beginResponse(200, "text/html", "<a href='/'>Upload click! Back!</a>");
String msg= "<a href='/'>Upload click! Back!</a>";
server.send(200, "text/html",msg);
}, handleUpload);
server.begin();
Serial.println("Web Server ON !");
}
void setup()
{
Serial.begin(115200);
pinMode(2,OUTPUT);
delay(3000);
WiFiManager wifiManager;
// 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称
wifiManager.autoConnect("AutoConnectAP");
// 如果您希望该WiFi添加密码,可以使用以下语句:// wifiManager.autoConnect("AutoConnectAP", "12345678");// 以上语句中的12345678是连接AutoConnectAP的密码
delay(1000); // WiFi连接成功后将通过串口监视器输出连接成功信息
Serial.println("");
Serial.print("ESP32 Connected to ");
Serial.println(WiFi.SSID()); // WiFi名称
Serial.print("IP address:\t");
Serial.println(WiFi.localIP());
// **************************** 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, "/", 0, false);
Serial.println("-----------------------------------------------------");
listDir(SD,"/gifs",0,false);
Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
//开启web文件服务
server.on("/", HTTP_GET, [](){//AsyncWebServerRequest *request){
GifPlayFlag=false;
server.send(200, "text/html;charset=utf-8", "<head><title>上传GIF文件</title></head><br><br><a >1.选择要上传的文件 2.点击Upload进行上传</a><br><br><form method='POST' action='/upload' enctype='multipart/form-data'><input type='file' name='image'><input type='submit' value='Upload'></form><br><form method='GET' action='/delete'><a>3.删除已上传GIF文件 </a><input type='submit' value='Delete'></form><br><hr><br><br><form method='POST' action='/listwebdir'><a>查看文件列表...</a><input type='submit' value='List GIF'></form>"); //<a href='/delete'> 按钮重复 <button>List GIF</button></a>
});
server.on("/delete", HTTP_GET, handleDelete);
server.on("/deletefile", HTTP_GET,[] (){//AsyncWebServerRequest *request){
String inputMessage1;
if (server.hasArg("filename")) {
inputMessage1 = server.arg("filename");//->value();
Serial.println(inputMessage1);
}
else {
inputMessage1 = "No message sent";
Serial.println(inputMessage1);
}
handleDeleteFile(inputMessage1);
server.send(200, "text/html", "<a href='/'>Delete Success!Back!</a>");
});
server.on("/listwebdir", HTTP_POST, handleListDir);
server.on("/upload", HTTP_POST, [](){ //AsyncWebServerRequest *request){
String msg= "<a href='/'>Upload click! Back!</a>";
server.send(200, "text/html",msg);
}, handleUpload);
server.begin();
Serial.println("Web Server ON !");
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.d = D_PIN;
mxconfig.gpio.e = E_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());
//存入到文件数组
GifFileName[totalFiles]=String(filename.c_str());
Serial.println("Adding to gif list: "+String(totalFiles)+": " + GifFileName[totalFiles]);
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. \n", totalFiles);
//totalFiles = getGifInventory("/gifs");
delay(1000);
// This is important - Set the right endianness.
gif.begin(LITTLE_ENDIAN_PIXELS);
Serial.print("IP address:\t");//再发送一次ip地址
Serial.println(WiFi.localIP());
dma_display->clearScreen();
u8g2Fonts.begin(*dma_display);
u8g2Fonts.setFontMode(1);
u8g2Fonts.setFontDirection(0);
u8g2Fonts.setFont(u8g2_font_wqy12_t_gb2312); //u8g2_font_wqy16_t_gb2312); //u8g2_font_wqy16_t_gb2312
u8g2Fonts.setForegroundColor(dma_display->color565(255, random(100), random(100)));
u8g2Fonts.drawUTF8(1,16,"Found GIFs to play.");
delay(1000);
u8g2Fonts.drawUTF8(1,32,(WiFi.SSID()).c_str());
delay(1000);
u8g2Fonts.drawUTF8(1,48,(String(WiFi.localIP())).c_str());
delay(5000);
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");
}
cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);
Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
}
void loop(){
//draw_test_patterns();
RunTimes++;
if(RunTimes%10==0){Serial.print("RunTimes: "); Serial.println(RunTimes); }
if(RunTimes>300){
Serial.print("RunTimes: "); Serial.println(RunTimes);
RunTimes=0;
server.stop();
updateServer();
}
server.handleClient();
if(GifPlayFlag==true){
int t_times=0;
for(auto & elem : GifFiles){
if(GifPlayFlag==false) break; // return;
gifPlay( elem.c_str() );
gif.reset();
Serial.printf("Found %d GIFs to play. \n", elem); //.c_str());
t_times++;
Serial.print("已播放文件: ");Serial.println(t_times);
digitalWrite(2,!digitalRead(2));
server.handleClient();
delay(200);
RunTimes++;
}
}
delay(200);
//listDir(SD, "/", 1, false);
//Serial.println("---------------------------------");
//listDir(SD,"/gifs",0,false);
//Serial.println("---------------------------------");
digitalWrite(2,!digitalRead(2));
}
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);
}
文件操作比较简单,3删除文件会保留原始两个,用于演示
文件列表可以单独删除文件,相对方便点。
2.sdcard_functions.hpp
库里的辅助文件,稍作了改动:
/************************ SD Card Code ************************/
// As per: https://github.com/espressif/arduino-esp32/tree/master/libraries/SD/examples/SD_Test
//format bytes
String formatBytes(size_t bytes) {
if (bytes < 1024) {
return String(bytes) + "B";
} else if (bytes < (1024 * 1024)) {
return String(bytes / 1024.0) + "KB";
} else if (bytes < (1024 * 1024 * 1024)) {
return String(bytes / 1024.0 / 1024.0) + "MB";
} else {
return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB";
}
}
void createDir(fs::FS &fs, const char * path){
Serial.printf("Creating Dir: %s\n", path);
if(fs.mkdir(path)){
Serial.println("Dir created");
} else {
Serial.println("mkdir failed");
}
}
void removeDir(fs::FS &fs, const char * path){
Serial.printf("Removing Dir: %s\n", path);
if(fs.rmdir(path)){
Serial.println("Dir removed");
} else {
Serial.println("rmdir failed");
}
}
void writeFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Writing file: %s\n", path);
File file = fs.open(path, FILE_WRITE);
if(!file){
Serial.println("Failed to open file for writing");
return;
}
if(file.print(message)){
Serial.println("File written");
} else {
Serial.println("Write failed");
}
file.close();
}
void appendFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Appending to file: %s\n", path);
File file = fs.open(path, FILE_APPEND);
if(!file){
Serial.println("Failed to open file for appending");
return;
}
if(file.print(message)){
Serial.println("Message appended");
} else {
Serial.println("Append failed");
}
file.close();
}
void renameFile(fs::FS &fs, const char * path1, const char * path2){
Serial.printf("Renaming file %s to %s\n", path1, path2);
if (fs.rename(path1, path2)) {
Serial.println("File renamed");
} else {
Serial.println("Rename failed");
}
}
void deleteFile(fs::FS &fs, const char * path){
Serial.printf("Deleting file: %s\n", path);
if(fs.remove(path)){
Serial.println("File deleted");
} else {
Serial.println("Delete failed");
}
}
bool loadFromSdCard(String path) {
String dataType = "text/plain";
if (path.endsWith("/")) {
path += "index.htm";
}
if (path.endsWith(".src")) {
path = path.substring(0, path.lastIndexOf("."));
} else if (path.endsWith(".htm")) {
dataType = "text/html";
} else if (path.endsWith(".css")) {
dataType = "text/css";
} else if (path.endsWith(".js")) {
dataType = "application/javascript";
} else if (path.endsWith(".png")) {
dataType = "image/png";
} else if (path.endsWith(".gif")) {
dataType = "image/gif";
} else if (path.endsWith(".jpg")) {
dataType = "image/jpeg";
} else if (path.endsWith(".ico")) {
dataType = "image/x-icon";
} else if (path.endsWith(".xml")) {
dataType = "text/xml";
} else if (path.endsWith(".pdf")) {
dataType = "application/pdf";
} else if (path.endsWith(".zip")) {
dataType = "application/zip";
}
File dataFile = SD.open(path.c_str());
if (dataFile.isDirectory()) {
path += "/index.htm";
dataType = "text/html";
dataFile = SD.open(path.c_str());
}
if (!dataFile) {
return false;
}
//if (server.hasArg("download")) {
//dataType = "application/octet-stream";
// }
//if (server.streamFile(dataFile, dataType) != dataFile.size()) {
// Serial.println("Sent less data than expected!");
//}
dataFile.close();
return true;
}
//上面内容增加针对sd卡文件操作
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();
}
2.gif_functions.hpp
gif辅助文件:
// 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*PANEL_CHAIN)
//iWidth = PANEL_RES_X*PANEL_CHAIN;
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() */
总结
比较简单,高手请批评指正。