From 385b47211ea8674f97f014537d694bb2efbd6ab9 Mon Sep 17 00:00:00 2001 From: lonkaars Date: Thu, 13 Oct 2022 15:59:14 +0200 Subject: WIP protocol implementation --- shared/bin.h | 12 ++++++++ shared/main | Bin 0 -> 21456 bytes shared/makefile | 24 +++++++++++++++ shared/protocol.c | 72 +++++++++++++++++++++++++++++++++++++++++++++ shared/protocol.h | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ shared/protocol.md | 54 ++++++++++++++++++++++++++++++++++ shared/test.c | 36 +++++++++++++++++++++++ shared/testcmd | 1 + 8 files changed, 284 insertions(+) create mode 100644 shared/bin.h create mode 100755 shared/main create mode 100644 shared/makefile create mode 100644 shared/protocol.c create mode 100644 shared/protocol.h create mode 100644 shared/protocol.md create mode 100644 shared/test.c create mode 100644 shared/testcmd (limited to 'shared') diff --git a/shared/bin.h b/shared/bin.h new file mode 100644 index 0000000..c7405be --- /dev/null +++ b/shared/bin.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +typedef struct { + uint16_t bytes; + uint8_t data[]; +} ws_s_bin; + +/** allocate new ws_s_bin struct and fill with `*data` for `bytes` bytes */ +ws_s_bin *ws_bin_s_alloc(uint16_t bytes, uint8_t *data); + diff --git a/shared/main b/shared/main new file mode 100755 index 0000000..7d7b7fe Binary files /dev/null and b/shared/main differ diff --git a/shared/makefile b/shared/makefile new file mode 100644 index 0000000..4d538da --- /dev/null +++ b/shared/makefile @@ -0,0 +1,24 @@ +CC = gcc +LD = gcc +RM = rm -f +CFLAGS = +LFLAGS = +TARGET = main + +SRCS := $(wildcard *.c) +OBJS := $(patsubst %.c,%.o, $(SRCS)) + +all: main + +%.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + +$(TARGET): $(OBJS) + $(LD) $^ $(LFLAGS) -o $@ + +clean: + $(RM) $(TARGET) $(OBJS) + +compile_commands: clean + compiledb make + diff --git a/shared/protocol.c b/shared/protocol.c new file mode 100644 index 0000000..85b320f --- /dev/null +++ b/shared/protocol.c @@ -0,0 +1,72 @@ +#include +#include + +#include "protocol.h" + +void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input) { + switch(input) { + case WS_PROTOCOL_C_NEWLINE: { + if (!state->valid) return; + break; + } + + case WS_PROTOCOL_C_SPACE: { + if (!state->valid) return; + printf("argument delimiter\n"); + return; + } + + case WS_PROTOCOL_C_NULL: { + state->valid = false; + return; + } + + default: { + if (!state->valid) return; + printf("recv byte 0x%02x, (\"%c\")\n", input, input); + state->cmd[state->cmd_len++] = input; + state->args_len[state->arg_len] += 1; + if (state->cmd_len == WS_PROTOCOL_CMD_BUFFER_LEN) state->valid = false; + return; + } + } + + printf("command done!\n"); +} + +void ws_protocol_parse_bytes(ws_s_protocol_parser_state* state, char* input, unsigned int length) { + for (unsigned int i = 0; i < length; i++) ws_protocol_parse_byte(state, input[i]); +} + +ws_s_protocol_parser_state* ws_protocol_parser_alloc() { + ws_s_protocol_parser_state* parser_state = malloc(sizeof(ws_s_protocol_parser_state) + sizeof(uint16_t) * WS_PROTOCOL_CMD_MAX_ARGUMENTS); + parser_state->cmd = malloc(sizeof(char) * WS_PROTOCOL_CMD_BUFFER_LEN); + parser_state->valid = true; + parser_state->cmd_len = 0; + parser_state->arg_len = 0; + parser_state->target = NULL; + return parser_state; +} + +void ws_protocol_cmd_init(ws_s_protocol_parser_state* state) { + state->target = malloc(sizeof(ws_s_protocol_parsed_cmd) + sizeof(char*) * state->arg_len); + for (unsigned int i = 0; i < state->arg_len; i++) + state->target->argv[i] = malloc(sizeof(char) * state->args_len[i]); + state->target->argc = state->arg_len; +} + +void ws_protocol_parser_free(ws_s_protocol_parser_state* state) { + if (state == NULL) return; + if (state->target != NULL) ws_protocol_cmd_free(state->target); + free(state->cmd); + free(state); + state = NULL; + return; +} +void ws_protocol_cmd_free(ws_s_protocol_parsed_cmd* cmd) { + for (unsigned int i = 0; i < cmd->argc; i++) + free(cmd->argv[i]); + free(cmd); + cmd = NULL; + return; +} diff --git a/shared/protocol.h b/shared/protocol.h new file mode 100644 index 0000000..5cf7ac6 --- /dev/null +++ b/shared/protocol.h @@ -0,0 +1,85 @@ +#pragma once + +#include +#include + +#include "bin.h" + +#define WS_PROTOCOL_CMD_MAX_ARGUMENTS (1) +#define WS_PROTOCOL_CMD_BUFFER_LEN (40) + +#define WS_PROTOCOL_CMD_AMOUNT (1) + +#define WS_PROTOCOL_C_NEWLINE (0x0a) +#define WS_PROTOCOL_C_SPACE (0x20) +#define WS_PROTOCOL_C_NULL (0x00) + +typedef struct { + int argc; + char* argv[]; +} ws_s_protocol_parsed_cmd; + +typedef struct { + ws_s_protocol_parsed_cmd* target; + bool valid; + char* cmd; + uint16_t cmd_len; + uint16_t arg_len; + uint16_t args_len[]; +} ws_s_protocol_parser_state; + +//TODO: document +ws_s_protocol_parser_state* ws_protocol_parser_alloc(); +void ws_protocol_parser_free(ws_s_protocol_parser_state* state); +void ws_protocol_cmd_init(ws_s_protocol_parser_state* state); +void ws_protocol_cmd_free(ws_s_protocol_parsed_cmd* cmd); + +/** + * @brief parse incoming data byte by byte until a finished command is detected + * + * @remark [server] + * + * @param state parser state object, each incoming request should have it's own parser state + * @param input input byte + */ +void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input); +/** + * @brief parse incoming data chunk + * + * @remark [server] + * + * @param state parser state object, each incoming request should have it's own parser state + * @param input input byte array + * @param length input byte array length + */ +void ws_protocol_parse_bytes(ws_s_protocol_parser_state* state, char* input, unsigned int length); + +/** + * @brief create a `last-records` request command + * @remark [client] + * @return ws_s_bin containing the command string + */ +ws_s_bin* ws_protocol_req_last_records(unsigned int record_amount); + +/** + * @brief `last-records` response handler + * + * @remark [server] + * + * gets fired when the weather station receives a complete `last-records` + * command, and returns the response string + * + * @param parsed_cmd complete parsed command from ws_protocol_parse_* + * + * @return ws_s_bin containing response string + */ +ws_s_bin* ws_protocol_res_last_records(ws_s_protocol_parsed_cmd* parsed_cmd); + +typedef enum { + WS_PROTOCOL_CMD_LAST_RECORDS = 0, +} ws_e_protocol_cmd; + +static ws_s_bin* (*g_ws_protocol_res_handlers[WS_PROTOCOL_CMD_AMOUNT])(ws_s_protocol_parsed_cmd* parsed_cmd) = { + [WS_PROTOCOL_CMD_LAST_RECORDS] = &ws_protocol_res_last_records +}; + diff --git a/shared/protocol.md b/shared/protocol.md new file mode 100644 index 0000000..bafec4d --- /dev/null +++ b/shared/protocol.md @@ -0,0 +1,54 @@ +# Protocol spec + +This is a brief overview of the protocol specifications that the weather +station uses to send and receive data between the weather station and qt +client. This protocol is text-based, and used over a TCP connection. This +document will only go into detail about the data sent over this connection, not +requirements about the connection itself. + +The protocol is only used in a request-response fashion, so all commands are +assumed to be sent by the qt client, and responded to by the weather station. + +Functions for generating commands and parsing incoming data are provided by the +protocol.c and protocol.h files. See [code +implementation](#code-implementation) section for more details about naming +conventions. + +- LF for newline instead of CRLF +- Commands are single-line +- Spaces used for separating command arguments +- Commands with malformed data are discarded and return error +- Response consist of `ok` or `error`, a comma, and the byte length of the + remaining response (if any) +- Numbers are sent as hexadecimal + +## Commands + +### `last-records ` + +Returns the last `n` records in csv format. The first line has the csv table +header, with the fields `id`, `temperature`, `humidity`, and +`atmospheric_pressure`. The rest of the response consists of 1 record per line. +When `n` is 0, or no records exist yet, the csv header is still returned, but +without any records. + +## Example transaction + +In the following example, newlines are indicated by `<0a>`, request by lines +starting with `<`, and response by lines starting with `>`. + +``` +< last-records 5<0a> +> ok,115<0a> +> id,temperature,humidity,atmospheric_pressure<0a> +> 10dc,2f,c5,7f<0a> +> 10dd,30,c6,7f<0a> +> 10de,31,c7,7f<0a> +> 10df,35,ca,7e<0a> +> 10e0,34,c9,7e<0a> +``` + +## Code implementation + + + diff --git a/shared/test.c b/shared/test.c new file mode 100644 index 0000000..788dc94 --- /dev/null +++ b/shared/test.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include + +#include "protocol.h" + +ws_s_bin* ws_protocol_res_last_records(ws_s_protocol_parsed_cmd* parsed_cmd) { + return NULL; +} + +int main() { + // disable echo and enable raw mode + fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); + struct termios term; + tcgetattr(STDIN_FILENO, &term); + term.c_lflag &= ~(ECHO | ICANON); + term.c_cc[VTIME] = 0; + term.c_cc[VMIN] = 1; + tcsetattr(STDIN_FILENO, 0, &term); + + ws_s_protocol_parser_state* parser1 = ws_protocol_parser_alloc(); + + for(;;) { + fflush(stdout); + + char byte; + while(read(STDIN_FILENO, &byte, 1) > 0) + ws_protocol_parse_byte(parser1, byte); + } + + ws_protocol_parser_free(parser1); + + return 0; +} diff --git a/shared/testcmd b/shared/testcmd new file mode 100644 index 0000000..17f8842 --- /dev/null +++ b/shared/testcmd @@ -0,0 +1 @@ +last-records 5 -- cgit v1.2.3 From 78f8f18e48d25547e837630d8317ef029bba6d72 Mon Sep 17 00:00:00 2001 From: lonkaars Date: Thu, 13 Oct 2022 20:37:23 +0200 Subject: more protocol parsing done --- shared/bin.c | 11 ++++++++++ shared/bin.h | 5 +++-- shared/main | Bin 21456 -> 28568 bytes shared/makefile | 2 +- shared/protocol.c | 42 ++++++++++++++++++++++++++++++++---- shared/protocol.h | 63 ++++++++++++++++++++++++++++++++++++++++++++++-------- 6 files changed, 107 insertions(+), 16 deletions(-) create mode 100644 shared/bin.c (limited to 'shared') diff --git a/shared/bin.c b/shared/bin.c new file mode 100644 index 0000000..def2aa8 --- /dev/null +++ b/shared/bin.c @@ -0,0 +1,11 @@ +#include +#include +#include + +#include "bin.h" + +ws_s_bin *ws_bin_s_alloc(uint16_t bytes) { + ws_s_bin *temp = malloc(sizeof(ws_s_bin) + sizeof(uint8_t) * bytes); + temp->bytes = bytes; + return temp; +} diff --git a/shared/bin.h b/shared/bin.h index c7405be..bfcda0c 100644 --- a/shared/bin.h +++ b/shared/bin.h @@ -2,11 +2,12 @@ #include +/** @brief binary data container with length */ typedef struct { uint16_t bytes; uint8_t data[]; } ws_s_bin; -/** allocate new ws_s_bin struct and fill with `*data` for `bytes` bytes */ -ws_s_bin *ws_bin_s_alloc(uint16_t bytes, uint8_t *data); +/** @brief allocate new ws_s_bin struct */ +ws_s_bin *ws_bin_s_alloc(uint16_t bytes); diff --git a/shared/main b/shared/main index 7d7b7fe..10b0d3c 100755 Binary files a/shared/main and b/shared/main differ diff --git a/shared/makefile b/shared/makefile index 4d538da..2ca254c 100644 --- a/shared/makefile +++ b/shared/makefile @@ -1,7 +1,7 @@ CC = gcc LD = gcc RM = rm -f -CFLAGS = +CFLAGS = -g LFLAGS = TARGET = main diff --git a/shared/protocol.c b/shared/protocol.c index 85b320f..49d1795 100644 --- a/shared/protocol.c +++ b/shared/protocol.c @@ -1,5 +1,6 @@ #include #include +#include #include "protocol.h" @@ -12,7 +13,7 @@ void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input) { case WS_PROTOCOL_C_SPACE: { if (!state->valid) return; - printf("argument delimiter\n"); + state->arg_len++; return; } @@ -23,15 +24,39 @@ void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input) { default: { if (!state->valid) return; - printf("recv byte 0x%02x, (\"%c\")\n", input, input); state->cmd[state->cmd_len++] = input; state->args_len[state->arg_len] += 1; if (state->cmd_len == WS_PROTOCOL_CMD_BUFFER_LEN) state->valid = false; return; } } + // arg_len is used as an index while parsing, so add 1 to get length + state->arg_len++; - printf("command done!\n"); + // parse cmd into argc and argv + ws_protocol_cmd_init(state); + // create response + ws_s_protocol_response* response = ws_protocol_parse_finished(state->target); + + //TODO: send response + + free(response->msg); + free(response); + + return; +} + +ws_s_protocol_response* ws_protocol_parse_finished(ws_s_protocol_parsed_cmd* parsed_cmd) { + ws_s_protocol_response* response = malloc(sizeof(ws_s_protocol_response)); + + if (strncmp("last-records", parsed_cmd->argv[0], 12) == 0) { + printf("last-records found!\n"); + } + + response->msg = ws_bin_s_alloc(50); + strncpy((char*) response->msg->data, "hello world!\n\0", 14); + + return response; } void ws_protocol_parse_bytes(ws_s_protocol_parser_state* state, char* input, unsigned int length) { @@ -51,8 +76,16 @@ ws_s_protocol_parser_state* ws_protocol_parser_alloc() { void ws_protocol_cmd_init(ws_s_protocol_parser_state* state) { state->target = malloc(sizeof(ws_s_protocol_parsed_cmd) + sizeof(char*) * state->arg_len); for (unsigned int i = 0; i < state->arg_len; i++) - state->target->argv[i] = malloc(sizeof(char) * state->args_len[i]); + state->target->argv[i] = malloc(sizeof(char) * (state->args_len[i] + 1)); + state->target->argc = state->arg_len; + + unsigned int head = 0; + for (unsigned int i = 0; i < state->arg_len; i++) { + strncpy(state->target->argv[i], &state->cmd[head], state->args_len[i]); + state->target->argv[i][state->args_len[i]] = 0x00; // terminate argument with null byte + head += state->args_len[i]; + } } void ws_protocol_parser_free(ws_s_protocol_parser_state* state) { @@ -63,6 +96,7 @@ void ws_protocol_parser_free(ws_s_protocol_parser_state* state) { state = NULL; return; } + void ws_protocol_cmd_free(ws_s_protocol_parsed_cmd* cmd) { for (unsigned int i = 0; i < cmd->argc; i++) free(cmd->argv[i]); diff --git a/shared/protocol.h b/shared/protocol.h index 5cf7ac6..3082af4 100644 --- a/shared/protocol.h +++ b/shared/protocol.h @@ -14,24 +14,54 @@ #define WS_PROTOCOL_C_SPACE (0x20) #define WS_PROTOCOL_C_NULL (0x00) +/** + * @brief parsed cmd struct, holds arguments similar to argc and argv provided + * to `int main()` + */ typedef struct { - int argc; - char* argv[]; + int argc; /** argument count */ + char* argv[]; /** argument array, null terminated strings */ } ws_s_protocol_parsed_cmd; +/** + * @brief holds parser state variables for `ws_protocol_parse_byte` function. + * each incoming tcp request should get it's own parser 'instance' + */ typedef struct { - ws_s_protocol_parsed_cmd* target; - bool valid; - char* cmd; - uint16_t cmd_len; - uint16_t arg_len; - uint16_t args_len[]; + ws_s_protocol_parsed_cmd* target; /** parsed cmd reference */ + bool valid; /** command still valid flag */ + char* cmd; /** raw cmd */ + uint16_t cmd_len; /** raw cmd string length */ + uint16_t arg_len; /** amount of arguments */ + uint16_t args_len[]; /** array of argument lengths */ } ws_s_protocol_parser_state; -//TODO: document +/** @brief return values for command handlers */ +typedef enum { + WS_PROTOCOL_CMD_RETURN_OK = 0, + WS_PROTOCOL_CMD_RETURN_ERROR = 1, +} ws_e_protocol_cmd_return_value; + +/** @brief request response data struct */ +typedef struct { + ws_e_protocol_cmd_return_value success; + ws_s_bin* msg; +} ws_s_protocol_response; + +/** + * @brief allocate parser struct + * + * @return pointer to newly allocated struct + */ ws_s_protocol_parser_state* ws_protocol_parser_alloc(); +/** @brief deallocate parser struct, automatically frees all child pointers */ void ws_protocol_parser_free(ws_s_protocol_parser_state* state); +/** + * @brief initialize ws_s_protocol_parsed_cmd struct pointer of + * ws_s_protocol_parser_state (internal only) + */ void ws_protocol_cmd_init(ws_s_protocol_parser_state* state); +/** @brief deallocate ws_s_protocol_parsed_cmd struct pointer (internal only) */ void ws_protocol_cmd_free(ws_s_protocol_parsed_cmd* cmd); /** @@ -53,6 +83,21 @@ void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input); * @param length input byte array length */ void ws_protocol_parse_bytes(ws_s_protocol_parser_state* state, char* input, unsigned int length); +/** + * @brief handle complete command + * + * this function gets called when ws_protocol_parse_byte has detected a + * finished command. this function decides which command handler gets called, + * given that argv[0] contains a valid command. command argument parsing is + * handled by the command handler function. + * + * @remark [server] + * + * @return response + * + * @param parsed_cmd cmd parsed into ws_s_protocol_parsed_cmd struct + */ +ws_s_protocol_response* ws_protocol_parse_finished(ws_s_protocol_parsed_cmd* parsed_cmd); /** * @brief create a `last-records` request command -- cgit v1.2.3 From 137f62eb368101dd0371f0c54e1a4a2a7be48890 Mon Sep 17 00:00:00 2001 From: lonkaars Date: Fri, 14 Oct 2022 15:57:47 +0200 Subject: remove executable from git --- .gitignore | 1 + shared/main | Bin 28568 -> 0 bytes 2 files changed, 1 insertion(+) delete mode 100755 shared/main (limited to 'shared') diff --git a/.gitignore b/.gitignore index 1c68c8a..aba4463 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,6 @@ stm32f091/main.bin client/makefile client/client client/moc_* +shared/main .qmake.stash .vscode/.cortex-debug.registers.state.json diff --git a/shared/main b/shared/main deleted file mode 100755 index 10b0d3c..0000000 Binary files a/shared/main and /dev/null differ -- cgit v1.2.3 From f58ed35f0b73e933b6dd1f98472941c1d5d7ce9c Mon Sep 17 00:00:00 2001 From: lonkaars Date: Fri, 14 Oct 2022 16:21:21 +0200 Subject: request handler function executed --- shared/makefile | 2 +- shared/protocol.c | 26 +++++++++++++++++++++----- shared/protocol.h | 11 +++++++---- shared/test.c | 4 ++-- 4 files changed, 31 insertions(+), 12 deletions(-) (limited to 'shared') diff --git a/shared/makefile b/shared/makefile index 2ca254c..2093f00 100644 --- a/shared/makefile +++ b/shared/makefile @@ -1,7 +1,7 @@ CC = gcc LD = gcc RM = rm -f -CFLAGS = -g +CFLAGS = -g -std=c11 LFLAGS = TARGET = main diff --git a/shared/protocol.c b/shared/protocol.c index 49d1795..6e3bc73 100644 --- a/shared/protocol.c +++ b/shared/protocol.c @@ -46,16 +46,32 @@ void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input) { return; } +#define WS_CMD_MAP(parsed_cmd, name, code) \ + if (strlen(parsed_cmd->argv[0]) == strlen(name) && strncmp(parsed_cmd->argv[0], name, strlen(name)) == 0) return code; + +static ws_e_protocol_cmd ws_protocol_get_cmd_code(ws_s_protocol_parsed_cmd* parsed_cmd) { + WS_CMD_MAP(parsed_cmd, "last-records", WS_PROTOCOL_CMD_LAST_RECORDS); + + return WS_PROTOCOL_CMD_UNKNOWN; +} + ws_s_protocol_response* ws_protocol_parse_finished(ws_s_protocol_parsed_cmd* parsed_cmd) { ws_s_protocol_response* response = malloc(sizeof(ws_s_protocol_response)); + response->success = WS_PROTOCOL_CMD_RETURN_ERROR; + response->msg = NULL; - if (strncmp("last-records", parsed_cmd->argv[0], 12) == 0) { - printf("last-records found!\n"); - } + ws_e_protocol_cmd cmd_code = ws_protocol_get_cmd_code(parsed_cmd); + if (cmd_code == WS_PROTOCOL_CMD_UNKNOWN) goto ws_protocol_parse_exit; + if (cmd_code >= WS_PROTOCOL_CMD_AMOUNT) goto ws_protocol_parse_exit; + + void (*ws_protocol_res_handler)(ws_s_protocol_parsed_cmd*, ws_s_protocol_response*) = + g_ws_protocol_res_handlers[cmd_code]; + if (ws_protocol_res_handler == NULL) goto ws_protocol_parse_exit; + (*ws_protocol_res_handler)(parsed_cmd, response); - response->msg = ws_bin_s_alloc(50); - strncpy((char*) response->msg->data, "hello world!\n\0", 14); +ws_protocol_parse_exit: + if (response->msg == NULL) response->msg = ws_bin_s_alloc(0); return response; } diff --git a/shared/protocol.h b/shared/protocol.h index 3082af4..a19fe48 100644 --- a/shared/protocol.h +++ b/shared/protocol.h @@ -115,16 +115,19 @@ ws_s_bin* ws_protocol_req_last_records(unsigned int record_amount); * command, and returns the response string * * @param parsed_cmd complete parsed command from ws_protocol_parse_* - * - * @return ws_s_bin containing response string + * @param response response struct with uninitialized pointer to msg */ -ws_s_bin* ws_protocol_res_last_records(ws_s_protocol_parsed_cmd* parsed_cmd); +void ws_protocol_res_last_records(ws_s_protocol_parsed_cmd* parsed_cmd, ws_s_protocol_response* response); +/** @brief cmd codes (used to call handlers) */ typedef enum { + WS_PROTOCOL_CMD_UNKNOWN = -1, + WS_PROTOCOL_CMD_LAST_RECORDS = 0, } ws_e_protocol_cmd; -static ws_s_bin* (*g_ws_protocol_res_handlers[WS_PROTOCOL_CMD_AMOUNT])(ws_s_protocol_parsed_cmd* parsed_cmd) = { +/** @brief response handlers, called when a command is parsed */ +static void (*g_ws_protocol_res_handlers[WS_PROTOCOL_CMD_AMOUNT])(ws_s_protocol_parsed_cmd*, ws_s_protocol_response*) = { [WS_PROTOCOL_CMD_LAST_RECORDS] = &ws_protocol_res_last_records }; diff --git a/shared/test.c b/shared/test.c index 788dc94..5f2cd60 100644 --- a/shared/test.c +++ b/shared/test.c @@ -6,8 +6,8 @@ #include "protocol.h" -ws_s_bin* ws_protocol_res_last_records(ws_s_protocol_parsed_cmd* parsed_cmd) { - return NULL; +void ws_protocol_res_last_records(ws_s_protocol_parsed_cmd* parsed_cmd, ws_s_protocol_response* response) { + printf("last-records detected!\n"); } int main() { -- cgit v1.2.3 From ec475b0469104a0a76542c52286ed08933ac750c Mon Sep 17 00:00:00 2001 From: lonkaars Date: Fri, 14 Oct 2022 16:47:12 +0200 Subject: more protocol stuff working --- shared/protocol.c | 9 +++++++++ shared/protocol.h | 9 +++++++++ shared/test.c | 16 +++++++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) (limited to 'shared') diff --git a/shared/protocol.c b/shared/protocol.c index 6e3bc73..bcb764e 100644 --- a/shared/protocol.c +++ b/shared/protocol.c @@ -39,10 +39,19 @@ void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input) { ws_s_protocol_response* response = ws_protocol_parse_finished(state->target); //TODO: send response + char response_first_line[16]; + sprintf(response_first_line, "%s,%x\n", response->success == WS_PROTOCOL_CMD_RETURN_OK ? "ok" : "error", response->msg->bytes); + ws_s_bin* response_first_line_bin = ws_bin_s_alloc(strlen(response_first_line)); + strncpy((char*) response_first_line_bin->data, response_first_line, strlen(response_first_line)); + ws_protocol_send_data(response_first_line_bin); + ws_protocol_send_data(response->msg); + free(response_first_line_bin); free(response->msg); free(response); + //TODO: reset command in parser_state for next command + return; } diff --git a/shared/protocol.h b/shared/protocol.h index a19fe48..74b289c 100644 --- a/shared/protocol.h +++ b/shared/protocol.h @@ -119,6 +119,15 @@ ws_s_bin* ws_protocol_req_last_records(unsigned int record_amount); */ void ws_protocol_res_last_records(ws_s_protocol_parsed_cmd* parsed_cmd, ws_s_protocol_response* response); +/** + * @brief data sender wrapper + * + * this function should be implemented in the source files of each target + * platform, as the send interface will be different on desktop and on the + * stm32. + */ +void ws_protocol_send_data(ws_s_bin* data); + /** @brief cmd codes (used to call handlers) */ typedef enum { WS_PROTOCOL_CMD_UNKNOWN = -1, diff --git a/shared/test.c b/shared/test.c index 5f2cd60..2c3c3a3 100644 --- a/shared/test.c +++ b/shared/test.c @@ -3,11 +3,25 @@ #include #include #include +#include #include "protocol.h" void ws_protocol_res_last_records(ws_s_protocol_parsed_cmd* parsed_cmd, ws_s_protocol_response* response) { - printf("last-records detected!\n"); + const char* response_text = "" + "id,temperature,humidity,atmospheric_pressure\n" + "10dc,2f,c5,7f\n" + "10dd,30,c6,7f\n" + "10de,31,c7,7f\n" + "10df,35,ca,7e\n" + "10e0,34,c9,7e\n"; + response->success = WS_PROTOCOL_CMD_RETURN_OK; + response->msg = ws_bin_s_alloc(strlen(response_text)); + strncpy((char*) response->msg->data, response_text, strlen(response_text)); +} + +void ws_protocol_send_data(ws_s_bin* data) { + printf("%.*s", data->bytes, data->data); } int main() { -- cgit v1.2.3 From a8ec533f4e548601e283cee628aaf5ffc1e7d662 Mon Sep 17 00:00:00 2001 From: lonkaars Date: Fri, 14 Oct 2022 17:06:02 +0200 Subject: parser done --- shared/protocol.c | 27 +++++++++++++++++---------- shared/protocol.h | 2 ++ shared/test.c | 1 + 3 files changed, 20 insertions(+), 10 deletions(-) (limited to 'shared') diff --git a/shared/protocol.c b/shared/protocol.c index bcb764e..377e79e 100644 --- a/shared/protocol.c +++ b/shared/protocol.c @@ -7,7 +7,6 @@ void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input) { switch(input) { case WS_PROTOCOL_C_NEWLINE: { - if (!state->valid) return; break; } @@ -34,11 +33,11 @@ void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input) { state->arg_len++; // parse cmd into argc and argv - ws_protocol_cmd_init(state); + if (state->valid) ws_protocol_cmd_init(state); // create response ws_s_protocol_response* response = ws_protocol_parse_finished(state->target); - //TODO: send response + // send response char response_first_line[16]; sprintf(response_first_line, "%s,%x\n", response->success == WS_PROTOCOL_CMD_RETURN_OK ? "ok" : "error", response->msg->bytes); ws_s_bin* response_first_line_bin = ws_bin_s_alloc(strlen(response_first_line)); @@ -46,11 +45,13 @@ void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input) { ws_protocol_send_data(response_first_line_bin); ws_protocol_send_data(response->msg); + // free response data containers free(response_first_line_bin); free(response->msg); free(response); - //TODO: reset command in parser_state for next command + // reset parser + ws_protocol_parser_reset(state); return; } @@ -59,6 +60,7 @@ void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input) { if (strlen(parsed_cmd->argv[0]) == strlen(name) && strncmp(parsed_cmd->argv[0], name, strlen(name)) == 0) return code; static ws_e_protocol_cmd ws_protocol_get_cmd_code(ws_s_protocol_parsed_cmd* parsed_cmd) { + if (parsed_cmd == NULL) return WS_PROTOCOL_CMD_UNKNOWN; // invalid command WS_CMD_MAP(parsed_cmd, "last-records", WS_PROTOCOL_CMD_LAST_RECORDS); return WS_PROTOCOL_CMD_UNKNOWN; @@ -91,10 +93,7 @@ void ws_protocol_parse_bytes(ws_s_protocol_parser_state* state, char* input, uns ws_s_protocol_parser_state* ws_protocol_parser_alloc() { ws_s_protocol_parser_state* parser_state = malloc(sizeof(ws_s_protocol_parser_state) + sizeof(uint16_t) * WS_PROTOCOL_CMD_MAX_ARGUMENTS); parser_state->cmd = malloc(sizeof(char) * WS_PROTOCOL_CMD_BUFFER_LEN); - parser_state->valid = true; - parser_state->cmd_len = 0; - parser_state->arg_len = 0; - parser_state->target = NULL; + ws_protocol_parser_reset(parser_state); return parser_state; } @@ -116,16 +115,24 @@ void ws_protocol_cmd_init(ws_s_protocol_parser_state* state) { void ws_protocol_parser_free(ws_s_protocol_parser_state* state) { if (state == NULL) return; if (state->target != NULL) ws_protocol_cmd_free(state->target); + state->target = NULL; free(state->cmd); free(state); - state = NULL; return; } +void ws_protocol_parser_reset(ws_s_protocol_parser_state* state) { + if (state->target != NULL) ws_protocol_cmd_free(state->target); + state->target = NULL; + state->valid = true; + state->cmd_len = 0; + state->arg_len = 0; + memset(state->args_len, 0, sizeof(uint16_t) * WS_PROTOCOL_CMD_MAX_ARGUMENTS); +} + void ws_protocol_cmd_free(ws_s_protocol_parsed_cmd* cmd) { for (unsigned int i = 0; i < cmd->argc; i++) free(cmd->argv[i]); free(cmd); - cmd = NULL; return; } diff --git a/shared/protocol.h b/shared/protocol.h index 74b289c..b7b92bb 100644 --- a/shared/protocol.h +++ b/shared/protocol.h @@ -56,6 +56,8 @@ typedef struct { ws_s_protocol_parser_state* ws_protocol_parser_alloc(); /** @brief deallocate parser struct, automatically frees all child pointers */ void ws_protocol_parser_free(ws_s_protocol_parser_state* state); +/** @brief reset parser state to parse a new request */ +void ws_protocol_parser_reset(ws_s_protocol_parser_state* state); /** * @brief initialize ws_s_protocol_parsed_cmd struct pointer of * ws_s_protocol_parser_state (internal only) diff --git a/shared/test.c b/shared/test.c index 2c3c3a3..588cd16 100644 --- a/shared/test.c +++ b/shared/test.c @@ -45,6 +45,7 @@ int main() { } ws_protocol_parser_free(parser1); + parser1 = NULL; return 0; } -- cgit v1.2.3 From 42bf616eac96eba1846c64123a4b3822061b418c Mon Sep 17 00:00:00 2001 From: lonkaars Date: Mon, 17 Oct 2022 12:11:37 +0200 Subject: update naming scheme in protocol.{c,h} --- shared/protocol.c | 44 +++++++++++++++++++++--------------------- shared/protocol.h | 57 +++++++++++++++++++++++-------------------------------- shared/test.c | 16 +++++++--------- 3 files changed, 53 insertions(+), 64 deletions(-) (limited to 'shared') diff --git a/shared/protocol.c b/shared/protocol.c index 377e79e..6795197 100644 --- a/shared/protocol.c +++ b/shared/protocol.c @@ -4,9 +4,9 @@ #include "protocol.h" -void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input) { +void ws_protocol_parse_req_byte(ws_s_protocol_req_parser_state* state, char input) { switch(input) { - case WS_PROTOCOL_C_NEWLINE: { + case WS_PROTOCOL_C_EOL: { break; } @@ -33,9 +33,9 @@ void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input) { state->arg_len++; // parse cmd into argc and argv - if (state->valid) ws_protocol_cmd_init(state); + if (state->valid) ws_protocol_req_cmd_init(state); // create response - ws_s_protocol_response* response = ws_protocol_parse_finished(state->target); + ws_s_protocol_res* response = ws_protocol_parse_req_finished(state->target); // send response char response_first_line[16]; @@ -51,7 +51,7 @@ void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input) { free(response); // reset parser - ws_protocol_parser_reset(state); + ws_protocol_req_parser_reset(state); return; } @@ -59,23 +59,23 @@ void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input) { #define WS_CMD_MAP(parsed_cmd, name, code) \ if (strlen(parsed_cmd->argv[0]) == strlen(name) && strncmp(parsed_cmd->argv[0], name, strlen(name)) == 0) return code; -static ws_e_protocol_cmd ws_protocol_get_cmd_code(ws_s_protocol_parsed_cmd* parsed_cmd) { +static ws_e_protocol_cmd ws_protocol_get_req_cmd_code(ws_s_protocol_parsed_req_cmd* parsed_cmd) { if (parsed_cmd == NULL) return WS_PROTOCOL_CMD_UNKNOWN; // invalid command WS_CMD_MAP(parsed_cmd, "last-records", WS_PROTOCOL_CMD_LAST_RECORDS); return WS_PROTOCOL_CMD_UNKNOWN; } -ws_s_protocol_response* ws_protocol_parse_finished(ws_s_protocol_parsed_cmd* parsed_cmd) { - ws_s_protocol_response* response = malloc(sizeof(ws_s_protocol_response)); +ws_s_protocol_res* ws_protocol_parse_req_finished(ws_s_protocol_parsed_req_cmd* parsed_cmd) { + ws_s_protocol_res* response = malloc(sizeof(ws_s_protocol_res)); response->success = WS_PROTOCOL_CMD_RETURN_ERROR; response->msg = NULL; - ws_e_protocol_cmd cmd_code = ws_protocol_get_cmd_code(parsed_cmd); + ws_e_protocol_cmd cmd_code = ws_protocol_get_req_cmd_code(parsed_cmd); if (cmd_code == WS_PROTOCOL_CMD_UNKNOWN) goto ws_protocol_parse_exit; if (cmd_code >= WS_PROTOCOL_CMD_AMOUNT) goto ws_protocol_parse_exit; - void (*ws_protocol_res_handler)(ws_s_protocol_parsed_cmd*, ws_s_protocol_response*) = + void (*ws_protocol_res_handler)(ws_s_protocol_parsed_req_cmd*, ws_s_protocol_res*) = g_ws_protocol_res_handlers[cmd_code]; if (ws_protocol_res_handler == NULL) goto ws_protocol_parse_exit; (*ws_protocol_res_handler)(parsed_cmd, response); @@ -86,19 +86,19 @@ ws_protocol_parse_exit: return response; } -void ws_protocol_parse_bytes(ws_s_protocol_parser_state* state, char* input, unsigned int length) { - for (unsigned int i = 0; i < length; i++) ws_protocol_parse_byte(state, input[i]); +void ws_protocol_parse_req_bytes(ws_s_protocol_req_parser_state* state, char* input, unsigned int length) { + for (unsigned int i = 0; i < length; i++) ws_protocol_parse_req_byte(state, input[i]); } -ws_s_protocol_parser_state* ws_protocol_parser_alloc() { - ws_s_protocol_parser_state* parser_state = malloc(sizeof(ws_s_protocol_parser_state) + sizeof(uint16_t) * WS_PROTOCOL_CMD_MAX_ARGUMENTS); +ws_s_protocol_req_parser_state* ws_protocol_req_parser_alloc() { + ws_s_protocol_req_parser_state* parser_state = malloc(sizeof(ws_s_protocol_req_parser_state) + sizeof(uint16_t) * WS_PROTOCOL_CMD_MAX_ARGUMENTS); parser_state->cmd = malloc(sizeof(char) * WS_PROTOCOL_CMD_BUFFER_LEN); - ws_protocol_parser_reset(parser_state); + ws_protocol_req_parser_reset(parser_state); return parser_state; } -void ws_protocol_cmd_init(ws_s_protocol_parser_state* state) { - state->target = malloc(sizeof(ws_s_protocol_parsed_cmd) + sizeof(char*) * state->arg_len); +void ws_protocol_req_cmd_init(ws_s_protocol_req_parser_state* state) { + state->target = malloc(sizeof(ws_s_protocol_parsed_req_cmd) + sizeof(char*) * state->arg_len); for (unsigned int i = 0; i < state->arg_len; i++) state->target->argv[i] = malloc(sizeof(char) * (state->args_len[i] + 1)); @@ -112,17 +112,17 @@ void ws_protocol_cmd_init(ws_s_protocol_parser_state* state) { } } -void ws_protocol_parser_free(ws_s_protocol_parser_state* state) { +void ws_protocol_req_parser_free(ws_s_protocol_req_parser_state* state) { if (state == NULL) return; - if (state->target != NULL) ws_protocol_cmd_free(state->target); + if (state->target != NULL) ws_protocol_req_cmd_free(state->target); state->target = NULL; free(state->cmd); free(state); return; } -void ws_protocol_parser_reset(ws_s_protocol_parser_state* state) { - if (state->target != NULL) ws_protocol_cmd_free(state->target); +void ws_protocol_req_parser_reset(ws_s_protocol_req_parser_state* state) { + if (state->target != NULL) ws_protocol_req_cmd_free(state->target); state->target = NULL; state->valid = true; state->cmd_len = 0; @@ -130,7 +130,7 @@ void ws_protocol_parser_reset(ws_s_protocol_parser_state* state) { memset(state->args_len, 0, sizeof(uint16_t) * WS_PROTOCOL_CMD_MAX_ARGUMENTS); } -void ws_protocol_cmd_free(ws_s_protocol_parsed_cmd* cmd) { +void ws_protocol_req_cmd_free(ws_s_protocol_parsed_req_cmd* cmd) { for (unsigned int i = 0; i < cmd->argc; i++) free(cmd->argv[i]); free(cmd); diff --git a/shared/protocol.h b/shared/protocol.h index b7b92bb..9f17e8d 100644 --- a/shared/protocol.h +++ b/shared/protocol.h @@ -10,31 +10,31 @@ #define WS_PROTOCOL_CMD_AMOUNT (1) -#define WS_PROTOCOL_C_NEWLINE (0x0a) +#define WS_PROTOCOL_C_EOL (0x0a) #define WS_PROTOCOL_C_SPACE (0x20) #define WS_PROTOCOL_C_NULL (0x00) /** - * @brief parsed cmd struct, holds arguments similar to argc and argv provided - * to `int main()` + * @brief parsed request cmd struct, holds arguments similar to argc and argv + * provided to `int main()` */ typedef struct { int argc; /** argument count */ char* argv[]; /** argument array, null terminated strings */ -} ws_s_protocol_parsed_cmd; +} ws_s_protocol_parsed_req_cmd; /** - * @brief holds parser state variables for `ws_protocol_parse_byte` function. + * @brief holds parser state variables for `ws_protocol_parse_req_byte` function. * each incoming tcp request should get it's own parser 'instance' */ typedef struct { - ws_s_protocol_parsed_cmd* target; /** parsed cmd reference */ + ws_s_protocol_parsed_req_cmd* target; /** parsed cmd reference */ bool valid; /** command still valid flag */ char* cmd; /** raw cmd */ uint16_t cmd_len; /** raw cmd string length */ uint16_t arg_len; /** amount of arguments */ uint16_t args_len[]; /** array of argument lengths */ -} ws_s_protocol_parser_state; +} ws_s_protocol_req_parser_state; /** @brief return values for command handlers */ typedef enum { @@ -46,64 +46,57 @@ typedef enum { typedef struct { ws_e_protocol_cmd_return_value success; ws_s_bin* msg; -} ws_s_protocol_response; +} ws_s_protocol_res; /** * @brief allocate parser struct * * @return pointer to newly allocated struct */ -ws_s_protocol_parser_state* ws_protocol_parser_alloc(); +ws_s_protocol_req_parser_state* ws_protocol_req_parser_alloc(); /** @brief deallocate parser struct, automatically frees all child pointers */ -void ws_protocol_parser_free(ws_s_protocol_parser_state* state); +void ws_protocol_req_parser_free(ws_s_protocol_req_parser_state* state); /** @brief reset parser state to parse a new request */ -void ws_protocol_parser_reset(ws_s_protocol_parser_state* state); +void ws_protocol_req_parser_reset(ws_s_protocol_req_parser_state* state); /** - * @brief initialize ws_s_protocol_parsed_cmd struct pointer of - * ws_s_protocol_parser_state (internal only) + * @brief initialize ws_s_protocol_parsed_req_cmd struct pointer of + * ws_s_protocol_req_parser_state (internal only) */ -void ws_protocol_cmd_init(ws_s_protocol_parser_state* state); -/** @brief deallocate ws_s_protocol_parsed_cmd struct pointer (internal only) */ -void ws_protocol_cmd_free(ws_s_protocol_parsed_cmd* cmd); +void ws_protocol_req_cmd_init(ws_s_protocol_req_parser_state* state); +/** @brief deallocate ws_s_protocol_parsed_req_cmd struct pointer (internal only) */ +void ws_protocol_req_cmd_free(ws_s_protocol_parsed_req_cmd* cmd); /** * @brief parse incoming data byte by byte until a finished command is detected * - * @remark [server] - * * @param state parser state object, each incoming request should have it's own parser state * @param input input byte */ -void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input); +void ws_protocol_parse_req_byte(ws_s_protocol_req_parser_state* state, char input); /** * @brief parse incoming data chunk * - * @remark [server] - * * @param state parser state object, each incoming request should have it's own parser state * @param input input byte array * @param length input byte array length */ -void ws_protocol_parse_bytes(ws_s_protocol_parser_state* state, char* input, unsigned int length); +void ws_protocol_parse_req_bytes(ws_s_protocol_req_parser_state* state, char* input, unsigned int length); /** * @brief handle complete command * - * this function gets called when ws_protocol_parse_byte has detected a + * this function gets called when ws_protocol_parse_req_byte(s) has detected a * finished command. this function decides which command handler gets called, * given that argv[0] contains a valid command. command argument parsing is * handled by the command handler function. * - * @remark [server] - * * @return response * - * @param parsed_cmd cmd parsed into ws_s_protocol_parsed_cmd struct + * @param parsed_cmd cmd parsed into ws_s_protocol_parsed_req_cmd struct */ -ws_s_protocol_response* ws_protocol_parse_finished(ws_s_protocol_parsed_cmd* parsed_cmd); +ws_s_protocol_res* ws_protocol_parse_req_finished(ws_s_protocol_parsed_req_cmd* parsed_cmd); /** * @brief create a `last-records` request command - * @remark [client] * @return ws_s_bin containing the command string */ ws_s_bin* ws_protocol_req_last_records(unsigned int record_amount); @@ -111,15 +104,13 @@ ws_s_bin* ws_protocol_req_last_records(unsigned int record_amount); /** * @brief `last-records` response handler * - * @remark [server] - * * gets fired when the weather station receives a complete `last-records` * command, and returns the response string * - * @param parsed_cmd complete parsed command from ws_protocol_parse_* + * @param parsed_cmd complete parsed command from ws_protocol_parse_req_* * @param response response struct with uninitialized pointer to msg */ -void ws_protocol_res_last_records(ws_s_protocol_parsed_cmd* parsed_cmd, ws_s_protocol_response* response); +void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s_protocol_res* response); /** * @brief data sender wrapper @@ -138,7 +129,7 @@ typedef enum { } ws_e_protocol_cmd; /** @brief response handlers, called when a command is parsed */ -static void (*g_ws_protocol_res_handlers[WS_PROTOCOL_CMD_AMOUNT])(ws_s_protocol_parsed_cmd*, ws_s_protocol_response*) = { +static void (*g_ws_protocol_res_handlers[WS_PROTOCOL_CMD_AMOUNT])(ws_s_protocol_parsed_req_cmd*, ws_s_protocol_res*) = { [WS_PROTOCOL_CMD_LAST_RECORDS] = &ws_protocol_res_last_records }; diff --git a/shared/test.c b/shared/test.c index 588cd16..f9d4bbc 100644 --- a/shared/test.c +++ b/shared/test.c @@ -7,7 +7,7 @@ #include "protocol.h" -void ws_protocol_res_last_records(ws_s_protocol_parsed_cmd* parsed_cmd, ws_s_protocol_response* response) { +void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s_protocol_res* response) { const char* response_text = "" "id,temperature,humidity,atmospheric_pressure\n" "10dc,2f,c5,7f\n" @@ -34,17 +34,15 @@ int main() { term.c_cc[VMIN] = 1; tcsetattr(STDIN_FILENO, 0, &term); - ws_s_protocol_parser_state* parser1 = ws_protocol_parser_alloc(); + ws_s_protocol_req_parser_state* parser1 = ws_protocol_req_parser_alloc(); - for(;;) { - fflush(stdout); + fflush(stdout); - char byte; - while(read(STDIN_FILENO, &byte, 1) > 0) - ws_protocol_parse_byte(parser1, byte); - } + char byte; + while(read(STDIN_FILENO, &byte, 1) > 0) + ws_protocol_parse_req_byte(parser1, byte); - ws_protocol_parser_free(parser1); + ws_protocol_req_parser_free(parser1); parser1 = NULL; return 0; -- cgit v1.2.3 From e555b5b5e0ad94279ef62d16feacee4976e2970d Mon Sep 17 00:00:00 2001 From: lonkaars Date: Wed, 19 Oct 2022 16:38:11 +0200 Subject: custom response data send handlers implemented --- shared/protocol.c | 35 ++++++++++++++++++----------------- shared/protocol.h | 42 +++++++++++++++++++++++++++--------------- shared/test.c | 17 +++++++++++++---- 3 files changed, 58 insertions(+), 36 deletions(-) (limited to 'shared') diff --git a/shared/protocol.c b/shared/protocol.c index 6795197..225b46f 100644 --- a/shared/protocol.c +++ b/shared/protocol.c @@ -4,6 +4,16 @@ #include "protocol.h" +#define WS_CMD_MAP(parsed_cmd, name, code) \ + if (strlen(parsed_cmd->argv[0]) == strlen(name) && strncmp(parsed_cmd->argv[0], name, strlen(name)) == 0) return code; + +static ws_e_protocol_cmd ws_protocol_get_req_cmd_code(ws_s_protocol_parsed_req_cmd* parsed_cmd) { + if (parsed_cmd == NULL) return WS_PROTOCOL_CMD_UNKNOWN; // invalid command + WS_CMD_MAP(parsed_cmd, "last-records", WS_PROTOCOL_CMD_LAST_RECORDS); + + return WS_PROTOCOL_CMD_UNKNOWN; +} + void ws_protocol_parse_req_byte(ws_s_protocol_req_parser_state* state, char input) { switch(input) { case WS_PROTOCOL_C_EOL: { @@ -43,7 +53,8 @@ void ws_protocol_parse_req_byte(ws_s_protocol_req_parser_state* state, char inpu ws_s_bin* response_first_line_bin = ws_bin_s_alloc(strlen(response_first_line)); strncpy((char*) response_first_line_bin->data, response_first_line, strlen(response_first_line)); ws_protocol_send_data(response_first_line_bin); - ws_protocol_send_data(response->msg); + if (!response->csh) ws_protocol_send_data(response->msg); + else (*g_ws_protocol_res_handlers[response->cmd_code])(state->target, response, true); // free response data containers free(response_first_line_bin); @@ -56,29 +67,19 @@ void ws_protocol_parse_req_byte(ws_s_protocol_req_parser_state* state, char inpu return; } -#define WS_CMD_MAP(parsed_cmd, name, code) \ - if (strlen(parsed_cmd->argv[0]) == strlen(name) && strncmp(parsed_cmd->argv[0], name, strlen(name)) == 0) return code; - -static ws_e_protocol_cmd ws_protocol_get_req_cmd_code(ws_s_protocol_parsed_req_cmd* parsed_cmd) { - if (parsed_cmd == NULL) return WS_PROTOCOL_CMD_UNKNOWN; // invalid command - WS_CMD_MAP(parsed_cmd, "last-records", WS_PROTOCOL_CMD_LAST_RECORDS); - - return WS_PROTOCOL_CMD_UNKNOWN; -} - ws_s_protocol_res* ws_protocol_parse_req_finished(ws_s_protocol_parsed_req_cmd* parsed_cmd) { ws_s_protocol_res* response = malloc(sizeof(ws_s_protocol_res)); response->success = WS_PROTOCOL_CMD_RETURN_ERROR; + response->csh = false; response->msg = NULL; + response->cmd_code = ws_protocol_get_req_cmd_code(parsed_cmd); - ws_e_protocol_cmd cmd_code = ws_protocol_get_req_cmd_code(parsed_cmd); - if (cmd_code == WS_PROTOCOL_CMD_UNKNOWN) goto ws_protocol_parse_exit; - if (cmd_code >= WS_PROTOCOL_CMD_AMOUNT) goto ws_protocol_parse_exit; + if (response->cmd_code == WS_PROTOCOL_CMD_UNKNOWN) goto ws_protocol_parse_exit; + if (response->cmd_code >= WS_PROTOCOL_CMD_AMOUNT) goto ws_protocol_parse_exit; - void (*ws_protocol_res_handler)(ws_s_protocol_parsed_req_cmd*, ws_s_protocol_res*) = - g_ws_protocol_res_handlers[cmd_code]; + ws_protocol_res_handler_t* ws_protocol_res_handler = g_ws_protocol_res_handlers[response->cmd_code]; if (ws_protocol_res_handler == NULL) goto ws_protocol_parse_exit; - (*ws_protocol_res_handler)(parsed_cmd, response); + (*ws_protocol_res_handler)(parsed_cmd, response, false); ws_protocol_parse_exit: diff --git a/shared/protocol.h b/shared/protocol.h index 9f17e8d..c31ebc6 100644 --- a/shared/protocol.h +++ b/shared/protocol.h @@ -42,10 +42,22 @@ typedef enum { WS_PROTOCOL_CMD_RETURN_ERROR = 1, } ws_e_protocol_cmd_return_value; +/** @brief cmd codes (used to call handlers) */ +typedef enum { + WS_PROTOCOL_CMD_UNKNOWN = -1, + + WS_PROTOCOL_CMD_LAST_RECORDS = 0, +} ws_e_protocol_cmd; + /** @brief request response data struct */ typedef struct { - ws_e_protocol_cmd_return_value success; - ws_s_bin* msg; + ws_e_protocol_cmd_return_value success; /** status code for response + validity, defaults to + WS_PROTOCOL_CMD_RETURN_ERROR */ + bool csh; /** whether the response handler has logic for a custom send + handler, false by default */ + ws_s_bin* msg; /** pointer to response data, uninitialized by default */ + ws_e_protocol_cmd cmd_code; /** cmd code */ } ws_s_protocol_res; /** @@ -102,15 +114,22 @@ ws_s_protocol_res* ws_protocol_parse_req_finished(ws_s_protocol_parsed_req_cmd* ws_s_bin* ws_protocol_req_last_records(unsigned int record_amount); /** - * @brief `last-records` response handler + * @brief response handler * - * gets fired when the weather station receives a complete `last-records` - * command, and returns the response string + * gets fired when the weather station receives a complete command, and returns + * a response struct with a success code and an optional message. if + * response->csh is set to `true` within the handler, it gets fired a second + * time after the response header is sent, but with the `send` parameter set to + * `true`. this is so response handlers can send large amounts of data without + * allocating large areas of memory. * * @param parsed_cmd complete parsed command from ws_protocol_parse_req_* * @param response response struct with uninitialized pointer to msg + * @param send `false` on first run, `true` on second run if `response->csh` was set to true */ -void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s_protocol_res* response); +typedef void ws_protocol_res_handler_t(ws_s_protocol_parsed_req_cmd*, ws_s_protocol_res*, bool); + +ws_protocol_res_handler_t ws_protocol_res_last_records; /** * @brief data sender wrapper @@ -121,15 +140,8 @@ void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s */ void ws_protocol_send_data(ws_s_bin* data); -/** @brief cmd codes (used to call handlers) */ -typedef enum { - WS_PROTOCOL_CMD_UNKNOWN = -1, - - WS_PROTOCOL_CMD_LAST_RECORDS = 0, -} ws_e_protocol_cmd; - /** @brief response handlers, called when a command is parsed */ -static void (*g_ws_protocol_res_handlers[WS_PROTOCOL_CMD_AMOUNT])(ws_s_protocol_parsed_req_cmd*, ws_s_protocol_res*) = { - [WS_PROTOCOL_CMD_LAST_RECORDS] = &ws_protocol_res_last_records +static ws_protocol_res_handler_t* g_ws_protocol_res_handlers[WS_PROTOCOL_CMD_AMOUNT] = { + [WS_PROTOCOL_CMD_LAST_RECORDS] = &ws_protocol_res_last_records, }; diff --git a/shared/test.c b/shared/test.c index f9d4bbc..0968252 100644 --- a/shared/test.c +++ b/shared/test.c @@ -7,7 +7,7 @@ #include "protocol.h" -void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s_protocol_res* response) { +void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s_protocol_res* response, bool send) { const char* response_text = "" "id,temperature,humidity,atmospheric_pressure\n" "10dc,2f,c5,7f\n" @@ -15,9 +15,18 @@ void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s "10de,31,c7,7f\n" "10df,35,ca,7e\n" "10e0,34,c9,7e\n"; - response->success = WS_PROTOCOL_CMD_RETURN_OK; - response->msg = ws_bin_s_alloc(strlen(response_text)); - strncpy((char*) response->msg->data, response_text, strlen(response_text)); + + if (!send) { + response->success = WS_PROTOCOL_CMD_RETURN_OK; + response->csh = true; + response->msg = ws_bin_s_alloc(0); + response->msg->bytes = strlen(response_text); + } else { + // example send routine + ws_s_bin* response_test = ws_bin_s_alloc(strlen(response_text)); + strncpy((char*) response_test->data, response_text, strlen(response_text)); + ws_protocol_send_data(response_test); + } } void ws_protocol_send_data(ws_s_bin* data) { -- cgit v1.2.3 From 89b8217435286081dc0fe481559da3ee5c20c72a Mon Sep 17 00:00:00 2001 From: lonkaars Date: Wed, 19 Oct 2022 18:15:36 +0200 Subject: last-records response handler implemented --- shared/backlog.c | 47 ++++++++++++++++++++++++++++++++++++++++++ shared/backlog.h | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++ shared/protocol.c | 7 ++----- shared/protocol.h | 5 ++++- shared/test.c | 50 +++++++++++++++++++++++++++++++++------------ shared/util.h | 4 ++++ stm32f091/backlog.c | 41 ++++++++++++++++++++++++------------- stm32f091/backlog.h | 4 +++- 8 files changed, 183 insertions(+), 34 deletions(-) create mode 100644 shared/backlog.c create mode 100644 shared/backlog.h create mode 100644 shared/util.h (limited to 'shared') diff --git a/shared/backlog.c b/shared/backlog.c new file mode 100644 index 0000000..926ccad --- /dev/null +++ b/shared/backlog.c @@ -0,0 +1,47 @@ +#include + +#include "backlog.h" + +ws_s_backlog_database* g_ws_backlog_database = NULL; + +void ws_backlog_alloc(uint16_t record_amt) { + g_ws_backlog_database = malloc(sizeof(ws_s_backlog_database) + sizeof(ws_s_backlog_record) * record_amt); + g_ws_backlog_database->buffer_size = record_amt; + g_ws_backlog_database->buffer_start = 0; + g_ws_backlog_database->buffer_end = 0; +} + +void ws_backlog_add_record(ws_s_backlog_record record) { + static uint16_t id = 0; + + g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].id = id++; + g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].sens_atm_pressure = record.sens_atm_pressure; + g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].sens_humidity = record.sens_humidity; + g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].sens_temperature = record.sens_temperature; + + // shift buffer start/end + g_ws_backlog_database->buffer_end = (g_ws_backlog_database->buffer_end + 1) % g_ws_backlog_database->buffer_size; + if (g_ws_backlog_database->buffer_end == g_ws_backlog_database->buffer_start) + g_ws_backlog_database->buffer_start = (g_ws_backlog_database->buffer_start + 1) % g_ws_backlog_database->buffer_size; +} + +ws_s_backlog_record* ws_backlog_get_record(uint16_t record_index) { + return &g_ws_backlog_database->records[record_index]; +} + +ws_s_backlog_record* ws_backlog_get_last_record(uint16_t record_offset) { + return ws_backlog_get_record((g_ws_backlog_database->buffer_end - record_offset - 1) % g_ws_backlog_database->buffer_size); +} + +static uint16_t mod(uint16_t a, uint16_t b) { + uint16_t m = a % b; + return m < 0 ? (b < 0) ? m - b : m + b : m; +} + +uint16_t ws_backlog_get_record_count() { + // add buffer_size to the result of the modulo operation if it's result is negative + // (only works when buffer_size is less than 2^15) + // this is a consequence of the way in which c handles negative numbers in modulo operations + int16_t mod = (g_ws_backlog_database->buffer_end - g_ws_backlog_database->buffer_start) % g_ws_backlog_database->buffer_size; + return mod < 0 ? mod + g_ws_backlog_database->buffer_size : mod; +} diff --git a/shared/backlog.h b/shared/backlog.h new file mode 100644 index 0000000..c8ea019 --- /dev/null +++ b/shared/backlog.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +/** + * @brief allocate backlog buffer and set global backlog pointer + * @param record_amt amount of records to keep before overwriting oldest record + */ +void ws_backlog_alloc(uint16_t record_amt); + +// enable struct packing +#pragma pack(push, 1) + +/** @brief backlog record */ +typedef struct { + uint16_t id; /**< unique record identifier, numbered sequentially */ + uint8_t sens_temperature; /**< temperature reading */ + uint8_t sens_humidity; /**< humidity reading */ + uint8_t sens_atm_pressure; /**< atmospheric pressure reading */ +} ws_s_backlog_record; + +typedef struct { + uint16_t buffer_size; /**< buffer size (amount of records) */ + uint16_t buffer_start; /** first record index */ + uint16_t buffer_end; /** last record index */ + ws_s_backlog_record records[]; /** record array */ +} ws_s_backlog_database; + +// disable struct packing +#pragma pack(pop) + +/** @brief global record backlog database pointer */ +extern ws_s_backlog_database* g_ws_backlog_database; + +/** + * @brief add record to database + * + * automatically sets record.id, pushes buffer_end forwards and overwrites the + * last record if the buffer is full + */ +void ws_backlog_add_record(ws_s_backlog_record record); + +/** + * there's intentionally no function to retrieve multiple records as an array, + * as this would either require + * (a) copying the selection which is not possible with the current memory + * constraints, or + * (b) giving a direct pointer, but this would cause undefined behavior at the + * ring buffer seam + */ + +/** @brief get pointer to record with index `record_index` from the database */ +ws_s_backlog_record* ws_backlog_get_record(uint16_t record_index); + +/** @brief get pointer to last record with offset `record_offset` from the database */ +ws_s_backlog_record* ws_backlog_get_last_record(uint16_t record_offset); + +/** @brief return amount of valid records in database */ +uint16_t ws_backlog_get_record_count(); diff --git a/shared/protocol.c b/shared/protocol.c index 225b46f..55e6759 100644 --- a/shared/protocol.c +++ b/shared/protocol.c @@ -50,14 +50,11 @@ void ws_protocol_parse_req_byte(ws_s_protocol_req_parser_state* state, char inpu // send response char response_first_line[16]; sprintf(response_first_line, "%s,%x\n", response->success == WS_PROTOCOL_CMD_RETURN_OK ? "ok" : "error", response->msg->bytes); - ws_s_bin* response_first_line_bin = ws_bin_s_alloc(strlen(response_first_line)); - strncpy((char*) response_first_line_bin->data, response_first_line, strlen(response_first_line)); - ws_protocol_send_data(response_first_line_bin); - if (!response->csh) ws_protocol_send_data(response->msg); + ws_protocol_send_data(response_first_line, strlen(response_first_line)); + if (!response->csh) ws_protocol_send_data((char*) response->msg->data, response->msg->bytes); else (*g_ws_protocol_res_handlers[response->cmd_code])(state->target, response, true); // free response data containers - free(response_first_line_bin); free(response->msg); free(response); diff --git a/shared/protocol.h b/shared/protocol.h index c31ebc6..fbe29d6 100644 --- a/shared/protocol.h +++ b/shared/protocol.h @@ -137,8 +137,11 @@ ws_protocol_res_handler_t ws_protocol_res_last_records; * this function should be implemented in the source files of each target * platform, as the send interface will be different on desktop and on the * stm32. + * + * @param data pointer to data char array + * @param length length of data array */ -void ws_protocol_send_data(ws_s_bin* data); +void ws_protocol_send_data(const char* data, unsigned int length); /** @brief response handlers, called when a command is parsed */ static ws_protocol_res_handler_t* g_ws_protocol_res_handlers[WS_PROTOCOL_CMD_AMOUNT] = { diff --git a/shared/test.c b/shared/test.c index 0968252..287332a 100644 --- a/shared/test.c +++ b/shared/test.c @@ -6,34 +6,40 @@ #include #include "protocol.h" +#include "backlog.h" +#include "util.h" void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s_protocol_res* response, bool send) { - const char* response_text = "" - "id,temperature,humidity,atmospheric_pressure\n" - "10dc,2f,c5,7f\n" - "10dd,30,c6,7f\n" - "10de,31,c7,7f\n" - "10df,35,ca,7e\n" - "10e0,34,c9,7e\n"; + static unsigned int record_amount = 0; + const char* response_header = "id,temperature,humidity,atmospheric_pressure\n"; + const size_t response_line_size = sizeof("xxxx,xx,xx,xx\n"); if (!send) { response->success = WS_PROTOCOL_CMD_RETURN_OK; + if (sscanf(parsed_cmd->argv[1], "%u", &record_amount) < 1) response->success = WS_PROTOCOL_CMD_RETURN_ERROR; + record_amount = WS_MIN(record_amount, ws_backlog_get_record_count()); response->csh = true; response->msg = ws_bin_s_alloc(0); - response->msg->bytes = strlen(response_text); + response->msg->bytes = strlen(response_header) + response_line_size * record_amount; } else { // example send routine - ws_s_bin* response_test = ws_bin_s_alloc(strlen(response_text)); - strncpy((char*) response_test->data, response_text, strlen(response_text)); - ws_protocol_send_data(response_test); + ws_protocol_send_data(response_header, strlen(response_header)); + char line[response_line_size + 1]; // +1 for null terminator -> sprintf + for (unsigned int i = 0; i < record_amount; i++) { + ws_s_backlog_record* record = ws_backlog_get_last_record(i); + sprintf(line, "%04x,%02x,%02x,%02x\n", record->id, record->sens_temperature, record->sens_humidity, record->sens_atm_pressure); + ws_protocol_send_data(line, response_line_size); + } } } -void ws_protocol_send_data(ws_s_bin* data) { - printf("%.*s", data->bytes, data->data); +void ws_protocol_send_data(const char* data, unsigned int length) { + printf("%.*s", length, data); } int main() { + ws_backlog_alloc(10); + // disable echo and enable raw mode fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); struct termios term; @@ -45,6 +51,24 @@ int main() { ws_s_protocol_req_parser_state* parser1 = ws_protocol_req_parser_alloc(); + ws_backlog_add_record((ws_s_backlog_record) { + .sens_temperature = 0x29, + .sens_humidity = 0x34, + .sens_atm_pressure = 0x69, + }); + + ws_backlog_add_record((ws_s_backlog_record) { + .sens_temperature = 0x00, + .sens_humidity = 0x00, + .sens_atm_pressure = 0x00, + }); + + ws_backlog_add_record((ws_s_backlog_record) { + .sens_temperature = 0x01, + .sens_humidity = 0x01, + .sens_atm_pressure = 0x01, + }); + fflush(stdout); char byte; diff --git a/shared/util.h b/shared/util.h new file mode 100644 index 0000000..f39ff34 --- /dev/null +++ b/shared/util.h @@ -0,0 +1,4 @@ +#pragma once + +#define WS_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define WS_MAX(a, b) (((a) > (b)) ? (a) : (b)) diff --git a/stm32f091/backlog.c b/stm32f091/backlog.c index 3f21924..926ccad 100644 --- a/stm32f091/backlog.c +++ b/stm32f091/backlog.c @@ -2,33 +2,46 @@ #include "backlog.h" -ws_s_backlog_database* WS_G_BACKLOG_DATABASE = NULL; +ws_s_backlog_database* g_ws_backlog_database = NULL; void ws_backlog_alloc(uint16_t record_amt) { - WS_G_BACKLOG_DATABASE = malloc(sizeof(ws_s_backlog_database) + sizeof(ws_s_backlog_record) * record_amt); - WS_G_BACKLOG_DATABASE->buffer_size = record_amt; - WS_G_BACKLOG_DATABASE->buffer_start = 0; - WS_G_BACKLOG_DATABASE->buffer_end = 0; + g_ws_backlog_database = malloc(sizeof(ws_s_backlog_database) + sizeof(ws_s_backlog_record) * record_amt); + g_ws_backlog_database->buffer_size = record_amt; + g_ws_backlog_database->buffer_start = 0; + g_ws_backlog_database->buffer_end = 0; } void ws_backlog_add_record(ws_s_backlog_record record) { static uint16_t id = 0; - WS_G_BACKLOG_DATABASE->records[WS_G_BACKLOG_DATABASE->buffer_end].id = id++; - WS_G_BACKLOG_DATABASE->records[WS_G_BACKLOG_DATABASE->buffer_end].sens_atm_pressure = record.sens_atm_pressure; - WS_G_BACKLOG_DATABASE->records[WS_G_BACKLOG_DATABASE->buffer_end].sens_humidity = record.sens_humidity; - WS_G_BACKLOG_DATABASE->records[WS_G_BACKLOG_DATABASE->buffer_end].sens_temperature = record.sens_temperature; + g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].id = id++; + g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].sens_atm_pressure = record.sens_atm_pressure; + g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].sens_humidity = record.sens_humidity; + g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].sens_temperature = record.sens_temperature; // shift buffer start/end - WS_G_BACKLOG_DATABASE->buffer_end = (WS_G_BACKLOG_DATABASE->buffer_end + 1) % WS_G_BACKLOG_DATABASE->buffer_size; - if (WS_G_BACKLOG_DATABASE->buffer_end == WS_G_BACKLOG_DATABASE->buffer_start) - WS_G_BACKLOG_DATABASE->buffer_start = (WS_G_BACKLOG_DATABASE->buffer_start + 1) % WS_G_BACKLOG_DATABASE->buffer_size; + g_ws_backlog_database->buffer_end = (g_ws_backlog_database->buffer_end + 1) % g_ws_backlog_database->buffer_size; + if (g_ws_backlog_database->buffer_end == g_ws_backlog_database->buffer_start) + g_ws_backlog_database->buffer_start = (g_ws_backlog_database->buffer_start + 1) % g_ws_backlog_database->buffer_size; } ws_s_backlog_record* ws_backlog_get_record(uint16_t record_index) { - return &WS_G_BACKLOG_DATABASE->records[record_index]; + return &g_ws_backlog_database->records[record_index]; } ws_s_backlog_record* ws_backlog_get_last_record(uint16_t record_offset) { - return ws_backlog_get_record((WS_G_BACKLOG_DATABASE->buffer_end - record_offset - 1) % WS_G_BACKLOG_DATABASE->buffer_size); + return ws_backlog_get_record((g_ws_backlog_database->buffer_end - record_offset - 1) % g_ws_backlog_database->buffer_size); +} + +static uint16_t mod(uint16_t a, uint16_t b) { + uint16_t m = a % b; + return m < 0 ? (b < 0) ? m - b : m + b : m; +} + +uint16_t ws_backlog_get_record_count() { + // add buffer_size to the result of the modulo operation if it's result is negative + // (only works when buffer_size is less than 2^15) + // this is a consequence of the way in which c handles negative numbers in modulo operations + int16_t mod = (g_ws_backlog_database->buffer_end - g_ws_backlog_database->buffer_start) % g_ws_backlog_database->buffer_size; + return mod < 0 ? mod + g_ws_backlog_database->buffer_size : mod; } diff --git a/stm32f091/backlog.h b/stm32f091/backlog.h index 465b3c0..c8ea019 100644 --- a/stm32f091/backlog.h +++ b/stm32f091/backlog.h @@ -30,7 +30,7 @@ typedef struct { #pragma pack(pop) /** @brief global record backlog database pointer */ -extern ws_s_backlog_database* WS_G_BACKLOG_DATABASE; +extern ws_s_backlog_database* g_ws_backlog_database; /** * @brief add record to database @@ -55,3 +55,5 @@ ws_s_backlog_record* ws_backlog_get_record(uint16_t record_index); /** @brief get pointer to last record with offset `record_offset` from the database */ ws_s_backlog_record* ws_backlog_get_last_record(uint16_t record_offset); +/** @brief return amount of valid records in database */ +uint16_t ws_backlog_get_record_count(); -- cgit v1.2.3 From aab4ed17b94f54813368201d3a0cba3f3d432589 Mon Sep 17 00:00:00 2001 From: lonkaars Date: Wed, 19 Oct 2022 20:04:06 +0200 Subject: integrate protocol code into stm32 code --- shared/backlog.c | 47 ---------------------------- shared/backlog.h | 59 ----------------------------------- shared/makefile | 24 --------------- shared/protocol.c | 87 ++++++++++++++++++++++++++-------------------------- shared/protocol.md | 7 +++-- shared/shared.mk | 1 + shared/test.c | 82 ------------------------------------------------- shared/testcmd | 1 - stm32f091/backlog.c | 5 --- stm32f091/esp8266.c | 7 +++-- stm32f091/main.c | 1 - stm32f091/makefile | 8 +++++ stm32f091/protocol.c | 33 ++++++++++++++++++++ stm32f091/sensor.c | 6 ++++ stm32f091/server.c | 2 +- stm32f091/util.h | 3 +- 16 files changed, 103 insertions(+), 270 deletions(-) delete mode 100644 shared/backlog.c delete mode 100644 shared/backlog.h delete mode 100644 shared/makefile create mode 100644 shared/shared.mk delete mode 100644 shared/test.c delete mode 100644 shared/testcmd create mode 100644 stm32f091/protocol.c (limited to 'shared') diff --git a/shared/backlog.c b/shared/backlog.c deleted file mode 100644 index 926ccad..0000000 --- a/shared/backlog.c +++ /dev/null @@ -1,47 +0,0 @@ -#include - -#include "backlog.h" - -ws_s_backlog_database* g_ws_backlog_database = NULL; - -void ws_backlog_alloc(uint16_t record_amt) { - g_ws_backlog_database = malloc(sizeof(ws_s_backlog_database) + sizeof(ws_s_backlog_record) * record_amt); - g_ws_backlog_database->buffer_size = record_amt; - g_ws_backlog_database->buffer_start = 0; - g_ws_backlog_database->buffer_end = 0; -} - -void ws_backlog_add_record(ws_s_backlog_record record) { - static uint16_t id = 0; - - g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].id = id++; - g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].sens_atm_pressure = record.sens_atm_pressure; - g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].sens_humidity = record.sens_humidity; - g_ws_backlog_database->records[g_ws_backlog_database->buffer_end].sens_temperature = record.sens_temperature; - - // shift buffer start/end - g_ws_backlog_database->buffer_end = (g_ws_backlog_database->buffer_end + 1) % g_ws_backlog_database->buffer_size; - if (g_ws_backlog_database->buffer_end == g_ws_backlog_database->buffer_start) - g_ws_backlog_database->buffer_start = (g_ws_backlog_database->buffer_start + 1) % g_ws_backlog_database->buffer_size; -} - -ws_s_backlog_record* ws_backlog_get_record(uint16_t record_index) { - return &g_ws_backlog_database->records[record_index]; -} - -ws_s_backlog_record* ws_backlog_get_last_record(uint16_t record_offset) { - return ws_backlog_get_record((g_ws_backlog_database->buffer_end - record_offset - 1) % g_ws_backlog_database->buffer_size); -} - -static uint16_t mod(uint16_t a, uint16_t b) { - uint16_t m = a % b; - return m < 0 ? (b < 0) ? m - b : m + b : m; -} - -uint16_t ws_backlog_get_record_count() { - // add buffer_size to the result of the modulo operation if it's result is negative - // (only works when buffer_size is less than 2^15) - // this is a consequence of the way in which c handles negative numbers in modulo operations - int16_t mod = (g_ws_backlog_database->buffer_end - g_ws_backlog_database->buffer_start) % g_ws_backlog_database->buffer_size; - return mod < 0 ? mod + g_ws_backlog_database->buffer_size : mod; -} diff --git a/shared/backlog.h b/shared/backlog.h deleted file mode 100644 index c8ea019..0000000 --- a/shared/backlog.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include - -/** - * @brief allocate backlog buffer and set global backlog pointer - * @param record_amt amount of records to keep before overwriting oldest record - */ -void ws_backlog_alloc(uint16_t record_amt); - -// enable struct packing -#pragma pack(push, 1) - -/** @brief backlog record */ -typedef struct { - uint16_t id; /**< unique record identifier, numbered sequentially */ - uint8_t sens_temperature; /**< temperature reading */ - uint8_t sens_humidity; /**< humidity reading */ - uint8_t sens_atm_pressure; /**< atmospheric pressure reading */ -} ws_s_backlog_record; - -typedef struct { - uint16_t buffer_size; /**< buffer size (amount of records) */ - uint16_t buffer_start; /** first record index */ - uint16_t buffer_end; /** last record index */ - ws_s_backlog_record records[]; /** record array */ -} ws_s_backlog_database; - -// disable struct packing -#pragma pack(pop) - -/** @brief global record backlog database pointer */ -extern ws_s_backlog_database* g_ws_backlog_database; - -/** - * @brief add record to database - * - * automatically sets record.id, pushes buffer_end forwards and overwrites the - * last record if the buffer is full - */ -void ws_backlog_add_record(ws_s_backlog_record record); - -/** - * there's intentionally no function to retrieve multiple records as an array, - * as this would either require - * (a) copying the selection which is not possible with the current memory - * constraints, or - * (b) giving a direct pointer, but this would cause undefined behavior at the - * ring buffer seam - */ - -/** @brief get pointer to record with index `record_index` from the database */ -ws_s_backlog_record* ws_backlog_get_record(uint16_t record_index); - -/** @brief get pointer to last record with offset `record_offset` from the database */ -ws_s_backlog_record* ws_backlog_get_last_record(uint16_t record_offset); - -/** @brief return amount of valid records in database */ -uint16_t ws_backlog_get_record_count(); diff --git a/shared/makefile b/shared/makefile deleted file mode 100644 index 2093f00..0000000 --- a/shared/makefile +++ /dev/null @@ -1,24 +0,0 @@ -CC = gcc -LD = gcc -RM = rm -f -CFLAGS = -g -std=c11 -LFLAGS = -TARGET = main - -SRCS := $(wildcard *.c) -OBJS := $(patsubst %.c,%.o, $(SRCS)) - -all: main - -%.o: %.c - $(CC) -c $(CFLAGS) $< -o $@ - -$(TARGET): $(OBJS) - $(LD) $^ $(LFLAGS) -o $@ - -clean: - $(RM) $(TARGET) $(OBJS) - -compile_commands: clean - compiledb make - diff --git a/shared/protocol.c b/shared/protocol.c index 55e6759..c6e5ddd 100644 --- a/shared/protocol.c +++ b/shared/protocol.c @@ -15,30 +15,30 @@ static ws_e_protocol_cmd ws_protocol_get_req_cmd_code(ws_s_protocol_parsed_req_c } void ws_protocol_parse_req_byte(ws_s_protocol_req_parser_state* state, char input) { - switch(input) { - case WS_PROTOCOL_C_EOL: { - break; - } + switch(input) { + case WS_PROTOCOL_C_EOL: { + break; + } - case WS_PROTOCOL_C_SPACE: { - if (!state->valid) return; + case WS_PROTOCOL_C_SPACE: { + if (!state->valid) return; state->arg_len++; - return; - } - - case WS_PROTOCOL_C_NULL: { - state->valid = false; - return; - } - - default: { - if (!state->valid) return; - state->cmd[state->cmd_len++] = input; - state->args_len[state->arg_len] += 1; - if (state->cmd_len == WS_PROTOCOL_CMD_BUFFER_LEN) state->valid = false; - return; - } - } + return; + } + + case WS_PROTOCOL_C_NULL: { + state->valid = false; + return; + } + + default: { + if (!state->valid) return; + state->cmd[state->cmd_len++] = input; + state->args_len[state->arg_len] += 1; + if (state->cmd_len == WS_PROTOCOL_CMD_BUFFER_LEN) state->valid = false; + return; + } + } // arg_len is used as an index while parsing, so add 1 to get length state->arg_len++; @@ -85,22 +85,23 @@ ws_protocol_parse_exit: } void ws_protocol_parse_req_bytes(ws_s_protocol_req_parser_state* state, char* input, unsigned int length) { - for (unsigned int i = 0; i < length; i++) ws_protocol_parse_req_byte(state, input[i]); + for (unsigned int i = 0; i < length; i++) ws_protocol_parse_req_byte(state, input[i]); } ws_s_protocol_req_parser_state* ws_protocol_req_parser_alloc() { - ws_s_protocol_req_parser_state* parser_state = malloc(sizeof(ws_s_protocol_req_parser_state) + sizeof(uint16_t) * WS_PROTOCOL_CMD_MAX_ARGUMENTS); - parser_state->cmd = malloc(sizeof(char) * WS_PROTOCOL_CMD_BUFFER_LEN); + ws_s_protocol_req_parser_state* parser_state = malloc(sizeof(ws_s_protocol_req_parser_state) + sizeof(uint16_t) * WS_PROTOCOL_CMD_MAX_ARGUMENTS); + parser_state->cmd = malloc(sizeof(char) * WS_PROTOCOL_CMD_BUFFER_LEN); + parser_state->target = NULL; ws_protocol_req_parser_reset(parser_state); - return parser_state; + return parser_state; } void ws_protocol_req_cmd_init(ws_s_protocol_req_parser_state* state) { - state->target = malloc(sizeof(ws_s_protocol_parsed_req_cmd) + sizeof(char*) * state->arg_len); - for (unsigned int i = 0; i < state->arg_len; i++) - state->target->argv[i] = malloc(sizeof(char) * (state->args_len[i] + 1)); + state->target = malloc(sizeof(ws_s_protocol_parsed_req_cmd) + sizeof(char*) * state->arg_len); + for (unsigned int i = 0; i < state->arg_len; i++) + state->target->argv[i] = malloc(sizeof(char) * (state->args_len[i] + 1)); - state->target->argc = state->arg_len; + state->target->argc = state->arg_len; unsigned int head = 0; for (unsigned int i = 0; i < state->arg_len; i++) { @@ -111,26 +112,26 @@ void ws_protocol_req_cmd_init(ws_s_protocol_req_parser_state* state) { } void ws_protocol_req_parser_free(ws_s_protocol_req_parser_state* state) { - if (state == NULL) return; - if (state->target != NULL) ws_protocol_req_cmd_free(state->target); + if (state == NULL) return; + if (state->target != NULL) ws_protocol_req_cmd_free(state->target); state->target = NULL; - free(state->cmd); - free(state); - return; + free(state->cmd); + free(state); + return; } void ws_protocol_req_parser_reset(ws_s_protocol_req_parser_state* state) { if (state->target != NULL) ws_protocol_req_cmd_free(state->target); - state->target = NULL; - state->valid = true; - state->cmd_len = 0; - state->arg_len = 0; + state->target = NULL; + state->valid = true; + state->cmd_len = 0; + state->arg_len = 0; memset(state->args_len, 0, sizeof(uint16_t) * WS_PROTOCOL_CMD_MAX_ARGUMENTS); } void ws_protocol_req_cmd_free(ws_s_protocol_parsed_req_cmd* cmd) { - for (unsigned int i = 0; i < cmd->argc; i++) - free(cmd->argv[i]); - free(cmd); - return; + for (int i = 0; i < cmd->argc; i++) + free(cmd->argv[i]); + free(cmd); + return; } diff --git a/shared/protocol.md b/shared/protocol.md index bafec4d..b6e955c 100644 --- a/shared/protocol.md +++ b/shared/protocol.md @@ -29,8 +29,9 @@ conventions. Returns the last `n` records in csv format. The first line has the csv table header, with the fields `id`, `temperature`, `humidity`, and `atmospheric_pressure`. The rest of the response consists of 1 record per line. -When `n` is 0, or no records exist yet, the csv header is still returned, but -without any records. +The amount of records is limited to the amount of valid records in the backlog +buffer. When the amount of returned records is 0, the response consists of the +csv header, but without any following records. ## Example transaction @@ -39,7 +40,7 @@ starting with `<`, and response by lines starting with `>`. ``` < last-records 5<0a> -> ok,115<0a> +> ok,73<0a> > id,temperature,humidity,atmospheric_pressure<0a> > 10dc,2f,c5,7f<0a> > 10dd,30,c6,7f<0a> diff --git a/shared/shared.mk b/shared/shared.mk new file mode 100644 index 0000000..f9586ff --- /dev/null +++ b/shared/shared.mk @@ -0,0 +1 @@ +OBJS += $(patsubst %.c,%-stm.o, $(wildcard ../shared/*.c)) diff --git a/shared/test.c b/shared/test.c deleted file mode 100644 index 287332a..0000000 --- a/shared/test.c +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "protocol.h" -#include "backlog.h" -#include "util.h" - -void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s_protocol_res* response, bool send) { - static unsigned int record_amount = 0; - const char* response_header = "id,temperature,humidity,atmospheric_pressure\n"; - const size_t response_line_size = sizeof("xxxx,xx,xx,xx\n"); - - if (!send) { - response->success = WS_PROTOCOL_CMD_RETURN_OK; - if (sscanf(parsed_cmd->argv[1], "%u", &record_amount) < 1) response->success = WS_PROTOCOL_CMD_RETURN_ERROR; - record_amount = WS_MIN(record_amount, ws_backlog_get_record_count()); - response->csh = true; - response->msg = ws_bin_s_alloc(0); - response->msg->bytes = strlen(response_header) + response_line_size * record_amount; - } else { - // example send routine - ws_protocol_send_data(response_header, strlen(response_header)); - char line[response_line_size + 1]; // +1 for null terminator -> sprintf - for (unsigned int i = 0; i < record_amount; i++) { - ws_s_backlog_record* record = ws_backlog_get_last_record(i); - sprintf(line, "%04x,%02x,%02x,%02x\n", record->id, record->sens_temperature, record->sens_humidity, record->sens_atm_pressure); - ws_protocol_send_data(line, response_line_size); - } - } -} - -void ws_protocol_send_data(const char* data, unsigned int length) { - printf("%.*s", length, data); -} - -int main() { - ws_backlog_alloc(10); - - // disable echo and enable raw mode - fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); - struct termios term; - tcgetattr(STDIN_FILENO, &term); - term.c_lflag &= ~(ECHO | ICANON); - term.c_cc[VTIME] = 0; - term.c_cc[VMIN] = 1; - tcsetattr(STDIN_FILENO, 0, &term); - - ws_s_protocol_req_parser_state* parser1 = ws_protocol_req_parser_alloc(); - - ws_backlog_add_record((ws_s_backlog_record) { - .sens_temperature = 0x29, - .sens_humidity = 0x34, - .sens_atm_pressure = 0x69, - }); - - ws_backlog_add_record((ws_s_backlog_record) { - .sens_temperature = 0x00, - .sens_humidity = 0x00, - .sens_atm_pressure = 0x00, - }); - - ws_backlog_add_record((ws_s_backlog_record) { - .sens_temperature = 0x01, - .sens_humidity = 0x01, - .sens_atm_pressure = 0x01, - }); - - fflush(stdout); - - char byte; - while(read(STDIN_FILENO, &byte, 1) > 0) - ws_protocol_parse_req_byte(parser1, byte); - - ws_protocol_req_parser_free(parser1); - parser1 = NULL; - - return 0; -} diff --git a/shared/testcmd b/shared/testcmd deleted file mode 100644 index 17f8842..0000000 --- a/shared/testcmd +++ /dev/null @@ -1 +0,0 @@ -last-records 5 diff --git a/stm32f091/backlog.c b/stm32f091/backlog.c index 926ccad..662fc75 100644 --- a/stm32f091/backlog.c +++ b/stm32f091/backlog.c @@ -33,11 +33,6 @@ ws_s_backlog_record* ws_backlog_get_last_record(uint16_t record_offset) { return ws_backlog_get_record((g_ws_backlog_database->buffer_end - record_offset - 1) % g_ws_backlog_database->buffer_size); } -static uint16_t mod(uint16_t a, uint16_t b) { - uint16_t m = a % b; - return m < 0 ? (b < 0) ? m - b : m + b : m; -} - uint16_t ws_backlog_get_record_count() { // add buffer_size to the result of the modulo operation if it's result is negative // (only works when buffer_size is less than 2^15) diff --git a/stm32f091/esp8266.c b/stm32f091/esp8266.c index 6f12191..4c5d0d2 100644 --- a/stm32f091/esp8266.c +++ b/stm32f091/esp8266.c @@ -22,7 +22,7 @@ void ws_esp8266_ATsendCommand(uint8_t* data){ } int ws_esp8266_checkOK(uint8_t *receiveData,int length){ char *ret=""; - char *ret1=""; + // char *ret1=""; HAL_UART_Transmit(&huart2, receiveData,length,1000); ret = strstr((char*)receiveData,"OK"); // ret = strstr((char*)receiveData,"change"); @@ -159,8 +159,9 @@ void ws_esp8266_serveraan(){ HAL_Delay(1000); } void ws_esp8266_serveruit(){ - int ret; - uint8_t buffer1[27]={0}; uint8_t Tx_server[]="AT+CIPSERVER=0\r\n"; + //int ret; + //uint8_t buffer1[27]={0}; + uint8_t Tx_server[]="AT+CIPSERVER=0\r\n"; // // while(ret!=1){ diff --git a/stm32f091/main.c b/stm32f091/main.c index 9235f1b..7cb0718 100644 --- a/stm32f091/main.c +++ b/stm32f091/main.c @@ -16,7 +16,6 @@ int main() { }); ws_backlog_alloc(24 * 60); - ws_sensor_read(); xTaskCreate(ws_sensor_read_task, "sensor", 128, NULL, 1, NULL); vTaskStartScheduler(); diff --git a/stm32f091/makefile b/stm32f091/makefile index 5a185de..20424c3 100644 --- a/stm32f091/makefile +++ b/stm32f091/makefile @@ -5,6 +5,8 @@ RM = rm -f TARGET = main +include ../shared/shared.mk + SHARED_FLAGS += -g SHARED_FLAGS += -DSTM32F091xC SHARED_FLAGS += -Wall @@ -74,9 +76,15 @@ $(TARGET).bin: $(TARGET).elf %.o: %.s $(CC) -c $(AFLAGS) $< -o $@ +lib/%.o: lib/%.c + $(CC) -c $(CFLAGS) -w $< -o $@ + %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ +%-stm.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + $(TARGET).elf: $(OBJS) $(LD) $(LFLAGS) $^ -o $@ diff --git a/stm32f091/protocol.c b/stm32f091/protocol.c new file mode 100644 index 0000000..589ee5d --- /dev/null +++ b/stm32f091/protocol.c @@ -0,0 +1,33 @@ +#include + +#include "../shared/protocol.h" +#include "backlog.h" +#include "util.h" + +void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s_protocol_res* response, bool send) { + static unsigned int record_amount = 0; + const char* response_header = "id,temperature,humidity,atmospheric_pressure\n"; + const size_t response_line_size = sizeof("xxxx,xx,xx,xx\n"); + + if (!send) { + response->success = WS_PROTOCOL_CMD_RETURN_OK; + if (sscanf(parsed_cmd->argv[1], "%u", &record_amount) < 1) response->success = WS_PROTOCOL_CMD_RETURN_ERROR; + record_amount = WS_MIN(record_amount, ws_backlog_get_record_count()); + response->csh = true; + response->msg = ws_bin_s_alloc(0); + response->msg->bytes = strlen(response_header) + response_line_size * record_amount; + } else { + ws_protocol_send_data(response_header, strlen(response_header)); + char line[response_line_size + 1]; // +1 for null terminator -> sprintf + for (unsigned int i = 0; i < record_amount; i++) { + ws_s_backlog_record* record = ws_backlog_get_last_record(i); + sprintf(line, "%04x,%02x,%02x,%02x\n", record->id, record->sens_temperature, record->sens_humidity, record->sens_atm_pressure); + ws_protocol_send_data(line, response_line_size); + } + } +} + +void ws_protocol_send_data(const char* data, unsigned int length) { + //TODO: implement on esp data channels + HAL_UART_Transmit(&huart2, (uint8_t*) data, length, HAL_MAX_DELAY); +} diff --git a/stm32f091/sensor.c b/stm32f091/sensor.c index bc3cfd3..1c94e2a 100644 --- a/stm32f091/sensor.c +++ b/stm32f091/sensor.c @@ -58,6 +58,12 @@ void ws_sensor_read() { .sens_humidity = ws_sensor_humidity() }; ws_backlog_add_record(record); + + // < DEBUG PROTOCOL PARSING CODE > + // ws_s_protocol_req_parser_state* parser = ws_protocol_req_parser_alloc(); + // const char* request = "last-records 5\n"; + // ws_protocol_parse_req_bytes(parser, (char*) request, strlen(request)); + // ws_protocol_req_parser_free(parser); } void ws_sensor_read_task() { diff --git a/stm32f091/server.c b/stm32f091/server.c index e289245..4840527 100644 --- a/stm32f091/server.c +++ b/stm32f091/server.c @@ -28,7 +28,7 @@ void ws_server_demo() { uint8_t receive[24]={0}; uint8_t sendToQTData[]="gelukt"; uint8_t test[]="test"; - int ret; + //int ret; //ATsendCommand(sendToQTData); while (1) { diff --git a/stm32f091/util.h b/stm32f091/util.h index 8e3258f..92f093f 100644 --- a/stm32f091/util.h +++ b/stm32f091/util.h @@ -6,10 +6,11 @@ #include #include "setup.h" +#include "../shared/util.h" #define ws_usb_printf(fmt, ...) { \ char temp[255]; \ - sprintf(temp, fmt, ##__VA_ARGS__); \ + snprintf(temp, 255, fmt, ##__VA_ARGS__); \ HAL_UART_Transmit(&huart2, (uint8_t*) temp, sizeof(char) * strlen(temp), HAL_MAX_DELAY); \ } -- cgit v1.2.3 From 89e652af2506f7cf7854ce2e6f5b9b97423d1d40 Mon Sep 17 00:00:00 2001 From: lonkaars Date: Thu, 27 Oct 2022 18:33:43 +0200 Subject: fix dumb typo's and added comments to faulty code --- shared/protocol.c | 5 ++++- stm32f091/server.c | 6 +++--- stm32f091/setup.c | 2 ++ 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'shared') diff --git a/shared/protocol.c b/shared/protocol.c index c6e5ddd..ca0edc4 100644 --- a/shared/protocol.c +++ b/shared/protocol.c @@ -99,7 +99,10 @@ ws_s_protocol_req_parser_state* ws_protocol_req_parser_alloc() { void ws_protocol_req_cmd_init(ws_s_protocol_req_parser_state* state) { state->target = malloc(sizeof(ws_s_protocol_parsed_req_cmd) + sizeof(char*) * state->arg_len); for (unsigned int i = 0; i < state->arg_len; i++) - state->target->argv[i] = malloc(sizeof(char) * (state->args_len[i] + 1)); + state->target->argv[i] = malloc(sizeof(char) * (state->args_len[i] + 1)); // segfaults on 2nd run? + // state->target->argv stays empty according to debugger + // goes to HardFault_Handler on 2nd iteration + // this might be the stm running out of heap memory though i'm not sure state->target->argc = state->arg_len; diff --git a/stm32f091/server.c b/stm32f091/server.c index 1b677c2..7e44774 100644 --- a/stm32f091/server.c +++ b/stm32f091/server.c @@ -16,7 +16,7 @@ static ws_s_server_parser g_ws_server_parser = { .current_channel = 0, .channel_data_length = 0, .channel_data_counter = 0, - .channel_listen_mode = WS_SERVER_CL_DATA_LENGTH, + .channel_listen_mode = WS_SERVER_CL_CHANNEL_ID, }; static ws_s_protocol_req_parser_state* g_ws_protocol_parsers[WS_SERVER_MAX_CHANNELS] = {0}; @@ -101,8 +101,8 @@ void ws_server_req_incoming(uint8_t* data, size_t size) { g_ws_server_parser.channel_data_ignore = true; break; } - g_ws_server_parser.current_channel *= 10; - g_ws_server_parser.current_channel += byte - '0'; // ascii to int + g_ws_server_parser.channel_data_length *= 10; + g_ws_server_parser.channel_data_length += byte - '0'; // ascii to int break; } case WS_SERVER_CL_DATA_READ: { diff --git a/stm32f091/setup.c b/stm32f091/setup.c index 2175ab3..5b6c6a4 100644 --- a/stm32f091/setup.c +++ b/stm32f091/setup.c @@ -90,7 +90,9 @@ void ws_io_setup() { .Pull = GPIO_NOPULL }); + // TODO: remove debug size ws_backlog_alloc(24 * 60); + // ws_backlog_alloc(10); #ifdef WS_ESP8266_WLAN_MAC ws_esp8266_set_mac(); -- cgit v1.2.3 From 1cac46e7701ef2783aea13d55261582f4d91f2fa Mon Sep 17 00:00:00 2001 From: lonkaars Date: Fri, 28 Oct 2022 09:57:50 +0200 Subject: working janky protocol! --- shared/protocol.c | 14 ++++++++++---- shared/protocol.h | 3 ++- shared/util.c | 7 +++++++ shared/util.h | 2 ++ stm32f091/esp8266.c | 27 -------------------------- stm32f091/protocol.c | 8 ++++++-- stm32f091/server.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++------ stm32f091/server.h | 13 +++++++++++-- stm32f091/setup.c | 3 ++- 9 files changed, 88 insertions(+), 43 deletions(-) create mode 100644 shared/util.c (limited to 'shared') diff --git a/shared/protocol.c b/shared/protocol.c index ca0edc4..fcc84a4 100644 --- a/shared/protocol.c +++ b/shared/protocol.c @@ -3,6 +3,7 @@ #include #include "protocol.h" +#include "util.h" #define WS_CMD_MAP(parsed_cmd, name, code) \ if (strlen(parsed_cmd->argv[0]) == strlen(name) && strncmp(parsed_cmd->argv[0], name, strlen(name)) == 0) return code; @@ -99,10 +100,7 @@ ws_s_protocol_req_parser_state* ws_protocol_req_parser_alloc() { void ws_protocol_req_cmd_init(ws_s_protocol_req_parser_state* state) { state->target = malloc(sizeof(ws_s_protocol_parsed_req_cmd) + sizeof(char*) * state->arg_len); for (unsigned int i = 0; i < state->arg_len; i++) - state->target->argv[i] = malloc(sizeof(char) * (state->args_len[i] + 1)); // segfaults on 2nd run? - // state->target->argv stays empty according to debugger - // goes to HardFault_Handler on 2nd iteration - // this might be the stm running out of heap memory though i'm not sure + state->target->argv[i] = malloc(sizeof(char) * (state->args_len[i] + 1)); state->target->argc = state->arg_len; @@ -138,3 +136,11 @@ void ws_protocol_req_cmd_free(ws_s_protocol_parsed_req_cmd* cmd) { free(cmd); return; } + +unsigned short ws_protocol_get_header_size(ws_s_protocol_res* response) { + unsigned short size = 2; // comma and trailing newline + if (response->success == WS_PROTOCOL_CMD_RETURN_OK) size += 2; // ok + if (response->success == WS_PROTOCOL_CMD_RETURN_ERROR) size += 5; // error + size += ws_log16(response->msg->bytes); // amount of characters for message size (hex) + return size; +} \ No newline at end of file diff --git a/shared/protocol.h b/shared/protocol.h index fbe29d6..5bcb114 100644 --- a/shared/protocol.h +++ b/shared/protocol.h @@ -5,7 +5,7 @@ #include "bin.h" -#define WS_PROTOCOL_CMD_MAX_ARGUMENTS (1) +#define WS_PROTOCOL_CMD_MAX_ARGUMENTS (2) #define WS_PROTOCOL_CMD_BUFFER_LEN (40) #define WS_PROTOCOL_CMD_AMOUNT (1) @@ -148,3 +148,4 @@ static ws_protocol_res_handler_t* g_ws_protocol_res_handlers[WS_PROTOCOL_CMD_AMO [WS_PROTOCOL_CMD_LAST_RECORDS] = &ws_protocol_res_last_records, }; +unsigned short ws_protocol_get_header_size(ws_s_protocol_res* response); \ No newline at end of file diff --git a/shared/util.c b/shared/util.c new file mode 100644 index 0000000..ea972b0 --- /dev/null +++ b/shared/util.c @@ -0,0 +1,7 @@ +#include "util.h" + +unsigned int ws_log16(unsigned int x) { + unsigned int l = 0; + while (x >>= 4) ++l; // bitshift right by 4 until x == 0 + return l; +} \ No newline at end of file diff --git a/shared/util.h b/shared/util.h index f39ff34..94a3dfe 100644 --- a/shared/util.h +++ b/shared/util.h @@ -2,3 +2,5 @@ #define WS_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define WS_MAX(a, b) (((a) > (b)) ? (a) : (b)) + +unsigned int ws_log16(unsigned int x); diff --git a/stm32f091/esp8266.c b/stm32f091/esp8266.c index d45954e..db134a0 100644 --- a/stm32f091/esp8266.c +++ b/stm32f091/esp8266.c @@ -80,30 +80,3 @@ void ws_esp8266_set_mac() { void ws_esp8266_set_ip() { ws_esp8266_send_seq("AT+CIPSTA=\"" WS_ESP8266_WLAN_IP "\"\r\n"); } - -// TODO: refactor code from here to EOF -void ws_esp8266_ATsendCommand(uint8_t* data){ - char dataChar[20]; - uint8_t Tx_send[]="AT+CIPSEND=0,"; - - itoa(strlen((char*)data),dataChar,10); - strcat((char*)Tx_send,dataChar); - strcat((char*)Tx_send,"\r\n"); - HAL_UART_Transmit(&huart1, Tx_send, strlen((char*)Tx_send),1000); - HAL_Delay(2000); - HAL_UART_Transmit(&huart1, data, strlen((char*)data),1000); - HAL_Delay(1000); - HAL_UART_Transmit(&huart2, data, strlen((char*)data),1000); - HAL_Delay(5000); -} -void ws_esp8266_close(){ - - uint8_t Tx_close[]="AT+CIPCLOSE=0\r\n"; - - - HAL_UART_Transmit(&huart1, Tx_close,strlen((char*)Tx_close), 100); - - HAL_Delay(3000); - -} - diff --git a/stm32f091/protocol.c b/stm32f091/protocol.c index 589ee5d..3a8d78b 100644 --- a/stm32f091/protocol.c +++ b/stm32f091/protocol.c @@ -3,6 +3,8 @@ #include "../shared/protocol.h" #include "backlog.h" #include "util.h" +#include "server.h" +#include "esp8266.h" void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s_protocol_res* response, bool send) { static unsigned int record_amount = 0; @@ -16,6 +18,7 @@ void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s response->csh = true; response->msg = ws_bin_s_alloc(0); response->msg->bytes = strlen(response_header) + response_line_size * record_amount; + ws_server_req_respond_start(0, response->msg->bytes + ws_protocol_get_header_size(response)); } else { ws_protocol_send_data(response_header, strlen(response_header)); char line[response_line_size + 1]; // +1 for null terminator -> sprintf @@ -24,10 +27,11 @@ void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s sprintf(line, "%04x,%02x,%02x,%02x\n", record->id, record->sens_temperature, record->sens_humidity, record->sens_atm_pressure); ws_protocol_send_data(line, response_line_size); } + ws_protocol_send_data("\r\n", 2); // test } } void ws_protocol_send_data(const char* data, unsigned int length) { - //TODO: implement on esp data channels - HAL_UART_Transmit(&huart2, (uint8_t*) data, length, HAL_MAX_DELAY); + ws_server_buffer_send_append((uint8_t*) data, length); + // HAL_UART_Transmit(&huart1, (uint8_t*) data, length, HAL_MAX_DELAY); } diff --git a/stm32f091/server.c b/stm32f091/server.c index 7e44774..d4f7e20 100644 --- a/stm32f091/server.c +++ b/stm32f091/server.c @@ -9,7 +9,7 @@ #include "setup.h" #include "consts.h" -static ws_s_server_parser g_ws_server_parser = { +ws_s_server_parser g_ws_server_parser = { .last_response = WS_SERVER_RC_NONE, .mode = WS_SERVER_LM_IDLE, @@ -20,7 +20,7 @@ static ws_s_server_parser g_ws_server_parser = { }; static ws_s_protocol_req_parser_state* g_ws_protocol_parsers[WS_SERVER_MAX_CHANNELS] = {0}; - +static unsigned int g_ws_esp8266_dma_tx_buffer_index = 0; void ws_server_req_parse_byte(unsigned int channel, uint8_t byte, bool ignore) { if (ignore) return; @@ -80,6 +80,9 @@ void ws_server_req_incoming(uint8_t* data, size_t size) { if (next_few_bytes_are("+IPD,")) { i += 4; // skip I, P, D, and comma g_ws_server_parser.mode = WS_SERVER_LM_IPD_LISTENING; + } else if (byte == '>') { + g_ws_server_parser.mode = WS_SERVER_LM_CIPSEND_LISTENING; + ws_server_buffer_send_finish(); } break; } @@ -114,6 +117,7 @@ void ws_server_req_incoming(uint8_t* data, size_t size) { g_ws_server_parser.channel_data_length = 0; g_ws_server_parser.channel_listen_mode = WS_SERVER_CL_CHANNEL_ID; ws_server_req_finish(g_ws_server_parser.current_channel, g_ws_server_parser.channel_data_ignore); + g_ws_server_parser.mode = WS_SERVER_LM_IDLE; } break; } @@ -121,6 +125,14 @@ void ws_server_req_incoming(uint8_t* data, size_t size) { } break; } + case WS_SERVER_LM_CIPSEND_LISTENING: { + if (next_few_bytes_are("SEND OK")) { + i += 6; + // g_ws_server_parser.mode = WS_SERVER_LM_IDLE; + ws_server_req_respond_end(0); + } + break; + } default: {} } } @@ -132,9 +144,39 @@ void ws_server_send(uint8_t* data, size_t size) { while (g_ws_server_parser.mode != WS_SERVER_LM_IDLE) {}; } -void ws_server_req_respond(unsigned int channel, uint8_t* data, size_t size) { - uint8_t Tx_send[]="AT+CIPSEND=0,LEN:"; - uint8_t Tx_close[]="AT+CIPCLOSE=0\r\n"; - return; +void ws_server_buffer_send_append(uint8_t* data, size_t size) { + // TODO: buffer overrun protection + // while (!__HAL_DMA_GET_FLAG(&hdma_usart1_tx, DMA_FLAG_TC2)); // make sure buffer isn't used + strncpy((char*) &g_ws_esp8266_dma_tx_buffer[g_ws_esp8266_dma_tx_buffer_index], (char*) data, size); // append string + g_ws_esp8266_dma_tx_buffer_index += size; // shift head +} + +void ws_server_buffer_send_finish() { +#ifdef WS_DBG_PRINT_ESP_OVER_USART2 + uint8_t green[] = { 0x1b, 0x5b, 0x33, 0x32, 0x6d }; + HAL_UART_Transmit(&huart2, green, sizeof(green), 100); + HAL_UART_Transmit(&huart2, g_ws_esp8266_dma_tx_buffer, strlen((char*) g_ws_esp8266_dma_tx_buffer), 100); +#endif + + HAL_UART_Transmit_DMA(&huart1, g_ws_esp8266_dma_tx_buffer, strlen((char*) g_ws_esp8266_dma_tx_buffer)); + __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE); + g_ws_esp8266_dma_tx_buffer_index = 0; + while (!__HAL_DMA_GET_FLAG(&hdma_usart1_tx, DMA_FLAG_TC2)); +} + +void ws_server_req_respond_start(unsigned int channel, size_t size) { + char* cmd = NULL; + size_t len = asiprintf(&cmd, "AT+CIPSEND=%d,%d\r\n", channel, size); + g_ws_server_parser.mode = WS_SERVER_LM_CMD_ECHO; + ws_esp8266_send((uint8_t*) cmd, len); + while (!__HAL_DMA_GET_FLAG(&hdma_usart1_tx, DMA_FLAG_TC2)); +} + +void ws_server_req_respond_end(unsigned int channel) { + char* cmd = NULL; + size_t len = asiprintf(&cmd, "AT+CIPCLOSE=%d\r\n", channel); + g_ws_server_parser.mode = WS_SERVER_LM_CMD_ECHO; + ws_esp8266_send((uint8_t*) cmd, len); + while (!__HAL_DMA_GET_FLAG(&hdma_usart1_tx, DMA_FLAG_TC2)); } diff --git a/stm32f091/server.h b/stm32f091/server.h index 44882e4..128b579 100644 --- a/stm32f091/server.h +++ b/stm32f091/server.h @@ -9,6 +9,7 @@ typedef enum { WS_SERVER_LM_STATUS_CODE, /** @brief listen for busy, ERROR or OK */ WS_SERVER_LM_IDLE, /** @brief listen for incoming +IPD commands */ WS_SERVER_LM_IPD_LISTENING, /** @brief +IPD received, now reading data */ + WS_SERVER_LM_CIPSEND_LISTENING, /** @brief AT+CIPSEND sent, now reading data */ } ws_e_server_listen_mode; typedef enum { @@ -34,6 +35,9 @@ typedef struct { bool channel_data_ignore; } ws_s_server_parser; +/** @brief global server parser struct */ +extern ws_s_server_parser g_ws_server_parser; + /** * @brief +IPD incoming request handler * @@ -46,8 +50,10 @@ typedef struct { */ void ws_server_req_incoming(uint8_t* data, size_t size); -/** @brief send response to incoming request on specific channel */ -void ws_server_req_respond(unsigned int channel, uint8_t* data, size_t size); +/** @brief send AT response header for incoming request on specific channel */ +void ws_server_req_respond_start(unsigned int channel, size_t size); +/** @brief send AT tcp close on specific channel */ +void ws_server_req_respond_end(unsigned int channel); /** @brief send data to esp, waiting until server returns to idle mode */ void ws_server_send(uint8_t* data, size_t size); @@ -74,3 +80,6 @@ void ws_server_req_parse_byte(unsigned int channel, uint8_t byte, bool ignore); * @param ignore ignore mode */ void ws_server_req_finish(unsigned int channel, bool ignore); + +void ws_server_buffer_send_append(uint8_t* data, size_t size); +void ws_server_buffer_send_finish(); diff --git a/stm32f091/setup.c b/stm32f091/setup.c index 5b6c6a4..1971809 100644 --- a/stm32f091/setup.c +++ b/stm32f091/setup.c @@ -8,6 +8,7 @@ #include "esp8266.h" #include "setup.h" #include "backlog.h" +#include "server.h" I2C_HandleTypeDef hi2c1 = { .Instance = I2C1, @@ -101,7 +102,7 @@ void ws_io_setup() { ws_esp8266_set_ip(); #endif ws_esp8266_ap_client_mode(); - ws_esp8266_connect(); + do ws_esp8266_connect(); while (g_ws_server_parser.last_response == WS_SERVER_RC_ERR); ws_esp8266_start_tcp_server(); } -- cgit v1.2.3 From 4cd465332087e4ab12709f28fae55df10e1a1154 Mon Sep 17 00:00:00 2001 From: lonkaars Date: Fri, 28 Oct 2022 12:14:50 +0200 Subject: add todo.md, fixed segfault in protocol.c, and fix response sending --- shared/protocol.c | 5 +++-- stm32f091/consts.h | 2 +- stm32f091/protocol.c | 13 +++++++------ stm32f091/server.c | 18 ++++++++++-------- stm32f091/todo.md | 10 ++++++++++ 5 files changed, 31 insertions(+), 17 deletions(-) create mode 100644 stm32f091/todo.md (limited to 'shared') diff --git a/shared/protocol.c b/shared/protocol.c index fcc84a4..e17a728 100644 --- a/shared/protocol.c +++ b/shared/protocol.c @@ -99,10 +99,11 @@ ws_s_protocol_req_parser_state* ws_protocol_req_parser_alloc() { void ws_protocol_req_cmd_init(ws_s_protocol_req_parser_state* state) { state->target = malloc(sizeof(ws_s_protocol_parsed_req_cmd) + sizeof(char*) * state->arg_len); - for (unsigned int i = 0; i < state->arg_len; i++) + unsigned int args = WS_MIN(state->arg_len, WS_PROTOCOL_CMD_MAX_ARGUMENTS); + for (unsigned int i = 0; i < args; i++) state->target->argv[i] = malloc(sizeof(char) * (state->args_len[i] + 1)); - state->target->argc = state->arg_len; + state->target->argc = args; unsigned int head = 0; for (unsigned int i = 0; i < state->arg_len; i++) { diff --git a/stm32f091/consts.h b/stm32f091/consts.h index 3d2ef5a..955ceea 100644 --- a/stm32f091/consts.h +++ b/stm32f091/consts.h @@ -6,7 +6,7 @@ #define WS_SERVER_MAX_CHANNELS 4 #define WS_DMA_RX_BUFFER_SIZE 100 -#define WS_DMA_TX_BUFFER_SIZE 1024 +#define WS_DMA_TX_BUFFER_SIZE 100 #define WS_PINOUT_I2C_SDA_PIN GPIO_PIN_9 #define WS_PINOUT_I2C_SDA_PORT GPIOB diff --git a/stm32f091/protocol.c b/stm32f091/protocol.c index 3a8d78b..21e6527 100644 --- a/stm32f091/protocol.c +++ b/stm32f091/protocol.c @@ -13,19 +13,21 @@ void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s if (!send) { response->success = WS_PROTOCOL_CMD_RETURN_OK; - if (sscanf(parsed_cmd->argv[1], "%u", &record_amount) < 1) response->success = WS_PROTOCOL_CMD_RETURN_ERROR; - record_amount = WS_MIN(record_amount, ws_backlog_get_record_count()); response->csh = true; response->msg = ws_bin_s_alloc(0); - response->msg->bytes = strlen(response_header) + response_line_size * record_amount; + response->msg->bytes = 0; + record_amount = WS_MIN(record_amount, ws_backlog_get_record_count()); + if (sscanf(parsed_cmd->argv[1], "%u", &record_amount) < 1) response->success = WS_PROTOCOL_CMD_RETURN_ERROR; + else response->msg->bytes = strlen(response_header) + response_line_size * record_amount; ws_server_req_respond_start(0, response->msg->bytes + ws_protocol_get_header_size(response)); } else { + if (response->success == WS_PROTOCOL_CMD_RETURN_ERROR) return; ws_protocol_send_data(response_header, strlen(response_header)); - char line[response_line_size + 1]; // +1 for null terminator -> sprintf + char line[response_line_size]; for (unsigned int i = 0; i < record_amount; i++) { ws_s_backlog_record* record = ws_backlog_get_last_record(i); sprintf(line, "%04x,%02x,%02x,%02x\n", record->id, record->sens_temperature, record->sens_humidity, record->sens_atm_pressure); - ws_protocol_send_data(line, response_line_size); + ws_protocol_send_data(line, response_line_size - 1); // remove string terminator } ws_protocol_send_data("\r\n", 2); // test } @@ -33,5 +35,4 @@ void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s void ws_protocol_send_data(const char* data, unsigned int length) { ws_server_buffer_send_append((uint8_t*) data, length); - // HAL_UART_Transmit(&huart1, (uint8_t*) data, length, HAL_MAX_DELAY); } diff --git a/stm32f091/server.c b/stm32f091/server.c index 8001380..1419da8 100644 --- a/stm32f091/server.c +++ b/stm32f091/server.c @@ -20,7 +20,7 @@ ws_s_server_parser g_ws_server_parser = { }; static ws_s_protocol_req_parser_state* g_ws_protocol_parsers[WS_SERVER_MAX_CHANNELS] = {0}; -static unsigned int g_ws_esp8266_dma_tx_buffer_index = 0; +static unsigned int g_ws_esp8266_dma_tx_buffer_size = 0; void ws_server_req_parse_byte(unsigned int channel, uint8_t byte, bool ignore) { if (ignore) return; @@ -126,7 +126,7 @@ void ws_server_req_incoming(uint8_t* data, size_t size) { break; } case WS_SERVER_LM_CIPSEND_LISTENING: { - if (next_few_bytes_are("SEND OK")) { + if (next_few_bytes_are("SEND OK") || next_few_bytes_are("ERROR")) { ws_server_req_respond_end(0); // g_ws_server_parser.mode = WS_SERVER_LM_IDLE; } @@ -146,23 +146,25 @@ void ws_server_send(uint8_t* data, size_t size) { void ws_server_buffer_send_append(uint8_t* data, size_t size) { // TODO: buffer overrun protection // while (!__HAL_DMA_GET_FLAG(&hdma_usart1_tx, DMA_FLAG_TC2)); // make sure buffer isn't used - strncpy((char*) &g_ws_esp8266_dma_tx_buffer[g_ws_esp8266_dma_tx_buffer_index], (char*) data, size); // append string - g_ws_esp8266_dma_tx_buffer_index += size; // shift head + strncpy((char*) &g_ws_esp8266_dma_tx_buffer[g_ws_esp8266_dma_tx_buffer_size], (char*) data, size); // append string + g_ws_esp8266_dma_tx_buffer_size += size; // shift head } +// TODO: refactor this void ws_server_buffer_send_finish() { #ifdef WS_DBG_PRINT_ESP_OVER_USART2 uint8_t green[] = { 0x1b, 0x5b, 0x33, 0x32, 0x6d }; HAL_UART_Transmit(&huart2, green, sizeof(green), 100); - HAL_UART_Transmit(&huart2, g_ws_esp8266_dma_tx_buffer, strlen((char*) g_ws_esp8266_dma_tx_buffer), 100); + HAL_UART_Transmit(&huart2, g_ws_esp8266_dma_tx_buffer, g_ws_esp8266_dma_tx_buffer_size, 100); #endif - HAL_UART_Transmit_DMA(&huart1, g_ws_esp8266_dma_tx_buffer, strlen((char*) g_ws_esp8266_dma_tx_buffer)); + HAL_UART_Transmit_DMA(&huart1, g_ws_esp8266_dma_tx_buffer, g_ws_esp8266_dma_tx_buffer_size); __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE); - g_ws_esp8266_dma_tx_buffer_index = 0; + g_ws_esp8266_dma_tx_buffer_size = 0; while (!__HAL_DMA_GET_FLAG(&hdma_usart1_tx, DMA_FLAG_TC2)); } +// TODO: refactor this void ws_server_req_respond_start(unsigned int channel, size_t size) { char* cmd = NULL; size_t len = asiprintf(&cmd, "AT+CIPSEND=%d,%d\r\n", channel, size); @@ -171,6 +173,7 @@ void ws_server_req_respond_start(unsigned int channel, size_t size) { while (!__HAL_DMA_GET_FLAG(&hdma_usart1_tx, DMA_FLAG_TC2)); } +// TODO: refactor this void ws_server_req_respond_end(unsigned int channel) { char* cmd = NULL; size_t len = asiprintf(&cmd, "AT+CIPCLOSE=%d\r\n", channel); @@ -178,4 +181,3 @@ void ws_server_req_respond_end(unsigned int channel) { ws_esp8266_send((uint8_t*) cmd, len); while (!__HAL_DMA_GET_FLAG(&hdma_usart1_tx, DMA_FLAG_TC2)); } - diff --git a/stm32f091/todo.md b/stm32f091/todo.md new file mode 100644 index 0000000..767d745 --- /dev/null +++ b/stm32f091/todo.md @@ -0,0 +1,10 @@ +- handle errors from `AT+CIPSEND`, these look like this: + ``` + > AT0,CONNECT + + > +IPD,0,15:last-records 5<0a> + < AT+CIPSEND=0,125 + > AT+CIPSEND=0,125 + + > ERROR + ``` -- cgit v1.2.3 From babb6dc29a5c4af60292ffad5216317d13e2a685 Mon Sep 17 00:00:00 2001 From: lonkaars Date: Fri, 28 Oct 2022 14:31:56 +0200 Subject: fix off-by-one error and create macro for switching serial debug print color --- shared/protocol.c | 2 +- stm32f091/consts.h | 2 +- stm32f091/esp8266.c | 3 +-- stm32f091/protocol.c | 14 ++++++++------ stm32f091/server.c | 8 +++----- stm32f091/setup.c | 7 +++++++ stm32f091/util.h | 4 ++++ 7 files changed, 25 insertions(+), 15 deletions(-) (limited to 'shared') diff --git a/shared/protocol.c b/shared/protocol.c index e17a728..8887070 100644 --- a/shared/protocol.c +++ b/shared/protocol.c @@ -142,6 +142,6 @@ unsigned short ws_protocol_get_header_size(ws_s_protocol_res* response) { unsigned short size = 2; // comma and trailing newline if (response->success == WS_PROTOCOL_CMD_RETURN_OK) size += 2; // ok if (response->success == WS_PROTOCOL_CMD_RETURN_ERROR) size += 5; // error - size += ws_log16(response->msg->bytes); // amount of characters for message size (hex) + size += ws_log16(response->msg->bytes) + 1; // amount of characters for message size (hex) return size; } \ No newline at end of file diff --git a/stm32f091/consts.h b/stm32f091/consts.h index 955ceea..3d2ef5a 100644 --- a/stm32f091/consts.h +++ b/stm32f091/consts.h @@ -6,7 +6,7 @@ #define WS_SERVER_MAX_CHANNELS 4 #define WS_DMA_RX_BUFFER_SIZE 100 -#define WS_DMA_TX_BUFFER_SIZE 100 +#define WS_DMA_TX_BUFFER_SIZE 1024 #define WS_PINOUT_I2C_SDA_PIN GPIO_PIN_9 #define WS_PINOUT_I2C_SDA_PORT GPIOB diff --git a/stm32f091/esp8266.c b/stm32f091/esp8266.c index 754b45b..b896567 100644 --- a/stm32f091/esp8266.c +++ b/stm32f091/esp8266.c @@ -58,8 +58,7 @@ void ws_esp8266_send(uint8_t* data, size_t size) { g_ws_esp8266_dma_tx_buffer[limited_size] = 0x00; #ifdef WS_DBG_PRINT_ESP_OVER_USART2 - uint8_t green[] = { 0x1b, 0x5b, 0x33, 0x32, 0x6d }; - HAL_UART_Transmit(&huart2, green, sizeof(green), 100); + ws_dbg_set_usart2_tty_color(2); HAL_UART_Transmit(&huart2, g_ws_esp8266_dma_tx_buffer, strlen((char*) g_ws_esp8266_dma_tx_buffer), 100); #endif diff --git a/stm32f091/protocol.c b/stm32f091/protocol.c index 21e6527..4d605fc 100644 --- a/stm32f091/protocol.c +++ b/stm32f091/protocol.c @@ -9,27 +9,29 @@ void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s_protocol_res* response, bool send) { static unsigned int record_amount = 0; const char* response_header = "id,temperature,humidity,atmospheric_pressure\n"; - const size_t response_line_size = sizeof("xxxx,xx,xx,xx\n"); + const unsigned int response_line_len = strlen("xxxx,xx,xx,xx\n"); if (!send) { response->success = WS_PROTOCOL_CMD_RETURN_OK; response->csh = true; response->msg = ws_bin_s_alloc(0); response->msg->bytes = 0; - record_amount = WS_MIN(record_amount, ws_backlog_get_record_count()); if (sscanf(parsed_cmd->argv[1], "%u", &record_amount) < 1) response->success = WS_PROTOCOL_CMD_RETURN_ERROR; - else response->msg->bytes = strlen(response_header) + response_line_size * record_amount; + else { + record_amount = WS_MIN(record_amount, ws_backlog_get_record_count()); + response->msg->bytes = strlen(response_header) + response_line_len * record_amount; + } ws_server_req_respond_start(0, response->msg->bytes + ws_protocol_get_header_size(response)); } else { if (response->success == WS_PROTOCOL_CMD_RETURN_ERROR) return; ws_protocol_send_data(response_header, strlen(response_header)); - char line[response_line_size]; + char line[response_line_len + 1]; // + 1 for string terminator for (unsigned int i = 0; i < record_amount; i++) { ws_s_backlog_record* record = ws_backlog_get_last_record(i); sprintf(line, "%04x,%02x,%02x,%02x\n", record->id, record->sens_temperature, record->sens_humidity, record->sens_atm_pressure); - ws_protocol_send_data(line, response_line_size - 1); // remove string terminator + ws_protocol_send_data(line, response_line_len); // remove string terminator } - ws_protocol_send_data("\r\n", 2); // test + // ws_protocol_send_data("\r\n", 2); // test } } diff --git a/stm32f091/server.c b/stm32f091/server.c index 1419da8..6225eae 100644 --- a/stm32f091/server.c +++ b/stm32f091/server.c @@ -8,6 +8,7 @@ #include "server.h" #include "setup.h" #include "consts.h" +#include "util.h" ws_s_server_parser g_ws_server_parser = { .last_response = WS_SERVER_RC_NONE, @@ -48,8 +49,7 @@ void ws_server_req_finish(unsigned int channel, bool ignore) { #define next_few_bytes_are(code) (i + sizeof(code) - 1 < size && strncmp((char*)&data[i], code, sizeof(code) - 1) == 0) void ws_server_req_incoming(uint8_t* data, size_t size) { #ifdef WS_DBG_PRINT_ESP_OVER_USART2 - uint8_t red[] = { 0x1b, 0x5b, 0x33, 0x31, 0x6d }; - HAL_UART_Transmit(&huart2, red, sizeof(red), 100); + ws_dbg_set_usart2_tty_color(1); HAL_UART_Transmit(&huart2, data, size, 100); #endif @@ -128,7 +128,6 @@ void ws_server_req_incoming(uint8_t* data, size_t size) { case WS_SERVER_LM_CIPSEND_LISTENING: { if (next_few_bytes_are("SEND OK") || next_few_bytes_are("ERROR")) { ws_server_req_respond_end(0); - // g_ws_server_parser.mode = WS_SERVER_LM_IDLE; } break; } @@ -153,8 +152,7 @@ void ws_server_buffer_send_append(uint8_t* data, size_t size) { // TODO: refactor this void ws_server_buffer_send_finish() { #ifdef WS_DBG_PRINT_ESP_OVER_USART2 - uint8_t green[] = { 0x1b, 0x5b, 0x33, 0x32, 0x6d }; - HAL_UART_Transmit(&huart2, green, sizeof(green), 100); + ws_dbg_set_usart2_tty_color(2); HAL_UART_Transmit(&huart2, g_ws_esp8266_dma_tx_buffer, g_ws_esp8266_dma_tx_buffer_size, 100); #endif diff --git a/stm32f091/setup.c b/stm32f091/setup.c index 3bf331f..1591649 100644 --- a/stm32f091/setup.c +++ b/stm32f091/setup.c @@ -9,6 +9,7 @@ #include "setup.h" #include "backlog.h" #include "server.h" +#include "util.h" I2C_HandleTypeDef hi2c1 = { .Instance = I2C1, @@ -95,6 +96,12 @@ void ws_io_setup() { ws_backlog_alloc(24 * 60); // ws_backlog_alloc(10); +#ifdef WS_DBG_PRINT_ESP_OVER_USART2 + ws_dbg_set_usart2_tty_color(7); + const char restart_str[] = "\r\n--- stm restart ---\r\n"; + HAL_UART_Transmit(&huart2, restart_str, strlen(restart_str), 100); +#endif + #ifdef WS_ESP8266_WLAN_MAC ws_esp8266_set_mac(); #endif diff --git a/stm32f091/util.h b/stm32f091/util.h index 92f093f..9f5c1ec 100644 --- a/stm32f091/util.h +++ b/stm32f091/util.h @@ -14,3 +14,7 @@ HAL_UART_Transmit(&huart2, (uint8_t*) temp, sizeof(char) * strlen(temp), HAL_MAX_DELAY); \ } +#define ws_dbg_set_usart2_tty_color(color) { \ + uint8_t sgr[] = { 0x1b, 0x5b, 0x33, 0x30 + color, 0x6d }; \ + HAL_UART_Transmit(&huart2, sgr, sizeof(sgr), 100); \ +} -- cgit v1.2.3 From 7022c6fbdbdee82339fbe161df169d97235471f0 Mon Sep 17 00:00:00 2001 From: lonkaars Date: Sat, 29 Oct 2022 18:26:16 +0200 Subject: update protocol to use hex digits in command arguments and accept offset value in `last-records` --- shared/protocol.h | 4 ++-- shared/protocol.md | 26 ++++++++++++++------------ stm32f091/protocol.c | 11 +++++++---- 3 files changed, 23 insertions(+), 18 deletions(-) (limited to 'shared') diff --git a/shared/protocol.h b/shared/protocol.h index 5bcb114..96c039a 100644 --- a/shared/protocol.h +++ b/shared/protocol.h @@ -5,7 +5,7 @@ #include "bin.h" -#define WS_PROTOCOL_CMD_MAX_ARGUMENTS (2) +#define WS_PROTOCOL_CMD_MAX_ARGUMENTS (3) #define WS_PROTOCOL_CMD_BUFFER_LEN (40) #define WS_PROTOCOL_CMD_AMOUNT (1) @@ -148,4 +148,4 @@ static ws_protocol_res_handler_t* g_ws_protocol_res_handlers[WS_PROTOCOL_CMD_AMO [WS_PROTOCOL_CMD_LAST_RECORDS] = &ws_protocol_res_last_records, }; -unsigned short ws_protocol_get_header_size(ws_s_protocol_res* response); \ No newline at end of file +unsigned short ws_protocol_get_header_size(ws_s_protocol_res* response); diff --git a/shared/protocol.md b/shared/protocol.md index b6e955c..1e52e42 100644 --- a/shared/protocol.md +++ b/shared/protocol.md @@ -9,10 +9,10 @@ requirements about the connection itself. The protocol is only used in a request-response fashion, so all commands are assumed to be sent by the qt client, and responded to by the weather station. -Functions for generating commands and parsing incoming data are provided by the -protocol.c and protocol.h files. See [code -implementation](#code-implementation) section for more details about naming -conventions. +~Functions for generating commands and parsing incoming data are provided by +the protocol.c and protocol.h files in this folder.~ A server using these files +should implement every protocol handler function in a seperate c file, along +with a data sending function that is also used internally. - LF for newline instead of CRLF - Commands are single-line @@ -24,22 +24,28 @@ conventions. ## Commands -### `last-records ` +### `last-records ` -Returns the last `n` records in csv format. The first line has the csv table -header, with the fields `id`, `temperature`, `humidity`, and +Returns the last `n` records with offset `` in csv format. The first line +has the csv table header, with the fields `id`, `temperature`, `humidity`, and `atmospheric_pressure`. The rest of the response consists of 1 record per line. The amount of records is limited to the amount of valid records in the backlog buffer. When the amount of returned records is 0, the response consists of the csv header, but without any following records. +Offset `` is a positive integer, representing the starting point for the +most recent record that is returned, this will get subtracted from the id of +the most recent record. E.g. if the last record has id `00f0`, and a request is +sent with parameters `n=3` and `o=5`, the records with id's `00eb`, `00ea`, and +`00e9` will be returned. + ## Example transaction In the following example, newlines are indicated by `<0a>`, request by lines starting with `<`, and response by lines starting with `>`. ``` -< last-records 5<0a> +< last-records 5 0<0a> > ok,73<0a> > id,temperature,humidity,atmospheric_pressure<0a> > 10dc,2f,c5,7f<0a> @@ -49,7 +55,3 @@ starting with `<`, and response by lines starting with `>`. > 10e0,34,c9,7e<0a> ``` -## Code implementation - - - diff --git a/stm32f091/protocol.c b/stm32f091/protocol.c index ca46b3b..f4be08f 100644 --- a/stm32f091/protocol.c +++ b/stm32f091/protocol.c @@ -7,7 +7,8 @@ #include "esp8266.h" void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s_protocol_res* response, bool send) { - static unsigned int record_amount = 0; + static int record_amount = 0; + static unsigned int record_offset = 0; const char* response_header = "id,temperature,humidity,atmospheric_pressure\n"; const unsigned int response_line_len = strlen("xxxx,xx,xx,xx\n"); @@ -16,9 +17,11 @@ void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s response->csh = true; response->msg = ws_bin_s_alloc(0); response->msg->bytes = 0; - if (sscanf(parsed_cmd->argv[1], "%u", &record_amount) < 1) response->success = WS_PROTOCOL_CMD_RETURN_ERROR; + if (sscanf(parsed_cmd->argv[1], "%x", &record_amount) < 1) response->success = WS_PROTOCOL_CMD_RETURN_ERROR; + if (sscanf(parsed_cmd->argv[2], "%x", &record_offset) < 1) response->success = WS_PROTOCOL_CMD_RETURN_ERROR; else { - record_amount = WS_MIN(record_amount, ws_backlog_get_record_count()); + record_amount = WS_MIN(record_amount + record_offset, ws_backlog_get_record_count()); + record_amount = WS_MAX(0, record_amount - record_offset); response->msg->bytes = strlen(response_header) + response_line_len * record_amount; } } else { @@ -26,7 +29,7 @@ void ws_protocol_res_last_records(ws_s_protocol_parsed_req_cmd* parsed_cmd, ws_s ws_protocol_send_data(response_header, strlen(response_header)); char line[response_line_len + 1]; // + 1 for string terminator for (unsigned int i = 0; i < record_amount; i++) { - ws_s_backlog_record* record = ws_backlog_get_last_record(i); + ws_s_backlog_record* record = ws_backlog_get_last_record(i + record_offset); sprintf(line, "%04x,%02x,%02x,%02x\n", record->id, record->sens_temperature, record->sens_humidity, record->sens_atm_pressure); ws_protocol_send_data(line, response_line_len); } -- cgit v1.2.3