experimental.ino 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. #include <WiFi.h>
  2. #include <WiFiMulti.h>
  3. #include <WebServer.h>
  4. #include <FS.h>
  5. #include <SD_MMC.h>
  6. WiFiMulti WiFiMulti;
  7. WebServer server(80); //Starting a server on port 80
  8. //Home Dynamic IoT Communication Protocol Headers and Information
  9. const String uuid = "d9ed122a-b359-4564-8bf1-6cd27ec517b2"; //Please change this UUIDv4 if you have more than one identical setup in the same subnet
  10. const String driver="3dprcu.ext.hd.imuslab";
  11. const String devName="3D Printer Remote Control Unit";
  12. //WebServer settings
  13. const String webroot = "/web";
  14. //System Settings
  15. bool forceCORS = true; //Some versions of WebServer.h do not include Access Control Allow Origin * by default. Set this to true to send it out manually
  16. void setup(){
  17. Serial.begin(115200);
  18. delay(10);
  19. Serial.println("");
  20. Serial.println("[info] ESP32 3D Printer Remote Controller Unit");
  21. Serial.println("[info] Open source prototype for the aCloud 3D Printer Control System.");
  22. // Add more WiFi AP for backups
  23. WiFiMulti.addAP("Toby Room Automation", "homedynamicsystem");
  24. //WiFiMulti.addAP("Backup-SSID", "password");
  25. if (initSDcard() == false){
  26. //Init SD card failed. Terminate the startup process.
  27. return;
  28. }
  29. Serial.print("[info] Waiting for WiFi... ");
  30. while(WiFiMulti.run() != WL_CONNECTED) {
  31. Serial.print(".");
  32. delay(500);
  33. }
  34. Serial.println("");
  35. Serial.println("[OK] WiFi connected");
  36. Serial.print("[info] IP address: ");
  37. Serial.println(WiFi.localIP());
  38. delay(500);
  39. //Define handler for index
  40. server.on("/", handle_OnConnect);
  41. //Define Web Server handler for Home Dynamic Protocol
  42. server.on("/uuid", handle_uuid);
  43. server.on("/info", handle_info);
  44. server.on("/status", handle_status);
  45. //Define handler for file operations
  46. server.on("/list", HTTP_GET, printDirectory); //Usage: /list?dir=/{dirname}
  47. server.on("/upload", HTTP_POST, []() {
  48. sendCORS();
  49. server.send(200, "text/plain", "");
  50. }, handleFileUpload);
  51. //Define handler for NOT FOUND exception
  52. server.onNotFound(handle_NotFound);
  53. server.begin();
  54. Serial.println("[info] RESTFUL API server started");
  55. Serial.println("[OK] System Ready!");
  56. }
  57. void loop()
  58. {
  59. server.handleClient();
  60. }
  61. //Home Dynamic Communication Protocol related response handler
  62. void handle_uuid(){
  63. if (forceCORS){
  64. server.sendHeader("Access-Control-Allow-Origin", "*");
  65. }
  66. server.send(200, "text/plain", uuid);
  67. }
  68. void handle_info(){
  69. if (forceCORS){
  70. server.sendHeader("Access-Control-Allow-Origin", "*");
  71. }
  72. server.send(200, "text/plain", devName + "_" + driver);
  73. }
  74. void handle_status(){
  75. //Report printing progress here. Work in progress
  76. }
  77. void handle_OnConnect(){
  78. server.sendHeader("Location", "/web/index.html",true);
  79. server.send(302, "text/plane","");
  80. }
  81. File uploadFile;
  82. void handleFileUpload() {
  83. if (server.uri() != "/upload") {
  84. return;
  85. }
  86. HTTPUpload& upload = server.upload();
  87. if (upload.status == UPLOAD_FILE_START) {
  88. if (SD_MMC.exists((char *)upload.filename.c_str())) {
  89. SD_MMC.remove((char *)upload.filename.c_str());
  90. }
  91. uploadFile = SD_MMC.open(upload.filename.c_str(), FILE_WRITE);
  92. Serial.print("[info] Upload: START, filename: "); Serial.println(upload.filename);
  93. } else if (upload.status == UPLOAD_FILE_WRITE) {
  94. if (uploadFile) {
  95. uploadFile.write(upload.buf, upload.currentSize);
  96. }
  97. Serial.print("[info] Upload: WRITE, Bytes: "); Serial.println(upload.currentSize);
  98. } else if (upload.status == UPLOAD_FILE_END) {
  99. if (uploadFile) {
  100. uploadFile.close();
  101. }
  102. Serial.print("[info] Upload: END, Size: "); Serial.println(upload.totalSize);
  103. }
  104. }
  105. void handleHTMLDelivery(String path){
  106. if (!loadFromSdCard(path)){
  107. //This file do not exists or it is empty. Deliver 404 page instead.
  108. sendCORS();
  109. server.send(404, "text/plain", "Page not found");
  110. }
  111. }
  112. void handle_NotFound(){
  113. String path = server.uri();
  114. int index = path.indexOf("/web");
  115. int deleteIndicator = path.indexOf("/delete");
  116. if (index >= 0 && deleteIndicator == -1) {
  117. handleHTMLDelivery(path);
  118. }else if (deleteIndicator >= 0 ){
  119. handleDeleteRequest(path);
  120. }else{
  121. sendCORS();
  122. server.send(404, "text/plain", "Page not found");
  123. }
  124. }
  125. bool initSDcard(){
  126. Serial.println("[info] Trying to initiate SD card file system.");
  127. if(!SD_MMC.begin()){
  128. Serial.println("[error] Card Mount Failed");
  129. return false;
  130. }
  131. uint8_t cardType = SD_MMC.cardType();
  132. if(cardType == CARD_NONE){
  133. Serial.println("[error] No SD_MMC card attached");
  134. return false;
  135. }
  136. Serial.print("[info] SD_MMC Card Type: ");
  137. if(cardType == CARD_MMC){
  138. Serial.println("MMC");
  139. } else if(cardType == CARD_SD){
  140. Serial.println("SDSC");
  141. } else if(cardType == CARD_SDHC){
  142. Serial.println("SDHC");
  143. } else {
  144. Serial.println("UNKNOWN");
  145. }
  146. uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024);
  147. Serial.printf("[OK] SD_MMC Card Size: %lluMB\n", cardSize);
  148. return true;
  149. }
  150. void printDirectory() {
  151. if (!server.hasArg("dir")) {
  152. server.send(500, "text/plain", "Not directory.");
  153. return;
  154. }
  155. String path = server.arg("dir");
  156. if (path != "/" && !SD_MMC.exists((char *)path.c_str())) {
  157. server.send(500, "text/plain", "Bad path");
  158. return;
  159. }
  160. File dir = SD_MMC.open((char *)path.c_str());
  161. path = String();
  162. if (!dir.isDirectory()) {
  163. dir.close();
  164. server.send(500, "text/plain", "Not directory");
  165. return;
  166. }
  167. dir.rewindDirectory();
  168. if (forceCORS){
  169. server.sendHeader("Access-Control-Allow-Origin", "*");
  170. }
  171. server.setContentLength(CONTENT_LENGTH_UNKNOWN);
  172. server.send(200, "application/json", "");
  173. WiFiClient client = server.client();
  174. server.sendContent("[");
  175. for (int cnt = 0; true; ++cnt) {
  176. File entry = dir.openNextFile();
  177. if (!entry) {
  178. break;
  179. }
  180. String output;
  181. if (cnt > 0) {
  182. output = ',';
  183. }
  184. output += "{\"type\":\"";
  185. output += (entry.isDirectory()) ? "dir" : "file";
  186. output += "\",\"name\":\"";
  187. output += entry.name();
  188. output += "\"";
  189. output += "}";
  190. server.sendContent(output);
  191. entry.close();
  192. }
  193. server.sendContent("]");
  194. dir.close();
  195. }
  196. //Test read and write speed using test.txt file
  197. void testFileIO(fs::FS &fs, const char * path){
  198. File file = fs.open(path);
  199. static uint8_t buf[512];
  200. size_t len = 0;
  201. uint32_t start = millis();
  202. uint32_t end = start;
  203. if(file){
  204. len = file.size();
  205. size_t flen = len;
  206. start = millis();
  207. while(len){
  208. size_t toRead = len;
  209. if(toRead > 512){
  210. toRead = 512;
  211. }
  212. file.read(buf, toRead);
  213. len -= toRead;
  214. }
  215. end = millis() - start;
  216. Serial.printf("%u bytes read for %u ms\n", flen, end);
  217. file.close();
  218. } else {
  219. Serial.println("Failed to open file for reading");
  220. }
  221. file = fs.open(path, FILE_WRITE);
  222. if(!file){
  223. Serial.println("Failed to open file for writing");
  224. return;
  225. }
  226. size_t i;
  227. start = millis();
  228. for(i=0; i<2048; i++){
  229. file.write(buf, 512);
  230. }
  231. end = millis() - start;
  232. Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
  233. file.close();
  234. }
  235. bool loadFromSdCard(String path){
  236. String dataType = "text/plain";
  237. if(path.endsWith("/")) path += "index.htm";
  238. if(path.endsWith(".src")) path = path.substring(0, path.lastIndexOf("."));
  239. else if(path.endsWith(".htm")) dataType = "text/html";
  240. else if(path.endsWith(".html")) dataType = "text/html";
  241. else if(path.endsWith(".css")) dataType = "text/css";
  242. else if(path.endsWith(".js")) dataType = "application/javascript";
  243. else if(path.endsWith(".png")) dataType = "image/png";
  244. else if(path.endsWith(".gif")) dataType = "image/gif";
  245. else if(path.endsWith(".jpg")) dataType = "image/jpeg";
  246. else if(path.endsWith(".ico")) dataType = "image/x-icon";
  247. else if(path.endsWith(".svg")) dataType = "image/svg+xml";
  248. else if(path.endsWith(".xml")) dataType = "text/xml";
  249. else if(path.endsWith(".pdf")) dataType = "application/pdf";
  250. else if(path.endsWith(".zip")) dataType = "application/zip";
  251. else if(path.endsWith(".mp3")) dataType = "audio/mpeg";
  252. else if(path.endsWith(".mp4")) dataType = "video/mp4";
  253. else if(path.endsWith(".otf")) dataType = "font/otf";
  254. else if(path.endsWith(".ttf")) dataType = "font/ttf";
  255. File dataFile = SD_MMC.open(path.c_str());
  256. if(dataFile.isDirectory()){
  257. path += "/index.html";
  258. dataType = "text/html";
  259. dataFile = SD_MMC.open(path.c_str());
  260. }
  261. if (!dataFile)
  262. return false;
  263. if (server.hasArg("download")) dataType = "application/octet-stream";
  264. if (server.streamFile(dataFile, dataType) != dataFile.size()) {
  265. Serial.println("[error] Sent less data than expected!");
  266. }
  267. dataFile.close();
  268. return true;
  269. }
  270. void deleteRecursive(String path) {
  271. File file = SD_MMC.open((char *)path.c_str());
  272. if (!file.isDirectory()) {
  273. file.close();
  274. SD_MMC.remove((char *)path.c_str());
  275. return;
  276. }
  277. file.rewindDirectory();
  278. while (true) {
  279. File entry = file.openNextFile();
  280. if (!entry) {
  281. break;
  282. }
  283. String entryPath = path + "/" + entry.name();
  284. if (entry.isDirectory()) {
  285. entry.close();
  286. deleteRecursive(entryPath);
  287. } else {
  288. entry.close();
  289. SD_MMC.remove((char *)entryPath.c_str());
  290. }
  291. yield();
  292. }
  293. SD_MMC.rmdir((char *)path.c_str());
  294. file.close();
  295. }
  296. void handleDeleteRequest(String fullPath) {
  297. int index = fullPath.indexOf(",");
  298. String path = fullPath.substring(index + 1, fullPath.length());
  299. Serial.println("[info] Request delete path: " + path);
  300. if (path == "/" || !SD_MMC.exists((char *)path.c_str())) {
  301. sendCORS();
  302. server.send(500, "text/plain", "Bad path");
  303. return;
  304. }
  305. deleteRecursive(path);
  306. sendCORS();
  307. server.send(200, "text/plain", "DONE");
  308. }
  309. void sendCORS(){
  310. if (forceCORS){
  311. server.sendHeader("Access-Control-Allow-Origin", "*");
  312. server.sendHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS");
  313. server.sendHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  314. }
  315. }