diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e2a0c98 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "files.associations": { + "*.page-template": "vue", + "*.layout-template": "vue", + "*.vue": "vue", + "*.tcc": "cpp", + "functional": "cpp" + } +} \ No newline at end of file diff --git a/src/example_config.h b/src/example_config.h index 2875c4a..27d815d 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 @@ -15,7 +11,7 @@ #define FRAMERATE 8 // Buttons and buzzer -#define PIN_BTN_L 12 // D6 +#define PIN_BTN_L 15 // D6 #define PIN_BTN_M 13 // D7 -#define PIN_BTN_R 15 // D8 +#define PIN_BTN_R 12 // D8 #define PIN_BUZZER 0 // D3 \ No newline at end of file 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..06e6d55 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,17 +3,17 @@ #include #include #include +#include "netman.h" #include "functions.h" #include "bitmaps.h" #include "octoprint.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..d8c131a --- /dev/null +++ b/src/netman.h @@ -0,0 +1,333 @@ +#include +#include +#include +#include +#include +#include +#include + +// Constants for the HTML pages and config file +static const String beginHtml = "SmartCube Configure

Configure AP


"; +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(); + void handleSelect(); +}; + +// Initialization +void netman::init(String ssid, String pass, bool hidden) { + // Ensure password meets minimum length + if (pass != "" && pass.length() < 8) { + display.clearDisplay(); + display.setCursor(0, 0); + display.println("Password too short"); + display.display(); + pass = "8characters"; + } + + // Get the last 4 characters of the MAC address + String macAddress = WiFi.macAddress(); + String macSuffix = macAddress.substring(macAddress.length() - 5); + macSuffix.replace(":", ""); + + // Set default SSID if none provided + _ssid = ssid.isEmpty() ? "SmartCube_" + macSuffix : ssid; + _pass = pass; + _hidden = hidden; + + + // Initialize LittleFS with error handling + if (!LittleFS.begin()) { + display.clearDisplay(); + display.setCursor(0, 0); + display.println("FS init failed"); + display.display(); + } +} + +// 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.clearDisplay(); + display.setCursor(0, 0); + display.println("Using default pass:"); + display.println("8characters"); + display.display(); + } + 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, dot animation, and IP display +bool netman::tryConnectToSsid(const char* ssid, const char* pass) { + WiFi.begin(ssid, pass); + unsigned long start = millis(); + + // Clear display and set initial message + display.clearDisplay(); + display.setCursor(0, 0); + display.println("Connecting to WiFi:"); + display.setCursor(0, 11); + display.println(String(ssid)); + display.display(); + + int dotCount = 0; + while (millis() - start < connectionTimeout) { + delay(500); + + // Check WiFi connection status + if (WiFi.status() == WL_CONNECTED) { + // Success message with IP address + display.clearDisplay(); + display.setCursor(0, 0); + display.println("Connected!"); + display.setCursor(0, 12); + display.print("IP: "); + display.println(WiFi.localIP()); + display.display(); + delay(500); + return true; + } + + // Animate by adding dots + display.setCursor(0, 20); + for (int i = 0; i < dotCount; i++) { + display.print("."); + } + display.display(); + dotCount = (dotCount + 1); + } + + // Connection failed + display.clearDisplay(); + display.setCursor(0, 0); + display.println("Connection failed."); + display.display(); + 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.clearDisplay(); + display.setCursor(0, 0); + display.println("AccessPoint created"); + display.setCursor(0, 14); + display.println("SSID:"); + display.setCursor(0, 24); + display.setTextSize(1); + display.println(_ssid.c_str()); + display.setTextSize(1); + display.setCursor(0, 40); + display.println("Config portal:"); + display.setCursor(0, 50); + display.print("http://"); + display.println(WiFi.softAPIP().toString()); + display.display(); + + 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->on("/select", std::bind(&netman::handleSelect, this)); + + server->begin(); + + 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; +} + +// Modify the addSsid function to take parameters from the `select` page +void netman::addSsid(String ssid, String password) { + _ssids[ssid] = password; + writeConfig(); + + // Attempt to connect to the selected network + if (tryConnectToSsid(ssid.c_str(), password.c_str())) { + // Redirect to the main page on success + server->sendHeader("Location", "/", true); + server->send(302, "text/plain", ""); + } else { + // Show error message if connection failed + server->send(200, "text/html", "

Connection failed. Please try again.

"); + } +} + +// 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.clearDisplay(); + display.setCursor(0, 0); + display.println("Config not found"); + display.display(); + 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(); +} + +String urlEncode(const String &str) { + String encoded = ""; + char c; + for (size_t i = 0; i < str.length(); i++) { + c = str.charAt(i); + if (isalnum(c)) { + encoded += c; + } else { + encoded += '%'; + char buf[3]; + snprintf(buf, sizeof(buf), "%02X", c); + encoded += buf; + } + } + return encoded; +} + +void netman::handleRoot() { + if (redirectToIp()) return; + + // Scan for available networks + int n = WiFi.scanNetworks(); + String result = beginHtml; + + // Add stored SSIDs to the page + result += "

Saved Networks

"; + for (const auto& item : _ssids) { + result += "" + item.first + "-" + item.second + ""; + } + + // Display available WiFi networks + result += "

Available Networks

"; + for (int i = 0; i < n; i++) { + // Get SSID and signal strength + String ssid = WiFi.SSID(i); + int rssi = WiFi.RSSI(i); + bool openNetwork = (WiFi.encryptionType(i) == ENC_TYPE_NONE); + + // Show network with button to select + result += ""; + } + 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(); +} + +// Add SSID to config and save +void netman::handleSelect() { + String ssid = server->arg("ssid"); + String selectPage = "Connect to " + ssid + "

Connect to " + ssid + "

"; + server->send(200, "text/html", selectPage); +} \ No newline at end of file diff --git a/src/octoprint.h b/src/octoprint.h index 45c97dc..77d5290 100644 --- a/src/octoprint.h +++ b/src/octoprint.h @@ -138,19 +138,20 @@ bool fetchPrintingStatus(Adafruit_SSD1306& display) { display.fillRect(0, 26, displayWidth, 11, BLACK); // Check if it’s time to scroll based on the delay - if (millis() - lastScrollTime > scrollDelay) { - // Update scroll position based on the current direction - scrollPos += scrollDirection; - lastScrollTime = millis(); + if(fileNameWidth > displayWidth) { + if (millis() - lastScrollTime > scrollDelay) { + // Update scroll position based on the current direction + scrollPos += scrollDirection; + lastScrollTime = millis(); - // Reverse direction if the text reaches either edge - if (scrollPos > 0) { // Reached the left edge - scrollDirection = -1; // Start moving left - } else if (scrollPos < -fileNameWidth + displayWidth) { // Reached the right edge - scrollDirection = 1; // Start moving right + // Reverse direction if the text reaches either edge + if (scrollPos > 0) { // Reached the left edge + scrollDirection = -1; // Start moving left + } else if (scrollPos < -fileNameWidth + displayWidth) { // Reached the right edge + scrollDirection = 1; // Start moving right + } } } - // Draw the file name with current scroll position display.setCursor(scrollPos, 27); display.print(fileName);
" + (openNetwork ? "(Open)" : "(Secured)") + "" + String(rssi) + " dBm