#include "web_config.h" #define STRING_LEN 33 // Should be enough for ip, addresses, passwords... #define STRING(x) #x #define STRINGIFY(x) STRING(x) // Allows to call STRINGIFY(SOME_MACRO) and convert the value of SOME_MACRO to string #include "config.h" #ifndef MQTT_TOPIC_PREFIX # error Missing config.h file. Please copy config.public.h to config.h. # warning config.h has a new structure (e.g. AMPEL_WIFI should be set to true or false) #endif #include "util.h" #include "sensor_console.h" #include "src/lib/IotWebConf/src/IotWebConf.h" #include "src/lib/IotWebConf/src/IotWebConfTParameter.h" #include "src/lib/IotWebConf/src/IotWebConfOptionalGroup.h" //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] = AMPEL_CONFIG_VERSION; // -- Configuration specific key. The value should be modified if config structure was changed. using namespace iotwebconf; /** * WiFi params */ CheckboxTParameter ampelWifiParam = Builder("WiFi").label("WiFi?").defaultValue(AMPEL_WIFI).build(); IntTParameter wifiTimeoutParam = Builder>("wifi_timeout").label("WiFi timeout").defaultValue(WIFI_TIMEOUT).min(0).placeholder( "[s]").build(); //TODO: Distribute to corresponding classes, possibly with callbacks? /** * CO2 sensor */ ParameterGroup co2Params = ParameterGroup("co2", "CO2 Sensor"); IntTParameter timestepParam = Builder>("timestep").label("Measurement timestep").defaultValue(MEASUREMENT_TIMESTEP).min( 2).max(1800).placeholder("[s]").build(); FloatTParameter temperatureOffsetParam = Builder("temp_offset").label("Temperature offset").defaultValue(TEMPERATURE_OFFSET).placeholder( "[K]").step(0.1).build(); IntTParameter altitudeParam = Builder>("altitude").label("Altitude").defaultValue( ALTITUDE_ABOVE_SEA_LEVEL).min(0).step(1).placeholder("[m]").build(); IntTParameter atmosphericCO2Param = Builder>("atmospheric_co2").label( "Atmospheric CO2 concentration").defaultValue( ATMOSPHERIC_CO2_CONCENTRATION).min(400).max(2000).step(1).placeholder("ppm").build(); CheckboxTParameter autoCalibrateParam = Builder("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 maxBrightnessParam = Builder>("max_brightness").label("Max Brightness").defaultValue(MAX_BRIGHTNESS).min(0).max( 255).build(); IntTParameter minBrightnessParam = Builder>("min_brightness").label("Min Brightness").defaultValue(MIN_BRIGHTNESS).min(0).max( 255).build(); IntTParameter ledCountParam = Builder>("led_count").label("LED ring").defaultValue( LED_COUNT).min(12).max(16).step(4).build(); /** * CSV */ OptionalParameterGroup csvParams = OptionalParameterGroup("CSV", "CSV", AMPEL_CSV); IntTParameter csvIntervalParam = Builder>("csv_interval").label("CSV interval").defaultValue(CSV_INTERVAL).min(0).step(1).placeholder( "[s]").build(); /** * MQTT */ OptionalParameterGroup mqttParams = OptionalParameterGroup("MQTT", "MQTT", AMPEL_MQTT); IntTParameter mqttIntervalParam = Builder>("mqtt_interval").label("MQTT interval").defaultValue(MQTT_SENDING_INTERVAL).min( 0).step(1).defaultValue(300).placeholder("[s]").build(); CheckboxTParameter mqttEncryptionParam = Builder("mqtt_encryption").label("Encrypt MQTT?").defaultValue(MQTT_ENCRYPTED).build(); CheckboxTParameter mqttCommandsParam = Builder("mqtt_commands").label("Allow MQTT commands?").defaultValue(ALLOW_MQTT_COMMANDS).build(); TextTParameter mqttServerParam = Builder>("mqtt_server").label("MQTT Server").defaultValue(MQTT_SERVER).build(); IntTParameter mqttPortParam = Builder>("mqtt_port").label("MQTT Port").defaultValue( MQTT_PORT).build(); TextTParameter mqttUserParam = Builder>("mqtt_user").label("MQTT User").defaultValue(MQTT_USER).build(); PasswordTParameter mqttPasswordParam = Builder>("mqtt_password").label( "MQTT password").defaultValue(MQTT_PASSWORD).build(); TextTParameter mqttTopicPrefixParam = Builder>("mqtt_topic").label("MQTT Topic prefix").defaultValue(MQTT_TOPIC_PREFIX).build(); /** * NTP Time */ ParameterGroup timeParams = ParameterGroup("NTP", "Time server"); TextTParameter ntpServerParam = Builder>("ntp_server").label("NTP Server").defaultValue(NTP_SERVER).build(); IntTParameter timeOffsetParam = Builder>("timezone").label("Timezone").defaultValue( UTC_OFFSET).min(-23).max(23).step(1).placeholder("[h]").build(); CheckboxTParameter dstParam = Builder("dst").label("Daylight Saving Time?").defaultValue( DAYLIGHT_SAVING_TIME).build(); /** * LoRaWAN */ #if defined(ESP32) OptionalParameterGroup loraParams = OptionalParameterGroup("LoRaWan", "LoRaWan", AMPEL_LORAWAN); IntTParameter loraIntervalParam = Builder>("lora_interval").label("LoRa interval").defaultValue(LORAWAN_SENDING_INTERVAL).min( 0).step(1).defaultValue(300).placeholder("[s]").build(); TextTParameter<17> deviceEUIParam = Builder>("device_eui").label("Device EUI (MSB)").defaultValue( LORAWAN_DEVICE_EUI).build(); TextTParameter<17> appEUIParam = Builder>("app_eui").label("App EUI (MSB)").defaultValue( LORAWAN_APPLICATION_EUI).build(); PasswordTParameter<33> appKeyParam = Builder>("app_key").label("App key (MSB)").defaultValue( LORAWAN_APPLICATION_KEY).build(); #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 fail_and_return_null = [fail_function]() { fail_function(); return nullptr; }; iotWebConf->setWifiConnectionFailedHandler(fail_and_return_null); } void defineStructure() { iotWebConf->setHtmlFormatProvider(&optionalGroupHtmlFormatProvider); iotWebConf->addSystemParameter(&elWifiParam); // Somehow, making ampelWifi invisible set it to 0 :-/ // ampelWifiParam.visible = false; // To avoid users getting locked out. In order to set WiFi on/off, console commands can be used. iotWebConf->addSystemParameter(&wifiTimeoutParam); iotWebConf->getThingNameParameter()->label = "Ampel name"; iotWebConf->getApPasswordParameter()->label = "Ampel password (user : 'admin')"; iotWebConf->getApTimeoutParameter()->label = "Access Point timeout"; iotWebConf->getApTimeoutParameter()->visible = true; iotWebConf->getApPasswordParameter()->defaultValue = AMPEL_PASSWORD; iotWebConf->getWifiSsidParameter()->defaultValue = WIFI_SSID; iotWebConf->getWifiPasswordParameter()->defaultValue = WIFI_PASSWORD; iotWebConf->getApTimeoutParameter()->defaultValue = STRINGIFY(ACCESS_POINT_TIMEOUT); // Defined as number in config.h but stored as string in webconf. co2Params.addItem(×tepParam); 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(&csvIntervalParam); mqttParams.addItem(&mqttIntervalParam); mqttParams.addItem(&mqttServerParam); mqttParams.addItem(&mqttPortParam); mqttParams.addItem(&mqttUserParam); mqttParams.addItem(&mqttPasswordParam); mqttParams.addItem(&mqttTopicPrefixParam); mqttParams.addItem(&mqttEncryptionParam); mqttParams.addItem(&mqttCommandsParam); #if defined(ESP32) loraParams.addItem(&loraIntervalParam); 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 } void defineCommands() { sensor_console::defineCommand("save_config", config::save, F("(Saves the config to EEPROM)")); 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::defineStringCommand("ssid", [](char *ssid) { Serial.print(F("Setting WiFi ssid to ")); Serial.println(ssid); strlcpy(iotWebConf->getWifiSsidParameter()->valueBuffer, ssid, iotWebConf->getWifiSsidParameter()->getLength()); iotWebConf->saveConfig(); }, F("name (Sets SSID to name)")); sensor_console::defineStringCommand("pwd", [](char *ssid) { Serial.print(F("Setting WiFi password to ")); Serial.println(ssid); strlcpy(iotWebConf->getWifiPasswordParameter()->valueBuffer, ssid, iotWebConf->getWifiPasswordParameter()->getLength()); iotWebConf->saveConfig(); }, F("abc (Sets WiFi password to abc)")); 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)")); } void initialize() { iotWebConf = new IotWebConf(strlen(AMPEL_NAME) == 0 ? ampel.sensorId : AMPEL_NAME, &dnsServer, &http, "", config_version); defineStructure(); defineCommands(); 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; } //NOTE: Can only work if wifi is on. sensor_console::defineIntCommand("ap", [](int onOff) { //TODO: Add On-board LED effects to show the changes? if (onOff) { Serial.print(F("Enable ")); } else { Serial.print(F("Disable ")); } Serial.println(F("AP mode!")); iotWebConf->forceApMode(onOff); }, F("0/1 (Enables/disables access point)")); iotWebConf->setWifiConnectionTimeoutMs(1000UL * config::wifi_timeout); iotWebConf->skipApStartup(); iotWebConf->init(); 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::csvIntervalParam.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(); char *mqtt_topic_prefix = web_config::mqttTopicPrefixParam.value(); uint16_t &mqtt_port = web_config::mqttPortParam.value(); uint16_t &mqtt_sending_interval = web_config::mqttIntervalParam.value(); bool &mqtt_encryption = web_config::mqttEncryptionParam.value(); bool &allow_mqtt_commands = web_config::mqttCommandsParam.value(); // LORAWAN #if defined(ESP32) bool is_lorawan_active() { return web_config::loraParams.isActive(); } uint16_t &lorawan_sending_interval = web_config::loraIntervalParam.value(); char *lorawan_device_eui = web_config::deviceEUIParam.value(); char *lorawan_app_key = web_config::appKeyParam.value(); char *lorawan_app_eui = web_config::appEUIParam.value(); #endif }