Merge pull request #5 from madcow/parser
Implement serial command parser with floating point support
This commit is contained in:
Submodule opt/tools updated: 1ba0329c99...565c8b7456
@@ -5,6 +5,7 @@
|
|||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "common/watchdog.h"
|
#include "common/watchdog.h"
|
||||||
#include "common/memory.h"
|
#include "common/memory.h"
|
||||||
|
#include "common/parser.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|||||||
72
src/common/parser.c
Normal file
72
src/common/parser.c
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#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 <float> <float>
|
||||||
|
if (tolower(head[0] == 's')) {
|
||||||
|
if ((tolower(head[1]) != 'e') ||
|
||||||
|
(tolower(head[2]) != 't')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
out->type = T_SET;
|
||||||
|
head += 3;
|
||||||
|
|
||||||
|
// Parse <float> 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;
|
||||||
|
}
|
||||||
18
src/common/parser.h
Normal file
18
src/common/parser.h
Normal file
@@ -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
|
||||||
@@ -3,13 +3,15 @@
|
|||||||
|
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
|
||||||
// Timeout Flags
|
// Timeout flags
|
||||||
#define WDT2000 0x7 // 2000 ms
|
#define WDT2000 0x7 // 2000 ms
|
||||||
#define WDT1000 0x6 // 1000 ms
|
#define WDT1000 0x6 // 1000 ms
|
||||||
#define WDT500 0x5 // 500 ms
|
#define WDT500 0x5 // 500 ms
|
||||||
#define WDT250 0x4 // 250 ms
|
#define WDT250 0x4 // 250 ms
|
||||||
#define WDT100 0x3 // 100 ms
|
#define WDT100 0x3 // 100 ms
|
||||||
// XXX: WDT...
|
#define WDT64 0x2 // 64 ms
|
||||||
|
#define WDT32 0x1 // 32 ms
|
||||||
|
#define WDT16 0x0 // 16 ms
|
||||||
|
|
||||||
void WDT_Enable(void);
|
void WDT_Enable(void);
|
||||||
void WDT_SetTimeoutFlag(byte flag);
|
void WDT_SetTimeoutFlag(byte flag);
|
||||||
|
|||||||
98
src/main.c
98
src/main.c
@@ -7,14 +7,13 @@
|
|||||||
#include <avr/interrupt.h>
|
#include <avr/interrupt.h>
|
||||||
|
|
||||||
// TODO: Config header for chip specifics like EEPROM size.
|
// 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: Implement primary state machine for update loop.
|
||||||
// TODO: Migrate to ATMega 1284P-PU for 2nd 16-bit timer.
|
// 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: Use 18.432MHz quarz crystal, burn required fuses.
|
||||||
// TODO: Make sure nothing breaks with negative temperatures.
|
// TODO: Implement optional CRC8 sensor measurement check.
|
||||||
// TODO: Write an improved command parser (low priority).
|
|
||||||
// TODO: Proper error handling and recovery (after testing).
|
// TODO: Proper error handling and recovery (after testing).
|
||||||
// TODO: Check why the MCUCSR EXTRF reset flag is set.
|
// TODO: Check why the MCUCSR EXTRF reset flag is set.
|
||||||
|
|
||||||
@@ -27,8 +26,13 @@ enum state_e
|
|||||||
};
|
};
|
||||||
|
|
||||||
static enum state_e state;
|
static enum state_e state;
|
||||||
static word temp, temp_target;
|
static float temp, temp_target;
|
||||||
static word dewp, dewp_target;
|
static float dewp, dewp_target;
|
||||||
|
static float relh;
|
||||||
|
|
||||||
|
static int Init(void);
|
||||||
|
static void Update(void);
|
||||||
|
static void UpdateSensors(void);
|
||||||
|
|
||||||
static int Init(void)
|
static int Init(void)
|
||||||
{
|
{
|
||||||
@@ -82,8 +86,11 @@ static int Init(void)
|
|||||||
|
|
||||||
if (MEM_Read(&mem) == 0) {
|
if (MEM_Read(&mem) == 0) {
|
||||||
Info("Found persistent configuration in EEPROM!");
|
Info("Found persistent configuration in EEPROM!");
|
||||||
Info("Setting targets TEMP='%.2f', DEWP='%.2f'.",
|
Info("Setting targets TEMP='%.2fC', DEWP='%.2fC'.",
|
||||||
mem.temp, mem.dewp);
|
mem.temp, mem.dewp);
|
||||||
|
|
||||||
|
temp_target = mem.temp;
|
||||||
|
dewp_target = mem.dewp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// There is a possiblity to use interrupt signals
|
// There is a possiblity to use interrupt signals
|
||||||
@@ -131,9 +138,47 @@ static int Init(void)
|
|||||||
|
|
||||||
static void Update(void)
|
static void Update(void)
|
||||||
{
|
{
|
||||||
float t[6], rh[3], dp[3];
|
char ch;
|
||||||
float t_avg, rh_avg, dp_avg;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UpdateSensors(void)
|
||||||
|
{
|
||||||
word raw;
|
word raw;
|
||||||
|
float t[6], rh[3], dp[3];
|
||||||
|
|
||||||
Info("Reading sensor values...");
|
Info("Reading sensor values...");
|
||||||
|
|
||||||
@@ -159,33 +204,14 @@ static void Update(void)
|
|||||||
dp[1] = Dewpoint(t[1], rh[1]);
|
dp[1] = Dewpoint(t[1], rh[1]);
|
||||||
dp[2] = Dewpoint(t[2], rh[2]);
|
dp[2] = Dewpoint(t[2], rh[2]);
|
||||||
|
|
||||||
t_avg = (t[0] + t[1] + t[2]) / 3;
|
temp = (t[0] + t[1] + t[2]) / 3;
|
||||||
rh_avg = (rh[0] + rh[1] + rh[2]) / 3;
|
relh = (rh[0] + rh[1] + rh[2]) / 3;
|
||||||
dp_avg = Dewpoint(t_avg, rh_avg);
|
dewp = Dewpoint(temp, relh);
|
||||||
|
|
||||||
Info("T1=%.2fC, RH1=%.2f%%, NT1=%.2fC, DEW1=%.2fC", t[0], rh[0], t[3], dp[0]);
|
Info("T1=%.2fC, RH1=%.2f%%, NT1=%.2fC, DP1=%.2fC", t[0], rh[0], t[3], dp[0]);
|
||||||
Info("T2=%.2fC, RH2=%.2f%%, NT2=%.2fC, DEW2=%.2fC", t[1], rh[1], t[4], dp[1]);
|
Info("T2=%.2fC, RH2=%.2f%%, NT2=%.2fC, DP2=%.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("T3=%.2fC, RH3=%.2f%%, NT3=%.2fC, DP3=%.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);
|
Info("T_AVG=%.2fC, RH_AVG=%.2f%%, DP_AVG=%.2fC", temp, relh, dewp);
|
||||||
|
|
||||||
// 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)
|
int main(void)
|
||||||
|
|||||||
Reference in New Issue
Block a user