Skip to content

Commit

Permalink
captive portal support for AccessPoint
Browse files Browse the repository at this point in the history
  • Loading branch information
tcsullivan committed Mar 8, 2024
1 parent 7fda1e6 commit e119ff6
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 40 deletions.
78 changes: 56 additions & 22 deletions noisemeter-device/access-point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@
"</body>" \
"</html>"

// The ESP32's IP address within its access point will be "4.3.2.1".
// Once connected to the access point, open up 4.3.2.1 in a browser to get
// to the credentials form.
const IPAddress AccessPoint::IP (4, 3, 2, 1);
constexpr int DNSPort = 53;

const IPAddress AccessPoint::IP (8, 8, 4, 4);
const IPAddress AccessPoint::Netmask (255, 255, 255, 0);

// Main webpage HTML with form to collect WiFi credentials.
Expand All @@ -42,37 +41,72 @@ const char *AccessPoint::htmlSubmit =
"<p>Connecting...</p>"
HTML_FOOTER;

void AccessPoint::begin()
[[noreturn]]
void AccessPoint::run()
{
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(IP, IP, Netmask);
WiFi.softAP(SSID, Passkey);

// GET request means user wants to see the form.
// POST request means user has submitted data through the form.
server.on("/", HTTP_GET,
[this] { server.send_P(200, PSTR("text/html"), htmlSetup); });
server.on("/", HTTP_POST,
[this] {
server.client().setNoDelay(true);
server.send_P(200, PSTR("text/html"), htmlSubmit);
if (funcOnCredentialsReceived)
funcOnCredentialsReceived(server);
});
dns.start(DNSPort, "*", IP);

server.addHandler(this);
server.begin();

SERIAL.println("Running setup access point.");

while (1) {
dns.processNextRequest();
server.handleClient();
delay(10);
}
}

[[noreturn]]
void AccessPoint::run()
bool AccessPoint::canHandle(HTTPMethod, String)
{
while (1)
server.handleClient();
return true;
}

void AccessPoint::onCredentialsReceived(void (*func)(WebServer&))
bool AccessPoint::handle(WebServer& server, HTTPMethod method, String uri)
{
funcOnCredentialsReceived = func;
if (method == HTTP_POST) {
if (uri == "/") {
server.client().setNoDelay(true);
server.send_P(200, PSTR("text/html"), htmlSubmit);
if (onCredentialsReceived)
onCredentialsReceived(server);
} else {
server.sendHeader("Location", "http://8.8.4.4/");
server.send(301);
}
} else if (method == HTTP_GET) {
// Redirects taken from https://github.com/CDFER/Captive-Portal-ESP32

if (uri == "/") {
server.send_P(200, PSTR("text/html"), htmlSetup);
} else if (uri == "/connecttest.txt") {
// windows 11 captive portal workaround
server.sendHeader("Location", "http://logout.net");
server.send(301);
} else if (uri == "/wpad.dat") {
// Honestly don't understand what this is but a 404 stops win 10
// keep calling this repeatedly and panicking the esp32 :)
server.send(404);
} else if (uri == "/success.txt") {
// firefox captive portal call home
server.send(200);
} else if (uri == "/favicon.ico") {
// return 404 to webpage icon
server.send(404);
} else {
server.sendHeader("Location", "http://8.8.4.4/");
server.send(301);
}
} else {
server.sendHeader("Location", "http://8.8.4.4/");
server.send(301);
}

return true;
}

24 changes: 13 additions & 11 deletions noisemeter-device/access-point.h
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
#ifndef ACCESS_POINT_H
#define ACCESS_POINT_H

#include <DNSServer.h>
#include <WebServer.h>

class AccessPoint
class AccessPoint : public RequestHandler
{
static constexpr auto SSID = "Noise meter";
static constexpr auto Passkey = "noisemeter";
// IP address set in access-point.cpp

public:
AccessPoint():
server(80), funcOnCredentialsReceived(nullptr) {}
// Begins hosting a WiFi access point with the credentials form as a
// captive portal.
AccessPoint(void (*func)(WebServer&)):
server(80),
onCredentialsReceived(func) {}

// Configure the WiFi radio to be an access point.
void begin();

// Enter main loop for executing the access point and web server.
// Enters an infinite loop to handle the access point and web server.
[[noreturn]] void run();

// Set handler for reception of WiFi credentials.
void onCredentialsReceived(void (*func)(WebServer&));
// RequestHandler implementation for web server functionality.
bool canHandle(HTTPMethod, String) override;
bool handle(WebServer&, HTTPMethod, String) override;

private:
DNSServer dns;
WebServer server;
void (*funcOnCredentialsReceived)(WebServer&);
void (*onCredentialsReceived)(WebServer&);

static const IPAddress IP;
static const IPAddress Netmask;
Expand Down
11 changes: 4 additions & 7 deletions noisemeter-device/noisemeter-device.ino
Original file line number Diff line number Diff line change
Expand Up @@ -160,16 +160,13 @@ void setup() {
#ifndef UPLOAD_DISABLED
// Run the access point if it is requested or if there are no valid credentials.
bool resetPressed = !digitalRead(PIN_BUTTON);
if (resetPressed || !Creds.valid()) {
AccessPoint ap;
if (resetPressed || !Creds.valid() || Creds.get(Storage::Entry::SSID).isEmpty()) {
AccessPoint ap (saveNetworkCreds);

SERIAL.println("Erasing stored credentials...");
SERIAL.print("Erasing stored credentials...");
Creds.clear();
SERIAL.print("Stored Credentials after erasing: ");
SERIAL.println(Creds);
SERIAL.println(" done.");

ap.onCredentialsReceived(saveNetworkCreds);
ap.begin();
ap.run(); // does not return
}

Expand Down

0 comments on commit e119ff6

Please sign in to comment.