Created
June 24, 2016 00:10
-
-
Save me-no-dev/a1975076563ab7146f69f89df5ca27d8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <ESP8266WiFi.h> | |
#include <ESP8266mDNS.h> | |
#include <ArduinoOTA.h> | |
#include <FS.h> | |
#include <ESPAsyncTCP.h> | |
#include <ESPAsyncWebServer.h> | |
#include "SPI.h" | |
#include "SD.h" | |
bool SD_exists(String path){ | |
bool exists = false; | |
sd::File test = SD.open(path); | |
if(test){ | |
test.close(); | |
exists = true; | |
} | |
return exists; | |
} | |
class AsyncSDFileResponse: public AsyncAbstractResponse { | |
private: | |
sd::File _content; | |
String _path; | |
void _setContentType(String path){ | |
if (path.endsWith(".html")) _contentType = "text/html"; | |
else if (path.endsWith(".htm")) _contentType = "text/html"; | |
else if (path.endsWith(".css")) _contentType = "text/css"; | |
else if (path.endsWith(".txt")) _contentType = "text/plain"; | |
else if (path.endsWith(".js")) _contentType = "application/javascript"; | |
else if (path.endsWith(".png")) _contentType = "image/png"; | |
else if (path.endsWith(".gif")) _contentType = "image/gif"; | |
else if (path.endsWith(".jpg")) _contentType = "image/jpeg"; | |
else if (path.endsWith(".ico")) _contentType = "image/x-icon"; | |
else if (path.endsWith(".svg")) _contentType = "image/svg+xml"; | |
else if (path.endsWith(".xml")) _contentType = "text/xml"; | |
else if (path.endsWith(".pdf")) _contentType = "application/pdf"; | |
else if (path.endsWith(".zip")) _contentType = "application/zip"; | |
else if(path.endsWith(".gz")) _contentType = "application/x-gzip"; | |
else _contentType = "application/octet-stream"; | |
} | |
public: | |
AsyncSDFileResponse(String path, String contentType=String(), bool download=false){ | |
_code = 200; | |
_path = path; | |
if(!download && !SD_exists(_path) && SD_exists(_path+".gz")){ | |
_path = _path+".gz"; | |
addHeader("Content-Encoding", "gzip"); | |
} | |
if(download) | |
_contentType = "application/octet-stream"; | |
else | |
_setContentType(path); | |
_content = SD.open(_path); | |
_contentLength = _content.size(); | |
} | |
~AsyncSDFileResponse(){ | |
if(_content) | |
_content.close(); | |
} | |
bool _sourceValid(){ return !!(_content); } | |
size_t _fillBuffer(uint8_t *buf, size_t maxLen){ | |
int r = _content.read(buf, maxLen); | |
if(r < 0){ | |
os_printf("Error\n"); | |
_content.close(); | |
return 0; | |
} | |
return r; | |
} | |
}; | |
class SDEditor: public AsyncWebHandler { | |
private: | |
String _username; | |
String _password; | |
bool _uploadAuthenticated; | |
uint32_t _startTime; | |
sd::File uploadFile; | |
void _delete(String path){ | |
sd::File file = SD.open((char *)path.c_str()); | |
if(!file.isDirectory()){ | |
file.close(); | |
SD.remove((char *)path.c_str()); | |
return; | |
} | |
file.rewindDirectory(); | |
sd::File entry; | |
String entryPath; | |
while(true) { | |
entry = file.openNextFile(); | |
if (!entry) break; | |
entryPath = path + "/" +entry.name(); | |
if(entry.isDirectory()){ | |
entry.close(); | |
_delete(entryPath); | |
} else { | |
entry.close(); | |
SD.remove((char *)entryPath.c_str()); | |
} | |
entryPath = String(); | |
} | |
SD.rmdir((char *)path.c_str()); | |
path = String(); | |
file.close(); | |
} | |
public: | |
SDEditor(String username=String(), String password=String()):_username(username),_password(password),_uploadAuthenticated(false),_startTime(0){} | |
bool canHandle(AsyncWebServerRequest *request){ | |
if(request->method() == HTTP_GET && request->url() == "/edit" && (SD_exists((char*)"/edit.htm") || SD_exists((char*)"/edit.htm.gz"))) | |
return true; | |
else if(request->method() == HTTP_GET && request->url() == "/list") | |
return true; | |
else if(request->method() == HTTP_GET && (request->url().endsWith("/") || SD_exists(request->url()) || (!request->hasParam("download") && SD_exists(request->url()+".gz")))) | |
return true; | |
else if(request->method() == HTTP_POST && request->url() == "/edit") | |
return true; | |
else if(request->method() == HTTP_DELETE && request->url() == "/edit") | |
return true; | |
else if(request->method() == HTTP_PUT && request->url() == "/edit") | |
return true; | |
return false; | |
} | |
void handleRequest(AsyncWebServerRequest *request){ | |
if(_username.length() && (request->method() != HTTP_GET || request->url() == "/edit" || request->url() == "/list") && !request->authenticate(_username.c_str(),_password.c_str())) | |
return request->requestAuthentication(); | |
if(request->method() == HTTP_GET && request->url() == "/edit"){ | |
request->send(new AsyncSDFileResponse("/edit.htm")); | |
} else if(request->method() == HTTP_GET && request->url() == "/list"){ | |
if(request->hasParam("dir")){ | |
String path = request->getParam("dir")->value(); | |
sd::File entry; | |
sd::File dir = SD.open((char *)path.c_str()); | |
if(!dir.isDirectory()){ | |
dir.close(); | |
request->send(400); | |
} else { | |
dir.rewindDirectory(); | |
String output = "["; | |
while(true) { | |
entry = dir.openNextFile(); | |
if (!entry) break; | |
if (output != "[") output += ','; | |
output += "{\"type\":\""; | |
output += (entry.isDirectory())?"dir":"file"; | |
output += "\",\"name\":\""; | |
String name = String(entry.name()); | |
name.toLowerCase(); | |
output += name; | |
output += "\"}"; | |
entry.close(); | |
} | |
output += "]"; | |
dir.close(); | |
request->send(200, "text/json", output); | |
output = String(); | |
} | |
} | |
else | |
request->send(400); | |
} else if(request->method() == HTTP_GET){ | |
String path = request->url(); | |
if(path.endsWith("/")) | |
path += "index.htm"; | |
request->send(new AsyncSDFileResponse(path, String(), request->hasParam("download"))); | |
} else if(request->method() == HTTP_DELETE){ | |
if(request->hasParam("path", true) && SD_exists(request->getParam("path", true)->value())){ | |
_delete(request->getParam("path", true)->value()); | |
request->send(200, "", "DELETE: "+request->getParam("path", true)->value()); | |
} else | |
request->send(404); | |
} else if(request->method() == HTTP_POST){ | |
if(request->hasParam("data", true, true) && SD_exists(request->getParam("data", true, true)->value())) | |
request->send(200, "", "UPLOADED: "+request->getParam("data", true, true)->value()); | |
else | |
request->send(500); | |
} else if(request->method() == HTTP_PUT){ | |
if(request->hasParam("path", true)){ | |
String filename = request->getParam("path", true)->value(); | |
if(SD_exists(filename)){ | |
request->send(200); | |
} else { | |
sd::File f = SD.open(filename, FILE_WRITE | O_TRUNC); | |
if(f){ | |
f.write((uint8_t)0x00); | |
f.close(); | |
request->send(200, "", "CREATE: "+filename); | |
} else { | |
request->send(500); | |
} | |
} | |
} else | |
request->send(400); | |
} | |
} | |
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ | |
if(!index){ | |
uploadFile = SD.open(filename, FILE_WRITE | O_TRUNC); | |
_startTime = millis(); | |
} | |
if(len && uploadFile) | |
uploadFile.write(data, len); | |
if(final){ | |
if(uploadFile) uploadFile.close(); | |
uint32_t uploadTime = millis() - _startTime; | |
os_printf("upload: %s, %u B, %u ms\n", filename.c_str(), index+len, uploadTime); | |
} | |
} | |
}; | |
const char *getSdTypeString(){ | |
if (SD.type() == SD_CARD_TYPE_SD1){ | |
return "SD1"; | |
} else if(SD.type() == SD_CARD_TYPE_SD2){ | |
return "SD2"; | |
} else if(SD.type() == SD_CARD_TYPE_SDHC){ | |
return "SDHC"; | |
} else { | |
return "UNKNOWN"; | |
} | |
} | |
String getSizeString(size_t bytes){ | |
if (bytes < 1024){ | |
return String(String(bytes)+"B"); | |
} else if(bytes < (1024 * 1024)){ | |
return String(String(bytes/1024.0)+"KB"); | |
} else if(bytes < (1024 * 1024 * 1024)){ | |
return String(String(bytes/1024.0/1024.0)+"MB"); | |
} else { | |
return String(String(bytes/1024.0/1024.0/1024.0)+"GB"); | |
} | |
} | |
bool initSD(){ | |
if (SD.begin(SS, 32000000)){ | |
Serial.print("\n==== SD Card Info ====\n"); | |
Serial.printf("SD Type: %s FAT%d, Size: %s\n", getSdTypeString(), SD.fatType(), getSizeString(SD.size()).c_str()); | |
Serial.printf("SD Blocks: total: %d, size: %s\n", SD.totalBlocks(), getSizeString(SD.blockSize()).c_str()); | |
Serial.printf("SD Clusters: total: %d, blocks: %d, size: %s\n", SD.totalClusters(), SD.blocksPerCluster(), getSizeString(SD.clusterSize()).c_str()); | |
sd::File entry; | |
sd::File dir = SD.open("/"); | |
dir.rewindDirectory(); | |
while(true){ | |
entry = dir.openNextFile(); | |
if (!entry) break; | |
Serial.printf("SD File: %s, type: %s, size: %s\n", entry.name(), (entry.isDirectory())?"dir":"file", getSizeString(entry.size()).c_str()); | |
entry.close(); | |
} | |
dir.close(); | |
Serial.println(); | |
return true; | |
} else | |
Serial.println("SD Card Not Found!"); | |
return false; | |
} | |
AsyncWebServer server(80); | |
const char* ssid = "***********"; | |
const char* password = "***********"; | |
const char* ap_ssid = "esp8266-async"; | |
const char* ap_password = ""; | |
const char* http_username = "admin"; | |
const char* http_password = "admin"; | |
extern "C" void system_set_os_print(uint8 onoff); | |
extern "C" void ets_install_putc1(void* routine); | |
extern "C" { | |
enum sleep_type { | |
NONE_SLEEP_T = 0, | |
LIGHT_SLEEP_T, | |
MODEM_SLEEP_T | |
}; | |
extern bool wifi_set_sleep_type(enum sleep_type type); | |
} | |
void onWiFiChange(bool connected); | |
/* | |
static void _u1_putc(char c){ | |
while(((U1S >> USTXC) & 0x7F) != 0); | |
U1F = c; | |
} | |
*/ | |
static void _u0_putc(char c){ | |
while(((U0S >> USTXC) & 0x7F) != 0); | |
U0F = c; | |
} | |
void initSerial(){ | |
//system_set_os_print(0); | |
Serial.begin(115200); | |
ets_install_putc1((void *) &_u0_putc); | |
system_set_os_print(1); | |
//enable debug on pin 2 | |
//Serial1.begin(115200); | |
//ets_install_putc1((void *) &_u1_putc); | |
//system_set_os_print(1); | |
} | |
void initWiFi(){ | |
WiFi.mode(WIFI_STA); | |
WiFi.begin(ssid, password); | |
if (WiFi.waitForConnectResult() != WL_CONNECTED) { | |
os_printf("STA: Failed!\n"); | |
//WiFi.disconnect(false); | |
//delay(1000); | |
//WiFi.begin(ssid, password); | |
} | |
//ArduinoOTA.setHostname(ap_ssid); | |
ArduinoOTA.begin(); | |
wifi_set_sleep_type(NONE_SLEEP_T); | |
} | |
bool checkWiFi(){ | |
static bool last = true; | |
bool ok = WiFi.status() == WL_CONNECTED; | |
ArduinoOTA.handle(); | |
if(ok != last){ | |
last = ok; | |
onWiFiChange(ok); | |
} | |
return ok; | |
} | |
void onWiFiChange(bool connected){ | |
//handle TCP here | |
} | |
void setup(){ | |
initSerial(); | |
SPIFFS.begin(); | |
initWiFi(); | |
initSD(); | |
//os_printf("format start\n"); SPIFFS.format(); os_printf("format end\n"); | |
server.serveStatic("/editor", SPIFFS, "/src/edit-src.htm"); | |
server.serveStatic("/fs", SPIFFS, "/"); | |
server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request){ | |
request->send(200, "text/plain", String(ESP.getFreeHeap())); | |
}); | |
server.addHandler(new SDEditor(http_username,http_password)); | |
server.onNotFound([](AsyncWebServerRequest *request){ | |
os_printf("NOT_FOUND: "); | |
if(request->method() == HTTP_GET) | |
os_printf("GET"); | |
else if(request->method() == HTTP_POST) | |
os_printf("POST"); | |
else if(request->method() == HTTP_DELETE) | |
os_printf("DELETE"); | |
else if(request->method() == HTTP_PUT) | |
os_printf("PUT"); | |
else if(request->method() == HTTP_PATCH) | |
os_printf("PATCH"); | |
else if(request->method() == HTTP_HEAD) | |
os_printf("HEAD"); | |
else if(request->method() == HTTP_OPTIONS) | |
os_printf("OPTIONS"); | |
else | |
os_printf("UNKNOWN"); | |
os_printf(" http://%s%s\n", request->host().c_str(), request->url().c_str()); | |
if(request->contentLength()){ | |
os_printf("_CONTENT_TYPE: %s\n", request->contentType().c_str()); | |
os_printf("_CONTENT_LENGTH: %u\n", request->contentLength()); | |
} | |
int headers = request->headers(); | |
int i; | |
for(i=0;i<headers;i++){ | |
AsyncWebHeader* h = request->getHeader(i); | |
os_printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str()); | |
} | |
int params = request->params(); | |
for(i=0;i<params;i++){ | |
AsyncWebParameter* p = request->getParam(i); | |
if(p->isFile()){ | |
os_printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); | |
} else if(p->isPost()){ | |
os_printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); | |
} else { | |
os_printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); | |
} | |
} | |
request->send(404); | |
}); | |
server.onFileUpload([](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ | |
if(!index) | |
os_printf("UploadStart: %s\n", filename.c_str()); | |
//size_t i; | |
//for(i=0;i<len;i++){ | |
//while(((U0S >> USTXC) & 0x7F) >= 0x7F); U0F = data[i]; | |
//} | |
if(final) | |
os_printf("UploadEnd: %s (%u)\n", filename.c_str(), index+len); | |
}); | |
server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){ | |
if(!index) | |
os_printf("BodyStart: %u\n", total); | |
/*size_t i; | |
for(i=0; i<len; i++){ | |
while(((U0S >> USTXC) & 0x7F) >= 0x7F); U0F = ((uint8_t*)data)[i]; | |
}*/ | |
if(index + len == total) | |
os_printf("BodyEnd: %u\n", total); | |
}); | |
server.begin(); | |
MDNS.addService("http","tcp",80); | |
} | |
static uint32_t lastTest = 0; | |
void loop(){ | |
checkWiFi(); | |
uint32_t newTest = ESP.getFreeHeap(); | |
if(newTest != lastTest){ | |
lastTest = newTest; | |
os_printf("%u\r\n", newTest); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment