esp-fs-webserver
Loading...
Searching...
No Matches
esp-fs-webserver.h
Go to the documentation of this file.
1#ifndef esp_fs_webserver_H
2#define esp_fs_webserver_H
3
4#include <Arduino.h>
5#include <memory>
6#include <typeinfo>
7#include <base64.h>
8#include <FS.h>
9
10#define INCLUDE_EDIT_HTM
11#ifdef INCLUDE_EDIT_HTM
12#include "edit_htm.h"
13#endif
14
15#define INCLUDE_SETUP_HTM
16#ifdef INCLUDE_SETUP_HTM
17#define ARDUINOJSON_USE_LONG_LONG 1
18#include <ArduinoJson.h>
19#include "setup_htm.h"
20#endif
21
22#if defined(ESP8266)
23#include <ESP8266WiFi.h>
24#include <ESP8266WebServer.h>
25#include <ESP8266mDNS.h>
26#include <ESP8266HTTPUpdateServer.h> // from Arduino core, OTA update via webbrowser
27using WebServerClass = ESP8266WebServer;
28using UpdateServerClass = ESP8266HTTPUpdateServer;
29#elif defined(ESP32)
30#include <esp_wifi.h>
31#include <WebServer.h>
32#include <WiFi.h>
33#include <ESPmDNS.h>
34#include <HTTPUpdateServer.h> // from Arduino core, OTA update via webbrowser
35using WebServerClass = WebServer;
36using UpdateServerClass = HTTPUpdateServer;
37#endif
38#include <DNSServer.h>
39
40#ifndef DEBUG_ESP_PORT
41#define DBG_OUTPUT_PORT Serial
42#define DEBUG_MODE_WS false
43#else
44#define DBG_OUTPUT_PORT DEBUG_ESP_PORT
45#endif
46
47#if DEBUG_MODE_WS
48#define DebugPrint(x) DBG_OUTPUT_PORT.print(x)
49#define DebugPrintln(x) DBG_OUTPUT_PORT.println(x)
50#define DebugPrintf(fmt, ...) DBG_OUTPUT_PORT.printf(fmt, ##__VA_ARGS__)
51#define DebugPrintf_P(fmt, ...) DBG_OUTPUT_PORT.printf_P(fmt, ##__VA_ARGS__)
52#else
53#define DebugPrint(x)
54#define DebugPrintln(x)
55#define DebugPrintf(x, ...)
56#define DebugPrintf_P(x, ...)
57#endif
58
59enum
60{
65 ERROR
66};
67#define TEXT_PLAIN "text/plain"
68#define FS_INIT_ERROR "FS INIT ERROR"
69#define FILE_NOT_FOUND "FileNotFound"
70
72{
73
74 // using CallbackF = std::function<void(void)>;
75
76public:
77 WebServerClass *webserver;
78
79 FSWebServer(fs::FS &fs, WebServerClass &server);
80
81 bool begin(const char *path = nullptr);
82
83 void run();
84
85 void addHandler(const Uri &uri, HTTPMethod method, WebServerClass::THandlerFunction fn);
86
87 void addHandler(const Uri &uri, WebServerClass::THandlerFunction handler);
88
89 void setCaptiveWebage(const char *url);
90
91 IPAddress setAPmode(const char *ssid, const char *psk);
92
93 IPAddress startWiFi(uint32_t timeout, const char *apSSID, const char *apPsw);
94
95 WebServerClass *getRequest();
96
97#ifdef INCLUDE_SETUP_HTM
98
99#define MIN_F -3.4028235E+38
100#define MAX_F 3.4028235E+38
101
102 inline bool clearOptions() {
103 File file = m_filesystem->open("/config.json", "r");
104 if (file)
105 {
106 file.close();
107 m_filesystem->remove("/config.json");
108 return true;
109 }
110 return false;
111 }
112
113 inline void addOptionBox(const char* boxTitle) {
114 addOption("param-box", boxTitle, false);
115 }
116
117 inline void addHTML(const char* html, const char* id) {
118 String elementId = "raw-html-";
119 elementId += id;
120
121 String trimmed = html;
122 removeWhiteSpaces(trimmed);
123 addOption(elementId.c_str(), trimmed, false);
124 }
125
126 inline void addCSS(const char* css) {
127 String trimmed = css;
128 removeWhiteSpaces(trimmed);
129 addOption("raw-css", trimmed, false);
130 }
131
132 inline void addJavascript(const char* script) {
133 String trimmed = script; // TO-DO: ESP8266 don't handle properly PROGMEM strings
134 addOption("raw-javascript", trimmed, true);
135 }
136
137 void addDropdownList(const char *label, const char** array, size_t size);
138
139 // Only for backward-compatibility
140 template <typename T>
141 inline void addOption(fs::FS &fs, const char *label, T val, bool hidden = false)
142 {
143 addOption(label, val, hidden);
144 }
145
146 // Add custom option to config webpage (float values)
147 template <typename T>
148 inline void addOption(const char *label, T val, double d_min, double d_max, double step)
149 {
150 addOption(label, val, false, d_min, d_max, step);
151 }
152
153
154 // Add custom option to config webpage (type of parameter will be deduced from variable itself)
155 template <typename T>
156 inline void addOption(const char *label, T val, bool hidden = false,
157 double d_min = MIN_F, double d_max = MAX_F, double step = 1.0)
158 {
159 File file = m_filesystem->open("/config.json", "r");
160 int sz = file.size() * 1.33;
161 int docSize = max(sz, 2048);
162 DynamicJsonDocument doc((size_t)docSize);
163 if (file)
164 {
165 // If file is present, load actual configuration
166 DeserializationError error = deserializeJson(doc, file);
167 if (error)
168 {
169 DebugPrintln(F("Failed to deserialize file, may be corrupted"));
170 DebugPrintln(error.c_str());
171 file.close();
172 return;
173 }
174 file.close();
175 }
176 else
177 {
178 DebugPrintln(F("File not found, will be created new configuration file"));
179 }
180
181 numOptions++ ;
182
183 String key = label;
184 if (hidden)
185 key += "-hidden";
186
187 // Univoque key name
188 if (key.equals("param-box")) {
189 key += numOptions ;
190 }
191 if (key.equals("raw-javascript")) {
192 key += numOptions ;
193 }
194
195
196
197 // If key is present in json, we don't need to create it.
198 if (doc.containsKey(key.c_str()))
199 return;
200
201 // if min, max, step != from default, treat this as object in order to set other properties
202 if (d_min != MIN_F || d_max != MAX_F || step != 1.0)
203 {
204 JsonObject obj = doc.createNestedObject(key);
205 obj["value"] = static_cast<T>(val);
206 obj["min"] = d_min;
207 obj["max"] = d_max;
208 obj["step"] = step;
209 }
210 else {
211 doc[key] = static_cast<T>(val);
212 }
213
214 file = m_filesystem->open("/config.json", "w");
215 if (serializeJsonPretty(doc, file) == 0)
216 {
217 DebugPrintln(F("Failed to write to file"));
218 }
219 file.close();
220 }
221
222
223
224 // Get current value for a specific custom option (true on success)
225 template <typename T>
226 bool getOptionValue(const char *label, T &var)
227 {
228 File file = m_filesystem->open("/config.json", "r");
229 DynamicJsonDocument doc(file.size() * 1.33);
230 if (file)
231 {
232 DeserializationError error = deserializeJson(doc, file);
233 if (error)
234 {
235 DebugPrintln(F("Failed to deserialize file, may be corrupted"));
236 DebugPrintln(error.c_str());
237 file.close();
238 return false;
239 }
240 file.close();
241 }
242 else
243 return false;
244
245 if (doc[label]["value"])
246 var = doc[label]["value"].as<T>();
247 else if (doc[label]["selected"])
248 var = doc[label]["selected"].as<T>();
249 else
250 var = doc[label].as<T>();
251 return true;
252 }
253
254 template <typename T>
255 bool saveOptionValue(const char *label, T val)
256 {
257 File file = m_filesystem->open("/config.json", "w");
258 DynamicJsonDocument doc(file.size() * 1.33);
259
260 if (file)
261 {
262 DeserializationError error = deserializeJson(doc, file);
263 if (error)
264 {
265 DebugPrintln(F("Failed to deserialize file, may be corrupted"));
266 DebugPrintln(error.c_str());
267 file.close();
268 return false;
269 }
270 file.close();
271 }
272 else
273 return false;
274
275 if (doc[label]["value"])
276 doc[label]["value"] = val;
277 else if (doc[label]["selected"])
278 doc[label]["selected"] = val;
279 else
280 doc[label] = val;
281 return true;
282 }
283
284#endif
285
286private:
287
288 char m_basePath[16];
289 UpdateServerClass m_httpUpdater;
290 DNSServer m_dnsServer;
291 fs::FS *m_filesystem;
292 File m_uploadFile;
293 bool m_fsOK = false;
294 bool m_apmode = false;
295 char *m_apWebpage = (char *)"/setup";
296 uint32_t m_timeout = 10000;
297
298 // Default handler for all URIs not defined above, use it to read files from filesystem
299 bool checkDir(char *dirname, uint8_t levels);
300 void doWifiConnection();
301 void doRestart();
302 void replyOK();
303 void getIpAddress();
304 void handleRequest();
305#ifdef INCLUDE_SETUP_HTM
306 void removeWhiteSpaces(String& str);
307 void handleSetup();
308 uint8_t numOptions = 0;
309#endif
310 void handleIndex();
311 bool handleFileRead(const String &path);
312 void handleFileUpload();
313 void replyToCLient(int msg_type, const char *msg);
314 void checkForUnsupportedPath(String &filename, String &error);
315 void setCrossOrigin();
316 void handleScanNetworks();
317 const char *getContentType(const char *filename);
318 bool captivePortal();
319
320 // edit page, in usefull in some situation, but if you need to provide only a web interface, you can disable
321#ifdef INCLUDE_EDIT_HTM
322 void handleGetEdit();
323 void handleFileCreate();
324 void handleFileDelete();
325 void handleStatus();
326 void handleFileList();
327#endif
328
329};
330
331#endif
IPAddress startWiFi(uint32_t timeout, const char *apSSID, const char *apPsw)
bool getOptionValue(const char *label, T &var)
void addOption(const char *label, T val, double d_min, double d_max, double step)
void addDropdownList(const char *label, const char **array, size_t size)
WebServerClass * webserver
void addOptionBox(const char *boxTitle)
void addOption(const char *label, T val, bool hidden=false, double d_min=MIN_F, double d_max=MAX_F, double step=1.0)
IPAddress setAPmode(const char *ssid, const char *psk)
void addJavascript(const char *script)
void addCSS(const char *css)
void addOption(fs::FS &fs, const char *label, T val, bool hidden=false)
void addHTML(const char *html, const char *id)
void addHandler(const Uri &uri, HTTPMethod method, WebServerClass::THandlerFunction fn)
WebServerClass * getRequest()
void setCaptiveWebage(const char *url)
bool begin(const char *path=nullptr)
bool saveOptionValue(const char *label, T val)
@ ERROR
@ MSG_OK
@ CUSTOM
@ NOT_FOUND
@ BAD_REQUEST
#define MAX_F
#define DebugPrintln(x)
#define MIN_F