aboutsummaryrefslogtreecommitdiff
path: root/shared
diff options
context:
space:
mode:
authorlonkaars <loek@pipeframe.xyz>2022-05-26 15:39:31 +0200
committerlonkaars <loek@pipeframe.xyz>2022-05-26 15:39:31 +0200
commit60f07661602a5dfe8e39b8038964b38bddcb33a5 (patch)
tree0b257acda0797a13cd09e7df2d16a6da0a6aef11 /shared
parent333eea840a17d0f8ecf0110d952df2857fea4da0 (diff)
parentf073c9d3848dab915bed4844e9d13684aa5e23eb (diff)
merge dev into master
Diffstat (limited to 'shared')
-rw-r--r--shared/bin.c80
-rw-r--r--shared/bin.h34
-rw-r--r--shared/consts.h21
-rw-r--r--shared/errors.h64
-rw-r--r--shared/makefile5
-rw-r--r--shared/protocol.c97
-rw-r--r--shared/protocol.h219
-rw-r--r--shared/readme.md8
-rw-r--r--shared/semver.c9
-rw-r--r--shared/semver.h11
-rw-r--r--shared/serial_parse.c44
-rw-r--r--shared/serial_parse.h8
-rw-r--r--shared/util.c6
-rw-r--r--shared/util.h7
14 files changed, 613 insertions, 0 deletions
diff --git a/shared/bin.c b/shared/bin.c
new file mode 100644
index 0000000..4b3dcc6
--- /dev/null
+++ b/shared/bin.c
@@ -0,0 +1,80 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "bin.h"
+
+#define W2_ENDIAN_LITTLE (1)
+#define W2_ENDIAN_BIG (0)
+
+#define _SHIFT_0B (8 * 0)
+#define _SHIFT_1B (8 * 1)
+#define _SHIFT_2B (8 * 2)
+#define _SHIFT_3B (8 * 3)
+#define _BYTE_0 ((uint32_t)(0xff << (_SHIFT_0B)))
+#define _BYTE_1 ((uint32_t)(0xff << (_SHIFT_1B)))
+#define _BYTE_2 ((uint32_t)(0xff << (_SHIFT_2B)))
+#define _BYTE_3 ((uint32_t)(0xff << (_SHIFT_3B)))
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshift-count-overflow"
+w2_s_bin *w2_bin_from_uint8_t(uint8_t data) {
+ size_t size = 1;
+ w2_s_bin *ret = malloc(sizeof(w2_s_bin) + sizeof(uint8_t) * size);
+ ret->bytes = size;
+ ret->data[0] = data;
+ return ret;
+}
+
+w2_s_bin *w2_bin_from_uint16_t(uint16_t data) {
+ size_t size = 2;
+ w2_s_bin *ret = malloc(sizeof(w2_s_bin) + sizeof(uint8_t) * size);
+ data = w2_bin_hton16(data);
+ ret->bytes = size;
+ ret->data[0] = (data & _BYTE_1) >> _SHIFT_1B;
+ ret->data[1] = (data & _BYTE_0) >> _SHIFT_0B;
+ return ret;
+}
+
+w2_s_bin *w2_bin_from_uint32_t(uint32_t data) {
+ size_t size = 4;
+ w2_s_bin *ret = malloc(sizeof(w2_s_bin) + sizeof(uint8_t) * size);
+ data = w2_bin_hton32(data);
+ ret->bytes = size;
+ ret->data[0] = (data & _BYTE_3) >> _SHIFT_3B;
+ ret->data[1] = (data & _BYTE_2) >> _SHIFT_2B;
+ ret->data[2] = (data & _BYTE_1) >> _SHIFT_1B;
+ ret->data[3] = (data & _BYTE_0) >> _SHIFT_0B;
+ return ret;
+}
+
+uint32_t w2_bin_hton32(uint32_t h32) {
+ if (g_w2_endianness == W2_ENDIAN_BIG) return h32;
+ return ((h32 & _BYTE_0) << _SHIFT_3B) | ((h32 & _BYTE_1) << _SHIFT_1B) |
+ ((h32 & _BYTE_2) >> _SHIFT_1B) | ((h32 & _BYTE_3) >> _SHIFT_3B);
+}
+#pragma GCC diagnostic pop
+
+uint16_t w2_bin_hton16(uint16_t h16) {
+ if (g_w2_endianness == W2_ENDIAN_BIG) return h16;
+ return ((h16 & _BYTE_0) << _SHIFT_1B) | ((h16 & _BYTE_1) >> _SHIFT_1B);
+}
+
+uint32_t w2_bin_ntoh32(uint32_t n32) { return w2_bin_hton32(n32); }
+uint16_t w2_bin_ntoh16(uint16_t n16) { return w2_bin_hton16(n16); }
+
+w2_s_bin *w2_bin_s_alloc(uint16_t bytes, uint8_t *data) {
+ w2_s_bin *temp = malloc(sizeof(w2_s_bin) + sizeof(uint8_t) * bytes);
+ temp->bytes = bytes;
+ memcpy(&temp->data, data, bytes);
+ return temp;
+}
+
+w2_s_bin *w2_bin_s_cat(w2_s_bin *a, w2_s_bin *b) {
+ uint8_t data[a->bytes + b->bytes];
+ memcpy(data, a->data, a->bytes);
+ memcpy(data + a->bytes, b->data, b->bytes);
+ w2_s_bin *c = w2_bin_s_alloc(a->bytes + b->bytes, data);
+ free(a);
+ free(b);
+ return c;
+}
diff --git a/shared/bin.h b/shared/bin.h
new file mode 100644
index 0000000..2b65c95
--- /dev/null
+++ b/shared/bin.h
@@ -0,0 +1,34 @@
+#pragma once
+/**
+ * helper file for binary data
+ *
+ * - fix endianness with functions inspired by UNIX arpa/inet.h
+ * - convert uint16_t and uint32_t to w2_s_bin
+ */
+
+#include <stdint.h>
+
+extern uint8_t g_w2_endianness;
+
+typedef struct {
+ uint16_t bytes;
+ uint8_t data[];
+} w2_s_bin;
+
+/** allocate new w2_s_bin struct and fill with `*data` for `bytes` bytes */
+w2_s_bin *w2_bin_s_alloc(uint16_t bytes, uint8_t *data);
+/** concatenate 2 w2_s_bin structs, deallocates `a` and `b` */
+w2_s_bin *w2_bin_s_cat(w2_s_bin *a, w2_s_bin *b);
+
+w2_s_bin *w2_bin_from_uint8_t(uint8_t data);
+w2_s_bin *w2_bin_from_uint16_t(uint16_t data);
+w2_s_bin *w2_bin_from_uint32_t(uint32_t data);
+
+/** convert 32-bit value from host endian to network (big-endian) */
+uint32_t w2_bin_hton32(uint32_t h32);
+/** convert 16-bit value from host endian to network (big-endian) */
+uint16_t w2_bin_hton16(uint16_t h16);
+/** convert 32-bit value from network (big-endian) to host endian */
+uint32_t w2_bin_ntoh32(uint32_t n32);
+/** convert 16-bit value from network (big-endian) to host endian */
+uint16_t w2_bin_ntoh16(uint16_t n16);
diff --git a/shared/consts.h b/shared/consts.h
new file mode 100644
index 0000000..25ca94f
--- /dev/null
+++ b/shared/consts.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#ifndef W2_BUILD_STR
+// is defined by CFLAGS += -DW2_BUILD_STR in makefile
+#define W2_BUILD_STR ("????????")
+#endif
+
+/** max logic module execution time in milliseconds */
+#define W2_MAX_MODULE_CYCLE_MS (20)
+/** serial baud rate (bit/s) */
+#define W2_SERIAL_BAUD (9600)
+/** size of the error handling buffer (in errors, not bytes) */
+#define W2_ERROR_BUFFER_SIZE (16)
+/** size of the serial communication buffer (in messages, not bytes) */
+#define W2_SERCOMM_BUFFER_SIZE (16)
+/** size of input (receive) buffer (in bytes) */
+#define W2_SERIAL_READ_BUFFER_SIZE (255)
+/** exponential moving average new measurement weight (double 0-1) */
+#define W2_EMA_WEIGHT (0.10)
+/** size of mode history buffer */
+#define W2_MODE_HISTORY_BUFFER_SIZE (4)
diff --git a/shared/errors.h b/shared/errors.h
new file mode 100644
index 0000000..ac8e95f
--- /dev/null
+++ b/shared/errors.h
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <stdint.h>
+
+#define W2_E_TYPE_MASK (0b11 << 6)
+
+#define W2_E_TYPE_CRIT (0b00 << 6)
+#define W2_E_TYPE_WARN (0b01 << 6)
+#define W2_E_TYPE_INFO (0b10 << 6)
+#define W2_E_TYPE_VERB (0b11 << 6)
+
+/**
+ * enum storing all error codes
+ *
+ * error codes are between 0-63 because the two most significant bits are
+ * reserved for error type checking
+ */
+typedef enum {
+ /** wireless connection lost from either robot or client-side */
+ W2_E_CRIT_CONN_LOST = 0x00 | W2_E_TYPE_CRIT,
+ /** serial COM-port unavalable. client-side only */
+ W2_E_CRIT_COM_UNAVAILABLE = 0x01 | W2_E_TYPE_CRIT,
+ /** line unable to be found automatically */
+ W2_E_CRIT_LINE_LOST = 0x02 | W2_E_TYPE_CRIT,
+ /** obstacle unavoidable, robot stuck */
+ W2_E_CRIT_OBSTACLE_STUCK = 0x03 | W2_E_TYPE_CRIT,
+ /** semver major version doesn't match */
+ W2_E_CRIT_VERSION_INCOMPATIBLE = 0x04 | W2_E_TYPE_CRIT,
+
+ /** battery low, returning to charging station */
+ W2_E_WARN_BATTERY_LOW = 0x00 | W2_E_TYPE_WARN,
+ /** obstacle detected, waiting then trying other route */
+ W2_E_WARN_OBSTACLE_DETECTED = 0x01 | W2_E_TYPE_WARN,
+ /** logic cycle took longer than `W2_MAX_MODULE_CYCLE_MS` */
+ W2_E_WARN_CYCLE_EXPIRED = 0x02 | W2_E_TYPE_WARN,
+ /** error thrown without handler, gets thrown on next cycle */
+ W2_E_WARN_UNCAUGHT_ERROR = 0x03 | W2_E_TYPE_WARN,
+ /** error buffer full, gets thrown on next cycle */
+ W2_E_WARN_ERR_BUFFER_FULL = 0x04 | W2_E_TYPE_WARN,
+ /** line lost, trying to calibrate */
+ W2_E_WARN_LINE_LOST = 0x05 | W2_E_TYPE_WARN,
+ /** serial buffer full, gets thrown on next cycle */
+ W2_E_WARN_SERCOMM_BUFFER_FULL = 0x06 | W2_E_TYPE_WARN,
+ /** semver minor version doesn't match */
+ W2_E_WARN_VERSION_INCOMPATIBLE = 0x07 | W2_E_TYPE_WARN,
+ /** serial byte took to long to receive */
+ W2_E_WARN_SERIAL_TIMEOUT = 0x08 | W2_E_TYPE_WARN,
+ /** unknown message encountered (noisy channel?) */
+ W2_E_WARN_SERIAL_NOISY = 0x09 | W2_E_TYPE_WARN,
+ /** mode history index out of bounds */
+ W2_E_WARN_MODE_HISTORY_BUFFER_IOB = 0x0a | W2_E_TYPE_WARN,
+} w2_e_errorcode;
+
+/**
+ * error struct
+ *
+ * holds an error with type `code`, and an optional `message` with length
+ * `message_length`
+ */
+typedef struct {
+ w2_e_errorcode code;
+ uint8_t message_length;
+ char message[];
+} w2_s_error;
diff --git a/shared/makefile b/shared/makefile
new file mode 100644
index 0000000..cfdf8ac
--- /dev/null
+++ b/shared/makefile
@@ -0,0 +1,5 @@
+SOURCES += $(wildcard ../shared/*.c)
+HEADERS += $(wildcard ../shared/*.h)
+
+clean::
+ rm -f ../shared/*.o
diff --git a/shared/protocol.c b/shared/protocol.c
new file mode 100644
index 0000000..77ec466
--- /dev/null
+++ b/shared/protocol.c
@@ -0,0 +1,97 @@
+#include <stdbool.h>
+
+#include "protocol.h"
+#ifdef W2_SIM
+#include "../robot/orangutan_shim.h"
+#endif
+
+void (*g_w2_cmd_handlers[W2_CMD_COUNT])(w2_s_bin *) = {0};
+void w2_cmd_setup_handlers() {
+ g_w2_cmd_handlers[W2_CMD_PING | W2_CMDDIR_RX] = w2_cmd_ping_rx;
+ g_w2_cmd_handlers[W2_CMD_PING | W2_CMDDIR_TX] = w2_cmd_ping_tx;
+ g_w2_cmd_handlers[W2_CMD_EXPT | W2_CMDDIR_TX] = w2_cmd_expt_tx;
+ g_w2_cmd_handlers[W2_CMD_MODE | W2_CMDDIR_RX] = w2_cmd_mode_rx;
+ g_w2_cmd_handlers[W2_CMD_MODE | W2_CMDDIR_TX] = w2_cmd_mode_tx;
+ g_w2_cmd_handlers[W2_CMD_SPED | W2_CMDDIR_RX] = w2_cmd_sped_rx;
+ g_w2_cmd_handlers[W2_CMD_DIRC | W2_CMDDIR_RX] = w2_cmd_dirc_rx;
+ g_w2_cmd_handlers[W2_CMD_CORD | W2_CMDDIR_RX] = w2_cmd_cord_rx;
+ g_w2_cmd_handlers[W2_CMD_CORD | W2_CMDDIR_TX] = w2_cmd_cord_tx;
+ g_w2_cmd_handlers[W2_CMD_BOMD | W2_CMDDIR_RX] = w2_cmd_bomd_rx;
+ g_w2_cmd_handlers[W2_CMD_BOMD | W2_CMDDIR_TX] = w2_cmd_bomd_tx;
+ g_w2_cmd_handlers[W2_CMD_SRES | W2_CMDDIR_RX] = w2_cmd_sres_rx;
+ g_w2_cmd_handlers[W2_CMD_MCFG | W2_CMDDIR_RX] = w2_cmd_mcfg_rx;
+ g_w2_cmd_handlers[W2_CMD_SENS | W2_CMDDIR_RX] = w2_cmd_sens_rx;
+ g_w2_cmd_handlers[W2_CMD_SENS | W2_CMDDIR_TX] = w2_cmd_sens_tx;
+ g_w2_cmd_handlers[W2_CMD_INFO | W2_CMDDIR_RX] = w2_cmd_info_rx;
+ g_w2_cmd_handlers[W2_CMD_INFO | W2_CMDDIR_TX] = w2_cmd_info_tx;
+ g_w2_cmd_handlers[W2_CMD_DISP | W2_CMDDIR_RX] = w2_cmd_disp_rx;
+ g_w2_cmd_handlers[W2_CMD_PLAY | W2_CMDDIR_RX] = w2_cmd_play_rx;
+ g_w2_cmd_handlers[W2_CMD_CLED | W2_CMDDIR_RX] = w2_cmd_cled_rx;
+}
+
+size_t w2_cmd_sizeof(uint8_t data[W2_SERIAL_READ_BUFFER_SIZE], uint8_t data_length) {
+ if (data[0] == (W2_CMD_PING | W2_CMDDIR_RX)) return sizeof(w2_s_cmd_ping_rx);
+ if (data[0] == (W2_CMD_PING | W2_CMDDIR_TX)) return sizeof(w2_s_cmd_ping_tx);
+
+ if (data[0] == (W2_CMD_MODE | W2_CMDDIR_RX)) return sizeof(w2_s_cmd_mode_rx);
+ if (data[0] == (W2_CMD_MODE | W2_CMDDIR_TX)) return sizeof(w2_s_cmd_mode_tx);
+
+ if (data[0] == (W2_CMD_SPED | W2_CMDDIR_RX)) return sizeof(w2_s_cmd_sped_rx);
+
+ if (data[0] == (W2_CMD_DIRC | W2_CMDDIR_RX)) return sizeof(w2_s_cmd_dirc_rx);
+
+ if (data[0] == (W2_CMD_CORD | W2_CMDDIR_RX)) return sizeof(w2_s_cmd_cord_rx);
+ if (data[0] == (W2_CMD_CORD | W2_CMDDIR_TX)) return sizeof(w2_s_cmd_cord_tx);
+
+ if (data[0] == (W2_CMD_BOMD | W2_CMDDIR_RX)) return sizeof(w2_s_cmd_bomd_rx);
+ if (data[0] == (W2_CMD_BOMD | W2_CMDDIR_TX)) return sizeof(w2_s_cmd_bomd_tx);
+
+ if (data[0] == (W2_CMD_SRES | W2_CMDDIR_RX)) return sizeof(w2_s_cmd_sres_rx);
+
+ if (data[0] == (W2_CMD_SENS | W2_CMDDIR_RX)) return sizeof(w2_s_cmd_sens_rx);
+ if (data[0] == (W2_CMD_SENS | W2_CMDDIR_TX)) return sizeof(w2_s_cmd_sens_tx);
+
+ if (data[0] == (W2_CMD_INFO | W2_CMDDIR_RX)) return sizeof(w2_s_cmd_info_rx);
+ if (data[0] == (W2_CMD_INFO | W2_CMDDIR_TX)) return sizeof(w2_s_cmd_info_tx);
+
+ w2_s_bin *copy = w2_bin_s_alloc(data_length, data);
+ uint8_t length = 1;
+
+ if (data[0] == (W2_CMD_EXPT | W2_CMDDIR_TX)) length = w2_cmd_expt_tx_sizeof(copy);
+ if (data[0] == (W2_CMD_MCFG | W2_CMDDIR_RX)) length = w2_cmd_mcfg_rx_sizeof(copy);
+
+ free(copy);
+
+ return length;
+}
+
+#define W2_DYN_MEMBER_SIZEOF(struct_t, length_byte, trailing_type) \
+ sizeof(struct_t) + \
+ (data->bytes > length_byte ? (sizeof(trailing_type) * data->data[length_byte]) : 0)
+
+size_t w2_cmd_expt_tx_sizeof(w2_s_bin *data) {
+ return W2_DYN_MEMBER_SIZEOF(w2_s_cmd_expt_tx, 2, uint8_t);
+}
+
+size_t w2_cmd_mcfg_rx_sizeof(w2_s_bin *data) {
+ return W2_DYN_MEMBER_SIZEOF(w2_s_cmd_mcfg_rx, 3, w2_s_cmd_mcfg_feature);
+}
+
+void w2_cmd_handler(uint8_t data[W2_SERIAL_READ_BUFFER_SIZE], uint8_t data_length) {
+ w2_s_bin *copy = w2_bin_s_alloc(data_length, data);
+ void (*handler)(w2_s_bin *) = g_w2_cmd_handlers[data[0]];
+
+ if (handler == NULL) {
+#ifdef W2_SIM
+ // TODO throw warning
+ simwarn("unknown serial message with code 0x%02x\n", data[0]);
+#endif
+ } else {
+#ifdef W2_SIM
+ w2_sim_print_serial(copy);
+#endif
+ handler(copy);
+ }
+
+ free(copy);
+}
diff --git a/shared/protocol.h b/shared/protocol.h
new file mode 100644
index 0000000..057f67d
--- /dev/null
+++ b/shared/protocol.h
@@ -0,0 +1,219 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "bin.h"
+#include "consts.h"
+
+#define W2_SERIAL_START_BYTE 0xff
+
+#define W2_CMDDIR_RX 0
+#define W2_CMDDIR_TX 1
+
+#define W2_CMD_CODE_MASK (~1)
+#define W2_CMD_DIRECTION_MASK (1)
+
+#define W2_CMD_COUNT 28
+typedef enum {
+ /** ping command */
+ W2_CMD_PING = 0x00,
+ /** exception command */
+ W2_CMD_EXPT = 0x02,
+ /** mode command */
+ W2_CMD_MODE = 0x04,
+ /** speed command */
+ W2_CMD_SPED = 0x06,
+ /** direct control command */
+ W2_CMD_DIRC = 0x08,
+ /** coordinate command */
+ W2_CMD_CORD = 0x0a,
+ /** backorder modify command */
+ W2_CMD_BOMD = 0x0c,
+ /** soft reset command */
+ W2_CMD_SRES = 0x0e,
+ /** map config command */
+ W2_CMD_MCFG = 0x10,
+ /** sensor data command */
+ W2_CMD_SENS = 0x12,
+ /** info command */
+ W2_CMD_INFO = 0x14,
+ /** display control command */
+ W2_CMD_DISP = 0x16,
+ /** play midi command */
+ W2_CMD_PLAY = 0x18,
+ /** control leds command */
+ W2_CMD_CLED = 0x1a,
+} w2_e_scmds;
+
+#pragma pack(push, 1)
+
+typedef struct {
+ uint8_t opcode;
+ uint8_t id;
+} w2_s_cmd_ping_rx;
+
+typedef struct {
+ uint8_t opcode;
+ uint8_t id;
+} w2_s_cmd_ping_tx;
+
+typedef struct {
+ uint8_t opcode;
+ uint8_t error;
+ uint8_t length;
+ uint8_t message[];
+} w2_s_cmd_expt_tx;
+
+typedef struct {
+ uint8_t opcode;
+ uint8_t mode;
+} w2_s_cmd_mode_rx;
+
+typedef struct {
+ uint8_t opcode;
+ uint8_t mode;
+} w2_s_cmd_mode_tx;
+
+typedef struct {
+ uint8_t opcode;
+ uint8_t speed;
+} w2_s_cmd_sped_rx;
+
+typedef struct {
+ uint8_t opcode;
+ uint16_t left;
+ uint16_t right;
+} w2_s_cmd_dirc_rx;
+
+typedef struct {
+ uint8_t opcode;
+ uint32_t position;
+ uint8_t orientation;
+} w2_s_cmd_cord_rx;
+
+typedef struct {
+ uint8_t opcode;
+ uint32_t position;
+ uint8_t orientation;
+} w2_s_cmd_cord_tx;
+
+typedef struct {
+ uint8_t opcode;
+ uint32_t id;
+ uint32_t position;
+} w2_s_cmd_bomd_rx;
+
+typedef struct {
+ uint8_t opcode;
+ uint32_t id;
+ uint8_t status;
+} w2_s_cmd_bomd_tx;
+
+typedef struct {
+ uint8_t opcode;
+ uint8_t type;
+} w2_s_cmd_sres_rx;
+
+typedef struct {
+ uint16_t position;
+ uint8_t kind;
+} w2_s_cmd_mcfg_feature;
+
+typedef struct {
+ uint8_t opcode;
+ uint8_t width;
+ uint8_t height;
+ uint8_t length;
+ w2_s_cmd_mcfg_feature features[];
+} w2_s_cmd_mcfg_rx;
+
+typedef struct {
+ uint8_t opcode;
+} w2_s_cmd_sens_rx;
+
+typedef struct {
+ uint8_t opcode;
+ // TODO: sensor data
+} w2_s_cmd_sens_tx;
+
+typedef struct {
+ uint8_t opcode;
+} w2_s_cmd_info_rx;
+
+typedef struct {
+ uint8_t opcode;
+ uint8_t build_str[32];
+ uint8_t errcatch_ms;
+ uint8_t io_ms;
+ uint8_t sercomm_ms;
+ uint8_t mode_ms;
+ uint32_t uptime_s;
+} w2_s_cmd_info_tx;
+
+typedef struct {
+} w2_s_cmd_disp_rx;
+
+typedef struct {
+} w2_s_cmd_play_rx;
+
+typedef struct {
+} w2_s_cmd_cled_rx;
+
+#pragma pack(pop)
+
+/** stores message handlers in array with opcode as index */
+extern void (*g_w2_cmd_handlers[W2_CMD_COUNT])(w2_s_bin *);
+/** fills g_w2_cmd_handlers with functions */
+void w2_cmd_setup_handlers();
+
+/** global handler for complete messages */
+void w2_cmd_handler(uint8_t data[W2_SERIAL_READ_BUFFER_SIZE], uint8_t length);
+/** calculate message length */
+size_t w2_cmd_sizeof(uint8_t data[W2_SERIAL_READ_BUFFER_SIZE], uint8_t length);
+
+/** handler for ping_rx (on complete message) */
+void w2_cmd_ping_rx(w2_s_bin *data);
+/** handler for ping_tx (on complete message) */
+void w2_cmd_ping_tx(w2_s_bin *data);
+/** handler for expt_tx (on complete message) */
+void w2_cmd_expt_tx(w2_s_bin *data);
+/** handler for mode_rx (on complete message) */
+void w2_cmd_mode_rx(w2_s_bin *data);
+/** handler for mode_tx (on complete message) */
+void w2_cmd_mode_tx(w2_s_bin *data);
+/** handler for sped_rx (on complete message) */
+void w2_cmd_sped_rx(w2_s_bin *data);
+/** handler for dirc_rx (on complete message) */
+void w2_cmd_dirc_rx(w2_s_bin *data);
+/** handler for cord_rx (on complete message) */
+void w2_cmd_cord_rx(w2_s_bin *data);
+/** handler for cord_tx (on complete message) */
+void w2_cmd_cord_tx(w2_s_bin *data);
+/** handler for bomd_rx (on complete message) */
+void w2_cmd_bomd_rx(w2_s_bin *data);
+/** handler for bomd_tx (on complete message) */
+void w2_cmd_bomd_tx(w2_s_bin *data);
+/** handler for sres_rx (on complete message) */
+void w2_cmd_sres_rx(w2_s_bin *data);
+/** handler for mcfg_rx (on complete message) */
+void w2_cmd_mcfg_rx(w2_s_bin *data);
+/** handler for sens_rx (on complete message) */
+void w2_cmd_sens_rx(w2_s_bin *data);
+/** handler for sens_tx (on complete message) */
+void w2_cmd_sens_tx(w2_s_bin *data);
+/** handler for info_rx (on complete message) */
+void w2_cmd_info_rx(w2_s_bin *data);
+/** handler for info_tx (on complete message) */
+void w2_cmd_info_tx(w2_s_bin *data);
+/** handler for disp_rx (on complete message) */
+void w2_cmd_disp_rx(w2_s_bin *data);
+/** handler for play_rx (on complete message) */
+void w2_cmd_play_rx(w2_s_bin *data);
+/** handler for cled_rx (on complete message) */
+void w2_cmd_cled_rx(w2_s_bin *data);
+
+/** calculate message length for expt_tx (incomplete message) */
+size_t w2_cmd_expt_tx_sizeof(w2_s_bin *data);
+/** calculate message length for mcfg_rx (incomplete message) */
+size_t w2_cmd_mcfg_rx_sizeof(w2_s_bin *data);
diff --git a/shared/readme.md b/shared/readme.md
new file mode 100644
index 0000000..870f015
--- /dev/null
+++ b/shared/readme.md
@@ -0,0 +1,8 @@
+# shared code
+
+this is the subdirectory for all code that is shared between the robot code and
+the client code. to use these, include the .h files with a relative path (e.g.
+`#include "../shared/consts.h"`). makefiles should add `include
+../shared/makefile` to add the .c and .h files to `$SOURCES` and `$HEADERS` in
+the makefile targets (this is already done for the robot and client
+subdirectories).
diff --git a/shared/semver.c b/shared/semver.c
new file mode 100644
index 0000000..07a7057
--- /dev/null
+++ b/shared/semver.c
@@ -0,0 +1,9 @@
+#include "semver.h"
+
+#include <stdio.h>
+
+w2_semver_t w2_semver_parse(const char *str, uint8_t length) {
+ w2_semver_t version;
+ sscanf(str, "%d.%d.%d", (int *)&version.major, (int *)&version.minor, (int *)&version.patch);
+ return version;
+}
diff --git a/shared/semver.h b/shared/semver.h
new file mode 100644
index 0000000..a99ae61
--- /dev/null
+++ b/shared/semver.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <stdint.h>
+
+typedef struct {
+ uint8_t major;
+ uint8_t minor;
+ uint8_t patch;
+} w2_semver_t;
+
+w2_semver_t w2_semver_parse(const char *str, uint8_t length);
diff --git a/shared/serial_parse.c b/shared/serial_parse.c
new file mode 100644
index 0000000..89c5809
--- /dev/null
+++ b/shared/serial_parse.c
@@ -0,0 +1,44 @@
+#include <stdbool.h>
+#include <string.h>
+
+#include "consts.h"
+#include "serial_parse.h"
+
+// TODO: give this function last time of byte, and measure if >5ms, throw warning
+void w2_serial_parse(uint8_t byte) {
+ static uint8_t current_message[W2_SERIAL_READ_BUFFER_SIZE] = {0};
+ static uint8_t current_message_index = 0;
+ static uint8_t complete_message_length = 2;
+
+ static bool attentive = false;
+ static bool listening = false;
+
+ if (byte == W2_SERIAL_START_BYTE) {
+ attentive = !attentive;
+ // if (attentive && listening) {
+ // current_message[current_message_index++] = byte;
+ // }
+ } else {
+ // activate listen after non-0xff byte after 0xff
+ if (attentive && !listening) {
+ attentive = false;
+ listening = true;
+ }
+ }
+
+ if (!listening) return;
+ current_message[current_message_index++] = byte;
+
+ complete_message_length = w2_cmd_sizeof(current_message, current_message_index);
+
+ if (current_message_index == complete_message_length) {
+ w2_cmd_handler(current_message, current_message_index);
+
+ memset(&current_message, 0, W2_SERIAL_READ_BUFFER_SIZE);
+ current_message_index = 0;
+ complete_message_length = 1;
+ attentive = false;
+ listening = false;
+ return;
+ }
+}
diff --git a/shared/serial_parse.h b/shared/serial_parse.h
new file mode 100644
index 0000000..a1c8fb9
--- /dev/null
+++ b/shared/serial_parse.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <stdint.h>
+
+#include "protocol.h"
+
+/** parse serial data byte by byte */
+void w2_serial_parse(uint8_t byte);
diff --git a/shared/util.c b/shared/util.c
new file mode 100644
index 0000000..55f3491
--- /dev/null
+++ b/shared/util.c
@@ -0,0 +1,6 @@
+#include "consts.h"
+
+unsigned long w2_util_exp_mov_avg(unsigned long current_avg, unsigned long new_meas) {
+ return (unsigned long)((((double)(current_avg)) * ((double)(1.f - W2_EMA_WEIGHT))) +
+ (((double)(new_meas)) * ((double)(W2_EMA_WEIGHT))));
+}
diff --git a/shared/util.h b/shared/util.h
new file mode 100644
index 0000000..9e4d8ac
--- /dev/null
+++ b/shared/util.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#define W2_MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define W2_MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define W2_RANGE(min, val, max) W2_MIN(max, W2_MAX(val, min))
+
+unsigned long w2_util_exp_mov_avg(unsigned long current_avg, unsigned long new_meas);