web_config.cpp 12.73 KiB
#include "web_config.h"
#define STRING_LEN 40 // Should be enough for ip, addresses, passwords...
#if defined(ESP8266)
#  include <ESP8266WebServer.h>
#elif defined(ESP32)
#  include <WebServer.h>
#endif
#include "config.h"
#ifndef MEASUREMENT_TIMESTEP
#  error Missing config.h file. Please copy config.public.h to config.h.
#endif
// Make sure AMPEL_WIFI is defined and not empty:
#if !defined(AMPEL_WIFI) || (7-AMPEL_WIFI-7 == 14)
#  error config.h has a new structure, please update it from config.public.h (e.g. AMPEL_WIFI should be set to true or false)
#endif
#include "util.h"
#include "sensor_console.h"
#include <IotWebConf.h>
#include <IotWebConfTParameter.h>
#include <IotWebConfOptionalGroup.h>
//TODO: Check memory consumption. Disable DEBUG info?
//TODO: Convert all strings to F-strings
namespace web_config {
#if defined(ESP8266)
  ESP8266WebServer http(80); // Create a webserver object that listens for HTTP request on port 80
#elif defined(ESP32)
  WebServer http(80);
#endif
  DNSServer dnsServer; //TODO: Check if needed
  IotWebConf *iotWebConf;
  const char config_version[IOTWEBCONF_CONFIG_VERSION_LENGTH] = "a09"; // -- Configuration specific key. The value should be modified if config structure was changed.
  using namespace iotwebconf;
  /**
   * WiFi params
  CheckboxTParameter ampelWifiParam =
      Builder<CheckboxTParameter>("WiFi").label("WiFi?").defaultValue(AMPEL_WIFI).build();
  IntTParameter<uint16_t> wifiTimeoutParam =
      Builder<IntTParameter<uint16_t>>("wifi_timeout").label("WiFi timeout").defaultValue(WIFI_TIMEOUT).min(0).placeholder(
          "[s]").build();
  //TODO: Distribute to corresponding classes, possibly with callbacks?
  //TODO: Chainedparameters?
  /**
   * CO2 sensor
  ParameterGroup co2Params = ParameterGroup("co2", "CO2 Sensor");
  IntTParameter<uint16_t> timestepParam =
      Builder<IntTParameter<uint16_t>>("timestep").label("Measurement timestep").defaultValue(MEASUREMENT_TIMESTEP).min(
          2).max(1800).placeholder("[s]").build();
  FloatTParameter temperatureOffsetParam =
      Builder<FloatTParameter>("temp_offset").label("Temperature offset").defaultValue(TEMPERATURE_OFFSET).placeholder(
          "[K]").step(0.1).build();
  IntTParameter<uint16_t> altitudeParam = Builder<IntTParameter<uint16_t>>("altitude").label("Altitude").defaultValue(
  ALTITUDE_ABOVE_SEA_LEVEL).min(0).step(1).placeholder("[m]").build();
IntTParameter<uint16_t> atmosphericCO2Param = Builder<IntTParameter<uint16_t>>("atmospheric_co2").label( "Atmospheric CO2 concentration").defaultValue( ATMOSPHERIC_CO2_CONCENTRATION).min(400).max(2000).step(1).placeholder("ppm").build(); CheckboxTParameter autoCalibrateParam = Builder<CheckboxTParameter>("asc").label("Auto-calibration?").defaultValue( AUTO_CALIBRATE_SENSOR).build(); /** * LED */ //NOTE: Could also be optional : for LED 0 / 1 ParameterGroup ledParams = ParameterGroup("LED", "LED"); IntTParameter<uint8_t> maxBrightnessParam = Builder<IntTParameter<uint8_t>>("max_brightness").label("Max Brightness").defaultValue(MAX_BRIGHTNESS).min(0).max( 255).build(); IntTParameter<uint8_t> minBrightnessParam = Builder<IntTParameter<uint8_t>>("min_brightness").label("Min Brightness").defaultValue(MIN_BRIGHTNESS).min(0).max( 255).build(); IntTParameter<uint16_t> ledCountParam = Builder<IntTParameter<uint16_t>>("led_count").label("LED ring").defaultValue( LED_COUNT).min(12).max(16).step(4).build(); /** * CSV */ OptionalParameterGroup csvParams = OptionalParameterGroup("CSV", "CSV", AMPEL_CSV); IntTParameter<uint16_t> csvTimestepParam = Builder<IntTParameter<uint16_t>>("csv_timestep").label("CSV timestep").defaultValue(CSV_INTERVAL).min(0).step(1).placeholder( "[s]").build(); /** * MQTT */ OptionalParameterGroup mqttParams = OptionalParameterGroup("MQTT", "MQTT", AMPEL_MQTT); IntTParameter<uint16_t> mqttTimestepParam = Builder<IntTParameter<uint16_t>>("mqtt_timestep").label("MQTT timestep").defaultValue( MQTT_SENDING_INTERVAL).min(0).step(1).defaultValue(300).placeholder("[s]").build(); CheckboxTParameter mqttEncryptionParam = Builder<CheckboxTParameter>("mqtt_encryption").label("Encrypt MQTT?").defaultValue(MQTT_ENCRYPTED).build(); CheckboxTParameter mqttCommandsParam = Builder<CheckboxTParameter>("mqtt_commands").label("Allow MQTT commands?").defaultValue(ALLOW_MQTT_COMMANDS).build(); TextTParameter<STRING_LEN> mqttServerParam = Builder<TextTParameter<STRING_LEN>>("mqtt_server").label("MQTT Server").defaultValue(MQTT_SERVER).build(); IntTParameter<uint16_t> mqttPortParam = Builder<IntTParameter<uint16_t>>("mqtt_port").label("MQTT Port").defaultValue( MQTT_PORT).build(); TextTParameter<STRING_LEN> mqttUserParam = Builder<TextTParameter<STRING_LEN>>("mqtt_user").label("MQTT User").defaultValue(MQTT_USER).build(); //TODO: Show the number of * for password? PasswordTParameter<STRING_LEN> mqttPasswordParam = Builder<PasswordTParameter<STRING_LEN>>("mqtt_password").label( "MQTT password").defaultValue(MQTT_PASSWORD).build(); /** * NTP Time */ ParameterGroup timeParams = ParameterGroup("NTP", "Time server"); TextTParameter<STRING_LEN> ntpServerParam = Builder<TextTParameter<STRING_LEN>>("ntp_server").label("NTP Server").defaultValue(NTP_SERVER).build(); IntTParameter<int16_t> timeOffsetParam = Builder<IntTParameter<int16_t>>("timezone").label("Timezone").defaultValue( (UTC_OFFSET_IN_SECONDS) / 3600).min(-23).max(23).step(1).placeholder("[h]").build(); CheckboxTParameter dstParam =
Builder<CheckboxTParameter>("dst").label("Daylight Saving Time?").defaultValue(false).build(); /** * LoRaWAN */ #if defined(ESP32) OptionalParameterGroup loraParams = OptionalParameterGroup("LoRaWan", "LoRaWan", AMPEL_LORAWAN); IntTParameter<uint16_t> loraTimestepParam = Builder<IntTParameter<uint16_t>>("lora_timestep").label("LoRa timestep").defaultValue( LORAWAN_SENDING_INTERVAL).min(0).step(1).defaultValue(300).placeholder("[s]").build(); //TODO: Use those parameters TextTParameter<17> deviceEUIParam = Builder<TextTParameter<17>>("device_eui").label("Device EUI").defaultValue( "70B3D5...").build(); TextTParameter<17> appEUIParam = Builder<TextTParameter<17>>("app_eui").label("App EUI").defaultValue("00EA07...").build(); TextTParameter<32> appKeyParam = Builder<TextTParameter<32>>("app_key").label("App key").defaultValue("81CCFE...").build(); //TODO: Save LoRa session to hidden parameter after first OTAA successful login #endif OptionalGroupHtmlFormatProvider optionalGroupHtmlFormatProvider; void update() { iotWebConf->doLoop(); // Listen for HTTP requests from clients } void setWifiConnectionCallback(void (*success_function)()) { iotWebConf->setWifiConnectionCallback(success_function); } void setWifiFailCallback(void (*fail_function)()) { std::function<WifiAuthInfo* ()> fail_and_return_null = [fail_function]() { fail_function(); iotWebConf->forceApMode(true); // Will stay in AP. return nullptr; }; iotWebConf->setWifiConnectionFailedHandler(fail_and_return_null); } void initialize() { iotWebConf = new IotWebConf(ampel.sensorId, &dnsServer, &http, HTTP_PASSWORD, config_version); iotWebConf->setHtmlFormatProvider(&optionalGroupHtmlFormatProvider); iotWebConf->addSystemParameter(&ampelWifiParam); iotWebConf->addSystemParameter(&wifiTimeoutParam); co2Params.addItem(&timestepParam); co2Params.addItem(&temperatureOffsetParam); co2Params.addItem(&altitudeParam); co2Params.addItem(&atmosphericCO2Param); co2Params.addItem(&autoCalibrateParam); ledParams.addItem(&minBrightnessParam); ledParams.addItem(&maxBrightnessParam); ledParams.addItem(&ledCountParam); timeParams.addItem(&ntpServerParam); timeParams.addItem(&timeOffsetParam); timeParams.addItem(&dstParam); csvParams.addItem(&csvTimestepParam); mqttParams.addItem(&mqttTimestepParam); mqttParams.addItem(&mqttServerParam); mqttParams.addItem(&mqttPortParam); mqttParams.addItem(&mqttUserParam); mqttParams.addItem(&mqttPasswordParam); mqttParams.addItem(&mqttEncryptionParam);
mqttParams.addItem(&mqttCommandsParam); #if defined(ESP32) loraParams.addItem(&loraTimestepParam); loraParams.addItem(&deviceEUIParam); loraParams.addItem(&appEUIParam); loraParams.addItem(&appKeyParam); #endif iotWebConf->addParameterGroup(&co2Params); iotWebConf->addParameterGroup(&ledParams); iotWebConf->addParameterGroup(&timeParams); iotWebConf->addParameterGroup(&csvParams); iotWebConf->addParameterGroup(&mqttParams); #if defined(ESP32) iotWebConf->addParameterGroup(&loraParams); #endif //TODO: Add "ap" command? //TODO: Add "wifi_ssid" command? //TODO: Add "wifi_password" command? sensor_console::defineCommand("reset_config", []() { Serial.println(F("Resetting config...")); iotWebConf->getRootParameterGroup()->applyDefaultValue(); iotWebConf->saveConfig(); Serial.println(F("Done!")); }, F("(resets the complete IotWeb config)")); sensor_console::defineIntCommand("wifi", [](int32_t onOff) { config::is_wifi_on = onOff; iotWebConf->saveConfig(); Serial.print(F("WiFi - ")); Serial.println(onOff ? F("On!") : F("Off!")); }, F("0/1 (Turns Wifi on/off).")); iotWebConf->loadConfig(); if (!config::is_wifi_on) { Serial.println(F("Wifi is off : no access point or connection. Use 'wifi 1' to turn on again!")); return; } const int ONBOARD_LED_PIN = 2; # ifdef ESP8266 iotWebConf->setStatusPin(ONBOARD_LED_PIN, LOW); # else iotWebConf->setStatusPin(ONBOARD_LED_PIN, HIGH); # endif iotWebConf->setWifiConnectionTimeoutMs(1000UL * config::wifi_timeout); iotWebConf->skipApStartup(); //TODO: Add callbacks //TODO: Add LED effects //TODO: Allow offline config loading //TODO: Add default values for SSID/password //TODO: Add other params //TODO: Use HTTP_USER / HTTP_PASSWORD for config //TODO: Remove AP Password config? //TODO: Save LoRaWAN key if possible? iotWebConf->init(); //TODO: Authenticate only if required? //TODO: / captive fast return? http.on("/config", [] { iotWebConf->handleConfig(); });
http.onNotFound([]() { iotWebConf->handleNotFound(); }); } } /*** * Define all the corresponding config values as reference, so that they can be updated. */ namespace config { char* ampel_name() { return web_config::iotWebConf->getThingName(); } char* selected_ssid() { return web_config::iotWebConf->getWifiSsidParameter()->valueBuffer; } char* ampel_password() { return web_config::iotWebConf->getApPasswordParameter()->valueBuffer; } // Sensor uint16_t &measurement_timestep = web_config::timestepParam.value(); // [s] Value between 2 and 1800 (range for SCD30 sensor). uint16_t &altitude_above_sea_level = web_config::altitudeParam.value(); // [m] uint16_t &co2_calibration_level = web_config::atmosphericCO2Param.value(); // [ppm] bool &auto_calibrate_sensor = web_config::autoCalibrateParam.value(); // [true / false] float &temperature_offset = web_config::temperatureOffsetParam.value(); // [K] Sign isn't relevant. bool &is_wifi_on = web_config::ampelWifiParam.value(); uint16_t &wifi_timeout = web_config::wifiTimeoutParam.value(); void save() { web_config::iotWebConf->saveConfig(); } // LEDs uint8_t &max_brightness = web_config::maxBrightnessParam.value(); uint8_t &min_brightness = web_config::minBrightnessParam.value(); uint16_t &led_count = web_config::ledCountParam.value(); // Time server char *ntp_server = web_config::ntpServerParam.value(); int16_t &time_zone = web_config::timeOffsetParam.value(); bool &daylight_saving_time = web_config::dstParam.value(); // CSV bool is_csv_active() { return web_config::csvParams.isActive(); } uint16_t &csv_interval = web_config::csvTimestepParam.value(); // MQTT bool is_mqtt_active() { return web_config::mqttParams.isActive(); } char *mqtt_server = web_config::mqttServerParam.value(); char *mqtt_user = web_config::mqttUserParam.value(); char *mqtt_password = web_config::mqttPasswordParam.value(); uint16_t &mqtt_port = web_config::mqttPortParam.value(); uint16_t &mqtt_sending_interval = web_config::mqttTimestepParam.value(); bool &mqtt_encryption = web_config::mqttEncryptionParam.value(); bool &allow_mqtt_commands = web_config::mqttCommandsParam.value(); // HTTP // const char *http_user = IOTWEBCONF_ADMIN_USER_NAME; // "admin" by default // char *http_password = web_config::iotWebConf->getApPasswordParameter()->valueBuffer; // LORAWAN #if defined(ESP32)
bool lorawan_active() { return web_config::loraParams.isActive(); } #endif }