diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1f4c742 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "files.associations": { + "*.page-template": "vue", + "*.layout-template": "vue", + "*.vue": "vue", + "*.tcc": "cpp" + } +} \ No newline at end of file diff --git a/src/example_config.h b/src/example_config.h index 2875c4a..3b642ff 100644 --- a/src/example_config.h +++ b/src/example_config.h @@ -1,7 +1,3 @@ -// WiFi settings -#define WIFI_SSID "Your_SSID" -#define WIFI_PASSWORD "Your_Password" - // OctoPrint settings #define OCTOPRINT_HOST "your.octoprint.local" #define OCTOPRINT_PORT 9000 diff --git a/src/functions.h b/src/functions.h index c71bacf..48ab8c1 100644 --- a/src/functions.h +++ b/src/functions.h @@ -38,51 +38,6 @@ void initSystems() { display.display(); } -// Connect to WiFi -void connectToWifi() { - - display.clearDisplay(); - display.setCursor(0, 0); - display.println("Connecting to WiFi"); - display.display(); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - - int attemptCount = 0; - while (WiFi.status() != WL_CONNECTED && attemptCount < 20) { // Timeout after 20 attempts - delay(500); - display.print("."); - display.display(); - attemptCount++; - } - - // Check if connected - if (WiFi.status() == WL_CONNECTED) { - Serial.println("\nConnected to WiFi!"); - Serial.print("IP Address: "); - Serial.println(WiFi.localIP()); - - // Display connection success and IP address - display.clearDisplay(); - display.setCursor(0, 0); - display.print("WiFi Connected!"); - display.setCursor(0, 10); - display.print("IP: "); - display.print(WiFi.localIP()); - display.display(); - beep(800); - } else { - Serial.println("\nFailed to connect to WiFi."); - display.clearDisplay(); - display.setCursor(0, 0); - display.print("WiFi Connection"); - display.setCursor(0, 10); - display.print("Failed"); - display.display(); - } - delay(1000); -} - void commonButtonHandler() { unsigned long currentMillis = millis(); static unsigned long leftPressStart = 0; diff --git a/src/main.cpp b/src/main.cpp index d48fa26..c5ec626 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,14 +6,14 @@ #include "functions.h" #include "bitmaps.h" #include "octoprint.h" +#include "netman.h" void setup() { - initSystems(); + netman netman(display); playTune(1); - connectToWifi(); + netman.start(); displayOctoPrintVersion(display); - } void loop() { diff --git a/src/netman.h b/src/netman.h new file mode 100644 index 0000000..61570da --- /dev/null +++ b/src/netman.h @@ -0,0 +1,217 @@ +#include +#include +#include +#include +#include +#include +#include + +// Constants for the HTML pages and config file +static const String beginHtml = "AP Configure

"; +static const String endHtml = "
"; +static const String configFile = "/netman"; + +// Timeout for WiFi connection attempts +static const unsigned long connectionTimeout = 15000; // 15 seconds + +struct netman { +public: + netman(Adafruit_SSD1306& display); + netman(String ssid, String pass, bool hidden, Adafruit_SSD1306& display); + bool start(); + void reset(); + void addSsid(String ssid, String password); + void removeSsid(String ssid, String password); + +private: + Adafruit_SSD1306& display; + std::unique_ptr server; + std::map _ssids; + String _ssid, _pass; + bool _hidden; + + void init(String ssid, String pass, bool hidden); + bool tryConnectToSsid(const char* ssid, const char* pass); + bool tryConnect(); + void createAP(); + bool redirectToIp(); + void readConfig(); + void writeConfig(); + void handleRoot(); + void handleAdd(); + void handleRemove(); +}; + +// Initialization +void netman::init(String ssid, String pass, bool hidden) { + // Ensure password meets minimum length + if (pass != "" && pass.length() < 8) { + display.println("Password too short. Using default: '8characters'"); + pass = "8characters"; + } + + // Set default SSID if none provided + _ssid = ssid.isEmpty() ? "ESP" + String(ESP.getChipId()) : ssid; + _pass = pass; + _hidden = hidden; + + // Initialize LittleFS with error handling + if (!LittleFS.begin()) { + display.println("Failed to initialize filesystem."); + } +} + +// Constructors +netman::netman(Adafruit_SSD1306& display) : display(display) { + init("", "", false); +} + +netman::netman(String ssid, String pass, bool hidden, Adafruit_SSD1306& display) : display(display) { + init(ssid, pass, hidden); +} + +// Attempt to start and connect or create AP +bool netman::start() { + if (_pass == "8characters") { + display.println("Using default password due to length requirement."); + } + return tryConnect() || (createAP(), false); +} + +// Attempt connection to each saved SSID in order +bool netman::tryConnect() { + readConfig(); + for (auto const& item : _ssids) { + if (tryConnectToSsid(item.first.c_str(), item.second.c_str())) { + return true; + } + } + return false; +} + +// Attempt to connect to a specific SSID with timeout +bool netman::tryConnectToSsid(const char* ssid, const char* pass) { + WiFi.begin(ssid, pass); + unsigned long start = millis(); + + display.println("Connecting to " + String(ssid) + "..."); + while (millis() - start < connectionTimeout) { + delay(500); + if (WiFi.status() == WL_CONNECTED) { + display.println("Connected!"); + return true; + } + } + display.println("Connection failed."); + WiFi.disconnect(); + return false; +} + +// Setup Access Point with DNS and HTTP server +void netman::createAP() { + WiFi.softAPdisconnect(true); + WiFi.mode(WIFI_AP); + WiFi.softAP(_ssid.c_str(), _pass.c_str(), 1, _hidden); + + display.println("AP created with IP: " + WiFi.softAPIP().toString()); + + server.reset(new ESP8266WebServer(80)); + DNSServer dnsServer; + dnsServer.start(53, "*", WiFi.softAPIP()); + + server->on("/", std::bind(&netman::handleRoot, this)); + server->on("/add", std::bind(&netman::handleAdd, this)); + server->on("/remove", std::bind(&netman::handleRemove, this)); + + server->begin(); + display.println("HTTP server started"); + + while (true) { + dnsServer.processNextRequest(); + server->handleClient(); + delay(10); + } +} + +// Redirect to AP IP if not accessed directly +bool netman::redirectToIp() { + if (server->hostHeader() == WiFi.softAPIP().toString()) { + return false; + } + server->sendHeader("Location", "http://" + WiFi.softAPIP().toString(), true); + server->send(302, "text/plain", ""); + server->client().stop(); + return true; +} + +// Add SSID to config and save +void netman::addSsid(String ssid, String password) { + _ssids[ssid] = password; + writeConfig(); +} + +// Remove SSID from config +void netman::removeSsid(String ssid, String password) { + if (_ssids.count(ssid) && _ssids[ssid] == password) { + _ssids.erase(ssid); + writeConfig(); + } +} + +// Handle file-based config loading +void netman::readConfig() { + _ssids.clear(); + File file = LittleFS.open(configFile, "r"); + if (!file) { + display.println("No config file found."); + return; + } + + while (file.available()) { + String ssid = file.readStringUntil('\n'); + ssid.trim(); + String pass = file.readStringUntil('\n'); + pass.trim(); + _ssids[ssid] = pass; + } + file.close(); +} + +// Handle file-based config saving +void netman::writeConfig() { + File file = LittleFS.open(configFile, "w"); + for (const auto& item : _ssids) { + file.println(item.first); + file.println(item.second); + } + file.close(); +} + +// Reset configuration +void netman::reset() { + LittleFS.remove(configFile); + _ssids.clear(); +} + +// Web handlers for adding/removing SSIDs +void netman::handleRoot() { + if (redirectToIp()) return; + String result = beginHtml; + for (const auto& item : _ssids) { + result += "" + item.first + "-" + item.second + ""; + } + result += endHtml; + server->send(200, "text/html", result); +} + +void netman::handleAdd() { + server->send(200, "text/html", "The ESP will now reboot."); + addSsid(server->arg("ssid"), server->arg("pass")); + delay(500); + ESP.restart(); +} + +void netman::handleRemove() { + removeSsid(server->arg("ssid"), server->arg("pass")); + handleRoot(); +}