Landing Page for IoT application / Avoid captive portal

BivvyCoder
Posts: 22
Joined: Sun Jan 28, 2024 5:20 pm

Landing Page for IoT application / Avoid captive portal

Postby BivvyCoder » Sun Jan 19, 2025 10:11 pm

I'm trying to create a web page to configure an IoT device running on an ESP32.
The idea is that the end user will connect their phone to a soft AP running on the ESP32 and then be taken straight to the web page.
I don't want the captive portal banner to appear at the top of the screen and I want the web page to open automatically.

Previously I've got the soft AP to work and can open the web page if I open a browser and manually enter the root page IP address. I just want to automate these last two steps.

Having done a lot of research I've manage to get the connection to work, and to get the captive portal to open and show the web page. However, whatever I try I cannot get rid of the captive portal banner.

My latest code (see below) works, I no longer get the captive portal banner - but the web page does not open automatically.

I know I'm not the first person to do this but I cannot find the solution however hard I look...

Thanks in advance for any advice (or a link to an example where this approach has been cracked :D )

Code: Select all

#include <string.h>
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"

#include "esp_http_server.h"
#include "mdns.h"               //Required for captive portal
#include "dns_server.h"
#include "esp_netif.h"
#include "esp_netif_types.h"    //Required to set DHCP captive portal option
#include "globals.h"
#include "lwip/ip4_addr.h"      //Rrequired to change IP address to 10.10.10.1

static const char *TAG = "captive_portal";

// DNS server configuration
dns_server_config_t dns_config = DNS_SERVER_CONFIG_SINGLE("*", "WIFI_AP_DEF");

// HTTP handler for connectivity check (e.g., Android)
esp_err_t connectivity_check_handler(httpd_req_t *req) {
    httpd_resp_set_status(req, "204 No Content");
    httpd_resp_send(req, NULL, 0);
    return ESP_OK;
}

// HTTP handler for iOS/macOS
esp_err_t apple_connectivity_handler(httpd_req_t *req) {
    httpd_resp_set_status(req, "204 No Content");
    httpd_resp_send(req, NULL, 0);
    return ESP_OK;
}

// HTTP handler for Windows
esp_err_t windows_connectivity_handler(httpd_req_t *req) {
    httpd_resp_set_status(req, "204 No Content");
    httpd_resp_send(req, NULL, 0);
    return ESP_OK;
}

// HTTP handler for Linux (NetworkManager)
esp_err_t linux_connectivity_handler(httpd_req_t *req) {
    httpd_resp_set_status(req, "204 No Content");
    httpd_resp_send(req, NULL, 0);
    return ESP_OK;
}



// HTTP handler for the landing page
esp_err_t landing_page_handler(httpd_req_t *req) {
    const char *response = "<html><body><h1>Configure Your IoT Device</h1></body></html>";
    httpd_resp_set_type(req, "text/html");
    httpd_resp_send(req, response, strlen(response));
    ESP_LOGI(TAG,"Landing Page");
    return ESP_OK;
}

// Function to start the web server
void start_webserver() {
    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();

    httpd_uri_t connectivity_check_uri = {
        .uri        = "/generate_204",
        .method     = HTTP_GET,
        .handler    = connectivity_check_handler,
        .user_ctx   = NULL
    };

    httpd_uri_t connectivity_check_uri1 = {
        .uri        = "/generate204",
        .method     = HTTP_GET,
        .handler    = connectivity_check_handler,
        .user_ctx   = NULL
    };

    httpd_uri_t apple_connectivity_uri = {
        .uri         = "/hotspot-detect.html",
        .method     = HTTP_GET,
        .handler    = apple_connectivity_handler,
        .user_ctx   = NULL
    };

    httpd_uri_t windows_connectivity_uri = {
        .uri        = "/connecttest.txt",
        .method     = HTTP_GET,
        .handler    = windows_connectivity_handler,
        .user_ctx   = NULL
    };

    httpd_uri_t linux_connectivity_uri = {
        .uri        = "/check_network_status.txt",
        .method     = HTTP_GET,
        .handler    = linux_connectivity_handler,
        .user_ctx   = NULL
    };

    httpd_uri_t landing_page_uri = {
        .uri        = "/",
        .method     = HTTP_GET,
        .handler    = landing_page_handler,
        .user_ctx   = NULL
    };

    if (httpd_start(&server, &config) == ESP_OK) {
        httpd_register_uri_handler(server, &connectivity_check_uri);
        httpd_register_uri_handler(server, &connectivity_check_uri1);
        httpd_register_uri_handler(server, &apple_connectivity_uri);
        httpd_register_uri_handler(server, &windows_connectivity_uri);
        httpd_register_uri_handler(server, &linux_connectivity_uri);
        httpd_register_uri_handler(server, &landing_page_uri);
    }
}

// Function to set DHCP option 114
void set_dhcp_option_114(esp_netif_t *netif) {
    esp_netif_dhcps_stop(netif);
    // Set DHCP option 114 
    char *venue_info_url = "http://10.10.10.1"; 
    ESP_ERROR_CHECK(esp_netif_dhcps_option(netif, ESP_NETIF_OP_SET, ESP_NETIF_CAPTIVEPORTAL_URI, venue_info_url, strlen(venue_info_url)));
    esp_netif_dhcps_start(netif);
}


void app_main() {
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    esp_netif_t *netif = esp_netif_create_default_wifi_ap();
    assert(netif);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
    ESP_ERROR_CHECK(esp_wifi_start());

    // Set the IP address for the AP
    esp_netif_ip_info_t ip_info;
    IP4_ADDR(&ip_info.ip, 10, 10, 10, 1);
    IP4_ADDR(&ip_info.gw, 10, 10, 10, 1);
    IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0);
    ESP_ERROR_CHECK(esp_netif_dhcps_stop(netif));
    ESP_ERROR_CHECK(esp_netif_set_ip_info(netif, &ip_info));
    ESP_ERROR_CHECK(esp_netif_dhcps_start(netif));

    // Initialize mDNS 
    ESP_ERROR_CHECK(mdns_init()); 
    ESP_ERROR_CHECK(mdns_hostname_set("Example")); 
    ESP_ERROR_CHECK(mdns_instance_name_set("Example Captive Portal"));

    set_dhcp_option_114(netif);

    start_dns_server(&dns_config);
    start_webserver();
}


BivvyCoder
Posts: 22
Joined: Sun Jan 28, 2024 5:20 pm

Re: Landing Page for IoT application / Avoid captive portal

Postby BivvyCoder » Sun Jan 19, 2025 10:13 pm

ps. If I put a redirect in the uri handlers, the web page opens but I get the captive portal banner again
eg

Code: Select all

// HTTP handler for connectivity check (e.g., Android)
esp_err_t connectivity_check_handler(httpd_req_t *req) {
    httpd_resp_set_status(req, "302 Found");
    httpd_resp_set_hdr(req, "Location", "http://10.10.10.1/");
    httpd_resp_send(req, NULL, 0);
    return ESP_OK;
}

boarchuz
Posts: 626
Joined: Tue Aug 21, 2018 5:28 am

Re: Landing Page for IoT application / Avoid captive portal

Postby boarchuz » Mon Jan 20, 2025 5:40 am

Can you share an image of this banner? It's likely a function of the device OS and there's nothing you will be able to do on the ESP32 side to hide it.

BivvyCoder
Posts: 22
Joined: Sun Jan 28, 2024 5:20 pm

Re: Landing Page for IoT application / Avoid captive portal

Postby BivvyCoder » Tue Jan 21, 2025 7:55 am

I suspected that might be the problem.
Here's the banner on my Android phone
Screenshot_2025-01-21-07-49-46-72_cbe8c7c25be320d0c6f8847e2dea9fd0.jpg
Screenshot_2025-01-21-07-49-46-72_cbe8c7c25be320d0c6f8847e2dea9fd0.jpg (57.23 KiB) Viewed 1204 times

mersin_jr
Posts: 4
Joined: Wed Dec 11, 2024 4:54 am

Re: Landing Page for IoT application / Avoid captive portal

Postby mersin_jr » Mon Feb 03, 2025 4:44 am

can you provide the full code ? :D

Who is online

Users browsing this forum: No registered users and 34 guests