Skip to content

Commit 3f9f611

Browse files
authored
Merge pull request #634 from DavidBerschauer/captive-portal
Captive Portal for easy configuration
2 parents db2b361 + 2b51657 commit 3f9f611

File tree

3 files changed

+73
-10
lines changed

3 files changed

+73
-10
lines changed

Firmware/RTK_Surveyor/Form.ino

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,49 @@ Form.ino
1111

1212
bool websocketConnected = false;
1313

14+
class CaptiveRequestHandler : public AsyncWebHandler
15+
{
16+
public:
17+
// https://en.wikipedia.org/wiki/Captive_portal
18+
String urls[5] = {"/hotspot-detect.html", "/library/test/success.html", "/generate_204", "/ncsi.txt",
19+
"/check_network_status.txt"};
20+
CaptiveRequestHandler()
21+
{
22+
}
23+
virtual ~CaptiveRequestHandler()
24+
{
25+
}
26+
27+
bool canHandle(AsyncWebServerRequest *request)
28+
{
29+
for (int i = 0; i < 5; i++)
30+
{
31+
if (request->url().equals(urls[i]))
32+
return true;
33+
}
34+
return false;
35+
}
36+
37+
// Provide a custom small site for redirecting the user to the config site
38+
// HTTP redirect does not work and the relative links on the default config site do not work, because the phone is
39+
// requesting a different server
40+
void handleRequest(AsyncWebServerRequest *request)
41+
{
42+
String logmessage = "Captive Portal Client:" + request->client()->remoteIP().toString() + " " + request->url();
43+
systemPrintln(logmessage);
44+
AsyncResponseStream *response = request->beginResponseStream("text/html");
45+
response->print("<!DOCTYPE html><html><head><title>RTK Config</title></head><body>");
46+
response->print("<div class='container'>");
47+
response->printf("<div align='center' class='col-sm-12'><img src='http://%s/src/rtk-setup.png' alt='SparkFun "
48+
"RTK WiFi Setup'></div>",
49+
WiFi.softAPIP().toString().c_str());
50+
response->printf("<div align='center'><h3>Configure your RTK receiver <a href='http://%s/'>here</a></h3></div>",
51+
WiFi.softAPIP().toString().c_str());
52+
response->print("</div></body></html>");
53+
request->send(response);
54+
}
55+
};
56+
1457
// Start webserver in AP mode
1558
bool startWebServer(bool startWiFi = true, int httpPort = 80)
1659
{
@@ -62,6 +105,7 @@ bool startWebServer(bool startWiFi = true, int httpPort = 80)
62105
}
63106

64107
websocket->onEvent(onWsEvent);
108+
webserver->addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER); // only when requested from AP
65109
webserver->addHandler(websocket);
66110

67111
// * index.html (not gz'd)
@@ -91,7 +135,8 @@ bool startWebServer(bool startWiFi = true, int httpPort = 80)
91135
handleUpload); // Run handleUpload function when any file is uploaded. Must be before server.on() calls.
92136

93137
webserver->on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
94-
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", index_html, sizeof(index_html));
138+
AsyncWebServerResponse *response =
139+
request->beginResponse_P(200, "text/html", index_html, sizeof(index_html));
95140
response->addHeader("Content-Encoding", "gzip");
96141
request->send(response);
97142
});
@@ -104,8 +149,8 @@ bool startWebServer(bool startWiFi = true, int httpPort = 80)
104149
});
105150

106151
webserver->on("/src/bootstrap.bundle.min.js", HTTP_GET, [](AsyncWebServerRequest *request) {
107-
AsyncWebServerResponse *response =
108-
request->beginResponse_P(200, "text/javascript", bootstrap_bundle_min_js, sizeof(bootstrap_bundle_min_js));
152+
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/javascript", bootstrap_bundle_min_js,
153+
sizeof(bootstrap_bundle_min_js));
109154
response->addHeader("Content-Encoding", "gzip");
110155
request->send(response);
111156
});
@@ -132,7 +177,8 @@ bool startWebServer(bool startWiFi = true, int httpPort = 80)
132177
});
133178

134179
webserver->on("/src/main.js", HTTP_GET, [](AsyncWebServerRequest *request) {
135-
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/javascript", main_js, sizeof(main_js));
180+
AsyncWebServerResponse *response =
181+
request->beginResponse_P(200, "text/javascript", main_js, sizeof(main_js));
136182
response->addHeader("Content-Encoding", "gzip");
137183
request->send(response);
138184
});
@@ -349,7 +395,7 @@ static void handleFileManager(AsyncWebServerRequest *request)
349395
{
350396
fileExists = SD_MMC.exists(slashFileName);
351397
}
352-
#endif // COMPILE_SD_MMC
398+
#endif // COMPILE_SD_MMC
353399

354400
if (fileExists == false)
355401
{
@@ -427,7 +473,7 @@ static void handleFileManager(AsyncWebServerRequest *request)
427473
#ifdef COMPILE_SD_MMC
428474
else
429475
SD_MMC.remove(slashFileName);
430-
#endif // COMPILE_SD_MMC
476+
#endif // COMPILE_SD_MMC
431477
request->send(200, "text/plain", "Deleted File: " + String(fileName));
432478
}
433479
else
@@ -731,7 +777,7 @@ void createSettingsString(char *newSettings)
731777
#ifdef COMPILE_L_BAND
732778
int daysRemaining = daysFromEpoch(settings.pointPerfectNextKeyStart + settings.pointPerfectNextKeyDuration + 1);
733779
snprintf(apDaysRemaining, sizeof(apDaysRemaining), "%d", daysRemaining);
734-
#endif // COMPILE_L_BAND
780+
#endif // COMPILE_L_BAND
735781
}
736782
else
737783
snprintf(apDaysRemaining, sizeof(apDaysRemaining), "No Keys");
@@ -1346,7 +1392,7 @@ void updateSettingWithValue(const char *settingName, const char *settingValueStr
13461392
requestChangeState(STATE_ROVER_NOT_STARTED); // If update failed, return to Rover mode.
13471393
}
13481394
else if (strcmp(settingName, "factoryDefaultReset") == 0)
1349-
factoryReset(false); //We do not have the sdSemaphore
1395+
factoryReset(false); // We do not have the sdSemaphore
13501396
else if (strcmp(settingName, "exitAndReset") == 0)
13511397
{
13521398
// Confirm receipt
@@ -1761,7 +1807,7 @@ void getFileList(String &returnText)
17611807

17621808
root.close();
17631809
}
1764-
#endif // COMPILE_SD_MMC
1810+
#endif // COMPILE_SD_MMC
17651811

17661812
xSemaphoreGive(sdCardSemaphore);
17671813
}
@@ -1883,4 +1929,4 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index,
18831929
}
18841930
}
18851931

1886-
#endif // COMPILE_AP
1932+
#endif // COMPILE_AP

Firmware/RTK_Surveyor/RTK_Surveyor.ino

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ unsigned int binBytesSent = 0; // Tracks firmware bytes sent over WiFi O
225225
#include <WiFi.h> //Built-in.
226226
#include <WiFiClientSecure.h> //Built-in.
227227
#include <WiFiMulti.h> //Built-in.
228+
#include <DNSServer.h> //Built-in.
228229

229230
#include "esp_wifi.h" //Needed for esp_wifi_set_protocol()
230231

Firmware/RTK_Surveyor/WiFi.ino

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ static unsigned long wifiDisplayTimer;
6060
// Last time the WiFi state was displayed
6161
static uint32_t lastWifiState;
6262

63+
//DNS server for Captive Portal
64+
static DNSServer dnsServer;
65+
66+
6367
//----------------------------------------
6468
// WiFi Routines
6569
//----------------------------------------
@@ -242,6 +246,12 @@ bool wifiStartAP()
242246
}
243247
systemPrint("WiFi AP Started with IP: ");
244248
systemPrintln(WiFi.softAPIP());
249+
250+
// Start DNS Server
251+
if(dnsServer.start(53, "*", WiFi.softAPIP()) == false){
252+
systemPrintln("WiFi DNS Server failed to start");
253+
return (false);
254+
};
245255
}
246256
else
247257
{
@@ -355,6 +365,9 @@ void wifiUpdate()
355365
wifiStop();
356366
break;
357367
}
368+
//Process the next DNS request
369+
dnsServer.processNextRequest();
370+
358371
}
359372

360373
// Starts the WiFi connection state machine (moves from WIFI_OFF to WIFI_CONNECTING)
@@ -395,6 +408,9 @@ void wifiStop()
395408
if (settings.mdnsEnable == true)
396409
MDNS.end();
397410

411+
//Stop the DNS server
412+
dnsServer.stop();
413+
398414
// Stop the other network clients and then WiFi
399415
networkStop(NETWORK_TYPE_WIFI);
400416
}

0 commit comments

Comments
 (0)