Commit 06467a3d authored by Eric Duminil's avatar Eric Duminil
Browse files

Merge branch 'refactor/console' into develop

parents ab3e5689 7b2385e5
Pipeline #3589 passed with stage
in 1 minute and 48 seconds
...@@ -103,15 +103,14 @@ namespace sensor { ...@@ -103,15 +103,14 @@ namespace sensor {
Serial.println(F(" s during acclimatization.")); Serial.println(F(" s during acclimatization."));
scd30.setMeasurementInterval(config::measurement_timestep_bootup); // [s] scd30.setMeasurementInterval(config::measurement_timestep_bootup); // [s]
sensor_console::defineIntCommand("co2", setCO2forDebugging, F(" 1500 (Sets co2 level, for debugging purposes)")); sensor_console::defineIntCommand("co2", setCO2forDebugging, F("1500 (Sets co2 level, for debugging purposes)"));
sensor_console::defineIntCommand("timer", setTimer, F(" 30 (Sets measurement interval, in s)")); sensor_console::defineIntCommand("timer", setTimer, F("30 (Sets measurement interval, in s)"));
sensor_console::defineCommand("calibrate", startCalibrationProcess, F(" (Starts calibration process)")); sensor_console::defineCommand("calibrate", startCalibrationProcess, F("(Starts calibration process)"));
sensor_console::defineIntCommand("calibrate", calibrateSensorToSpecificPPM, sensor_console::defineIntCommand("calibrate", calibrateSensorToSpecificPPM,
F(" 600 (Starts calibration process, to given ppm)")); F("600 (Starts calibration process, to given ppm)"));
sensor_console::defineIntCommand("calibrate!", calibrateSensorRightNow, sensor_console::defineIntCommand("calibrate!", calibrateSensorRightNow,
F(" 600 (Calibrates right now, to given ppm)")); F("600 (Calibrates right now, to given ppm)"));
sensor_console::defineIntCommand("auto_calibrate", setAutoCalibration, sensor_console::defineIntCommand("auto_calibrate", setAutoCalibration, F("0/1 (Disables/enables autocalibration)"));
F(" 0/1 (Disables/enables autocalibration)"));
} }
bool hasSensorSettled() { bool hasSensorSettled() {
......
...@@ -118,9 +118,9 @@ namespace csv_writer { ...@@ -118,9 +118,9 @@ namespace csv_writer {
showFilesystemContent(); showFilesystemContent();
Serial.println(); Serial.println();
sensor_console::defineIntCommand("csv", setCSVinterval, F(" 60 (Sets CSV writing interval, in s)")); sensor_console::defineIntCommand("csv", setCSVinterval, F("60 (Sets CSV writing interval, in s)"));
sensor_console::defineCommand("format_filesystem", formatFilesystem, F(" (Deletes the whole filesystem)")); sensor_console::defineCommand("format_filesystem", formatFilesystem, F("(Deletes the whole filesystem)"));
sensor_console::defineCommand("show_csv", showCSVContent, F(" (Displays the complete CSV file on Serial)")); sensor_console::defineCommand("show_csv", showCSVContent, F("(Displays the complete CSV file on Serial)"));
} }
File openOrCreate() { File openOrCreate() {
......
...@@ -70,11 +70,19 @@ namespace led_effects { ...@@ -70,11 +70,19 @@ namespace led_effects {
onBoardLEDOff(); onBoardLEDOff();
} }
void showColor(int32_t color) {
config::night_mode = true; // In order to avoid overwriting the desired color next time CO2 is displayed
pixels.setBrightness(255);
pixels.fill(color);
pixels.show();
}
void setupRing() { void setupRing() {
pixels.begin(); pixels.begin();
pixels.setBrightness(config::max_brightness); pixels.setBrightness(config::max_brightness);
LEDsOff(); LEDsOff();
sensor_console::defineCommand("night_mode", toggleNightMode, F(" (Toggles night mode on/off)")); sensor_console::defineCommand("night_mode", toggleNightMode, F("(Toggles night mode on/off)"));
sensor_console::defineIntCommand("color", showColor, F("0xFF0015 (Shows color, specified as RGB, for debugging)"));
} }
void toggleNightMode() { void toggleNightMode() {
......
...@@ -47,7 +47,7 @@ namespace lorawan { ...@@ -47,7 +47,7 @@ namespace lorawan {
LMIC_reset(); LMIC_reset();
// Join, but don't send anything yet. // Join, but don't send anything yet.
LMIC_startJoining(); LMIC_startJoining();
sensor_console::defineIntCommand("lora", setLoRaInterval, F(" 300 (Sets LoRaWAN sending interval, in s)")); sensor_console::defineIntCommand("lora", setLoRaInterval, F("300 (Sets LoRaWAN sending interval, in s)"));
} }
// Checks if OTAA is connected, or if payload should be sent. // Checks if OTAA is connected, or if payload should be sent.
...@@ -96,7 +96,7 @@ namespace lorawan { ...@@ -96,7 +96,7 @@ namespace lorawan {
printHex2(artKey[i]); printHex2(artKey[i]);
} }
Serial.println(); Serial.println();
Serial.print(" NwkSKey: "); Serial.print(F(" NwkSKey: "));
for (size_t i = 0; i < sizeof(nwkKey); ++i) { for (size_t i = 0; i < sizeof(nwkKey); ++i) {
if (i != 0) if (i != 0)
Serial.print("-"); Serial.print("-");
......
...@@ -36,9 +36,9 @@ namespace mqtt { ...@@ -36,9 +36,9 @@ namespace mqtt {
// mqttClient.setSocketTimeout(config::mqtt_timeout); //NOTE: somehow doesn't seem to have any effect on connect() // mqttClient.setSocketTimeout(config::mqtt_timeout); //NOTE: somehow doesn't seem to have any effect on connect()
mqttClient.setServer(config::mqtt_server, config::mqtt_port); mqttClient.setServer(config::mqtt_server, config::mqtt_port);
sensor_console::defineIntCommand("mqtt", setMQTTinterval, F(" 60 (Sets MQTT sending interval, in s)")); sensor_console::defineIntCommand("mqtt", setMQTTinterval, F("60 (Sets MQTT sending interval, in s)"));
sensor_console::defineCommand("send_local_ip", sendInfoAboutLocalNetwork, sensor_console::defineCommand("send_local_ip", sendInfoAboutLocalNetwork,
F(" (Sends local IP and SSID via MQTT. Can be useful to find sensor)")); F("(Sends local IP and SSID via MQTT. Can be useful to find sensor)"));
} }
void publish(const char *timestamp, int16_t co2, float temperature, float humidity) { void publish(const char *timestamp, int16_t co2, float temperature, float humidity) {
...@@ -79,7 +79,7 @@ namespace mqtt { ...@@ -79,7 +79,7 @@ namespace mqtt {
command[i] = message[i]; command[i] = message[i];
} }
command[length] = 0; command[length] = 0;
sensor_console::runCommand(command); sensor_console::execute(command);
led_effects::onBoardLEDOff(); led_effects::onBoardLEDOff();
} }
......
...@@ -6,75 +6,112 @@ namespace sensor_console { ...@@ -6,75 +6,112 @@ namespace sensor_console {
uint8_t commands_count = 0; uint8_t commands_count = 0;
enum input_type {
NONE,
INT32,
STRING
};
struct Command { struct Command {
const char *name; const char *name;
union { union {
void (*voidFunction)();
void (*intFunction)(int32_t); void (*intFunction)(int32_t);
void (*voidFunction)(void); void (*strFunction)(char*);
}; };
const char *doc; const char *doc;
bool has_parameter; input_type parameter_type;
};
struct CommandLine {
char function_name[MAX_COMMAND_SIZE];
input_type argument_type;
int32_t int_argument;
char str_argument[MAX_COMMAND_SIZE];
}; };
Command commands[MAX_COMMANDS]; Command commands[MAX_COMMANDS];
//NOTE: Probably possible to DRY (with templates?) bool addCommand(const char *name, const __FlashStringHelper *doc_fstring) {
void defineCommand(const char *name, void (*function)(void), const __FlashStringHelper *doc_fstring) {
const char *doc = (const char*) doc_fstring;
if (commands_count < MAX_COMMANDS) { if (commands_count < MAX_COMMANDS) {
commands[commands_count].name = name; commands[commands_count].name = name;
commands[commands_count].voidFunction = function; commands[commands_count].doc = (const char*) doc_fstring;
commands[commands_count].doc = doc; return true;
commands[commands_count].has_parameter = false;
commands_count++;
} else { } else {
Serial.println(F("Too many commands have been defined.")); Serial.println(F("Too many commands have been defined."));
return false;
}
}
void defineCommand(const char *name, void (*function)(), const __FlashStringHelper *doc_fstring) {
if (addCommand(name, doc_fstring)) {
commands[commands_count].voidFunction = function;
commands[commands_count++].parameter_type = NONE;
} }
} }
void defineIntCommand(const char *name, void (*function)(int32_t), const __FlashStringHelper *doc_fstring) { void defineIntCommand(const char *name, void (*function)(int32_t), const __FlashStringHelper *doc_fstring) {
const char *doc = (const char*) doc_fstring; if (addCommand(name, doc_fstring)) {
if (commands_count < MAX_COMMANDS) {
commands[commands_count].name = name;
commands[commands_count].intFunction = function; commands[commands_count].intFunction = function;
commands[commands_count].doc = doc; commands[commands_count++].parameter_type = INT32;
commands[commands_count].has_parameter = true; }
commands_count++; }
} else {
Serial.println(F("Too many commands have been defined.")); void defineStringCommand(const char *name, void (*function)(char*), const __FlashStringHelper *doc_fstring) {
if (addCommand(name, doc_fstring)) {
commands[commands_count].strFunction = function;
commands[commands_count++].parameter_type = STRING;
} }
} }
/* /*
* Tries to split a string command (e.g. 'mqtt 60' or 'show_csv') into a function_name and an argument. * Tries to split a string command (e.g. 'mqtt 60' or 'show_csv') into
* Returns 0 if both are found, 1 if there is a problem and 2 if no argument is found. * a CommandLine struct (function_name, argument_type and argument)
*/ */
uint8_t parseCommand(const char *command, char *function_name, int32_t &argument) { void parseCommand(const char *command, CommandLine &command_line) {
char split_command[MAX_COMMAND_SIZE]; if (strlen(command) == 0) {
strlcpy(split_command, command, MAX_COMMAND_SIZE);
char *arg;
char *part1;
part1 = strtok(split_command, " ");
if (!part1) {
Serial.println(F("Received empty command")); Serial.println(F("Received empty command"));
// Empty string command_line.argument_type = NONE;
return 1; return;
} }
strlcpy(function_name, part1, MAX_COMMAND_SIZE);
arg = strtok(NULL, " "); char *first_space;
uint8_t code = 0; first_space = strchr(command, ' ');
if (arg) {
char *end; if (first_space == NULL) {
argument = strtol(arg, &end, 10); command_line.argument_type = NONE;
if (*end) { strlcpy(command_line.function_name, command, MAX_COMMAND_SIZE);
// Second argument isn't a number return;
code = 2; }
}
strlcpy(command_line.function_name, command, first_space - command + 1);
strlcpy(command_line.str_argument, first_space + 1, MAX_COMMAND_SIZE - (first_space - command) - 1);
char *end;
command_line.int_argument = strtol(command_line.str_argument, &end, 0); // Accepts 123 or 0xFF00FF
if (*end) {
command_line.argument_type = STRING;
} else { } else {
// No argument command_line.argument_type = INT32;
code = 2; }
}
int compareCommandNames(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]), compareCommandNames);
for (uint8_t i = 0; i < commands_count; i++) {
Serial.print(F(" "));
Serial.print(commands[i].name);
Serial.print(F(" "));
Serial.print(commands[i].doc);
Serial.println(F("."));
} }
return code;
} }
/* /*
...@@ -88,7 +125,7 @@ namespace sensor_console { ...@@ -88,7 +125,7 @@ namespace sensor_console {
case '\n': // end of text case '\n': // end of text
Serial.println(); Serial.println();
input_line[input_pos] = 0; input_line[input_pos] = 0;
runCommand(input_line); execute(input_line);
input_pos = 0; input_pos = 0;
break; break;
case '\r': // discard carriage return case '\r': // discard carriage return
...@@ -112,50 +149,40 @@ namespace sensor_console { ...@@ -112,50 +149,40 @@ namespace sensor_console {
} }
} }
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(".");
}
}
/* /*
* Tries to find the corresponding callback for a given command. Name and number of argument should fit. * Tries to find the corresponding callback for a given command. Name and parameter type should fit.
*/ */
void runCommand(const char *command) { void execute(const char *command_str) {
char function_name[MAX_COMMAND_SIZE]; CommandLine input;
int32_t argument = 0; parseCommand(command_str, input);
bool has_argument;
has_argument = (parseCommand(command, function_name, argument) == 0);
for (uint8_t i = 0; i < commands_count; i++) { for (uint8_t i = 0; i < commands_count; i++) {
if (!strcmp(function_name, commands[i].name) && has_argument == commands[i].has_parameter) { if (!strcmp(input.function_name, commands[i].name) && input.argument_type == commands[i].parameter_type) {
Serial.print(F("Calling : ")); Serial.print(F("Calling : "));
Serial.print(function_name); Serial.print(input.function_name);
if (has_argument) { switch (input.argument_type) {
Serial.print(F("(")); case NONE:
Serial.print(argument);
Serial.println(F(")"));
commands[i].intFunction(argument);
} else {
Serial.println(F("()")); Serial.println(F("()"));
commands[i].voidFunction(); commands[i].voidFunction();
return;
case INT32:
Serial.print(F("("));
Serial.print(input.int_argument);
Serial.println(F(")"));
commands[i].intFunction(input.int_argument);
return;
case STRING:
Serial.print(F("('"));
Serial.print(input.str_argument);
Serial.println(F("')"));
commands[i].strFunction(input.str_argument);
return;
} }
return;
} }
} }
Serial.print(F("'")); Serial.print(F("'"));
Serial.print(command); Serial.print(command_str);
Serial.println(F("' not supported. Available commands :")); Serial.println(F("' not supported. Available commands :"));
listAvailableCommands(); listAvailableCommands();
} }
} }
...@@ -8,11 +8,13 @@ ...@@ -8,11 +8,13 @@
*/ */
namespace sensor_console { namespace sensor_console {
void defineCommand(const char *command, void (*function)(void), const __FlashStringHelper *ifsh); void defineCommand(const char *name, void (*function)(), const __FlashStringHelper *doc_fstring);
void defineIntCommand(const char *command, void (*function)(int32_t), const __FlashStringHelper *ifsh); void defineIntCommand(const char *name, void (*function)(int32_t), const __FlashStringHelper *doc_fstring);
void defineStringCommand(const char *name, void (*function)(char*), const __FlashStringHelper *doc_fstring);
void processSerialInput(const byte in_byte); void processSerialInput(const byte in_byte);
void runCommand(const char *command);
void execute(const char *command_line);
} }
#endif #endif
...@@ -74,11 +74,11 @@ char* getSensorId() { ...@@ -74,11 +74,11 @@ char* getSensorId() {
Ampel::Ampel() : Ampel::Ampel() :
board(current_board), sensorId(getSensorId()), max_loop_duration(0) { board(current_board), sensorId(getSensorId()), max_loop_duration(0) {
sensor_console::defineIntCommand("set_time", ntp::setLocalTime, F(" 1618829570 (Sets time to the given UNIX time)")); sensor_console::defineIntCommand("set_time", ntp::setLocalTime, F("1618829570 (Sets time to the given UNIX time)"));
sensor_console::defineCommand("free", Ampel::showFreeSpace, F(" (Displays available heap space)")); sensor_console::defineCommand("free", Ampel::showFreeSpace, F("(Displays available heap space)"));
sensor_console::defineCommand("reset", []() { sensor_console::defineCommand("reset", []() {
ESP.restart(); ESP.restart();
}, F(" (Restarts the sensor)")); }, F("(Restarts the sensor)"));
} }
Ampel ampel; Ampel ampel;
...@@ -292,7 +292,7 @@ namespace web_server { ...@@ -292,7 +292,7 @@ namespace web_server {
} }
http.sendHeader("Location", "/"); http.sendHeader("Location", "/");
http.send(303); http.send(303);
sensor_console::runCommand(http.arg("send").c_str()); sensor_console::execute(http.arg("send").c_str());
} }
void handlePageNotFound() { void handlePageNotFound() {
......
...@@ -43,8 +43,8 @@ namespace wifi { ...@@ -43,8 +43,8 @@ namespace wifi {
// Initialize Wi-Fi // Initialize Wi-Fi
void connect(const char *hostname) { void connect(const char *hostname) {
sensor_console::defineCommand("wifi_scan", scanNetworks, F(" (Scans available WiFi networks)")); sensor_console::defineCommand("wifi_scan", scanNetworks, F("(Scans available WiFi networks)"));
sensor_console::defineCommand("local_ip", showLocalIp, F(" (Displays local IP and current SSID)")); sensor_console::defineCommand("local_ip", showLocalIp, F("(Displays local IP and current SSID)"));
//NOTE: WiFi Multi could allow multiple SSID and passwords. //NOTE: WiFi Multi could allow multiple SSID and passwords.
WiFi.persistent(false); // Don't write user & password to Flash. WiFi.persistent(false); // Don't write user & password to Flash.
......
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