├── .gitattributes ├── .gitignore ├── 3D Files ├── Rogue1.2 │ ├── Rogue1.2.obj │ └── rogue1.2.amf ├── Rogue1 │ ├── Rogue1.amf │ ├── Rogue1_a.skp │ ├── Rogue1_all.skp │ ├── Rogue1_b.skp │ └── Rogue1_c.skp └── RogueMini │ ├── RogueMini.obj │ └── RogueMini.skp ├── Firmware ├── SDAPServer │ └── SDAPServer.ino └── SDAPServerCaptive │ └── SDAPServerCaptive.ino ├── Img ├── Rogue1.2.jpg ├── Rogue1.2_closeup.jpg ├── Rogue1.jpg └── RogueMini.jpg └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /3D Files/Rogue1.2/rogue1.2.amf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkfun/Rogue_Router/1c07fa04e1f1f9bc5f6657e20d4dfa4ae67e6d32/3D Files/Rogue1.2/rogue1.2.amf -------------------------------------------------------------------------------- /3D Files/Rogue1/Rogue1.amf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkfun/Rogue_Router/1c07fa04e1f1f9bc5f6657e20d4dfa4ae67e6d32/3D Files/Rogue1/Rogue1.amf -------------------------------------------------------------------------------- /3D Files/Rogue1/Rogue1_a.skp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkfun/Rogue_Router/1c07fa04e1f1f9bc5f6657e20d4dfa4ae67e6d32/3D Files/Rogue1/Rogue1_a.skp -------------------------------------------------------------------------------- /3D Files/Rogue1/Rogue1_all.skp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkfun/Rogue_Router/1c07fa04e1f1f9bc5f6657e20d4dfa4ae67e6d32/3D Files/Rogue1/Rogue1_all.skp -------------------------------------------------------------------------------- /3D Files/Rogue1/Rogue1_b.skp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkfun/Rogue_Router/1c07fa04e1f1f9bc5f6657e20d4dfa4ae67e6d32/3D Files/Rogue1/Rogue1_b.skp -------------------------------------------------------------------------------- /3D Files/Rogue1/Rogue1_c.skp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkfun/Rogue_Router/1c07fa04e1f1f9bc5f6657e20d4dfa4ae67e6d32/3D Files/Rogue1/Rogue1_c.skp -------------------------------------------------------------------------------- /3D Files/RogueMini/RogueMini.skp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkfun/Rogue_Router/1c07fa04e1f1f9bc5f6657e20d4dfa4ae67e6d32/3D Files/RogueMini/RogueMini.skp -------------------------------------------------------------------------------- /Firmware/SDAPServer/SDAPServer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | SDAPServer - A Sloppy Amalgam of SDWebServer and WiFiAccessPoint 3 | 4 | Copyright (c) 2015 Hristo Gochkov. All rights reserved. 5 | Modified 2016 Nick Poole. 6 | This file is part of the ESP8266WebServer library for Arduino environment. 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 | 22 | Have a FAT Formatted SD Card connected to the SPI port of the ESP8266 23 | The web root is the SD Card root folder 24 | File extensions with more than 3 charecters are not supported by the SD Library 25 | File Names longer than 8 charecters will be truncated by the SD library, so keep filenames shorter 26 | index.htm is the default index (works on subfolders as well) 27 | 28 | upload the contents of SdRoot to the root of the SDcard and access the editor by going to http://esp8266sd.local/edit 29 | 30 | */ 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | const char WiFiAPPSK[] = ""; 40 | 41 | const byte DNS_PORT = 53; 42 | IPAddress apIP(192, 168, 1, 1); 43 | DNSServer dnsServer; 44 | ESP8266WebServer server(80); 45 | 46 | static bool hasSD = false; 47 | File uploadFile; 48 | 49 | 50 | void returnOK() { 51 | server.sendHeader("Connection", "close"); 52 | server.sendHeader("Access-Control-Allow-Origin", "*"); 53 | server.send(200, "text/plain", ""); 54 | } 55 | 56 | void returnFail(String msg) { 57 | server.sendHeader("Connection", "close"); 58 | server.sendHeader("Access-Control-Allow-Origin", "*"); 59 | server.send(500, "text/plain", msg + "\r\n"); 60 | } 61 | 62 | bool loadFromSdCard(String path){ 63 | String dataType = "text/plain"; 64 | if(path.endsWith("/")) path += "index.htm"; 65 | 66 | if(path.endsWith(".src")) path = path.substring(0, path.lastIndexOf(".")); 67 | else if(path.endsWith(".htm")) dataType = "text/html"; 68 | else if(path.endsWith(".css")) dataType = "text/css"; 69 | else if(path.endsWith(".js")) dataType = "application/javascript"; 70 | else if(path.endsWith(".png")) dataType = "image/png"; 71 | else if(path.endsWith(".gif")) dataType = "image/gif"; 72 | else if(path.endsWith(".jpg")) dataType = "image/jpeg"; 73 | else if(path.endsWith(".ico")) dataType = "image/x-icon"; 74 | else if(path.endsWith(".xml")) dataType = "text/xml"; 75 | else if(path.endsWith(".pdf")) dataType = "application/pdf"; 76 | else if(path.endsWith(".zip")) dataType = "application/zip"; 77 | 78 | File dataFile = SD.open(path.c_str()); 79 | if(dataFile.isDirectory()){ 80 | path += "/index.htm"; 81 | dataType = "text/html"; 82 | dataFile = SD.open(path.c_str()); 83 | } 84 | 85 | if (!dataFile) 86 | return false; 87 | 88 | if (server.hasArg("download")) dataType = "application/octet-stream"; 89 | 90 | if (server.streamFile(dataFile, dataType) != dataFile.size()) { 91 | 92 | } 93 | 94 | dataFile.close(); 95 | return true; 96 | } 97 | 98 | void handleFileUpload(){ 99 | if(server.uri() != "/edit") return; 100 | HTTPUpload& upload = server.upload(); 101 | if(upload.status == UPLOAD_FILE_START){ 102 | if(SD.exists((char *)upload.filename.c_str())) SD.remove((char *)upload.filename.c_str()); 103 | uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE); 104 | 105 | } else if(upload.status == UPLOAD_FILE_WRITE){ 106 | if(uploadFile) uploadFile.write(upload.buf, upload.currentSize); 107 | 108 | } else if(upload.status == UPLOAD_FILE_END){ 109 | if(uploadFile) uploadFile.close(); 110 | 111 | } 112 | } 113 | 114 | void deleteRecursive(String path){ 115 | File file = SD.open((char *)path.c_str()); 116 | if(!file.isDirectory()){ 117 | file.close(); 118 | SD.remove((char *)path.c_str()); 119 | return; 120 | } 121 | 122 | file.rewindDirectory(); 123 | while(true) { 124 | File entry = file.openNextFile(); 125 | if (!entry) break; 126 | String entryPath = path + "/" +entry.name(); 127 | if(entry.isDirectory()){ 128 | entry.close(); 129 | deleteRecursive(entryPath); 130 | } else { 131 | entry.close(); 132 | SD.remove((char *)entryPath.c_str()); 133 | } 134 | yield(); 135 | } 136 | 137 | SD.rmdir((char *)path.c_str()); 138 | file.close(); 139 | } 140 | 141 | void handleDelete(){ 142 | if(server.args() == 0) return returnFail("BAD ARGS"); 143 | String path = server.arg(0); 144 | if(path == "/" || !SD.exists((char *)path.c_str())) { 145 | returnFail("BAD PATH"); 146 | return; 147 | } 148 | deleteRecursive(path); 149 | returnOK(); 150 | } 151 | 152 | void handleCreate(){ 153 | if(server.args() == 0) return returnFail("BAD ARGS"); 154 | String path = server.arg(0); 155 | if(path == "/" || SD.exists((char *)path.c_str())) { 156 | returnFail("BAD PATH"); 157 | return; 158 | } 159 | 160 | if(path.indexOf('.') > 0){ 161 | File file = SD.open((char *)path.c_str(), FILE_WRITE); 162 | if(file){ 163 | file.write((const char *)0); 164 | file.close(); 165 | } 166 | } else { 167 | SD.mkdir((char *)path.c_str()); 168 | } 169 | returnOK(); 170 | } 171 | 172 | void printDirectory() { 173 | if(!server.hasArg("dir")) return returnFail("BAD ARGS"); 174 | String path = server.arg("dir"); 175 | if(path != "/" && !SD.exists((char *)path.c_str())) return returnFail("BAD PATH"); 176 | File dir = SD.open((char *)path.c_str()); 177 | path = String(); 178 | if(!dir.isDirectory()){ 179 | dir.close(); 180 | return returnFail("NOT DIR"); 181 | } 182 | dir.rewindDirectory(); 183 | server.setContentLength(CONTENT_LENGTH_UNKNOWN); 184 | server.send(200, "text/json", ""); 185 | WiFiClient client = server.client(); 186 | 187 | server.sendContent("["); 188 | for (int cnt = 0; true; ++cnt) { 189 | File entry = dir.openNextFile(); 190 | if (!entry) 191 | break; 192 | 193 | String output; 194 | if (cnt > 0) 195 | output = ','; 196 | 197 | output += "{\"type\":\""; 198 | output += (entry.isDirectory()) ? "dir" : "file"; 199 | output += "\",\"name\":\""; 200 | output += entry.name(); 201 | output += "\""; 202 | output += "}"; 203 | server.sendContent(output); 204 | entry.close(); 205 | } 206 | server.sendContent("]"); 207 | dir.close(); 208 | } 209 | 210 | void handleNotFound(){ 211 | 212 | } 213 | 214 | void setup(void){ 215 | setupWiFi(); 216 | 217 | server.begin(); 218 | 219 | server.on("/list", HTTP_GET, printDirectory); 220 | server.on("/edit", HTTP_DELETE, handleDelete); 221 | server.on("/edit", HTTP_PUT, handleCreate); 222 | server.on("/edit", HTTP_POST, [](){ returnOK(); }); 223 | server.onNotFound([]() { 224 | loadFromSdCard(""); 225 | }); 226 | server.onFileUpload(handleFileUpload); 227 | 228 | server.begin(); 229 | 230 | if (SD.begin(SS)){ 231 | 232 | hasSD = true; 233 | } 234 | } 235 | 236 | void setupWiFi() 237 | { 238 | WiFi.mode(WIFI_AP); 239 | 240 | // Do a little work to get a unique-ish name. Append the 241 | // last two bytes of the MAC (HEX'd) to "Thing-": 242 | uint8_t mac[WL_MAC_ADDR_LENGTH]; 243 | WiFi.softAPmacAddress(mac); 244 | String macID = String(mac[WL_MAC_ADDR_LENGTH - 2], HEX) + 245 | String(mac[WL_MAC_ADDR_LENGTH - 1], HEX); 246 | macID.toUpperCase(); 247 | String AP_NameString = "R0gue_Mini #" + macID; 248 | 249 | char AP_NameChar[AP_NameString.length() + 1]; 250 | memset(AP_NameChar, 0, AP_NameString.length() + 1); 251 | 252 | for (int i=0; i 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | const char WiFiAPPSK[] = ""; 47 | 48 | const byte DNS_PORT = 53; 49 | IPAddress apIP(192, 168, 1, 1); 50 | DNSServer dnsServer; 51 | ESP8266WebServer server(80); 52 | 53 | static bool hasSD = false; 54 | File uploadFile; 55 | 56 | 57 | void returnOK() { 58 | server.sendHeader("Connection", "close"); 59 | server.sendHeader("Access-Control-Allow-Origin", "*"); 60 | server.send(200, "text/plain", ""); 61 | } 62 | 63 | void returnFail(String msg) { 64 | server.sendHeader("Connection", "close"); 65 | server.sendHeader("Access-Control-Allow-Origin", "*"); 66 | server.send(500, "text/plain", msg + "\r\n"); 67 | } 68 | 69 | bool loadFromSdCard(String path){ 70 | String dataType = "text/plain"; 71 | if(path.endsWith("/")) path += "index.htm"; 72 | 73 | if(path.endsWith(".src")) path = path.substring(0, path.lastIndexOf(".")); 74 | else if(path.endsWith(".htm")) dataType = "text/html"; 75 | else if(path.endsWith(".css")) dataType = "text/css"; 76 | else if(path.endsWith(".js")) dataType = "application/javascript"; 77 | else if(path.endsWith(".png")) dataType = "image/png"; 78 | else if(path.endsWith(".gif")) dataType = "image/gif"; 79 | else if(path.endsWith(".jpg")) dataType = "image/jpeg"; 80 | else if(path.endsWith(".ico")) dataType = "image/x-icon"; 81 | else if(path.endsWith(".xml")) dataType = "text/xml"; 82 | else if(path.endsWith(".pdf")) dataType = "application/pdf"; 83 | else if(path.endsWith(".zip")) dataType = "application/zip"; 84 | 85 | File dataFile = SD.open(path.c_str()); 86 | if(dataFile.isDirectory()){ 87 | path += "/index.htm"; 88 | dataType = "text/html"; 89 | dataFile = SD.open(path.c_str()); 90 | } 91 | 92 | if (!dataFile) 93 | return false; 94 | 95 | if (server.hasArg("download")) dataType = "application/octet-stream"; 96 | 97 | if (server.streamFile(dataFile, dataType) != dataFile.size()) { 98 | 99 | } 100 | 101 | dataFile.close(); 102 | return true; 103 | } 104 | 105 | void handleFileUpload(){ 106 | if(server.uri() != "/edit") return; 107 | HTTPUpload& upload = server.upload(); 108 | if(upload.status == UPLOAD_FILE_START){ 109 | if(SD.exists((char *)upload.filename.c_str())) SD.remove((char *)upload.filename.c_str()); 110 | uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE); 111 | 112 | } else if(upload.status == UPLOAD_FILE_WRITE){ 113 | if(uploadFile) uploadFile.write(upload.buf, upload.currentSize); 114 | 115 | } else if(upload.status == UPLOAD_FILE_END){ 116 | if(uploadFile) uploadFile.close(); 117 | 118 | } 119 | } 120 | 121 | void deleteRecursive(String path){ 122 | File file = SD.open((char *)path.c_str()); 123 | if(!file.isDirectory()){ 124 | file.close(); 125 | SD.remove((char *)path.c_str()); 126 | return; 127 | } 128 | 129 | file.rewindDirectory(); 130 | while(true) { 131 | File entry = file.openNextFile(); 132 | if (!entry) break; 133 | String entryPath = path + "/" +entry.name(); 134 | if(entry.isDirectory()){ 135 | entry.close(); 136 | deleteRecursive(entryPath); 137 | } else { 138 | entry.close(); 139 | SD.remove((char *)entryPath.c_str()); 140 | } 141 | yield(); 142 | } 143 | 144 | SD.rmdir((char *)path.c_str()); 145 | file.close(); 146 | } 147 | 148 | void handleDelete(){ 149 | if(server.args() == 0) return returnFail("BAD ARGS"); 150 | String path = server.arg(0); 151 | if(path == "/" || !SD.exists((char *)path.c_str())) { 152 | returnFail("BAD PATH"); 153 | return; 154 | } 155 | deleteRecursive(path); 156 | returnOK(); 157 | } 158 | 159 | void handleCreate(){ 160 | if(server.args() == 0) return returnFail("BAD ARGS"); 161 | String path = server.arg(0); 162 | if(path == "/" || SD.exists((char *)path.c_str())) { 163 | returnFail("BAD PATH"); 164 | return; 165 | } 166 | 167 | if(path.indexOf('.') > 0){ 168 | File file = SD.open((char *)path.c_str(), FILE_WRITE); 169 | if(file){ 170 | file.write((const char *)0); 171 | file.close(); 172 | } 173 | } else { 174 | SD.mkdir((char *)path.c_str()); 175 | } 176 | returnOK(); 177 | } 178 | 179 | void printDirectory() { 180 | if(!server.hasArg("dir")) return returnFail("BAD ARGS"); 181 | String path = server.arg("dir"); 182 | if(path != "/" && !SD.exists((char *)path.c_str())) return returnFail("BAD PATH"); 183 | File dir = SD.open((char *)path.c_str()); 184 | path = String(); 185 | if(!dir.isDirectory()){ 186 | dir.close(); 187 | return returnFail("NOT DIR"); 188 | } 189 | dir.rewindDirectory(); 190 | server.setContentLength(CONTENT_LENGTH_UNKNOWN); 191 | server.send(200, "text/json", ""); 192 | WiFiClient client = server.client(); 193 | 194 | server.sendContent("["); 195 | for (int cnt = 0; true; ++cnt) { 196 | File entry = dir.openNextFile(); 197 | if (!entry) 198 | break; 199 | 200 | String output; 201 | if (cnt > 0) 202 | output = ','; 203 | 204 | output += "{\"type\":\""; 205 | output += (entry.isDirectory()) ? "dir" : "file"; 206 | output += "\",\"name\":\""; 207 | output += entry.name(); 208 | output += "\""; 209 | output += "}"; 210 | server.sendContent(output); 211 | entry.close(); 212 | } 213 | server.sendContent("]"); 214 | dir.close(); 215 | } 216 | 217 | void handleNotFound(){ 218 | 219 | } 220 | 221 | void setup(void){ 222 | setupWiFi(); 223 | 224 | server.begin(); 225 | 226 | server.on("/list", HTTP_GET, printDirectory); 227 | server.on("/edit", HTTP_DELETE, handleDelete); 228 | server.on("/edit", HTTP_PUT, handleCreate); 229 | server.on("/edit", HTTP_POST, [](){ returnOK(); }); 230 | server.onNotFound([]() { 231 | loadFromSdCard(""); 232 | }); 233 | server.onFileUpload(handleFileUpload); 234 | 235 | server.begin(); 236 | 237 | if (SD.begin(SS)){ 238 | 239 | hasSD = true; 240 | } 241 | } 242 | 243 | void setupWiFi() 244 | { 245 | WiFi.mode(WIFI_AP); 246 | 247 | // Do a little work to get a unique-ish name. Append the 248 | // last two bytes of the MAC (HEX'd) to "Thing-": 249 | uint8_t mac[WL_MAC_ADDR_LENGTH]; 250 | WiFi.softAPmacAddress(mac); 251 | String macID = String(mac[WL_MAC_ADDR_LENGTH - 2], HEX) + 252 | String(mac[WL_MAC_ADDR_LENGTH - 1], HEX); 253 | macID.toUpperCase(); 254 | String AP_NameString = "R0gue_Mini#" + macID; 255 | 256 | char AP_NameChar[AP_NameString.length() + 1]; 257 | memset(AP_NameChar, 0, AP_NameString.length() + 1); 258 | 259 | for (int i=0; i