grmcdorman / esp8266_web_settings

General, non-specific web-based settings/status manager and classes for ESP8266 (possibly usable for other Arduino-class boards).
MIT License
1 stars 0 forks source link

esp8266_web_settings

Generic ESP8266 Web Settings Library

This repository contains a generic Web page to manage your device settings. Settings are presented in tabs, with a set of buttons below:

Save, Reboot, Factory Reset and Upload Firmware can be optionally password-protected.

Settings on each tab are displayed in a two-column table.

Full documentation

Usage

The main class is WebSettings. Create a single instance of this class, and then call its add_setting_set with lists of settings to create each panel in the web page.

There are several settings classes:

Public methods in the WebSettings class:

For the most part, use the get() and set() methods in the Settings classes to retrieve and set values. The InfoSetting contains an additional method, set_request_callback(); this callback is invoked just before the InfoSetting's value is sent to the web page for an update. Thus, by setting this callback, you can dynamically update data on the web page.

For each settings set, or panel, some JavaScript must be provided to include the panel in the periodic update. This can be done for the entire panel, or for a specific list of settings. However, do not update the entire panel if it includes input fields; doing so will result in the input fields being reset every 5 seconds.

To include an entire panel in the periodic update, include a Note setting with the following text, replacing panel name with the actual panel name:

periodicUpdateList.push("panel_identifier");

To limit the update to one or more specific settings in the periodic update, include a Note setting with the following text, again replacing panel_identifier and the setting IDs:

periodicUpdateList.push("panel_identifier&setting=setting_1_id&setting=setting_2_id");

The SettingPanel class is intended for internal use.

Screenshots:

Basic example:

#include <ESP8266WiFi.h>
#include <LittleFS.h>

#include "grmcdorman/WebSettings.h"

grmcdorman::WebSettings web_setings;

#define DECLARE_INFO_SETTING(name, text) \
static const char * PROGMEM name##_text = text; \
static const char * PROGMEM name##_id = #name; \
static ::grmcdorman::InfoSettingHtml name(FPSTR(name##_text), FPSTR(name##_id));

static const char * PROGMEM info_title_text = "<h1>" FIRMWARE_PREFIX "</h1>"
    "<h2>System information</h2>";
static const char * PROGMEM info_footer_text =
    "<script>periodicUpdateList.push(\"overview_tab\");</script>";

static ::grmcdorman::NoteSetting info_title(FPSTR(info_title_text));
// Applicable when connected to AP.
DECLARE_INFO_SETTING(host, "Host name");
DECLARE_INFO_SETTING(station_ssid, "Connected to");  // SSID
DECLARE_INFO_SETTING(rssi, "Signal strength");
// Applicable in AP mode.
DECLARE_INFO_SETTING(softap, "Soft Access Point SSID");
DECLARE_INFO_SETTING(free_heap, "Free heap memory");

static ::grmcdorman::NoteSetting info_footer(FPSTR(info_footer_text));

static const ::grmcdorman::SettingInterface::settings_list_t info_item_list{
    &info_title,
    &softap,
    &host,
    &station_ssid,
    &rssi,
    &free_heap,
    &uptime,
    &info_footer
};

static bool factory_reset_next_loop = false;
static bool restart_next_loop = false;
static uint32_t restart_reset_when = 0;
static constexpr uint32_t restart_reset_delay = 500;

static void on_factory_reset(::grmcdorman::WebSettings &)
{
    factory_reset_next_loop = true;
    restart_reset_when = millis();
}

static void on_restart(::grmcdorman::WebSettings &)
{
    restart_next_loop =-true;
    restart_reset_when = millis();
}

static void on_save(::grmcdorman::WebSettings &)
{
    // Save your settings to flash.
}

void setup()
{

    web_setings.add_setting_set(F("Overview"), F("overview_tab") info_item_list);
    // Callbacks for info page.
    static const char in_ap_mode[] PROGMEM = "(In Access Pont mode)";
    static const char in_sta_mode[] PROGMEM = "(In Station mode)";

    host.set_request_callback([] (const ::grmcdorman::InfoSettingHtml &) {
        if (!wifi_device.is_softap_mode())
        {
            host.set(WiFi.hostname() + F(" [") + WiFi.localIP().toString() + F("]"));
        }
        else
        {
            host.set(FPSTR(in_ap_mode));
        }
    });
    station_ssid.set_request_callback([] (const ::grmcdorman::InfoSettingHtml &) {
        if (!wifi_device.is_softap_mode())
        {
            station_ssid.set(WiFi.SSID());
        }
        else
        {
            station_ssid.set(FPSTR(in_ap_mode));
        }
    });
    rssi.set_request_callback([] (const ::grmcdorman::InfoSettingHtml &) {
        if (!wifi_device.is_softap_mode())
        {
            rssi.set(String(WiFi.RSSI()) + F(" dBm"));
        }
        else
        {
            rssi.set(FPSTR(in_ap_mode));
        }
    });
    softap.set_request_callback([] (const ::grmcdorman::InfoSettingHtml &) {
        if (wifi_device.is_softap_mode())
        {
            softap.set(WiFi.softAPSSID());
        }
        else
        {
            softap.set(FPSTR(in_sta_mode));
        }
    });
    free_heap.set_request_callback([] (const ::grmcdorman::InfoSettingHtml &) {
        free_heap.set(String(ESP.getFreeHeap()) + " bytes (fragmentation: " + String(ESP.getHeapFragmentation()) + ")");
    });

    // NOTE: SoftAP capture portal is not happy about authentication. You should not
    // make this call for SoftAP mode, or handle SoftAP differently.
    web_setings.set_credentials("admin", "my update password");

    // If `on_restart` is a `nullptr`, the Restart & Upload buttons are not shown.
    // If `on_factory_reset` is a `nullptr`, the Factory Reset button is not shown.
    web_setings.setup(on_save, on_restart, on_factory_reset);
}

void loop()
{
    web_setings.loop();      // This presently doesn't do anything. It is for future use.

    if (factory_reset_next_loop && millis() - restart_reset_when > restart_reset_delay)
    {
        // Clear file system.
        LittleFS.format();
        // Erase configuration
        ESP.eraseConfig();
        // Reset (not reboot, that may save current state)
        ESP.reset();
    }

    if (restart_next_loop && millis() - restart_reset_when > restart_reset_delay)
    {
        ESP.restart();
    }
}