Skip to content

Instantly share code, notes, and snippets.

@me-no-dev
Created June 24, 2016 00:10
Show Gist options
  • Save me-no-dev/a1975076563ab7146f69f89df5ca27d8 to your computer and use it in GitHub Desktop.
Save me-no-dev/a1975076563ab7146f69f89df5ca27d8 to your computer and use it in GitHub Desktop.
#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