1
0
mirror of https://github.com/s00500/ESPUI.git synced 2024-11-05 05:10:55 +00:00
ESPUI/src/ESPUI.cpp

536 lines
13 KiB
C++
Raw Normal View History

2017-10-19 15:43:39 +00:00
#include "ESPUI.h"
#include "uploadDataIndex.h"
#include "uploadDataNormalize.h"
2018-01-14 11:22:26 +00:00
#include "uploadDataStyle.h"
#include "uploadDataControls.h"
#include "uploadDataSlider.h"
2018-01-14 11:22:26 +00:00
#include "uploadDataZepto.h"
2017-10-16 13:00:53 +00:00
#include <ESPAsyncWebServer.h>
2017-05-18 22:05:32 +00:00
#include <functional>
// ################# Spiffs functions
2018-01-14 11:22:26 +00:00
#if defined(ESP32)
void listDir(const char *dirname, uint8_t levels) {
Serial.printf("Listing directory: %s\n", dirname);
2018-01-14 11:22:26 +00:00
File root = SPIFFS.open(dirname);
2018-01-14 11:22:26 +00:00
if (!root) {
Serial.println("Failed to open directory");
return;
}
if (!root.isDirectory()) {
Serial.println("Not a directory");
return;
}
File file = root.openNextFile();
2018-01-14 11:22:26 +00:00
while (file) {
if (file.isDirectory()) {
Serial.print(" DIR : ");
Serial.println(file.name());
if (levels) {
listDir(file.name(), levels - 1);
}
} else {
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.println(file.size());
}
2018-01-14 11:22:26 +00:00
file = root.openNextFile();
}
}
2018-01-14 11:22:26 +00:00
#else
2018-01-14 11:22:26 +00:00
void listDir(const char *dirname, uint8_t levels) {
// ignoring levels for esp8266
Serial.printf("Listing directory: %s\n", dirname);
String str = "";
Dir dir = SPIFFS.openDir("/");
while (dir.next()) {
Serial.print(" FILE: ");
Serial.print(dir.fileName());
Serial.print(" SIZE: ");
Serial.println(dir.fileSize());
}
}
#endif
void ESPUIClass::list() {
if (!SPIFFS.begin()) {
Serial.println("SPIFFS Mount Failed");
return;
}
listDir("/", 1);
#if defined(ESP32)
Serial.println(SPIFFS.totalBytes());
Serial.println(SPIFFS.usedBytes());
#else
FSInfo fs_info;
SPIFFS.info(fs_info);
Serial.println(fs_info.totalBytes);
Serial.println(fs_info.usedBytes);
#endif
}
void deleteFile(const char *path) {
Serial.print(SPIFFS.exists(path));
2018-01-14 11:22:26 +00:00
if (!SPIFFS.exists(path)) {
Serial.printf("File: %s does not exist, not deleting\n", path);
return;
}
2018-01-14 11:22:26 +00:00
Serial.printf("Deleting file: %s\n", path);
2017-11-29 10:32:07 +00:00
2018-01-14 11:22:26 +00:00
if (SPIFFS.remove(path)) {
Serial.println("File deleted");
} else {
Serial.println("Delete failed");
}
}
2018-01-14 11:22:26 +00:00
void writeFile(const char *path, const char *data) {
Serial.printf("Writing file: %s\n", path);
2017-11-29 10:32:07 +00:00
2018-01-14 11:22:26 +00:00
File file = SPIFFS.open(path, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
2018-04-17 17:54:07 +00:00
#if defined(ESP32)
if (file.print(data)) {
Serial.println("File written");
} else {
Serial.println("Write failed");
}
#else
2018-01-14 11:22:26 +00:00
if (file.print(FPSTR(data))) {
Serial.println("File written");
} else {
Serial.println("Write failed");
}
2018-04-17 17:54:07 +00:00
#endif
2018-01-14 11:22:26 +00:00
file.close();
}
// end Spiffs functions
2018-01-14 11:22:26 +00:00
void ESPUIClass::prepareFileSystem() {
// this function should only be used once
2018-01-14 11:22:26 +00:00
Serial.println("About to prepare filesystem...");
2017-12-25 15:39:54 +00:00
#if defined(ESP32)
SPIFFS.format();
2018-01-14 11:22:26 +00:00
if (!SPIFFS.begin(true)) {
Serial.println("SPIFFS Mount Failed");
return;
2017-12-25 15:39:54 +00:00
}
2018-01-14 11:22:26 +00:00
listDir("/", 1);
Serial.println("SPIFFS Mount ESP32 Done");
2017-12-25 15:39:54 +00:00
#else
SPIFFS.format();
SPIFFS.begin();
Serial.println("SPIFFS Mount ESP8266 Done");
2017-12-25 15:39:54 +00:00
#endif
2018-01-14 11:22:26 +00:00
// TODO: This is a workaround, have to find out why SPIFFS on ESP32 behaves
// incredibly strangely, see issue #6
/*
2018-04-17 17:54:07 +00:00
deleteFile("/index.htm");
2018-04-17 17:54:07 +00:00
deleteFile("/css/style.css");
deleteFile("/css/normalize.css");
2018-04-17 17:54:07 +00:00
deleteFile("/js/zepto.min.js");
deleteFile("/js/controls.js");
deleteFile("/js/slider.js");
*/
2018-01-14 11:22:26 +00:00
Serial.println("Cleanup done");
2018-01-14 11:22:26 +00:00
// Now write
writeFile("/index.htm", HTML_INDEX);
2018-01-14 11:22:26 +00:00
writeFile("/css/style.css", CSS_STYLE);
writeFile("/css/normalize.css", CSS_NORMALIZE);
2018-01-14 11:22:26 +00:00
writeFile("/js/zepto.min.js", JS_ZEPTO);
writeFile("/js/controls.js", JS_CONTROLS);
writeFile("/js/slider.js", JS_SLIDER);
2018-01-14 11:22:26 +00:00
Serial.println("Done Initializing filesystem :-)");
2018-01-14 11:22:26 +00:00
#if defined(ESP32)
listDir("/", 1);
2018-01-14 11:22:26 +00:00
#endif
2018-01-14 11:22:26 +00:00
SPIFFS.end();
}
2017-10-16 13:00:53 +00:00
// Handle Websockets Communication
2017-11-13 15:10:56 +00:00
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client,
AwsEventType type, void *arg, uint8_t *data, size_t len) {
switch (type) {
case WS_EVT_DISCONNECT:
2017-11-14 11:09:52 +00:00
if (debug)
Serial.printf("Disconnected!\n");
2017-11-13 15:10:56 +00:00
break;
case WS_EVT_CONNECT: {
2018-01-14 11:22:26 +00:00
if (debug) {
Serial.print("Connected: ");
Serial.println(client->id());
}
2017-11-13 15:10:56 +00:00
ESPUI.jsonDom(client);
2018-01-14 11:22:26 +00:00
if (debug) {
2017-11-14 11:09:52 +00:00
Serial.println("JSON Data Sent to Client!");
}
2018-01-14 11:22:26 +00:00
} break;
2017-11-13 15:10:56 +00:00
case WS_EVT_DATA:
String msg = "";
for (size_t i = 0; i < len; i++) {
msg += (char)data[i];
2017-05-18 22:05:32 +00:00
}
2017-11-29 10:32:07 +00:00
int id = msg.substring(msg.lastIndexOf(':') + 1).toInt();
2018-01-14 11:22:26 +00:00
if (id >= ESPUI.cIndex) {
if (debug)
Serial.println("Maleformated id in websocket message");
2017-11-29 10:32:07 +00:00
return;
}
2018-01-14 11:22:26 +00:00
Control *c =
2018-04-17 17:54:07 +00:00
ESPUI.controls[msg.substring(msg.lastIndexOf(':') + 1).toInt()];
2017-11-13 15:10:56 +00:00
if (msg.startsWith("bdown:")) {
2017-11-14 11:09:52 +00:00
c->callback(*c, B_DOWN);
2017-11-13 15:10:56 +00:00
} else if (msg.startsWith("bup:")) {
2017-11-14 11:09:52 +00:00
c->callback(*c, B_UP);
2017-11-13 15:10:56 +00:00
} else if (msg.startsWith("pfdown:")) {
2017-11-14 11:09:52 +00:00
c->callback(*c, P_FOR_DOWN);
2017-11-13 15:10:56 +00:00
} else if (msg.startsWith("pfup:")) {
2017-11-14 11:09:52 +00:00
c->callback(*c, P_FOR_UP);
2017-11-13 15:10:56 +00:00
} else if (msg.startsWith("pldown:")) {
2017-11-14 11:09:52 +00:00
c->callback(*c, P_LEFT_DOWN);
2017-11-13 15:10:56 +00:00
} else if (msg.startsWith("plup:")) {
2017-11-14 11:09:52 +00:00
c->callback(*c, P_LEFT_UP);
2017-11-13 15:10:56 +00:00
} else if (msg.startsWith("prdown:")) {
2017-11-14 11:09:52 +00:00
c->callback(*c, P_RIGHT_DOWN);
2017-11-13 15:10:56 +00:00
} else if (msg.startsWith("prup:")) {
2017-11-14 11:09:52 +00:00
c->callback(*c, P_RIGHT_UP);
2017-11-13 15:10:56 +00:00
} else if (msg.startsWith("pbdown:")) {
2017-11-14 11:09:52 +00:00
c->callback(*c, P_BACK_DOWN);
2017-11-13 15:10:56 +00:00
} else if (msg.startsWith("pbup:")) {
2017-11-14 11:09:52 +00:00
c->callback(*c, P_BACK_UP);
2017-11-13 15:10:56 +00:00
} else if (msg.startsWith("pcdown:")) {
2017-11-14 11:09:52 +00:00
c->callback(*c, P_CENTER_DOWN);
2017-11-13 15:10:56 +00:00
} else if (msg.startsWith("pcup:")) {
2017-11-14 11:09:52 +00:00
c->callback(*c, P_CENTER_UP);
2017-11-13 15:10:56 +00:00
} else if (msg.startsWith("sactive:")) {
ESPUI.updateSwitcher(c->id, true);
2017-11-14 11:09:52 +00:00
c->callback(*c, S_ACTIVE);
2017-11-13 15:10:56 +00:00
} else if (msg.startsWith("sinactive:")) {
ESPUI.updateSwitcher(c->id, false);
2017-11-14 11:09:52 +00:00
c->callback(*c, S_INACTIVE);
2017-11-29 10:32:07 +00:00
} else if (msg.startsWith("slvalue:")) {
2018-01-14 11:22:26 +00:00
int value =
2018-04-17 17:54:07 +00:00
msg.substring(msg.indexOf(':') + 1, msg.lastIndexOf(':')).toInt();
2017-11-29 10:32:07 +00:00
ESPUI.updateSlider(c->id, value, client->id());
c->callback(*c, SL_VALUE);
2017-11-13 15:10:56 +00:00
}
break;
}
2017-05-18 22:05:32 +00:00
}
2017-10-16 22:10:48 +00:00
2018-04-17 17:54:07 +00:00
int ESPUIClass::label(const char *label, int color, String value) {
2017-11-13 15:10:56 +00:00
if (labelExists(label)) {
2017-11-14 11:09:52 +00:00
if (debug)
Serial.println("UI ERROR: Element " + String(label) +
" exists, skipping creating element!");
2017-11-13 15:10:56 +00:00
return;
}
Control *newL = new Control();
2017-10-19 11:46:47 +00:00
newL->type = UI_LABEL;
newL->label = label;
2017-11-14 11:09:52 +00:00
newL->color = color;
2017-11-13 15:10:56 +00:00
if (value != "")
newL->value = value; // Init with labeltext
else
newL->value = String(label);
2017-10-19 11:46:47 +00:00
newL->callback = NULL;
2017-11-13 15:10:56 +00:00
newL->id = cIndex;
2017-10-19 11:46:47 +00:00
controls[cIndex] = newL;
cIndex++;
2018-04-17 17:54:07 +00:00
return cIndex - 1;
2017-05-18 22:05:32 +00:00
}
// TODO: this still needs a range setting
2018-04-17 17:54:07 +00:00
int ESPUIClass::slider(const char *label, void (*callBack)(Control, int),
int color, String value) {
if (labelExists(label)) {
if (debug)
Serial.println("UI ERROR: Element " + String(label) +
" exists, skipping creating element!");
return;
}
Control *newSL = new Control();
newSL->type = UI_SLIDER;
newSL->label = label;
newSL->color = color;
if (value != "")
newSL->value = value;
else
newSL->value = ""; // TODO: init with half value
newSL->callback = callBack;
newSL->id = cIndex;
controls[cIndex] = newSL;
cIndex++;
2018-04-17 17:54:07 +00:00
return cIndex - 1;
}
2018-04-17 17:54:07 +00:00
int ESPUIClass::button(const char *label, void (*callBack)(Control, int),
int color, String value) {
2017-11-13 15:10:56 +00:00
if (labelExists(label)) {
2017-11-14 11:09:52 +00:00
if (debug)
Serial.println("UI ERROR: Element " + String(label) +
" exists, skipping creating element!");
2017-11-13 15:10:56 +00:00
return;
}
Control *newB = new Control();
2017-10-19 11:46:47 +00:00
newB->type = UI_BUTTON;
newB->label = label;
2017-11-14 11:09:52 +00:00
newB->color = color;
2017-11-13 16:22:02 +00:00
if (value != "")
newB->value = value; // Init with labeltext
else
newB->value = String(label);
2017-10-16 23:10:29 +00:00
newB->callback = callBack;
2017-11-13 15:10:56 +00:00
newB->id = cIndex;
2017-10-19 11:46:47 +00:00
controls[cIndex] = newB;
cIndex++;
2018-04-17 17:54:07 +00:00
return cIndex - 1;
2017-10-16 13:00:53 +00:00
}
2017-10-19 11:46:47 +00:00
2018-04-17 17:54:07 +00:00
int ESPUIClass::switcher(const char *label, bool startState,
void (*callBack)(Control, int), int color) {
2017-11-13 15:10:56 +00:00
if (labelExists(label)) {
2017-11-14 11:09:52 +00:00
if (debug)
Serial.println("UI ERROR: Element " + String(label) +
" exists, skipping creating element!");
2017-11-13 15:10:56 +00:00
return;
}
Control *newS = new Control();
2017-10-19 11:46:47 +00:00
newS->type = UI_SWITCHER;
newS->label = label;
2017-11-14 11:09:52 +00:00
newS->color = color;
2017-10-19 15:30:32 +00:00
newS->value = String(startState);
2017-10-19 11:46:47 +00:00
newS->callback = callBack;
2017-11-13 15:10:56 +00:00
newS->id = cIndex;
2017-10-19 11:46:47 +00:00
controls[cIndex] = newS;
cIndex++;
2018-04-17 17:54:07 +00:00
return cIndex - 1;
2017-05-18 22:05:32 +00:00
}
2018-04-17 17:54:07 +00:00
int ESPUIClass::pad(const char *label, bool center,
void (*callBack)(Control, int), int color) {
2017-11-13 15:10:56 +00:00
if (labelExists(label)) {
2017-11-14 11:09:52 +00:00
if (debug)
Serial.println("UI ERROR: Element " + String(label) +
" exists, skipping creating element!");
2017-11-13 15:10:56 +00:00
return;
}
Control *newP = new Control();
if (center)
newP->type = UI_CPAD;
else
newP->type = UI_PAD;
2017-10-19 11:46:47 +00:00
newP->label = label;
2017-11-14 11:09:52 +00:00
newP->color = color;
2017-10-19 11:46:47 +00:00
newP->callback = callBack;
2017-11-13 15:10:56 +00:00
newP->id = cIndex;
2017-10-19 11:46:47 +00:00
controls[cIndex] = newP;
cIndex++;
2018-04-17 17:54:07 +00:00
return cIndex - 1;
2017-10-19 11:46:47 +00:00
}
2017-05-18 22:05:32 +00:00
2017-11-13 15:10:56 +00:00
void ESPUIClass::print(int id, String value) {
if (id < cIndex && controls[id]->type == UI_LABEL) {
2017-10-19 15:30:32 +00:00
controls[id]->value = value;
2017-10-19 11:46:47 +00:00
String json;
StaticJsonBuffer<200> jsonBuffer;
2017-11-13 15:10:56 +00:00
JsonObject &root = jsonBuffer.createObject();
2017-10-19 11:46:47 +00:00
root["type"] = UPDATE_LABEL;
2017-10-19 15:30:32 +00:00
root["value"] = value;
root["id"] = String(id);
2017-05-18 22:05:32 +00:00
root.printTo(json);
2017-10-19 11:46:47 +00:00
this->ws->textAll(json);
2017-11-13 15:10:56 +00:00
} else {
2017-11-14 11:09:52 +00:00
if (debug)
Serial.println(String("Error: ") + String(id) + String(" is no label"));
2017-10-19 15:30:32 +00:00
}
}
2017-11-13 15:10:56 +00:00
void ESPUIClass::print(String label, String value) {
if (!labelExists(label)) {
2017-11-14 11:09:52 +00:00
if (debug)
Serial.println("UI ERROR: Element does not " + String(label) +
" exist, cannot update!");
2017-11-13 15:10:56 +00:00
return;
}
print(getIdByLabel(label), value);
}
2018-01-14 11:22:26 +00:00
void ESPUIClass::updateSwitcher(int id, bool nValue, int clientId) {
2017-11-13 15:10:56 +00:00
if (id < cIndex && controls[id]->type == UI_SWITCHER) {
controls[id]->value = nValue ? 1 : 0;
2017-10-19 15:30:32 +00:00
String json;
StaticJsonBuffer<200> jsonBuffer;
2017-11-13 15:10:56 +00:00
JsonObject &root = jsonBuffer.createObject();
2017-11-29 10:32:07 +00:00
root["type"] = UPDATE_SWITCHER;
2017-11-13 15:10:56 +00:00
root["value"] = nValue ? 1 : 0;
2017-10-19 15:30:32 +00:00
root["id"] = String(id);
root.printTo(json);
2017-11-29 10:32:07 +00:00
textThem(json, clientId);
2017-11-13 15:10:56 +00:00
} else {
2017-11-14 11:09:52 +00:00
if (debug)
Serial.println(String("Error: ") + String(id) +
String(" is no switcher"));
2017-11-13 15:10:56 +00:00
}
}
2018-01-14 11:22:26 +00:00
void ESPUIClass::updateSlider(int id, int nValue, int clientId) {
2017-11-29 10:32:07 +00:00
if (id < cIndex && controls[id]->type == UI_SLIDER) {
controls[id]->value = nValue;
String json;
StaticJsonBuffer<200> jsonBuffer;
JsonObject &root = jsonBuffer.createObject();
root["type"] = UPDATE_SLIDER;
root["value"] = nValue;
root["id"] = String(id);
root.printTo(json);
textThem(json, clientId);
} else {
if (debug)
2018-01-14 11:22:26 +00:00
Serial.println(String("Error: ") + String(id) + String(" is no slider"));
2017-11-29 10:32:07 +00:00
}
}
void ESPUIClass::updateSwitcher(String label, bool nValue, int clientId) {
2017-11-13 15:10:56 +00:00
if (!labelExists(label)) {
if (debug)
2017-11-14 11:09:52 +00:00
Serial.println("UI ERROR: Element does not " + String(label) +
" exist, cannot update!");
2017-11-13 15:10:56 +00:00
return;
2017-05-18 22:05:32 +00:00
}
2017-11-29 10:32:07 +00:00
updateSwitcher(getIdByLabel(label), nValue, clientId);
}
2018-01-14 11:22:26 +00:00
// This is a hacky workaround because ESPAsyncWebServer does not have a function
// like this and it's clients array is private
void ESPUIClass::textThem(String text, int clientId) {
int tryId = 0;
2018-01-14 11:22:26 +00:00
for (int count = 0; count < this->ws->count();) {
if (this->ws->hasClient(tryId)) {
if (clientId != tryId) {
this->ws->client(tryId)->text(text);
}
count++;
2017-11-29 10:32:07 +00:00
}
tryId++;
2017-11-29 10:32:07 +00:00
}
2017-05-18 22:05:32 +00:00
}
2017-10-19 11:46:47 +00:00
2017-11-13 15:10:56 +00:00
int ESPUIClass::getIdByLabel(String label) {
for (int i = 0; i < cIndex; i++) {
if (String(controls[i]->label) == label)
return i;
}
return -1; // failed, nonexistant
}
bool ESPUIClass::labelExists(String label) {
for (int i = 0; i < cIndex; i++) {
2017-11-14 11:09:52 +00:00
if (String(controls[i]->label) == label)
return true;
2017-11-13 15:10:56 +00:00
}
return false;
}
2017-05-18 22:05:32 +00:00
2017-10-16 22:10:48 +00:00
// Convert & Transfer Arduino elements to JSON elements
2017-11-13 15:10:56 +00:00
void ESPUIClass::jsonDom(AsyncWebSocketClient *client) {
for (int i = -1; i < cIndex; i++) {
2017-05-18 22:05:32 +00:00
String json;
2017-10-19 11:46:47 +00:00
StaticJsonBuffer<200> jsonBuffer;
2017-11-13 15:10:56 +00:00
JsonObject &root = jsonBuffer.createObject();
if (i == -1) {
2017-10-19 11:46:47 +00:00
root["type"] = UI_TITEL;
root["label"] = String(ui_title);
2017-11-13 15:10:56 +00:00
} else {
2017-10-19 11:46:47 +00:00
root["type"] = controls[i]->type;
root["label"] = String(controls[i]->label);
2017-10-19 15:30:32 +00:00
root["value"] = String(controls[i]->value);
2017-11-14 11:09:52 +00:00
root["color"] = String(controls[i]->color);
2017-10-19 11:46:47 +00:00
root["id"] = String(i);
}
root.printTo(json);
2017-10-16 22:10:48 +00:00
client->text(json);
2017-05-18 22:05:32 +00:00
}
}
2017-10-16 22:10:48 +00:00
2017-11-13 15:10:56 +00:00
void ESPUIClass::begin(const char *_title) {
2017-10-19 11:46:47 +00:00
ui_title = _title;
2017-10-16 22:10:48 +00:00
server = new AsyncWebServer(80);
ws = new AsyncWebSocket("/ws");
2018-01-14 11:22:26 +00:00
if (!SPIFFS.begin()) {
Serial.println("SPIFFS Mount Failed, PLEASE CHECK THE README ON HOW TO "
"PREPARE YOUR ESP!!!!!!!");
return;
}
listDir("/", 1);
2018-01-14 11:22:26 +00:00
if (!SPIFFS.exists("/index.htm")) {
Serial.println("Please read the README!!!!!!!, Make sure to "
"ESPUI.prepareFileSystem() once in an empty sketch");
return;
}
2017-10-16 22:10:48 +00:00
ws->onEvent(onWsEvent);
server->addHandler(ws);
server->serveStatic("/", SPIFFS, "/").setDefaultFile("index.htm");
2017-10-16 13:00:53 +00:00
2017-11-13 15:10:56 +00:00
// Heap for general Servertest
server->on("/heap", HTTP_GET, [](AsyncWebServerRequest *request) {
2017-10-16 13:00:53 +00:00
request->send(200, "text/plain", String(ESP.getFreeHeap()));
});
2017-11-13 15:10:56 +00:00
server->onNotFound(
2018-04-17 17:54:07 +00:00
[](AsyncWebServerRequest *request) {
request->send(404);
});
2017-10-16 13:00:53 +00:00
server->begin();
2017-11-14 11:09:52 +00:00
if (debug)
Serial.println("UI Initialized");
2017-05-18 22:05:32 +00:00
}
2017-10-19 15:43:39 +00:00
ESPUIClass ESPUI;