From 010383444171343959a2b6176b75dd9427421dca Mon Sep 17 00:00:00 2001 From: Leon Krieg Date: Sun, 22 Sep 2024 03:54:28 +0200 Subject: [PATCH] Implement serial command parser with floating point support --- opt/tools | 2 +- src/common.h | 1 + src/common/parser.c | 72 ++++++++++++++++++++++++++++++++++++++++ src/common/parser.h | 18 ++++++++++ src/main.c | 81 +++++++++++++++++++++++++++++++-------------- 5 files changed, 148 insertions(+), 26 deletions(-) create mode 100644 src/common/parser.c create mode 100644 src/common/parser.h diff --git a/opt/tools b/opt/tools index 1ba0329..565c8b7 160000 --- a/opt/tools +++ b/opt/tools @@ -1 +1 @@ -Subproject commit 1ba0329c9967b3732b4c927534c5d7d73bb07895 +Subproject commit 565c8b74569b900b3de35cc5ecf16d4789525881 diff --git a/src/common.h b/src/common.h index 18412db..85e3556 100644 --- a/src/common.h +++ b/src/common.h @@ -5,6 +5,7 @@ #include "common/types.h" #include "common/watchdog.h" #include "common/memory.h" +#include "common/parser.h" #include #include diff --git a/src/common/parser.c b/src/common/parser.c new file mode 100644 index 0000000..42fa254 --- /dev/null +++ b/src/common/parser.c @@ -0,0 +1,72 @@ +#include "common.h" + +#include +#include +#include + +#define CMD_MAX_LEN 128 + +// TODO: Write documentation. +// TODO: Implement start and stop commands. +// TODO: Reset command buffer on timeout? + +static char cmdbuf[CMD_MAX_LEN + 1]; +static char *tail = cmdbuf + CMD_MAX_LEN - 1; +static char *head = cmdbuf; + +static bool ParseLine(cmd_t *out); + +bool CMD_Parse(char ch, cmd_t *out) +{ + bool is_valid; + + if (ch == '\n' || ch == '\r') { + is_valid = ParseLine(out); + tail = cmdbuf + CMD_MAX_LEN - 1; + head = cmdbuf; + return is_valid; + } + + if (head < tail) { + *head++ = ch; + *head = '\0'; + } + + return false; +} + +static bool ParseLine(cmd_t *out) +{ + head = cmdbuf; + + // Command SET + if (tolower(head[0] == 's')) { + if ((tolower(head[1]) != 'e') || + (tolower(head[2]) != 't')) { + return false; + } + + out->type = T_SET; + head += 3; + + // Parse arguments + for (int i = 0; i < 2; i++) { + errno = 0; + + // Try converting string to floating point + // Skips whitespace and moves tail pointer + out->args[i] = (float) strtod(head, &tail); + + if ((errno == ERANGE) || + (out->args[i] == 0 && head == tail)) { + return false; // Conversion failed + } + + head = tail; + } + + return true; + } + + return false; +} diff --git a/src/common/parser.h b/src/common/parser.h new file mode 100644 index 0000000..eafef2c --- /dev/null +++ b/src/common/parser.h @@ -0,0 +1,18 @@ +#ifndef MAD_CORE_COMMON_PARSER +#define MAD_CORE_COMMON_PARSER + +#include "common/types.h" + +// Command types +#define T_SET 0 + +struct cmd_s { + int type; + float args[2]; +}; + +typedef struct cmd_s cmd_t; + +bool CMD_Parse(char ch, cmd_t *out); + +#endif // MAD_CORE_COMMON_PARSER diff --git a/src/main.c b/src/main.c index e9abfbf..9fd6e96 100644 --- a/src/main.c +++ b/src/main.c @@ -7,14 +7,13 @@ #include // TODO: Config header for chip specifics like EEPROM size. -// TODO: Make sure wear leveling resets memory correctly. +// TODO: Fix huge serial command input triggering watchdog. +// TODO: Only update EEPROM if value differs from previous. +// TODO: Check thermistor conversion results /w thermometer. // TODO: Implement primary state machine for update loop. // TODO: Migrate to ATMega 1284P-PU for 2nd 16-bit timer. -// TODO: Check thermistor conversion results /w thermometer. -// TODO: Implement optional CRC8 sensor measurement check. // TODO: Use 18.432MHz quarz crystal, burn required fuses. -// TODO: Make sure nothing breaks with negative temperatures. -// TODO: Write an improved command parser (low priority). +// TODO: Implement optional CRC8 sensor measurement check. // TODO: Proper error handling and recovery (after testing). // TODO: Check why the MCUCSR EXTRF reset flag is set. @@ -30,6 +29,10 @@ static enum state_e state; static word temp, temp_target; static word dewp, dewp_target; +static int Init(void); +static void Update(void); +static void UpdateSensors(void); + static int Init(void) { mem_block_t mem; @@ -82,8 +85,11 @@ static int Init(void) if (MEM_Read(&mem) == 0) { Info("Found persistent configuration in EEPROM!"); - Info("Setting targets TEMP='%.2f', DEWP='%.2f'.", + Info("Setting targets TEMP='%.2fC', DEWP='%.2fC'.", mem.temp, mem.dewp); + + temp_target = mem.temp; + dewp_target = mem.dewp; } // There is a possiblity to use interrupt signals @@ -130,6 +136,50 @@ static int Init(void) } static void Update(void) +{ + char ch; + cmd_t cmd; + mem_block_t mem; + + // Parse serial commands + while ((ch = USART_Getc()) >= 0) { + if (!CMD_Parse(ch, &cmd)) { + WDT_Reset(); + continue; + } + switch(cmd.type) { + case T_SET: // Configure new persistent target + Info("Setting new target TEMP='%.2fC'...", cmd.args[0]); + Info("Setting new target DEWP='%.2fC'...", cmd.args[1]); + mem.temp = temp_target = cmd.args[0]; + mem.dewp = dewp_target = cmd.args[1]; + MEM_Write(&mem); + return; + } + } + + // Poll sensor values + UpdateSensors(); + + // Handle state + switch (state) { + case S_IDLE: + break; + case S_HEAT_UP: + break; + case S_COOL_DOWN: + break; + case S_DEHUMIDIFY: + break; + } + + UNUSED(temp); + UNUSED(dewp); + UNUSED(temp_target); + UNUSED(dewp_target); +} + +static void UpdateSensors(void) { float t[6], rh[3], dp[3]; float t_avg, rh_avg, dp_avg; @@ -167,25 +217,6 @@ static void Update(void) Info("T2=%.2fC, RH2=%.2f%%, NT2=%.2fC, DEW2=%.2fC", t[1], rh[1], t[4], dp[1]); Info("T3=%.2fC, RH3=%.2f%%, NT3=%.2fC, DEW3=%.2fC", t[2], rh[2], t[5], dp[2]); Info("T_AVG=%.2fC, RH_AVG=%.2f%%, DP_AVG=%.2fC", t_avg, rh_avg, dp_avg); - - // TODO: Implement state machine - // TODO: Handle serial commands - - switch (state) { - case S_IDLE: - break; - case S_HEAT_UP: - break; - case S_COOL_DOWN: - break; - case S_DEHUMIDIFY: - break; - } - - UNUSED(temp); - UNUSED(dewp); - UNUSED(temp_target); - UNUSED(dewp_target); } int main(void)