Commit 58daf119 authored by Eric Duminil's avatar Eric Duminil
Browse files

Merge branch 'feature/sensor_console' into develop

parents 77831ea4 8dfc9f69
......@@ -33,11 +33,12 @@
#endif
#include "util.h"
#include "sensor_console.h"
#include "co2_sensor.h"
#include "led_effects.h"
void keepServicesAlive();
void checkFlashButton();
void checkSerialInput();
#endif
......@@ -56,7 +56,6 @@
* and define your credentials and parameters in 'config.h'.
*/
/*****************************************************************
* PreInit *
*****************************************************************/
......@@ -68,7 +67,6 @@ void preinit() {
#endif
}
/*****************************************************************
* Setup *
*****************************************************************/
......@@ -147,6 +145,8 @@ void loop() {
// Short press for night mode, Long press for calibration.
checkFlashButton();
checkSerialInput();
if (sensor::processData()) {
#ifdef AMPEL_CSV
csv_writer::logIfTimeHasCome(sensor::timestamp, sensor::co2, sensor::temperature, sensor::humidity);
......@@ -166,7 +166,13 @@ void loop() {
max_loop_duration = duration;
Serial.print(F("Debug - Max loop duration : "));
Serial.print(max_loop_duration);
Serial.println(" ms.");
Serial.println(F(" ms."));
}
}
void checkSerialInput() {
while (Serial.available() > 0) {
sensor_console::processSerialInput(Serial.read());
}
}
......
......@@ -5,6 +5,8 @@ namespace config {
uint16_t measurement_timestep = MEASUREMENT_TIMESTEP; // [s] Value between 2 and 1800 (range for SCD30 sensor)
const uint16_t altitude_above_sea_level = ALTITUDE_ABOVE_SEA_LEVEL; // [m]
uint16_t co2_calibration_level = ATMOSPHERIC_CO2_CONCENTRATION; // [ppm]
int8_t max_deviation_during_calibration = 30; // [ppm]
int8_t enough_stable_measurements = 60;
#ifdef TEMPERATURE_OFFSET
// Residual heat from CO2 sensor seems to be high enough to change the temperature reading. How much should it be offset?
// NOTE: Sign isn't relevant. The returned temperature will always be shifted down.
......@@ -17,7 +19,7 @@ namespace config {
namespace sensor {
SCD30 scd30;
int16_t co2 = 0;
uint16_t co2 = 0;
float temperature = 0;
float humidity = 0;
String timestamp = "";
......@@ -68,13 +70,25 @@ namespace sensor {
Serial.print(F("Auto-calibration is "));
Serial.println(config::auto_calibrate_sensor ? "ON." : "OFF.");
sensor_console::defineIntCommand("co2", setCO2forDebugging, " 1500 (Sets co2 level, for debugging purposes)");
sensor_console::defineIntCommand("timer", setTimer, " 30 (Sets measurement interval, in s)");
sensor_console::defineCommand("calibrate", startCalibrationProcess, " (Starts calibration process)");
sensor_console::defineIntCommand("calibrate", calibrateSensorToSpecificPPM,
" 600 (Starts calibration process, to given ppm)");
sensor_console::defineIntCommand("calibrate!", calibrateSensorRightNow,
" 600 (Calibrates right now, to given ppm)");
sensor_console::defineCommand("reset", []() {
ESP.restart();
}, " (Restarts the sensor)");
sensor_console::defineCommand("night_mode", led_effects::toggleNightMode, " (Toggles night mode on/off)");
}
//NOTE: should timer deviation be used to adjust measurement_timestep?
void checkTimerDeviation() {
static int32_t previous_measurement_at = 0;
int32_t now = millis();
Serial.print("Measurement time offset : ");
Serial.print(F("Measurement time offset : "));
Serial.print(now - previous_measurement_at - config::measurement_timestep * 1000);
Serial.println(" ms.");
previous_measurement_at = now;
......@@ -82,7 +96,8 @@ namespace sensor {
void countStableMeasurements() {
static int16_t previous_co2 = 0;
if (co2 > (previous_co2 - 30) && co2 < (previous_co2 + 30)) {
if (co2 > (previous_co2 - config::max_deviation_during_calibration)
&& co2 < (previous_co2 + config::max_deviation_during_calibration)) {
stable_measurements++;
Serial.print(F("Number of stable measurements : "));
Serial.println(stable_measurements);
......@@ -177,7 +192,7 @@ namespace sensor {
}
if (should_calibrate) {
if (stable_measurements == 60) {
if (stable_measurements == config::enough_stable_measurements) {
calibrateAndRestart();
}
led_effects::showWaitingLED(waiting_color);
......@@ -187,4 +202,39 @@ namespace sensor {
displayCO2OnLedRing();
return freshData;
}
/*****************************************************************
* Callbacks for sensor commands *
*****************************************************************/
void setCO2forDebugging(int32_t fakeCo2) {
Serial.print(F("DEBUG. Setting CO2 to "));
co2 = fakeCo2;
Serial.println(co2);
}
void setTimer(int32_t timestep) {
if (timestep >= 2 && timestep <= 1800) {
Serial.print(F("Setting Measurement Interval to : "));
Serial.print(timestep);
Serial.println("s.");
sensor::scd30.setMeasurementInterval(timestep);
config::measurement_timestep = timestep;
led_effects::showKITTWheel(color::green, 1);
}
}
void calibrateSensorToSpecificPPM(int32_t calibrationLevel) {
if (calibrationLevel >= 400 && calibrationLevel <= 2000) {
Serial.print(F("Force calibration, at "));
config::co2_calibration_level = calibrationLevel;
Serial.print(config::co2_calibration_level);
Serial.println(" ppm.");
sensor::startCalibrationProcess();
}
}
void calibrateSensorRightNow(int32_t calibrationLevel) {
stable_measurements = config::enough_stable_measurements;
calibrateSensorToSpecificPPM(calibrationLevel);
}
}
......@@ -7,6 +7,7 @@
#include "config.h"
#include "led_effects.h"
#include "util.h"
#include "sensor_console.h"
#include <Wire.h>
namespace config {
......@@ -18,7 +19,7 @@ namespace config {
namespace sensor {
extern SCD30 scd30;
extern int16_t co2;
extern uint16_t co2;
extern float temperature;
extern float humidity;
extern String timestamp;
......@@ -26,5 +27,10 @@ namespace sensor {
void initialize();
bool processData();
void startCalibrationProcess();
void setCO2forDebugging(int32_t fakeCo2);
void setTimer(int32_t timestep);
void calibrateSensorToSpecificPPM(int32_t calibrationLevel);
void calibrateSensorRightNow(int32_t calibrationLevel);
}
#endif
#include "csv_writer.h"
//TODO: Allow CSV download via USB Serial, when requested (e.g. via a Python script)
namespace config {
// Values should be defined in config.h
uint16_t csv_interval = CSV_INTERVAL; // [s]
......@@ -113,9 +111,13 @@ namespace csv_writer {
Serial.println();
// Open dir folder
Serial.println("Filesystem content:");
Serial.println(F("Filesystem content:"));
showFilesystemContent();
Serial.println();
sensor_console::defineIntCommand("csv", setCSVinterval, " 60 (Sets CSV writing interval, in s)");
sensor_console::defineCommand("format_filesystem", formatFilesystem, " (Deletes the whole filesystem)");
sensor_console::defineCommand("show_csv", showCSVContent, " (Displays the complete CSV file on Serial)");
}
File openOrCreate() {
......@@ -161,4 +163,35 @@ namespace csv_writer {
log(timeStamp, co2, temperature, humidity);
}
}
/*****************************************************************
* Callbacks for sensor commands *
*****************************************************************/
void setCSVinterval(int32_t csv_interval) {
config::csv_interval = csv_interval;
Serial.print(F("Setting CSV Interval to : "));
Serial.print(config::csv_interval);
Serial.println("s.");
led_effects::showKITTWheel(color::green, 1);
}
void showCSVContent() {
Serial.print(F("### "));
Serial.print(filename);
Serial.println(F(" ###"));
File csv_file;
if (FS_LIB.exists(filename)) {
csv_file = FS_LIB.open(filename, "r");
while (csv_file.available()) {
Serial.write(csv_file.read());
}
csv_file.close();
}
Serial.println(F("################"));
}
void formatFilesystem() {
FS_LIB.format();
led_effects::showKITTWheel(color::blue, 2);
}
}
......@@ -14,6 +14,7 @@
#include "config.h"
#include "util.h"
#include "led_effects.h"
#include "sensor_console.h"
namespace config {
extern uint16_t csv_interval; // [s]
......@@ -24,6 +25,10 @@ namespace csv_writer {
void logIfTimeHasCome(const String &timeStamp, const int16_t &co2, const float &temperature, const float &humidity);
int getAvailableSpace();
extern const String filename;
void setCSVinterval(int32_t csv_interval);
void showCSVContent();
void formatFilesystem();
}
#endif
......@@ -47,11 +47,12 @@ namespace lorawan {
LMIC_reset();
// Join, but don't send anything yet.
LMIC_startJoining();
sensor_console::defineIntCommand("lora", setLoRaInterval, " 300 (Sets LoRaWAN sending interval, in s)");
}
// Checks if OTAA is connected, or if payload should be sent.
// NOTE: while a transaction is in process (i.e. until the TXcomplete event has been received, no blocking code (e.g. delay loops etc.) are allowed, otherwise the LMIC/OS code might miss the event.
// If this rule is not followed, a typical symptom is that the first send is ok and all following ones end with the TX not complete failure.
// If this rule is not followed, a typical symptom is that the first send is ok and all following ones end with the 'TX not complete' failure.
void process() {
os_runloop_once();
}
......@@ -189,6 +190,17 @@ namespace lorawan {
preparePayload(co2, temperature, humidity);
}
}
/*****************************************************************
* Callbacks for sensor commands *
*****************************************************************/
void setLoRaInterval(int32_t sending_interval) {
config::lorawan_sending_interval = sending_interval;
Serial.print(F("Setting LoRa sending interval to : "));
Serial.print(config::lorawan_sending_interval);
Serial.println("s.");
led_effects::showKITTWheel(color::green, 1);
}
}
void onEvent(ev_t ev) {
......
......@@ -13,7 +13,7 @@
#include <SPI.h>
#include "led_effects.h"
#include "sensor_console.h"
#include "util.h"
namespace config {
......@@ -43,6 +43,8 @@ namespace lorawan {
void initialize();
void process();
void preparePayloadIfTimeHasCome(const int16_t &co2, const float &temp, const float &hum);
void setLoRaInterval(int32_t sending_interval);
}
#endif
......
......@@ -2,7 +2,7 @@
namespace config {
// Values should be defined in config.h
uint16_t sending_interval = MQTT_SENDING_INTERVAL; // [s]
uint16_t mqtt_sending_interval = MQTT_SENDING_INTERVAL; // [s]
//INFO: Listen to every CO2 sensor which is connected to the server:
// mosquitto_sub -h MQTT_SERVER -t 'CO2sensors/#' -p 443 --capath /etc/ssl/certs/ -u "MQTT_USER" -P "MQTT_PASSWORD" -v
const char *mqtt_server = MQTT_SERVER;
......@@ -35,6 +35,10 @@ namespace mqtt {
#endif
// mqttClient.setSocketTimeout(config::mqtt_timeout); //NOTE: somehow doesn't seem to have any effect on connect()
mqttClient.setServer(config::mqtt_server, config::mqtt_port);
sensor_console::defineIntCommand("mqtt", setMQTTinterval, " 60 (Sets MQTT sending interval, in s)");
sensor_console::defineCommand("local_ip", sendInfoAboutLocalNetwork,
" (Sends local IP and SSID via MQTT. Can be useful to find sensor)");
}
void publish(const String &timestamp, int16_t co2, float temperature, float humidity) {
......@@ -55,70 +59,6 @@ namespace mqtt {
}
}
//TODO: Move all those setters to a separate class, which could be used by Serial/MQTT/WebServer
void setTimer(String messageString) {
messageString.replace("timer ", "");
int timestep = messageString.toInt();
if (timestep >= 2 && timestep <= 1800) {
Serial.print(F("Setting Measurement Interval to : "));
Serial.print(timestep);
Serial.println("s.");
sensor::scd30.setMeasurementInterval(messageString.toInt());
config::measurement_timestep = messageString.toInt();
led_effects::showKITTWheel(color::green, 1);
}
}
void setMQTTinterval(String messageString) {
messageString.replace("mqtt ", "");
config::sending_interval = messageString.toInt();
Serial.print(F("Setting Sending Interval to : "));
Serial.print(config::sending_interval);
Serial.println("s.");
led_effects::showKITTWheel(color::green, 1);
}
#ifdef AMPEL_CSV
void setCSVinterval(String messageString) {
messageString.replace("csv ", "");
config::csv_interval = messageString.toInt();
Serial.print(F("Setting CSV Interval to : "));
Serial.print(config::csv_interval);
Serial.println("s.");
led_effects::showKITTWheel(color::green, 1);
}
#endif
void calibrateSensorToSpecificPPM(String messageString) {
messageString.replace("calibrate ", "");
long int calibrationLevel = messageString.toInt();
if (calibrationLevel >= 400 && calibrationLevel <= 2000) {
Serial.print(F("Force calibration, at "));
config::co2_calibration_level = messageString.toInt();
Serial.print(config::co2_calibration_level);
Serial.println(" ppm.");
sensor::startCalibrationProcess();
}
}
void setCO2forDebugging(String messageString) {
Serial.print(F("DEBUG. Setting CO2 to "));
messageString.replace("co2 ", "");
sensor::co2 = messageString.toInt();
Serial.println(sensor::co2);
}
void sendInfoAboutLocalNetwork() {
char info_topic[60]; // Should be enough for "CO2sensors/ESPd03cc5/info"
snprintf(info_topic, sizeof(info_topic), "%s/info", publish_topic.c_str());
char payload[75]; // Should be enough for info json...
const char *json_info_format = PSTR("{\"local_ip\":\"%s\", \"ssid\":\"%s\"}");
snprintf(payload, sizeof(payload), json_info_format, WiFi.localIP().toString().c_str(), WiFi.SSID().c_str());
mqttClient.publish(info_topic, payload);
}
/**
* Allows sensor to be controlled by commands over MQTT
*
......@@ -133,48 +73,13 @@ namespace mqtt {
}
led_effects::onBoardLEDOn();
Serial.print(F("Message arrived on topic: "));
Serial.print(sub_topic);
Serial.print(F(". Message: '"));
String messageString;
Serial.println(sub_topic);
char command[length + 1];
for (unsigned int i = 0; i < length; i++) {
Serial.print((char) message[i]);
messageString += (char) message[i];
command[i] = message[i];
}
Serial.println("'.");
//TODO: Move this logic to a separate class, which could be used by Serial/MQTT/WebServer
if (messageString.startsWith("co2 ")) {
setCO2forDebugging(messageString);
} else if (messageString.startsWith("timer ")) {
setTimer(messageString);
} else if (messageString == "calibrate") {
sensor::startCalibrationProcess();
} else if (messageString.startsWith("calibrate ")) {
calibrateSensorToSpecificPPM(messageString);
} else if (messageString.startsWith("mqtt ")) {
setMQTTinterval(messageString);
} else if (messageString == "publish") {
Serial.println(F("Forcing MQTT publish now."));
publish(sensor::timestamp, sensor::co2, sensor::temperature, sensor::humidity);
#ifdef AMPEL_CSV
} else if (messageString.startsWith("csv ")) {
setCSVinterval(messageString);
} else if (messageString == "format_filesystem") {
FS_LIB.format();
led_effects::showKITTWheel(color::blue, 2);
#endif
} else if (messageString == "night_mode") {
led_effects::toggleNightMode();
} else if (messageString == "local_ip") {
sendInfoAboutLocalNetwork();
} else if (messageString == "reset") {
ESP.restart(); // softer than ESP.reset()
} else {
led_effects::showKITTWheel(color::red, 1);
Serial.println(F("Message not supported. Doing nothing."));
}
delay(50);
command[length] = 0;
sensor_console::runCommand(command);
led_effects::onBoardLEDOff();
}
......@@ -218,7 +123,7 @@ namespace mqtt {
void publishIfTimeHasCome(const String &timeStamp, const int16_t &co2, const float &temp, const float &hum) {
// Send message via MQTT according to sending interval
unsigned long now = seconds();
if (now - last_sent_at > config::sending_interval) {
if (now - last_sent_at > config::mqtt_sending_interval) {
last_sent_at = now;
publish(timeStamp, co2, temp, hum);
}
......@@ -232,4 +137,28 @@ namespace mqtt {
mqttClient.loop();
}
/*****************************************************************
* Callbacks for sensor commands *
*****************************************************************/
void setMQTTinterval(int32_t sending_interval) {
config::mqtt_sending_interval = sending_interval;
Serial.print(F("Setting MQTT sending interval to : "));
Serial.print(config::mqtt_sending_interval);
Serial.println("s.");
led_effects::showKITTWheel(color::green, 1);
}
// It can be hard to find the local IP of a sensor if it isn't connected to Serial port, and if mDNS is disabled.
// If the sensor can be reach by MQTT, it can answer with info about local_ip and ssid.
// The sensor will send the info to "CO2sensors/ESP123456/info".
void sendInfoAboutLocalNetwork() {
char info_topic[60]; // Should be enough for "CO2sensors/ESP123456/info"
snprintf(info_topic, sizeof(info_topic), "%s/info", publish_topic.c_str());
char payload[75]; // Should be enough for info json...
const char *json_info_format = PSTR("{\"local_ip\":\"%s\", \"ssid\":\"%s\"}");
snprintf(payload, sizeof(payload), json_info_format, WiFi.localIP().toString().c_str(), WiFi.SSID().c_str());
mqttClient.publish(info_topic, payload);
}
}
......@@ -4,14 +4,11 @@
#include <Arduino.h>
#include "config.h"
#include "led_effects.h"
#ifdef AMPEL_CSV
# include "csv_writer.h"
#endif
#include "co2_sensor.h"
#include "sensor_console.h"
#include "src/lib/PubSubClient/src/PubSubClient.h"
#include "wifi_util.h"
namespace config {
extern uint16_t sending_interval; // [s]
extern uint16_t mqtt_sending_interval; // [s]
}
namespace mqtt {
extern String last_successful_publish;
......@@ -19,5 +16,8 @@ namespace mqtt {
void initialize(String &topic);
void keepConnection();
void publishIfTimeHasCome(const String &timeStamp, const int16_t &co2, const float &temp, const float &hum);
void setMQTTinterval(int32_t sending_interval);
void sendInfoAboutLocalNetwork();
}
#endif
#include "sensor_console.h"
namespace sensor_console {
const uint8_t MAX_COMMANDS = 20;
const uint8_t MAX_COMMAND_SIZE = 30;
uint8_t commands_count = 0;
struct Command {
const char *name;
union {
void (*intFunction)(int32_t);
void (*voidFunction)(void);
};
const char *doc;
bool has_parameter;
};
Command commands[MAX_COMMANDS];
//NOTE: Probably possible to DRY (with templates?)
void defineCommand(const char *name, void (*function)(void), const char *doc) {
if (commands_count < MAX_COMMANDS) {
commands[commands_count].name = name;
commands[commands_count].voidFunction = function;
commands[commands_count].doc = doc;
commands[commands_count].has_parameter = false;
commands_count++;
} else {
Serial.println(F("Too many commands have been defined."));
}
}
void defineIntCommand(const char *name, void (*function)(int32_t), const char *doc) {
if (commands_count < MAX_COMMANDS) {
commands[commands_count].name = name;
commands[commands_count].intFunction = function;
commands[commands_count].doc = doc;
commands[commands_count].has_parameter = true;
commands_count++;
} else {
Serial.println(F("Too many commands have been defined."));
}
}
/*
* Tries to split a string command (e.g. 'mqtt 60' or 'show_csv') into a function_name and an argument.
* Returns 0 if both are found, 1 if there is a problem and 2 if no argument is found.
*/
uint8_t parseCommand(const char *command, char *function_name, int32_t &argument) {
char split_command[MAX_COMMAND_SIZE];
strlcpy(split_command, command, MAX_COMMAND_SIZE);
Serial.print(F("Received : '"));
Serial.print(command);
Serial.println("'");
char *arg;
char *part1;
part1 = strtok(split_command, " ");
if (!part1) {
// Empty string
return 1;
}
strlcpy(function_name, part1, MAX_COMMAND_SIZE);
arg = strtok(NULL, " ");
uint8_t code = 0;
if (arg) {
char *end;
argument = strtol(arg, &end, 10);
if (*end) {
// Second argument isn't a number
code = 2;
}
} else {
// No argument
code = 2;
}
return code;
}
/*
* Saves bytes from Serial.read() until enter is pressed, and tries to run the corresponding command.
* http://www.gammon.com.au/serial
*/
void processSerialInput(const byte input_byte) {
static char input_line[MAX_COMMAND_SIZE];
static unsigned int input_pos = 0;
switch (input_byte) {
case '\n': // end of text
Serial.println();
input_line[input_pos] = 0;
runCommand(input_line);
input_pos = 0;
break;
case '\r': // discard carriage return
break;
case '\b': // backspace
if (input_pos > 0) {
input_pos--;
Serial.print(F("\b \b"));
}
break;
default:
if (input_pos == 0) {
Serial.print(F("> "));
}
// keep adding if not full ... allow for terminating null byte
if (input_pos < (MAX_COMMAND_SIZE - 1)) {
input_line[input_pos++] = input_byte;
Serial.print((char) input_byte);
}
break;
}
}
int compareName(const void *s1, const void *s2) {
struct Command *c1 = (struct Command*) s1;
struct Command *c2 = (struct Command*) s2;
return strcmp(c1->name, c2->name);
}
void listAvailableCommands() {
qsort(commands, commands_count, sizeof(commands[0]), compareName);
for (uint8_t i = 0; i < commands_count; i++) {
Serial.print(" ");
Serial.print(commands[i].name);
Serial.print(commands[i].doc);
Serial.println(".");
}
led_effects::showKITTWheel(color::red, 1);
}
/*
* Tries to find the corresponding callback for a given command. Name and number of argument should fit.
*/
void runCommand(const char *command) {
char function_name[MAX_COMMAND_SIZE];
int32_t argument = 0;
bool has_argument;
has_argument = (parseCommand(command, function_name, argument) == 0);
for (uint8_t i = 0; i < commands_count; i++) {
if (!strcmp(function_name, commands[i].name) && has_argument == commands[i].has_parameter) {
Serial.print(F("Calling : "));
Serial.print(function_name);
if (has_argument) {
Serial.print("(");
Serial.print(argument);
Serial.println(")");
commands[i].intFunction(argument);
} else {
Serial.println("()");
commands[i].voidFunction();
}
return;
}
}
Serial.println(F("Message not supported. Available commands :"));
listAvailableCommands();
}
}
#include <Arduino.h>
#include "led_effects.h"
/** Other scripts can use this namespace, in order to define commands, via callbacks.
* Those callbacks can then be used to send commands to the sensor (reset, calibrate, night mode, ...)
* The callbacks can either have no parameter, or one int32_t parameter.
*/
namespace sensor_console {
void processSerialInput(const byte in_byte);
void runCommand(const char *command);
void defineIntCommand(const char *command, void (*function)(int32_t), const char *doc);
void defineCommand(const char *command, void (*function)(void), const char *doc);
}
......@@ -22,6 +22,7 @@ namespace web_server {
const char *script_template;
void handleWebServerRoot();
void handlePageNotFound();
void handleWebServerCommand();
#ifdef AMPEL_CSV
void handleDeleteCSV();
......@@ -72,16 +73,15 @@ namespace web_server {
// Show a colored dot on the webpage, with a similar color than on LED Ring.
"hue=(1-(Math.min(Math.max(parseInt(document.title),500),1600)-500)/1100)*120;\n"
"document.getElementById('led').style.color=['hsl(',hue,',100%%,50%%)'].join('');\n"
"</script>\n");
body_template =
PSTR("<div class='pure-g'>\n"
"<div class='pure-u-1' id='graph'></div>\n" // Graph placeholder
"</script>\n"
"<div class='pure-g'>\n"
"<div class='pure-u-1' id='graph'></div>\n"// Graph placeholder
"</div>\n"
"<div class='pure-g'>\n"
//Sensor table
"<table id='table' class='pure-table-striped pure-u-1 pure-u-md-1-2'>\n"
"<tr><th colspan='2'>%s</th></tr>\n"
"<table id='table' class='pure-table-striped pure-u-1 pure-u-md-1-2'>\n");
body_template =
PSTR("<tr><th colspan='2'>%s</th></tr>\n"
"<tr><td>CO<sub>2</sub> concentration</td><td>%5d ppm</td></tr>\n"
"<tr><td>Temperature</td><td>%.1f&#8451;</td></tr>\n"
"<tr><td>Humidity</td><td>%.1f%%</td></tr>\n"
......@@ -108,6 +108,7 @@ namespace web_server {
#endif
"<tr><th colspan='2'>Sensor</th></tr>\n"
"<tr><td>Temperature offset</td><td>%.1fK</td></tr>\n" //TODO: Read it from sensor?
"<tr><td>Auto-calibration?</td><td>%s</td></tr>\n"
"<tr><td>Local address</td><td><a href='http://%s.local/'>%s.local</a></td></tr>\n"
"<tr><td>Local IP</td><td><a href='http://%s'>%s</a></td></tr>\n"
"<tr><td>Free heap space</td><td>%6d bytes</td></tr>\n"
......@@ -116,6 +117,7 @@ namespace web_server {
"<tr><td>Uptime</td><td>%2d d %4d h %02d min %02d s</td></tr>\n"
"</table>\n"
"<div id='log' class='pure-u-1 pure-u-md-1-2'></div>\n"
"<form action='/command'><input type='text' id='send' name='send'><input type='submit' value='Send'></form>\n"
#ifdef AMPEL_CSV
"<form action='/delete_csv' method='POST' onsubmit=\"return confirm('Are you really sure you want to delete all data?') && (document.body.style.cursor = 'wait');\">"
"<input type='submit' value='Delete CSV'/>"
......@@ -173,6 +175,7 @@ namespace web_server {
// Web-server
http.on("/", handleWebServerRoot);
http.on("/command", handleWebServerCommand);
#ifdef AMPEL_CSV
http.on("/" + csv_writer::filename, handleWebServerCSV);
http.on("/delete_csv", HTTP_POST, handleDeleteCSV);
......@@ -207,9 +210,8 @@ namespace web_server {
//NOTE: Splitting in multiple parts in order to use less RAM
char content[2000]; // Update if needed
// Header size : 1611 - Body size : 1800 - Script size : 1920
// INFO - Header size : 1767 - Body size : 1812 - Script size : 1909
// Header
snprintf_P(content, sizeof(content), header_template, sensor::co2, SENSOR_ID.c_str(),
WiFi.localIP().toString().c_str()
#ifdef AMPEL_CSV
......@@ -217,6 +219,8 @@ namespace web_server {
#endif
);
Serial.print(F("INFO - Header size : "));
Serial.print(strlen(content));
http.setContentLength(CONTENT_LENGTH_UNKNOWN);
http.send_P(200, PSTR("text/html"), content);
......@@ -227,16 +231,19 @@ namespace web_server {
csv_writer::last_successful_write.c_str(), config::csv_interval, csv_writer::getAvailableSpace() / 1024,
#endif
#ifdef AMPEL_MQTT
mqtt::connected ? "Yes" : "No", mqtt::last_successful_publish.c_str(), config::sending_interval,
mqtt::connected ? "Yes" : "No", mqtt::last_successful_publish.c_str(), config::mqtt_sending_interval,
#endif
#if defined(AMPEL_LORAWAN) && defined(ESP32)
lorawan::connected ? "Yes" : "No", LMIC_FREQUENCY_PLAN, lorawan::last_transmission.c_str(),
config::lorawan_sending_interval,
#endif
config::temperature_offset, SENSOR_ID.c_str(), SENSOR_ID.c_str(), WiFi.localIP().toString().c_str(),
WiFi.localIP().toString().c_str(), get_free_heap_size(), max_loop_duration, BOARD, dd, hh, mm, ss);
config::temperature_offset, config::auto_calibrate_sensor ? "Yes" : "No", SENSOR_ID.c_str(), SENSOR_ID.c_str(),
WiFi.localIP().toString().c_str(), WiFi.localIP().toString().c_str(), get_free_heap_size(), max_loop_duration,
BOARD, dd, hh, mm, ss);
Serial.print(F(" - Body size : "));
http.sendContent(content);
Serial.print(strlen(content));
// Script
snprintf_P(content, sizeof(content), script_template
......@@ -245,6 +252,8 @@ namespace web_server {
#endif
);
Serial.print(F(" - Script size : "));
Serial.println(strlen(content));
http.sendContent(content);
}
......@@ -275,6 +284,15 @@ namespace web_server {
}
#endif
void handleWebServerCommand() {
if (!shouldBeAllowed()) {
return http.requestAuthentication(DIGEST_AUTH);
}
http.sendHeader("Location", "/");
http.send(303);
sensor_console::runCommand(http.arg("send").c_str());
}
void handlePageNotFound() {
http.send(404, F("text/plain"), F("404: Not found"));
}
......
#ifndef WEB_SERVER_H_
#define WEB_SERVER_H_
#if defined(ESP8266)
# include <ESP8266WebServer.h>
#elif defined(ESP32)
......@@ -9,6 +10,7 @@
#include "config.h"
#include "util.h"
#include "co2_sensor.h"
#include "sensor_console.h"
#ifdef AMPEL_CSV
# include "csv_writer.h"
#endif
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment