summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--robot/consts.h5
-rw-r--r--robot/errcatch.c58
-rw-r--r--robot/errcatch.h29
-rw-r--r--robot/hypervisor.c26
-rw-r--r--robot/makefile28
-rw-r--r--robot/orangutan_shim.h7
-rw-r--r--robot/readme.md37
-rw-r--r--robot/setup.c2
-rw-r--r--robot/sim.c64
-rw-r--r--robot/sim.h40
10 files changed, 266 insertions, 30 deletions
diff --git a/robot/consts.h b/robot/consts.h
index 1195422..a81908d 100644
--- a/robot/consts.h
+++ b/robot/consts.h
@@ -1,17 +1,19 @@
#pragma once
#ifndef W2_BUILD_STR
-// should be defined with -DBUILD_STR in makefile
+// is defined by CFLAGS += -DW2_BUILD_STR in makefile
#define W2_BUILD_STR ("????????")
#endif
#define W2_MAX_MODULE_CYCLE_MS (20)
#define W2_SERIAL_BAUD (9600)
+#define W2_ERROR_BUFFER_SIZE (16)
#define W2_ERR_TYPE_CRIT (0b00 << 6)
#define W2_ERR_TYPE_WARN (0b01 << 6)
#define W2_ERR_TYPE_INFO (0b10 << 6)
#define W2_ERR_TYPE_VERB (0b11 << 6)
+#define W2_ERR_TYPE_MASK (0b11 << 6)
/**
* enum storing all error codes
@@ -30,4 +32,5 @@ enum w2_e_errorcodes {
W2_ERR_BATTERY_LOW = 0x00 | W2_ERR_TYPE_WARN,
W2_ERR_OBSTACLE_DETECTED = 0x01 | W2_ERR_TYPE_WARN,
W2_ERR_CYCLE_EXPIRED = 0x02 | W2_ERR_TYPE_WARN,
+ W2_ERR_UNCAUGHT_ERROR = 0x03 | W2_ERR_TYPE_WARN,
};
diff --git a/robot/errcatch.c b/robot/errcatch.c
index 5b42bb4..d5ad320 100644
--- a/robot/errcatch.c
+++ b/robot/errcatch.c
@@ -1,7 +1,61 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "consts.h"
#include "errcatch.h"
+#include "halt.h"
+#include "modes.h"
+#include "orangutan_shim.h"
+
+w2_s_error g_w2_error_buffer[W2_ERROR_BUFFER_SIZE] = {};
+uint8_t g_w2_error_index = 0;
+uint8_t g_w2_error_offset = 0;
-void w2_errcatch_main() {}
+void w2_errcatch_main() {
+ while (g_w2_error_index != g_w2_error_offset) {
+ w2_s_error *error = &g_w2_error_buffer[g_w2_error_offset];
+ w2_errcatch_handle_error(error);
+ free(error);
+ g_w2_error_offset = (g_w2_error_offset + 1) % W2_ERROR_BUFFER_SIZE;
+ }
+}
-void w2_errcatch_throw_msg(enum w2_e_errorcodes code, uint16_t length, const char *message) {}
+w2_s_error *w2_alloc_error(enum w2_e_errorcodes code, uint16_t length, const char *message) {
+ w2_s_error *error = calloc(sizeof(w2_s_error) + length, 1);
+ memcpy(error, &(w2_s_error const){.code = code, .message_length = length}, sizeof(w2_s_error));
+ strncpy(error->message, message, length);
+
+ return error;
+}
void w2_errcatch_throw(enum w2_e_errorcodes code) { w2_errcatch_throw_msg(code, 0, ""); }
+void w2_errcatch_throw_msg(enum w2_e_errorcodes code, uint16_t length, const char *message) {
+ w2_s_error error = *w2_alloc_error(code, length, message);
+ g_w2_error_buffer[g_w2_error_index] = error;
+ g_w2_error_index = (g_w2_error_index + 1) % W2_ERROR_BUFFER_SIZE;
+}
+
+void w2_errcatch_handle_error(w2_s_error *error) {
+ uint8_t severity = error->code & W2_ERR_TYPE_MASK;
+
+ // trigger emergency mode for critical errors
+ if ((severity ^ W2_ERR_TYPE_CRIT) == 0) g_w2_current_mode = &w2_mode_halt;
+
+ // TODO: handle more error types
+ switch (error->code) {
+ case W2_ERR_UNCAUGHT_ERROR: {
+ break;
+ }
+ default: {
+ w2_errcatch_throw(W2_ERR_UNCAUGHT_ERROR);
+#ifdef W2_SIM
+ simwarn("Uncaught/unhandled error found with code 0x%02x", error->code);
+#endif
+ }
+ }
+
+ // TODO: forward error to sercomm
+
+ return;
+}
diff --git a/robot/errcatch.h b/robot/errcatch.h
index 48e2a75..2297886 100644
--- a/robot/errcatch.h
+++ b/robot/errcatch.h
@@ -4,11 +4,40 @@
#include "consts.h"
+/**
+ * error struct
+ *
+ * holds an error with type `code`, and an optional `message` with length
+ * `message_length`
+ */
+typedef struct {
+ enum w2_e_errorcodes code;
+ uint8_t message_length;
+ char message[];
+} w2_s_error;
+
+/** error ring buffer */
+extern w2_s_error g_w2_error_buffer[W2_ERROR_BUFFER_SIZE];
+/** stores head of ring buffer */
+extern uint8_t g_w2_error_index;
+/** stores start of ring buffer */
+extern uint8_t g_w2_error_offset;
+
/** error-handler module main */
void w2_errcatch_main();
+/** handle error */
+void w2_errcatch_handle_error(w2_s_error *error);
+
/** append error to error buffer */
void w2_errcatch_throw(enum w2_e_errorcodes code);
/** append error to error buffer (with debug message) */
void w2_errcatch_throw_msg(enum w2_e_errorcodes code, uint16_t length, const char *message);
+
+/**
+ * allocate and initialize error struct
+ *
+ * TODO: doesn't handle null pointers from calloc
+ */
+w2_s_error *w2_alloc_error(enum w2_e_errorcodes code, uint16_t length, const char *message);
diff --git a/robot/hypervisor.c b/robot/hypervisor.c
index 381d9af..6b32776 100644
--- a/robot/hypervisor.c
+++ b/robot/hypervisor.c
@@ -1,19 +1,31 @@
-#include <pololu/orangutan.h>
-
+#include "hypervisor.h"
#include "consts.h"
#include "errcatch.h"
-#include "hypervisor.h"
#include "io.h"
#include "modes.h"
+#include "orangutan_shim.h"
#include "sercomm.h"
void w2_hypervisor_main() {
+ time_reset();
+
w2_sercomm_main();
+ unsigned long sercomm_time = get_ms();
w2_errcatch_main();
+ unsigned long errcatch_time = get_ms() - sercomm_time;
w2_io_main();
-
- time_reset();
+ unsigned long io_time = get_ms() - errcatch_time;
w2_modes_main();
- unsigned long elapsed_ms = get_ms();
- if (elapsed_ms > W2_MAX_MODULE_CYCLE_MS) w2_errcatch_throw(W2_ERR_CYCLE_EXPIRED);
+ unsigned long mode_time = get_ms() - io_time;
+
+#ifdef W2_SIM
+ siminfo("sercomm: %lums\n", sercomm_time);
+ siminfo("errcatch: %lums\n", errcatch_time);
+ siminfo("io: %lums\n", io_time);
+ siminfo("mode: %lums\n", mode_time);
+
+ usleep(100e3);
+#endif
+
+ if (mode_time > W2_MAX_MODULE_CYCLE_MS) w2_errcatch_throw(W2_ERR_CYCLE_EXPIRED);
}
diff --git a/robot/makefile b/robot/makefile
index 598e0fa..6f50519 100644
--- a/robot/makefile
+++ b/robot/makefile
@@ -5,23 +5,33 @@ DEVICE ?= atmega168
MCU ?= atmega168
AVRDUDE_DEVICE ?= m168
-CFLAGS=-g -Wall -mcall-prologues -mmcu=$(MCU) $(DEVICE_SPECIFIC_CFLAGS) -Os
-LDFLAGS=-Wl,-gc-sections -lpololu_$(DEVICE) -Wl,-relax
-
PORT ?= /dev/ttyACM0
-SOURCES := $(wildcard *.c)
-HEADERS := $(wildcard *.h)
+CFLAGS=-g -Wall $(DEVICE_SPECIFIC_CFLAGS) -Os
+LDFLAGS=-Wl,-gc-sections -Wl,-relax
+
+SOURCES := $(filter-out sim.c, $(wildcard *.c))
+HEADERS := $(filter-out sim.h, $(wildcard *.h))
+
+# simulation
+# SIM = true
+CFLAGS += $(if $(SIM), -DW2_SIM, -mcall-prologues -mmcu=$(MCU))
+LDFLAGS += $(if $(SIM), , -lpololu_$(DEVICE))
+PREFIX := $(if $(SIM), , avr-)
+SOURCES += $(if $(SIM), sim.c, )
+HEADERS += $(if $(SIM), sim.h, )
+
OBJECTS := $(patsubst %.c,%.o, $(SOURCES))
AVRDUDE=avrdude
-CC=avr-gcc
-OBJ2HEX=avr-objcopy
+CC=$(PREFIX)gcc
+OBJ2HEX=$(PREFIX)objcopy
+# debug build info string
BUILD_STR=$(shell git update-index -q --refresh; git describe --tags --dirty='*' --broken='x' | cut -c1-20)
CFLAGS += -DW2_BUILD_STR="$(BUILD_STR)"
-all: out.hex
+all: $(if $(SIM), a.out, out.hex)
clean:
rm -f *.o out.hex a.out compile_commands.json
@@ -40,6 +50,8 @@ flash: out.hex
$(AVRDUDE) -p $(AVRDUDE_DEVICE) -c avrisp2 -P $(PORT) -U flash:w:out.hex
format:
+ $(eval SOURCES := $(filter-out sim.c, $(SOURCES)))
+ $(eval HEADERS := $(filter-out sim.h, $(HEADERS)))
clang-format -i $(SOURCES) $(HEADERS)
clang-tidy --fix-errors $(SOURCES) $(HEADERS)
diff --git a/robot/orangutan_shim.h b/robot/orangutan_shim.h
new file mode 100644
index 0000000..de57c98
--- /dev/null
+++ b/robot/orangutan_shim.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#ifdef W2_SIM
+#include "sim.h"
+#else
+#include <pololu/orangutan.h>
+#endif
diff --git a/robot/readme.md b/robot/readme.md
index bb88e80..4a7aca3 100644
--- a/robot/readme.md
+++ b/robot/readme.md
@@ -17,6 +17,13 @@ device manager on windows, or by running `ls /dev/ttyACM*` on linux. once the
com port is configured, run `make flash` to upload the executable and
automatically reboot the robot.
+another fun option in the makefile is the SIM mode. by uncommenting the line `#
+SIM = true`, the robot code can be compiled for desktop debugging instead. all
+used pololu functions must be manually implemented in sim.c for this to work,
+but it allows easier debugging. *it's important that the `orangutan_shim.h`
+header is used instead of including `<pololu/orangutan.h>` directly for this to
+keep working!*
+
## module hierarchy
the software is divided into seperate 'modules' for organizational,
@@ -48,12 +55,12 @@ what they're supposed to do:
|module |internal name|author|purpose|
|----------------|-------------|-|-|
|hypervisor |`hypervisor `|N/a| backbone of all other modules; stores global variables; controls when other modules run|
-|pc communication|`sercomm `|Jorn & Abdullaahi| reads and parses incoming serial data; sends all data in the message buffer|
+|pc communication|`sercomm `|Fiona| reads and parses incoming serial data; sends all data in the message buffer|
|error handling |`errcatch `|Loek| receives error codes; controls how errors are handled|
-|i/o read & write|`io `|Fiona| reads all inputs to global state; writes all outputs|
+|i/o read & write|`io `|Jorn & Abdullaahi| reads all inputs to global state; writes all outputs|
|mode logic |`modes `|N/a| executes the appropriate module for current mode|
-|maze |`mode_maze `|TBD| controls robot during maze portion of map; hands off control to warehouse module|
-|warehouse |`mode_grid `|TDB| controls robot during warehouse portion of map; hands off control to maze module|
+|maze |`mode_maze `|Jorn & Abdullaahi| controls robot during maze portion of map; hands off control to warehouse module|
+|warehouse |`mode_grid `|Loek| controls robot during warehouse portion of map; hands off control to maze module|
|emergency stop |`mode_halt `|Fiona| stops all execution until emergency mode is reset by software or user|
|calibration |`mode_calb `|Fiona| find line by turning on own axis if lost|
@@ -86,6 +93,8 @@ this list will probably get updated from time to time:
- arbitrary numbers should be aliased to `#define` statements or `enum`s if
part of a series.
- general constants should be placed in `consts.h`
+- don't import `<pololu/orangutan.h>` directly, instead use
+ `"orangutan_shim.h"` to keep code compatible with the simulator
## todo
@@ -120,19 +129,25 @@ to act on accordingly.
the error handling module (a) provides functions for other modules to report
errors, and (b) handles errors accordingly.
-- [ ] create an error `struct` that holds:
- - [ ] error code
- - [ ] message length
- - [ ] message contents
-- [ ] create a global error ring buffer with an appropriate size that holds
+- [x] create an error `struct` that holds:
+ - [x] error code
+ - [x] message length
+ - [x] message contents
+- [x] create a global error ring buffer with an appropriate size that holds
error messages
- [ ] handle errors in the error buffer, referencing the functional
specification for details on what the robot should do to resolve each kind of
error
- [ ] forward error codes to the pc-communication module
-empty function declarations are in place for providing other modules an error
-reporting function.
+~empty function declarations are in place for providing other modules an error
+reporting function.~
+
+this module is as good as finished but full functionality is currently
+dependent on:
+
+- [ ] pc communication
+- [ ] other mode implementations
### i/o read & write
diff --git a/robot/setup.c b/robot/setup.c
index 10001c7..c74cca9 100644
--- a/robot/setup.c
+++ b/robot/setup.c
@@ -1,9 +1,9 @@
-#include <pololu/orangutan.h>
#include <stdlib.h>
#include "consts.h"
#include "halt.h"
#include "modes.h"
+#include "orangutan_shim.h"
#include "setup.h"
void w2_setup_main() {
diff --git a/robot/sim.c b/robot/sim.c
new file mode 100644
index 0000000..6bd5838
--- /dev/null
+++ b/robot/sim.c
@@ -0,0 +1,64 @@
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+
+#include "sim.h"
+
+struct timespec reference_time; // NOLINT
+
+void time_reset() {
+ simprintfunc("time_reset", "");
+ clock_gettime(CLOCK_MONOTONIC, &reference_time);
+ return;
+}
+
+unsigned long get_ms() {
+ simprintfunc("get_ms", "");
+ struct timespec elapsed;
+ clock_gettime(CLOCK_MONOTONIC, &elapsed);
+ return ((elapsed.tv_sec * 1000) + (elapsed.tv_nsec / 1000000)) -
+ ((reference_time.tv_sec * 1000) + (reference_time.tv_nsec / 1000000));
+}
+
+void red_led(unsigned char on) {
+ simprintfunc("red_led", "%i", on);
+ return;
+}
+
+void green_led(unsigned char on) {
+ simprintfunc("green_led", "%i", on);
+ return;
+}
+
+void clear() {
+ simprintfunc("clear", "");
+ return;
+}
+
+void play(const char* melody) {
+ simprintfunc("play", "\"%s\"", melody);
+ return;
+}
+
+void serial_set_baud_rate(unsigned int rate) {
+ simprintfunc("serial_set_baud_rate", "%u", rate);
+ return;
+}
+
+void serial_send(char* message, unsigned int length) {
+ simprintfunc("serial_send", "<see below>, %u", length);
+ unsigned int bytes = 0;
+ simprintf("");
+ for (unsigned int byte = 0; byte < length; byte++) {
+ if (bytes > DBG_BYTES_PER_LINE) {
+ bytes = 0;
+ printf("\n");
+ simprintf("");
+ }
+ printf("%02x ", message[byte]);
+ bytes++;
+ }
+ printf("\n");
+ return;
+}
+
diff --git a/robot/sim.h b/robot/sim.h
new file mode 100644
index 0000000..15a1b4b
--- /dev/null
+++ b/robot/sim.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+// debug print options
+#define DBG_BYTES_PER_LINE 16
+
+// debug colors
+#define COL_BLK "\e[0;30m"
+#define COL_RED "\e[0;31m"
+#define COL_GRN "\e[0;32m"
+#define COL_YEL "\e[0;33m"
+#define COL_BLU "\e[0;34m"
+#define COL_MAG "\e[0;35m"
+#define COL_CYN "\e[0;36m"
+#define COL_WHT "\e[0;37m"
+#define COL_RST "\e[0m"
+
+// debug stdout print macros
+#define simprintf(message, ...) printf(COL_RED "[SIM] " COL_RST message, ##__VA_ARGS__)
+#define simprint(message) simprintf(message "\n")
+#define simprintfunc(name, fmt, ...) simprintf(COL_BLU "[FUNC] " \
+ COL_CYN name COL_RST "(" COL_YEL fmt COL_RST ")\n", ##__VA_ARGS__)
+#define simwarn(message, ...) simprintf(COL_YEL "[WARN] " COL_RST message, ##__VA_ARGS__)
+#define siminfo(message, ...) simprintf(COL_MAG "[INFO] " COL_RST message, ##__VA_ARGS__)
+
+/**
+ * simulates pololu library functions for local testing
+ * NOLINT is so clang-tidy doesn't correct function names
+ */
+void time_reset(); // NOLINT
+unsigned long get_ms(); // NOLINT
+void red_led(unsigned char on); // NOLINT
+void green_led(unsigned char on); // NOLINT
+void clear(); // NOLINT
+void play(const char* melody); // NOLINT
+void serial_set_baud_rate(unsigned int rate); // NOLINT
+void serial_send(char* message, unsigned int length); // NOLINT