web_config.cpp 11.3 KB
Newer Older
1
2
#include "web_config.h"

Eric Duminil's avatar
Eric Duminil committed
3
4
5
6
7
8
9
10
11
#if defined(ESP8266)
#  include <ESP8266WebServer.h>
#elif defined(ESP32)
#  include <WebServer.h>
#endif

#include "config.h"
#include "util.h"
#include "sensor_console.h"
Eric Duminil's avatar
Eric Duminil committed
12
13

#include <IotWebConfTParameter.h>
Eric Duminil's avatar
Eric Duminil committed
14
15
16
#include <IotWebConf.h>
#include <IotWebConfOptionalGroup.h>

17
18
//TODO: Check memory consumption. Disable DEBUG info?
//TODO: Convert all strings to F-strings
Eric Duminil's avatar
Notes    
Eric Duminil committed
19

20
21
22
23
24
25
26
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

Eric Duminil's avatar
Eric Duminil committed
27
  DNSServer dnsServer; //TODO: Check if needed
28
29
30

  IotWebConf *iotWebConf;

Eric Duminil's avatar
Eric Duminil committed
31
  const char config_version[IOTWEBCONF_CONFIG_VERSION_LENGTH] = "a08"; // -- Configuration specific key. The value should be modified if config structure was changed.
32
  using namespace iotwebconf;
33

Eric Duminil's avatar
Eric Duminil committed
34
  /**
Eric Duminil's avatar
Eric Duminil committed
35
   * WiFi params
Eric Duminil's avatar
Eric Duminil committed
36
   */
Eric Duminil's avatar
Eric Duminil committed
37
  CheckboxTParameter ampelWifiParam = Builder<CheckboxTParameter>("WiFi").label("WiFi?").defaultValue(true).build();
Eric Duminil's avatar
Eric Duminil committed
38
39
40
  IntTParameter<uint16_t> wifiTimeoutParam =
      Builder<IntTParameter<uint16_t>>("wifi_timeout").label("WiFi timeout").defaultValue(WIFI_TIMEOUT).min(0).placeholder(
          "[s]").build();
Eric Duminil's avatar
Eric Duminil committed
41

Eric Duminil's avatar
Eric Duminil committed
42
43
44
45
  //TODO: Distribute to corresponding classes, possibly with callbacks?
  /**
   * CO2 sensor
   */
Eric Duminil's avatar
Done    
Eric Duminil committed
46
  ParameterGroup co2Params = ParameterGroup("co2", "CO2 Sensor");
Eric Duminil's avatar
Eric Duminil committed
47

Eric Duminil's avatar
Eric Duminil committed
48
49
  IntTParameter<uint16_t> timestepParam =
      Builder<IntTParameter<uint16_t>>("timestep").label("Measurement timestep").defaultValue(MEASUREMENT_TIMESTEP).min(
Eric Duminil's avatar
Eric Duminil committed
50
          2).max(1800).placeholder("[s]").build();
Eric Duminil's avatar
Eric Duminil committed
51

Eric Duminil's avatar
Eric Duminil committed
52
  FloatTParameter temperatureOffsetParam =
Eric Duminil's avatar
Eric Duminil committed
53
      Builder<FloatTParameter>("temp_offset").label("Temperature offset").defaultValue(TEMPERATURE_OFFSET).placeholder(
Eric Duminil's avatar
Eric Duminil committed
54
          "[K]").step(0.1).build();
Eric Duminil's avatar
Eric Duminil committed
55

Eric Duminil's avatar
Eric Duminil committed
56
  IntTParameter<uint16_t> altitudeParam = Builder<IntTParameter<uint16_t>>("altitude").label("Altitude").defaultValue(
Eric Duminil's avatar
Eric Duminil committed
57
  ALTITUDE_ABOVE_SEA_LEVEL).min(0).step(1).placeholder("[m]").build();
Eric Duminil's avatar
Eric Duminil committed
58

Eric Duminil's avatar
Eric Duminil committed
59
60
  IntTParameter<uint16_t> atmosphericCO2Param = Builder<IntTParameter<uint16_t>>("atmospheric_co2").label(
      "Atmospheric CO2 concentration").defaultValue(
Eric Duminil's avatar
Eric Duminil committed
61
  ATMOSPHERIC_CO2_CONCENTRATION).min(400).max(2000).step(1).placeholder("ppm").build();
Eric Duminil's avatar
Eric Duminil committed
62

Eric Duminil's avatar
Eric Duminil committed
63
  CheckboxTParameter autoCalibrateParam = Builder<CheckboxTParameter>("asc").label("Auto-calibration?").defaultValue(
Eric Duminil's avatar
Eric Duminil committed
64
  AUTO_CALIBRATE_SENSOR).build();
Eric Duminil's avatar
Eric Duminil committed
65

Eric Duminil's avatar
Eric Duminil committed
66
67
68
69
70
71
72
73
74
75
76
  /**
   * LED
   */
  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();
77
  IntTParameter<uint16_t> ledCountParam = Builder<IntTParameter<uint16_t>>("led_count").label("LED ring").defaultValue(
Eric Duminil's avatar
Eric Duminil committed
78
79
80
81
82
83
84
85
86
  LED_COUNT).min(12).max(16).step(4).build();

//  #  define HTTP_USER "co2ampel"
//  #  define HTTP_PASSWORD "my_password"

// WARNING: If AMPEL_LORAWAN is enabled, you need to modify the 3 following constants!
// This EUI must be in little-endian format, so least-significant-byte first.
// When copying an EUI from ttnctl output, this means to reverse the bytes.

Eric Duminil's avatar
Eric Duminil committed
87
88
89
  /**
   * CSV
   */
Eric Duminil's avatar
Eric Duminil committed
90
  OptionalParameterGroup csvParams = OptionalParameterGroup("CSV", "CSV", true);
Eric Duminil's avatar
Eric Duminil committed
91

Eric Duminil's avatar
Eric Duminil committed
92
  IntTParameter<uint16_t> csvTimestepParam =
Eric Duminil's avatar
Eric Duminil committed
93
94
      Builder<IntTParameter<uint16_t>>("csv_timestep").label("CSV timestep").defaultValue(CSV_INTERVAL).min(0).step(1).placeholder(
          "[s]").build();
Eric Duminil's avatar
Eric Duminil committed
95
96
97
98

  /**
   * MQTT
   */
Eric Duminil's avatar
Eric Duminil committed
99
  OptionalParameterGroup mqttParams = OptionalParameterGroup("MQTT", "MQTT", true);
Eric Duminil's avatar
Eric Duminil committed
100

Eric Duminil's avatar
Eric Duminil committed
101
102
  IntTParameter<uint16_t> mqttTimestepParam =
      Builder<IntTParameter<uint16_t>>("mqtt_timestep").label("MQTT timestep").defaultValue(
Eric Duminil's avatar
Eric Duminil committed
103
      MQTT_SENDING_INTERVAL).min(0).step(1).defaultValue(300).placeholder("[s]").build();
Eric Duminil's avatar
Eric Duminil committed
104

Eric Duminil's avatar
Eric Duminil committed
105
106
107
108
#if !defined(MQTT_ENCRYPTED)
#  define MQTT_ENCRYPTED true // Old config files might not define it, and encryption was on by default.
#endif

Eric Duminil's avatar
Eric Duminil committed
109
110
111
  CheckboxTParameter mqttEncryptionParam =
      Builder<CheckboxTParameter>("mqtt_encryption").label("Encrypt MQTT?").defaultValue(MQTT_ENCRYPTED).build();

Eric Duminil's avatar
Eric Duminil committed
112
  CheckboxTParameter mqttCommandsParam =
Eric Duminil's avatar
Eric Duminil committed
113
      Builder<CheckboxTParameter>("mqtt_commands").label("Allow MQTT commands?").defaultValue(ALLOW_MQTT_COMMANDS).build();
Eric Duminil's avatar
Eric Duminil committed
114

Eric Duminil's avatar
Eric Duminil committed
115
116
  TextTParameter<STRING_LEN> mqttServerParam =
      Builder<TextTParameter<STRING_LEN>>("mqtt_server").label("MQTT Server").defaultValue(MQTT_SERVER).build();
Eric Duminil's avatar
Eric Duminil committed
117

Eric Duminil's avatar
Eric Duminil committed
118
119
  IntTParameter<uint16_t> mqttPortParam = Builder<IntTParameter<uint16_t>>("mqtt_port").label("MQTT Port").defaultValue(
  MQTT_PORT).build();
Eric Duminil's avatar
Eric Duminil committed
120

Eric Duminil's avatar
Eric Duminil committed
121
122
  TextTParameter<STRING_LEN> mqttUserParam =
      Builder<TextTParameter<STRING_LEN>>("mqtt_user").label("MQTT User").defaultValue(MQTT_USER).build();
Eric Duminil's avatar
Eric Duminil committed
123

Eric Duminil's avatar
TODO    
Eric Duminil committed
124
  //TODO: Show the number of * for password?
Eric Duminil's avatar
Eric Duminil committed
125
  PasswordTParameter<STRING_LEN> mqttPasswordParam = Builder<PasswordTParameter<STRING_LEN>>("mqtt_password").label(
Eric Duminil's avatar
Eric Duminil committed
126
      "MQTT password").defaultValue(MQTT_PASSWORD).build();
Eric Duminil's avatar
Eric Duminil committed
127

Eric Duminil's avatar
Eric Duminil committed
128
129
130
  /**
   * NTP Time
   */
Eric Duminil's avatar
Eric Duminil committed
131

Eric Duminil's avatar
Eric Duminil committed
132
  ParameterGroup timeParams = ParameterGroup("NTP", "Time server");
Eric Duminil's avatar
Eric Duminil committed
133
  TextTParameter<STRING_LEN> ntpServerParam =
Eric Duminil's avatar
Eric Duminil committed
134
      Builder<TextTParameter<STRING_LEN>>("ntp_server").label("NTP Server").defaultValue(NTP_SERVER).build();
Eric Duminil's avatar
Eric Duminil committed
135
  IntTParameter<int16_t> timeOffsetParam = Builder<IntTParameter<int16_t>>("timezone").label("Timezone").defaultValue(
Eric Duminil's avatar
Eric Duminil committed
136
      (UTC_OFFSET_IN_SECONDS) / 3600).min(-23).max(23).step(1).placeholder("[h]").build();
Eric Duminil's avatar
Eric Duminil committed
137
  CheckboxTParameter dstParam =
Eric Duminil's avatar
Eric Duminil committed
138
      Builder<CheckboxTParameter>("dst").label("Daylight Saving Time?").defaultValue(false).build();
139

Eric Duminil's avatar
Eric Duminil committed
140
141
142
  /**
   * LED
   */
Eric Duminil's avatar
Eric Duminil committed
143

Eric Duminil's avatar
Eric Duminil committed
144
145
146
  /**
   * LoRa & Stuff
   */
Eric Duminil's avatar
Eric Duminil committed
147
148
149
  OptionalParameterGroup loraParams = OptionalParameterGroup("LoRaWan", "LoRaWan", false);
  IntTParameter<uint16_t> loraTimestepParam =
      Builder<IntTParameter<uint16_t>>("lora_timestep").label("LoRa timestep").defaultValue(
Eric Duminil's avatar
Eric Duminil committed
150
151
152
153
154
155
156
157
      LORAWAN_SENDING_INTERVAL).min(0).step(1).defaultValue(300).placeholder("[s]").build();

  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();
158

Eric Duminil's avatar
Eric Duminil committed
159
  OptionalGroupHtmlFormatProvider optionalGroupHtmlFormatProvider;
160
161
162
163
164

  void update() {
    iotWebConf->doLoop(); // Listen for HTTP requests from clients
  }

Eric Duminil's avatar
Eric Duminil committed
165
166
167
168
169
  void setWifiConnectionCallback(void (*function)()) {
    iotWebConf->setWifiConnectionCallback(function);
  }

  void setWifiConnectionFailedCallback(void (*function)()) {
Eric Duminil's avatar
Eric Duminil committed
170
    iotWebConf->setWifiConnectionFailedHandler([&function]() -> WifiAuthInfo* {
Eric Duminil's avatar
Eric Duminil committed
171
172
173
174
175
      function();
      return NULL;
    });
  }

176
177
178
  void initialize() {
    iotWebConf = new IotWebConf(ampel.sensorId, &dnsServer, &http, HTTP_PASSWORD, config_version);

179
180
181
    iotWebConf->setHtmlFormatProvider(&optionalGroupHtmlFormatProvider);

    iotWebConf->addSystemParameter(&ampelWifiParam);
Eric Duminil's avatar
Eric Duminil committed
182
    iotWebConf->addSystemParameter(&wifiTimeoutParam);
Eric Duminil's avatar
Eric Duminil committed
183
184
185

    co2Params.addItem(&timestepParam);
    co2Params.addItem(&temperatureOffsetParam);
Eric Duminil's avatar
Eric Duminil committed
186
187
188
    co2Params.addItem(&altitudeParam);
    co2Params.addItem(&atmosphericCO2Param);
    co2Params.addItem(&autoCalibrateParam);
Eric Duminil's avatar
Eric Duminil committed
189

Eric Duminil's avatar
Eric Duminil committed
190
    ledParams.addItem(&minBrightnessParam);
Eric Duminil's avatar
Eric Duminil committed
191
    ledParams.addItem(&maxBrightnessParam);
Eric Duminil's avatar
Eric Duminil committed
192
193
    ledParams.addItem(&ledCountParam);

Eric Duminil's avatar
Eric Duminil committed
194
    timeParams.addItem(&ntpServerParam);
195
196
197
    timeParams.addItem(&timeOffsetParam);
    timeParams.addItem(&dstParam);

Eric Duminil's avatar
Eric Duminil committed
198
199
200
201
202
203
204
    csvParams.addItem(&csvTimestepParam);

    mqttParams.addItem(&mqttTimestepParam);
    mqttParams.addItem(&mqttServerParam);
    mqttParams.addItem(&mqttPortParam);
    mqttParams.addItem(&mqttUserParam);
    mqttParams.addItem(&mqttPasswordParam);
Eric Duminil's avatar
Eric Duminil committed
205
206
    mqttParams.addItem(&mqttEncryptionParam);
    mqttParams.addItem(&mqttCommandsParam);
Eric Duminil's avatar
Eric Duminil committed
207

Eric Duminil's avatar
Eric Duminil committed
208
    //TODO: Only for ESP32
209
    loraParams.addItem(&loraTimestepParam);
Eric Duminil's avatar
Eric Duminil committed
210
211
212
    loraParams.addItem(&deviceEUIParam);
    loraParams.addItem(&appEUIParam);
    loraParams.addItem(&appKeyParam);
213

Eric Duminil's avatar
Eric Duminil committed
214
    iotWebConf->addParameterGroup(&co2Params);
Eric Duminil's avatar
Eric Duminil committed
215
    iotWebConf->addParameterGroup(&ledParams);
216
    iotWebConf->addParameterGroup(&timeParams);
Eric Duminil's avatar
Eric Duminil committed
217
218
    iotWebConf->addParameterGroup(&csvParams);
    iotWebConf->addParameterGroup(&mqttParams);
219
    iotWebConf->addParameterGroup(&loraParams);
Eric Duminil's avatar
Eric Duminil committed
220
221
222
223
224
225
226
227
228
229
230
231
232

    sensor_console::defineCommand("reset_config", []() {
      Serial.println(F("Resetting config..."));
      iotWebConf->getSystemParameterGroup()->applyDefaultValue();
      iotWebConf->saveConfig();
      Serial.println(F("Done!"));
    }, F("(resets the complete IotWeb config)"));

#if !defined(AMPEL_WIFI)
    iotWebConf->loadConfig();
    return;
#endif

233
234
235
236
237
238
239
240
241
    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 * WIFI_TIMEOUT);

#if defined(ESP8266)
Eric Duminil's avatar
Eric Duminil committed
242
  WiFi.hostname(ampel.sensorId);
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
#elif defined(ESP32)
    WiFi.setHostname(ampel.sensorId);
#endif

    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();

Eric Duminil's avatar
Eric Duminil committed
259
260
    //TODO: Authenticate only if required?
    //TODO: / captive fast return?
261
262
263
264

    http.on("/config", [] {
      iotWebConf->handleConfig();
    });
Eric Duminil's avatar
Eric Duminil committed
265

266
267
268
269
270
    http.onNotFound([]() {
      iotWebConf->handleNotFound();
    });
  }
}
Eric Duminil's avatar
Eric Duminil committed
271

Eric Duminil's avatar
Eric Duminil committed
272
273
274
/***
 * Define all the corresponding config values as reference, so that they can be updated.
 */
Eric Duminil's avatar
Eric Duminil committed
275
namespace config {
Eric Duminil's avatar
Eric Duminil committed
276
  // Sensor
277
278
279
280
281
282
  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.

Eric Duminil's avatar
Eric Duminil committed
283
  // LEDs
284
285
286
  uint8_t &max_brightness = web_config::maxBrightnessParam.value();
  uint8_t &min_brightness = web_config::minBrightnessParam.value();
  uint16_t &led_count = web_config::ledCountParam.value();
Eric Duminil's avatar
Eric Duminil committed
287

Eric Duminil's avatar
Eric Duminil committed
288
289
  // Time server
  char *ntp_server = web_config::ntpServerParam.value();
Eric Duminil's avatar
Eric Duminil committed
290
  int16_t &time_zone = web_config::timeOffsetParam.value();
Eric Duminil's avatar
Eric Duminil committed
291
292
  bool &daylight_saving_time = web_config::dstParam.value();

Eric Duminil's avatar
Eric Duminil committed
293
  // CSV
294
295
296
  bool csv_active() {
    return web_config::csvParams.isActive();
  }
Eric Duminil's avatar
Eric Duminil committed
297
298
  uint16_t &csv_interval = web_config::csvTimestepParam.value();

299
  // MQTT
Eric Duminil's avatar
Eric Duminil committed
300
301
302
  bool mqtt_active() {
    return web_config::mqttParams.isActive();
  }
Eric Duminil's avatar
Eric Duminil committed
303
304
305
306
  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();
Eric Duminil's avatar
Eric Duminil committed
307
  uint16_t &mqtt_sending_interval = web_config::mqttTimestepParam.value();
Eric Duminil's avatar
Eric Duminil committed
308
  bool &mqtt_encryption = web_config::mqttEncryptionParam.value();
Eric Duminil's avatar
Eric Duminil committed
309
  bool &allow_mqtt_commands = web_config::mqttCommandsParam.value();
Eric Duminil's avatar
Eric Duminil committed
310
}