Add AP mode option for file transfers (#98)
## Summary
* **What is the goal of this PR?** Adds WiFi Access Point (AP) mode
support for File Transfer, allowing the device to create its own WiFi
network that users can connect to directly - useful when no existing
WiFi network is available. And in my experience is faster when the
device is right next to your laptop (but maybe further from your wifi)
* **What changes are included?**
- New `NetworkModeSelectionActivity` - an interstitial screen asking
users to choose between:
- "Join a Network" - connects to an existing WiFi network (existing
behavior)
- "Create Hotspot" - creates a WiFi access point named
"CrossPoint-Reader"
- Modified `CrossPointWebServerActivity` to:
- Launch the network mode selection screen before proceeding
- Support starting an Access Point with mDNS (`crosspoint.local`) and
DNS server for captive portal behavior
- Display appropriate connection info for both modes
- Modified `CrossPointWebServer` to support starting when WiFi is in AP
mode (not just STA connected mode)
## Additional Context
* **AP Mode Details**: The device creates an open WiFi network named
"CrossPoint-Reader". Once connected, users can access the file transfer
page at `http://crosspoint.local/` or `http://192.168.4.1/`
* **DNS Captive Portal**: A DNS server redirects all domain requests to
the device's IP, enabling captive portal behavior on some devices
* **mDNS**: Hostname resolution via `crosspoint.local` is enabled for
both AP and STA modes
* **No breaking changes**: The "Join a Network" option preserves the
existing WiFi connection flow
* **Memory impact**: Minimal - the AP mode uses roughly the same
resources as STA mode
This commit is contained in:
@@ -30,12 +30,22 @@ void CrossPointWebServer::begin() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.printf("[%lu] [WEB] Cannot start webserver - WiFi not connected\n", millis());
|
||||
// Check if we have a valid network connection (either STA connected or AP mode)
|
||||
const wifi_mode_t wifiMode = WiFi.getMode();
|
||||
const bool isStaConnected = (wifiMode & WIFI_MODE_STA) && (WiFi.status() == WL_CONNECTED);
|
||||
const bool isInApMode = (wifiMode & WIFI_MODE_AP) && (WiFi.softAPgetStationNum() >= 0); // AP is running
|
||||
|
||||
if (!isStaConnected && !isInApMode) {
|
||||
Serial.printf("[%lu] [WEB] Cannot start webserver - no valid network (mode=%d, status=%d)\n", millis(), wifiMode,
|
||||
WiFi.status());
|
||||
return;
|
||||
}
|
||||
|
||||
// Store AP mode flag for later use (e.g., in handleStatus)
|
||||
apMode = isInApMode;
|
||||
|
||||
Serial.printf("[%lu] [WEB] [MEM] Free heap before begin: %d bytes\n", millis(), ESP.getFreeHeap());
|
||||
Serial.printf("[%lu] [WEB] Network mode: %s\n", millis(), apMode ? "AP" : "STA");
|
||||
|
||||
Serial.printf("[%lu] [WEB] Creating web server on port %d...\n", millis(), port);
|
||||
server.reset(new WebServer(port));
|
||||
@@ -70,7 +80,9 @@ void CrossPointWebServer::begin() {
|
||||
running = true;
|
||||
|
||||
Serial.printf("[%lu] [WEB] Web server started on port %d\n", millis(), port);
|
||||
Serial.printf("[%lu] [WEB] Access at http://%s/\n", millis(), WiFi.localIP().toString().c_str());
|
||||
// Show the correct IP based on network mode
|
||||
const String ipAddr = apMode ? WiFi.softAPIP().toString() : WiFi.localIP().toString();
|
||||
Serial.printf("[%lu] [WEB] Access at http://%s/\n", millis(), ipAddr.c_str());
|
||||
Serial.printf("[%lu] [WEB] [MEM] Free heap after server.begin(): %d bytes\n", millis(), ESP.getFreeHeap());
|
||||
}
|
||||
|
||||
@@ -141,10 +153,14 @@ void CrossPointWebServer::handleNotFound() const {
|
||||
}
|
||||
|
||||
void CrossPointWebServer::handleStatus() const {
|
||||
// Get correct IP based on AP vs STA mode
|
||||
const String ipAddr = apMode ? WiFi.softAPIP().toString() : WiFi.localIP().toString();
|
||||
|
||||
String json = "{";
|
||||
json += "\"version\":\"" + String(CROSSPOINT_VERSION) + "\",";
|
||||
json += "\"ip\":\"" + WiFi.localIP().toString() + "\",";
|
||||
json += "\"rssi\":" + String(WiFi.RSSI()) + ",";
|
||||
json += "\"ip\":\"" + ipAddr + "\",";
|
||||
json += "\"mode\":\"" + String(apMode ? "AP" : "STA") + "\",";
|
||||
json += "\"rssi\":" + String(apMode ? 0 : WiFi.RSSI()) + ","; // RSSI not applicable in AP mode
|
||||
json += "\"freeHeap\":" + String(ESP.getFreeHeap()) + ",";
|
||||
json += "\"uptime\":" + String(millis() / 1000);
|
||||
json += "}";
|
||||
|
||||
@@ -35,6 +35,7 @@ class CrossPointWebServer {
|
||||
private:
|
||||
std::unique_ptr<WebServer> server = nullptr;
|
||||
bool running = false;
|
||||
bool apMode = false; // true when running in AP mode, false for STA mode
|
||||
uint16_t port = 80;
|
||||
|
||||
// File scanning
|
||||
|
||||
Reference in New Issue
Block a user