diff --git a/.clang-tidy b/.clang-tidy index 2269755a..9e5db847 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,13 +1,19 @@ Checks: | -*, + bugprone-*, clang-analyzer-*, cppcoreguidelines-*, misc-*, readability-*, - -readability-identifier-length, + + -clang-analyzer-optin.core.EnumCastOutOfRange, + -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, -cppcoreguidelines-avoid-non-const-global-variables, - -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling + -cppcoreguidelines-macro-to-enum, + -misc-include-cleaner, + -readability-identifier-length + WarningsAsErrors: '*' HeaderFilterRegex: '.*, -subprojects' FormatStyle: file diff --git a/.clangd b/.clangd index fe783898..08bd2059 100644 --- a/.clangd +++ b/.clangd @@ -1,4 +1,6 @@ CompileFlags: Add: - -ferror-limit=0 - - -Wno-gnu-zero-variadic-macro-arguments # Suppress FFF macro warnings + # Suppress FFF macro warnings + - -Wno-gnu-zero-variadic-macro-arguments + - -Wno-c23-extensions diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7ad9500a..656b27a1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -7,14 +7,10 @@ on: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - name: Cache python venv uses: actions/cache@v4 with: @@ -48,6 +44,3 @@ jobs: - name: Build documentation run: meson compile -C build docs - - - name: Build release - run: meson compile -C build release diff --git a/README.md b/README.md index 8ca64056..cf45ba14 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Board applications utilize [FreeRTOS](https://www.freertos.org/). This project is built using [Meson](https://mesonbuild.com/) with the Ninja backend. -Supported OS: Ubuntu 22.04. If you're using Windows, you can utilize the Ubuntu 22.04 [WSL](https://learn.microsoft.com/en-us/windows/wsl/) distribution. +Supported OS: Ubuntu 24.04. If you're using Windows, you can utilize the Ubuntu 24.04 [WSL](https://learn.microsoft.com/en-us/windows/wsl/) distribution. ### Build Steps diff --git a/apps/battery-monitor/src/main.c b/apps/battery-monitor/src/main.c index dc6db81d..74481242 100644 --- a/apps/battery-monitor/src/main.c +++ b/apps/battery-monitor/src/main.c @@ -3,7 +3,7 @@ #include "ck-data.h" #include "freertos-tasks.h" #include "peripherals.h" -#include "rover.h" +#include "rover-defs.h" // CK #include "ck-types.h" @@ -50,7 +50,7 @@ void mayor_init(void) { ck_data_init(); ck_data_t *ck_data = get_ck_data(); - uint32_t city_address = ROVER_BATTERY_MONITOR_ID; + uint32_t city_address = ROVER_CITY_BATTERY_MONITOR; ck_id_t ck_id; diff --git a/apps/battery-monitor/tests/battery-fakes.c b/apps/battery-monitor/tests/battery-fakes.c index d8a9d076..f58670d3 100644 --- a/apps/battery-monitor/tests/battery-fakes.c +++ b/apps/battery-monitor/tests/battery-fakes.c @@ -23,9 +23,9 @@ DEFINE_FAKE_VALUE_FUNC(power_state_t, get_vbat_power_state) DEFINE_FAKE_VALUE_FUNC(power_state_t, get_reg_out_power_state) DEFINE_FAKE_VALUE_FUNC(int, read_file, file_t *) DEFINE_FAKE_VALUE_FUNC(int, write_file_async, const file_t *) -// NOLINTEND void reset_fakes(void) { FFF_FAKES_LIST(RESET_FAKE); FFF_RESET_HISTORY(); } +// NOLINTEND diff --git a/apps/brake/src/ck.c b/apps/brake/src/ck.c index 8f0da809..a11796d9 100644 --- a/apps/brake/src/ck.c +++ b/apps/brake/src/ck.c @@ -5,7 +5,7 @@ #include "wheel-speed.h" // Libs -#include "rover.h" +#include "rover-defs.h" // STM32Common #include "device-id.h" @@ -43,7 +43,7 @@ void mayor_init(void) { ck_data_init(); ck_data_t *ck_data = get_ck_data(); - uint32_t city_address = ROVER_WHEEL_FRONT_LEFT_ID; + uint32_t city_address = ROVER_CITY_WHEEL_FRONT_LEFT; ck_id_t ck_id; diff --git a/apps/brake/src/report.c b/apps/brake/src/report.c index 08a317d0..28f0d548 100644 --- a/apps/brake/src/report.c +++ b/apps/brake/src/report.c @@ -70,6 +70,10 @@ void report(void *unused) { xTimerChangePeriod(timer, report_period_ms, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + if (ck_get_action_mode() == CK_ACTION_MODE_FREEZE) { + continue; + } + if (ck_send_document(ck_data->wheel_speed_folder->folder_no) != CK_OK) { printf("failed to send doc.\r\n"); } diff --git a/apps/obstacle-detector/include/adc.h b/apps/obstacle-detector/include/adc.h index f2fee9c4..ddcbc088 100644 --- a/apps/obstacle-detector/include/adc.h +++ b/apps/obstacle-detector/include/adc.h @@ -8,7 +8,7 @@ extern "C" { #include #define ADC1_NUM_CHANNELS 4 -#define ADC_NUM_SAMPLES 10 +#define ADC_NUM_SAMPLES 100 typedef struct { uint16_t adc1_buf[ADC1_NUM_CHANNELS]; diff --git a/apps/obstacle-detector/include/ck-data.h b/apps/obstacle-detector/include/ck-data.h new file mode 100644 index 00000000..c3ca0e33 --- /dev/null +++ b/apps/obstacle-detector/include/ck-data.h @@ -0,0 +1,45 @@ +#ifndef CK_DATA_H +#define CK_DATA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ck-types.h" + +#define CK_DATA_TX_PAGE_COUNT 1 +#define CK_DATA_TX_DOC_COUNT 1 +#define CK_DATA_LIST_COUNT 2 +#define CK_DATA_TX_FOLDER_COUNT 1 +#define CK_DATA_RX_FOLDER_COUNT 1 +#define CK_DATA_FOLDER_COUNT \ + (2 + CK_DATA_TX_FOLDER_COUNT + CK_DATA_RX_FOLDER_COUNT) + +typedef struct { + ck_page_t pages[CK_DATA_TX_PAGE_COUNT]; + ck_document_t docs[CK_DATA_TX_DOC_COUNT]; + ck_list_t lists[CK_DATA_LIST_COUNT]; + ck_folder_t folders[CK_DATA_FOLDER_COUNT]; + + // Convenience pointers + ck_list_t *tx_list; + ck_list_t *rx_list; + + ck_page_t *object_distance_page; + + // Transmit + ck_folder_t *object_distance_folder; + + // Receive + ck_folder_t *set_report_freq_folder; + +} ck_data_t; + +void ck_data_init(void); +ck_data_t *get_ck_data(void); + +#ifdef __cplusplus +} +#endif + +#endif /* CK_DATA_H */ diff --git a/apps/obstacle-detector/include/ck.h b/apps/obstacle-detector/include/ck.h new file mode 100644 index 00000000..e1d6410c --- /dev/null +++ b/apps/obstacle-detector/include/ck.h @@ -0,0 +1,16 @@ +#ifndef CK_H +#define CK_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void init_ck_task(uint8_t priority); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* end of include guard: CK_H */ diff --git a/apps/obstacle-detector/include/obstacle-detector.h b/apps/obstacle-detector/include/obstacle-detector.h new file mode 100644 index 00000000..c9830842 --- /dev/null +++ b/apps/obstacle-detector/include/obstacle-detector.h @@ -0,0 +1,16 @@ +#ifndef OBSTACLE_DETECTOR_H +#define OBSTACLE_DETECTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void init_obstacle_detector_task(uint8_t priority); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* end of include guard: OBSTACLE_DETECTOR_H */ diff --git a/apps/obstacle-detector/include/report.h b/apps/obstacle-detector/include/report.h new file mode 100644 index 00000000..ccc43d9a --- /dev/null +++ b/apps/obstacle-detector/include/report.h @@ -0,0 +1,19 @@ +#ifndef REPORT_H +#define REPORT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "ck-types.h" + +void init_report_task(uint8_t priority); +int process_report_freq_letter(const ck_letter_t *letter); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* end of include guard: REPORT_H */ diff --git a/apps/obstacle-detector/meson.build b/apps/obstacle-detector/meson.build index 249431d4..5dd2a43a 100644 --- a/apps/obstacle-detector/meson.build +++ b/apps/obstacle-detector/meson.build @@ -2,9 +2,13 @@ obstacle_detector_inc = include_directories('include') obstacle_detector_src = files( 'src' / 'adc.c', + 'src' / 'ck.c', + 'src' / 'ck-data.c', 'src' / 'interrupts.c', 'src' / 'main.c', + 'src' / 'obstacle-detector.c', 'src' / 'peripherals.c', + 'src' / 'report.c', ) obstacle_detector_elf = executable( diff --git a/apps/obstacle-detector/src/adc.c b/apps/obstacle-detector/src/adc.c index afdddda0..fe10c5a5 100644 --- a/apps/obstacle-detector/src/adc.c +++ b/apps/obstacle-detector/src/adc.c @@ -10,18 +10,21 @@ void adc_average_samples(adc_reading_t *average, sum_adc1[i % ADC1_NUM_CHANNELS] += samples->adc1_buf[i]; } - for (uint8_t j = 0; j < ADC1_NUM_CHANNELS; j++) { - average->adc1_buf[j] = sum_adc1[j] / ADC_NUM_SAMPLES; + for (uint32_t i = 0; i < ADC1_NUM_CHANNELS; i++) { + average->adc1_buf[i] = sum_adc1[i] / ADC_NUM_SAMPLES; } } // Datasheet says multiply by 6 for 10-bit ADC. -// Since we have 12 bit ADC, we divide by 4 as well. +// Since we have 12 bit ADC, we divide by 4 to convert to 10 bit. float adc_value_to_distance(uint16_t adc_value) { const float scale_factor = 6 / 4.0F; const float offset = -300.0F; - float distance = scale_factor * (float)adc_value + offset; + float distance = (scale_factor * (float)adc_value) + offset; + if (distance < 0) { + distance = 0; + } return distance; } diff --git a/apps/obstacle-detector/src/ck-data.c b/apps/obstacle-detector/src/ck-data.c new file mode 100644 index 00000000..11d84d8a --- /dev/null +++ b/apps/obstacle-detector/src/ck-data.c @@ -0,0 +1,177 @@ +#include "ck-data.h" + +#include +#include + +#include "error.h" +#include "json.h" +#include "jsondb.h" + +static ck_data_t ck_data; + +static void page_init(void); +static void doc_init(void); +static void list_init(void); +static void folder_init(void); +static void assign_stored(void); + +static int check_folder(json_object_t *folder); +static int check_envelope(json_object_t *envelope); +static void assign(json_object_t *folder, json_object_t *envelope); + +void ck_data_init(void) { + page_init(); + doc_init(); + list_init(); + folder_init(); + + assign_stored(); +} + +ck_data_t *get_ck_data(void) { + return &ck_data; +} + +static void page_init(void) { + ck_data.object_distance_page = &ck_data.pages[0]; + ck_data.object_distance_page->line_count = 4 * sizeof(uint16_t); +} + +static void doc_init(void) { + for (uint8_t i = 0; i < CK_DATA_TX_DOC_COUNT; i++) { + ck_data.docs[i].direction = CK_DIRECTION_TRANSMIT; + ck_data.docs[i].page_count = 1; + ck_data.docs[i].pages[0] = &ck_data.pages[i]; + } +} + +static void list_init(void) { + ck_data.tx_list = &ck_data.lists[0]; + ck_data.rx_list = &ck_data.lists[1]; + + // Set up the doc lists + ck_data.rx_list->type = CK_LIST_DOCUMENT; + ck_data.rx_list->direction = CK_DIRECTION_RECEIVE; + ck_data.rx_list->list_no = 0; + ck_data.rx_list->record_count = 1; // Only 1 slot, for the king's doc. + + ck_data.tx_list->type = CK_LIST_DOCUMENT; + ck_data.tx_list->direction = CK_DIRECTION_TRANSMIT; + ck_data.tx_list->list_no = 0; + + // CK needs 1 slot for the mayor's doc. + ck_data.tx_list->record_count = CK_DATA_TX_DOC_COUNT + 1; + + for (uint8_t i = 0; i < CK_DATA_TX_DOC_COUNT; i++) { + ck_data.tx_list->records[i + 1] = &ck_data.docs[i]; + } +} + +static void folder_init(void) { + // Set up the transmit folders + ck_data.object_distance_folder = &ck_data.folders[2]; + + for (int i = 2; i < 2 + CK_DATA_TX_FOLDER_COUNT; i++) { + ck_data.folders[i].folder_no = i; + ck_data.folders[i].direction = CK_DIRECTION_TRANSMIT; + ck_data.folders[i].doc_list_no = 0; + ck_data.folders[i].doc_no = i - 1; // 0 reserved by mayor's doc + ck_data.folders[i].enable = true; + } + + ck_data.object_distance_folder->dlc = + ck_data.object_distance_page->line_count; + + // Set up the receive folders + ck_data.set_report_freq_folder = &ck_data.folders[3]; + + for (int i = 2 + CK_DATA_TX_FOLDER_COUNT; i < CK_DATA_FOLDER_COUNT; i++) { + ck_data.folders[i].folder_no = i; + ck_data.folders[i].direction = CK_DIRECTION_RECEIVE; + ck_data.folders[i].doc_list_no = 0; + ck_data.folders[i].doc_no = i - (2 + CK_DATA_TX_FOLDER_COUNT); + ck_data.folders[i].enable = true; + } + + ck_data.set_report_freq_folder->dlc = sizeof(uint16_t); +} + +static void assign_stored(void) { + json_object_t *json = get_jsondb(); + json_object_t *assignments = json_get_object("assignments", json); + if (!assignments || !assignments->child) { + printf("note: no assignments stored\r\n"); + return; + } + + json_object_t *assignment = assignments->child; + + do { + json_object_t *folder = json_get_object("folder", assignment); + json_object_t *envelope = json_get_object("envelope", assignment); + assign(folder, envelope); + assignment = assignment->next; + } while (assignment != NULL); +} + +static int check_folder(json_object_t *folder) { + if (folder->type != JSON_INT || folder->value->int_ < 2 || + folder->value->int_ >= CK_DATA_FOLDER_COUNT) { + return APP_NOT_OK; + } + return APP_OK; +} + +static int check_envelope(json_object_t *envelope) { + if (envelope->type != JSON_INT || envelope->value->int_ < 0) { + return APP_NOT_OK; + } + + ck_envelope_t e = { + .envelope_no = envelope->value->int_, + .has_extended_id = false, + }; + + if (ck_check_envelope(&e) != CK_OK) { + return APP_NOT_OK; + } + + return APP_OK; +} + +static void assign(json_object_t *folder, json_object_t *envelope) { + static int i = 0; + char s[32]; // NOLINT(*-magic-numbers) + if (check_folder(folder) != APP_OK) { + json_sprint(s, folder); + printf("array element %d: couldn't parse folder: %s\r\n", i++, s); + return; + } + if (check_envelope(envelope) != APP_OK) { + json_sprint(s, envelope); + printf("array element %d: couldn't parse envelope: %s\r\n", i++, s); + return; + } + + i++; + + ck_folder_t *f = &ck_data.folders[folder->value->int_]; + if (f->envelope_count >= CK_MAX_ENVELOPES_PER_FOLDER) { + return; + } + + ck_envelope_t e = { + .envelope_no = envelope->value->int_, + .enable = true, + .is_compressed = false, + .is_remote = false, + .has_extended_id = false, + }; + + // Only add envelope if it's not assigned to this folder already + int envelope_index = ck_find_envelope(f, &e); + if (envelope_index < 0) { + f->envelopes[f->envelope_count] = e; + f->envelope_count++; + } +} diff --git a/apps/obstacle-detector/src/ck.c b/apps/obstacle-detector/src/ck.c new file mode 100644 index 00000000..00afd138 --- /dev/null +++ b/apps/obstacle-detector/src/ck.c @@ -0,0 +1,109 @@ +#include + +#include "ck-data.h" +#include "report.h" + +// Libs +#include "rover-defs.h" + +// STM32Common +#include "device-id.h" +#include "error.h" +#include "letter-reader.h" +#include "stm32f3xx_hal_cortex.h" + +// CK +#include "mayor.h" + +// FreeRTOS +#include "FreeRTOS.h" +#include "task.h" + +void mayor_init(void); +ck_err_t set_action_mode(ck_action_mode_t mode); +ck_err_t set_city_mode(ck_city_mode_t mode); + +int handle_letter(const ck_folder_t *folder, const ck_letter_t *letter); + +void init_ck_task(uint8_t priority) { + letter_reader_cfg_t letter_reader_cfg = { + .priority = priority++, + .app_letter_handler_func = handle_letter, + }; + + if (init_letter_reader_task(letter_reader_cfg) != APP_OK) { + error(); + } + + mayor_init(); +} + +void mayor_init(void) { + ck_data_init(); + ck_data_t *ck_data = get_ck_data(); + + uint32_t city_address = ROVER_CITY_OBSTACLE_DETECTOR_FRONT; + + ck_id_t ck_id; + + // Hard coded ID for each rover city. + // Written to flash here to be shared with the bootloader. + // Can be overridden, in that case the original ID can be restored by + // formatting the FS. + int err = read_ck_id(&ck_id); + if (err != APP_OK) { + printf("error: couldn't read ck_id. Writing default ck_id to FS.\r\n"); + ck_id = get_default_ck_id(city_address); + err = write_ck_id(&ck_id); + if (err != APP_OK) { + printf("Error writing ck_id to FS: 0x%x\r\n", err); + } + } + + ck_mayor_t mayor = { + .ean_no = 100 + city_address, // NOLINT(*-magic-numbers) + .serial_no = get_device_serial(), + .ck_id = ck_id, + .set_action_mode = set_action_mode, + .set_city_mode = set_city_mode, + .folder_count = CK_DATA_FOLDER_COUNT, + .folders = ck_data->folders, + .list_count = CK_DATA_LIST_COUNT, + .lists = ck_data->lists, + .skip_startup = true, + }; + + if (ck_mayor_init(&mayor) != CK_OK) { + printf("Error setting up mayor.\r\n"); + error(); + } +} + +ck_err_t set_action_mode(ck_action_mode_t mode) { + if (mode == CK_ACTION_MODE_RESET) { + // This delay is there because otherwise error frames will be generated on + // the CAN bus. The root cause is still unknown. + vTaskDelay(pdMS_TO_TICKS(10)); + HAL_NVIC_SystemReset(); + } + return CK_OK; +} + +ck_err_t set_city_mode(ck_city_mode_t mode) { + (void)mode; + return CK_OK; +} + +int handle_letter(const ck_folder_t *folder, const ck_letter_t *letter) { + if (ck_get_action_mode() == CK_ACTION_MODE_FREEZE) { + return APP_OK; + } + + ck_data_t *ck_data = get_ck_data(); + + if (folder->folder_no == ck_data->set_report_freq_folder->folder_no) { + return process_report_freq_letter(letter); + } + + return APP_OK; +} diff --git a/apps/obstacle-detector/src/main.c b/apps/obstacle-detector/src/main.c index 910ceab9..ec2f2761 100644 --- a/apps/obstacle-detector/src/main.c +++ b/apps/obstacle-detector/src/main.c @@ -1,65 +1,19 @@ -#include #include -#include "adc.h" +#include "ck.h" +#include "obstacle-detector.h" #include "peripherals.h" +#include "report.h" // STM32Common #include "clock.h" +#include "error.h" +#include "jsondb.h" +#include "lfs-wrapper.h" // FreeRTOS #include "FreeRTOS.h" #include "task.h" -#include "timers.h" - -#define TASK_STACK_SIZE (sizeof(adc_samples_t) + configMINIMAL_STACK_SIZE) - -static TaskHandle_t main_task; -static StaticTask_t task_buf; -static StackType_t task_stack[TASK_STACK_SIZE]; - -void task_timer(TimerHandle_t timer) { - (void)timer; - xTaskNotifyGive(main_task); -} - -void task(void* unused) { - (void)unused; - - peripherals_t* peripherals = get_peripherals(); - - adc_samples_t adc_samples; - adc_reading_t adc_reading; - - uint16_t distance[ADC1_NUM_CHANNELS]; - - StaticTimer_t timer_buf; - TimerHandle_t timer = xTimerCreateStatic("timer", pdMS_TO_TICKS(100), - pdTRUE, // Auto reload timer - NULL, // Timer ID, unused - task_timer, &timer_buf); - - xTimerStart(timer, portMAX_DELAY); - - while (1) { - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // Wait for task activation - - HAL_ADC_Start_DMA(&peripherals->hadc1, (uint32_t*)adc_samples.adc1_buf, - ADC_NUM_SAMPLES * ADC1_NUM_CHANNELS); - - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // Wait for DMA - - adc_average_samples(&adc_reading, &adc_samples); - - for (int i = 0; i < ADC1_NUM_CHANNELS; i++) { - float measured_distance = adc_value_to_distance(adc_reading.adc1_buf[i]); - distance[i] = (uint16_t)roundf(measured_distance); - printf("distance %d: %umm\r\n", i, distance[i]); - } - - printf("\r\n"); - } -} int main(void) { // Reset of all peripherals, Initializes the Flash interface and the Systick. @@ -71,19 +25,22 @@ int main(void) { // Initialize all configured peripherals peripherals_init(); - main_task = xTaskCreateStatic(task, "main", TASK_STACK_SIZE, NULL, - LOWEST_TASK_PRIORITY, task_stack, &task_buf); + uint8_t priority = LOWEST_TASK_PRIORITY; + + if (init_lfs_task(priority++) < 0) { + error(); + } + + jsondb_init(); + + init_obstacle_detector_task(priority++); + init_report_task(priority++); + init_ck_task(priority++); + + printf("Starting application...\r\n"); vTaskStartScheduler(); while (1) { } } - -void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { - if (hadc->Instance == ADC1) { - BaseType_t higher_priority_task_woken = pdFALSE; - vTaskNotifyGiveFromISR(main_task, &higher_priority_task_woken); - portYIELD_FROM_ISR(higher_priority_task_woken); - } -} diff --git a/apps/obstacle-detector/src/obstacle-detector.c b/apps/obstacle-detector/src/obstacle-detector.c new file mode 100644 index 00000000..9a70ffa6 --- /dev/null +++ b/apps/obstacle-detector/src/obstacle-detector.c @@ -0,0 +1,66 @@ +#include "obstacle-detector.h" + +#include +#include + +#include "adc.h" +#include "ck-data.h" +#include "peripherals.h" + +// FreeRTOS +#include "FreeRTOS.h" +#include "task.h" +#include "timers.h" + +#define OBSTACLE_DETECTOR_TASK_STACK_SIZE \ + (sizeof(adc_samples_t) + configMINIMAL_STACK_SIZE) + +static TaskHandle_t obstacle_detector_task; +static StaticTask_t obstacle_detector_task_buf; +static StackType_t + obstacle_detector_task_stack[OBSTACLE_DETECTOR_TASK_STACK_SIZE]; + +static void obstacle_detector(void* unused); + +void init_obstacle_detector_task(uint8_t priority) { + obstacle_detector_task = xTaskCreateStatic( + obstacle_detector, "obstacle detector", OBSTACLE_DETECTOR_TASK_STACK_SIZE, + NULL, priority, obstacle_detector_task_stack, + &obstacle_detector_task_buf); +} + +static void obstacle_detector(void* unused) { + (void)unused; + + peripherals_t* peripherals = get_peripherals(); + ck_data_t* ck_data = get_ck_data(); + + while (1) { + adc_samples_t adc_samples = {0}; + adc_reading_t adc_reading = {0}; + uint16_t distance[ADC1_NUM_CHANNELS] = {0}; + + HAL_ADC_Start_DMA(&peripherals->hadc1, (uint32_t*)adc_samples.adc1_buf, + ADC_NUM_SAMPLES * ADC1_NUM_CHANNELS); + + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // Wait for DMA + + adc_average_samples(&adc_reading, &adc_samples); + + for (int i = 0; i < ADC1_NUM_CHANNELS; i++) { + float measured_distance = adc_value_to_distance(adc_reading.adc1_buf[i]); + distance[i] = (uint16_t)roundf(measured_distance); + } + + memcpy(ck_data->object_distance_page->lines, distance, + ck_data->object_distance_page->line_count); + } +} + +void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { + if (hadc->Instance == ADC1) { + BaseType_t higher_priority_task_woken = pdFALSE; + vTaskNotifyGiveFromISR(obstacle_detector_task, &higher_priority_task_woken); + portYIELD_FROM_ISR(higher_priority_task_woken); + } +} diff --git a/apps/obstacle-detector/src/peripherals.c b/apps/obstacle-detector/src/peripherals.c index d0eafc3b..6abf4479 100644 --- a/apps/obstacle-detector/src/peripherals.c +++ b/apps/obstacle-detector/src/peripherals.c @@ -82,7 +82,7 @@ void adc1_init(void) { config.Channel = ADC_CHANNEL_1; config.Rank = ADC_REGULAR_RANK_1; config.SingleDiff = ADC_SINGLE_ENDED; - config.SamplingTime = ADC_SAMPLETIME_19CYCLES_5; + config.SamplingTime = ADC_SAMPLETIME_601CYCLES_5; config.OffsetNumber = ADC_OFFSET_NONE; config.Offset = 0; if (HAL_ADC_ConfigChannel(hadc1, &config) != HAL_OK) { diff --git a/apps/obstacle-detector/src/report.c b/apps/obstacle-detector/src/report.c new file mode 100644 index 00000000..4e10dc5e --- /dev/null +++ b/apps/obstacle-detector/src/report.c @@ -0,0 +1,81 @@ +#include +#include + +#include "ck-data.h" + +// STM32Common +#include "error.h" + +// CK +#include "mayor.h" + +// FreeRTOS +#include "FreeRTOS.h" +#include "task.h" +#include "timers.h" + +static TaskHandle_t report_task; +static StaticTask_t report_task_buf; +static StackType_t report_task_stack[configMINIMAL_STACK_SIZE]; + +#define DEFAULT_REPORT_PERIOD_MS 200 +static uint16_t report_period_ms = DEFAULT_REPORT_PERIOD_MS; + +void report(void *unused); + +void init_report_task(uint8_t priority) { + report_task = + xTaskCreateStatic(report, "report", configMINIMAL_STACK_SIZE, NULL, + priority++, report_task_stack, &report_task_buf); +} + +// 2 bytes in page +// +// bytes 0-1: reporting period in ms, i.e. how often to send +// measurements over CAN. +int process_report_freq_letter(const ck_letter_t *letter) { + ck_data_t *ck_data = get_ck_data(); + if (letter->page.line_count != ck_data->set_report_freq_folder->dlc) { + return APP_NOT_OK; + } + + uint16_t new_period = 0; + memcpy(&new_period, letter->page.lines, sizeof(new_period)); + if (new_period != 0) { + report_period_ms = new_period; + } + + return APP_OK; +} + +void report_timer(TimerHandle_t timer) { + (void)timer; + xTaskNotifyGive(report_task); +} + +void report(void *unused) { + (void)unused; + + ck_data_t *ck_data = get_ck_data(); + + StaticTimer_t timer_buf; + + TimerHandle_t timer = + xTimerCreateStatic("report timer", pdMS_TO_TICKS(report_period_ms), + pdFALSE, // Don't auto reload timer + NULL, // Timer ID, unused + report_timer, &timer_buf); + + while (1) { + xTimerChangePeriod(timer, report_period_ms, portMAX_DELAY); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + if (ck_get_action_mode() == CK_ACTION_MODE_FREEZE) { + continue; + } + + if (ck_send_document(ck_data->object_distance_folder->folder_no) != CK_OK) { + printf("failed to send doc.\r\n"); + } + } +} diff --git a/apps/sbus-receiver/src/main.c b/apps/sbus-receiver/src/main.c index 78bfaa54..574046ab 100644 --- a/apps/sbus-receiver/src/main.c +++ b/apps/sbus-receiver/src/main.c @@ -2,7 +2,7 @@ #include "ck-data.h" #include "freertos-tasks.h" -#include "rover.h" +#include "rover-defs.h" // CK #include "mayor.h" @@ -50,7 +50,7 @@ void mayor_init(void) { ck_data_init(); ck_data_t *ck_data = get_ck_data(); - uint32_t city_address = ROVER_SBUS_RECEIVER_ID; + uint32_t city_address = ROVER_CITY_SBUS_RECEIVER; ck_id_t ck_id; diff --git a/apps/servo/src/adc.c b/apps/servo/src/adc.c index 4616fc00..848784e8 100644 --- a/apps/servo/src/adc.c +++ b/apps/servo/src/adc.c @@ -94,7 +94,7 @@ uint16_t adc_to_h_bridge_current(uint16_t adc_value) { if (current > max_current) { current = max_current; } - return (uint16_t)current; + return current; } // Returns voltage in mV. diff --git a/apps/servo/src/main.c b/apps/servo/src/main.c index 62e70cf8..35e9d6a1 100644 --- a/apps/servo/src/main.c +++ b/apps/servo/src/main.c @@ -4,7 +4,7 @@ #include "freertos-tasks.h" #include "peripherals.h" #include "pwm.h" -#include "rover.h" +#include "rover-defs.h" #include "servo.h" #include "settings.h" @@ -55,10 +55,10 @@ void mayor_init(void) { ck_data_init(); ck_data_t *ck_data = get_ck_data(); - uint32_t city_address = ROVER_SERVO_ID; + uint32_t city_address = ROVER_CITY_SERVO; #ifdef MOTOR - city_address = ROVER_MOTOR_ID; + city_address = ROVER_CITY_MOTOR; #endif ck_id_t ck_id; diff --git a/apps/servo/tests/test-servo.c b/apps/servo/tests/test-servo.c index d3a2340b..4cc0d52e 100644 --- a/apps/servo/tests/test-servo.c +++ b/apps/servo/tests/test-servo.c @@ -21,7 +21,6 @@ FAKE_VOID_FUNC(init_potentiometers) FAKE_VALUE_FUNC(int, read_servo_potentiometer, uint8_t*) FAKE_VALUE_FUNC(int, write_servo_potentiometer, uint8_t) FAKE_VALUE_FUNC(int, write_sensor_potentiometer, uint8_t) -// NOLINTEND #define FFF_FAKES_LIST(FAKE) \ FAKE(failsafe_init) \ @@ -38,6 +37,7 @@ void reset_fakes(void) { FFF_FAKES_LIST(RESET_FAKE); FFF_RESET_HISTORY(); } +// NOLINTEND void test_update_servo_pulse(void); void test_update_servo_pulse_reverse(void); diff --git a/libs/can-kingdom/include/ck-types.h b/libs/can-kingdom/include/ck-types.h index 700fa7be..97641411 100644 --- a/libs/can-kingdom/include/ck-types.h +++ b/libs/can-kingdom/include/ck-types.h @@ -193,40 +193,40 @@ typedef enum { /// Action mode as defined by the CAN Kingdom specification. typedef enum { /// Keep the current mode. - CK_ACTION_MODE_KEEP_CURRENT = 0x0, + CK_ACTION_MODE_KEEP_CURRENT = 0, /// Start the city. - CK_ACTION_MODE_RUN = 0x1, + CK_ACTION_MODE_RUN = 1, /// Stop the city. - CK_ACTION_MODE_FREEZE = 0x2, + CK_ACTION_MODE_FREEZE = 2, /// Reset the city. - CK_ACTION_MODE_RESET = 0x3, + CK_ACTION_MODE_RESET = 3, } ck_action_mode_t; /// Communication mode as defined by the CAN Kingdom specification. typedef enum { /// Keep the current mode. - CK_COMM_MODE_KEEP_CURRENT = 0x0, + CK_COMM_MODE_KEEP_CURRENT = 0, /// Set the CAN controller in silent mode. - CK_COMM_MODE_SILENT = 0x1, + CK_COMM_MODE_SILENT = 1, /// Set the CAN controller in listen-only mode. - CK_COMM_MODE_LISTEN_ONLY = 0x2, + CK_COMM_MODE_LISTEN_ONLY = 2, /// Enable CAN communication. - CK_COMM_MODE_COMMUNICATE = 0x3, + CK_COMM_MODE_COMMUNICATE = 3, } ck_comm_mode_t; /// Communication mode flags, used in KP0. typedef enum { /// Reset communication by going through the startup sequence with new bit /// timing settings. - CK_COMM_RESET = 0x4, + CK_COMM_RESET = 1 << 2, /// Skip listening for a good message during startup sequence. - CK_COMM_SKIP_LISTEN = 0x8, + CK_COMM_SKIP_LISTEN = 1 << 3, /// Don't skip listening for a good message during startup sequence. - CK_COMM_DONT_SKIP_LISTEN = 0x10, + CK_COMM_DONT_SKIP_LISTEN = 1 << 4, /// Skip 200ms wait for default letter during startup sequence. - CK_COMM_SKIP_WAIT = 0x20, + CK_COMM_SKIP_WAIT = 1 << 5, /// Don't 200ms wait for default letter during startup sequence. - CK_COMM_DONT_SKIP_WAIT = 0x40, + CK_COMM_DONT_SKIP_WAIT = 1 << 6, } ck_comm_flags_t; /// Only #CK_CITY_MODE_KEEP_CURRENT is defined, the rest is defined by the user. diff --git a/libs/can-kingdom/src/mayor.c b/libs/can-kingdom/src/mayor.c index ac2d53b3..d4021069 100644 --- a/libs/can-kingdom/src/mayor.c +++ b/libs/can-kingdom/src/mayor.c @@ -1007,7 +1007,7 @@ static ck_err_t remove_envelope_from_folder(ck_folder_t *folder, const ck_envelope_t *envelope) { int envelope_index = ck_find_envelope(folder, envelope); if (envelope_index < 0) { - return -1; + return CK_ERR_ITEM_NOT_FOUND; } // We need to shift all envelopes one step to the left in the array to keep diff --git a/libs/can-kingdom/tests/ck-types-test.c b/libs/can-kingdom/tests/ck-types-test.c index 525b7daa..57ecdbff 100644 --- a/libs/can-kingdom/tests/ck-types-test.c +++ b/libs/can-kingdom/tests/ck-types-test.c @@ -28,6 +28,7 @@ void test_check_action_mode(void) { ASSERT(ck_check_action_mode(CK_ACTION_MODE_RESET) == CK_OK, ""); const int invalid_mode = 8; + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) ASSERT(ck_check_action_mode(invalid_mode) != CK_OK, ""); } @@ -38,6 +39,7 @@ void test_check_comm_mode(void) { ASSERT(ck_check_comm_mode(CK_COMM_MODE_COMMUNICATE) == CK_OK, ""); const int invalid_mode = 8; + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) ASSERT(ck_check_comm_mode(invalid_mode) != CK_OK, ""); } @@ -48,6 +50,7 @@ void test_check_list_type(void) { ASSERT(ck_check_list_type(CK_LIST_DOCUMENT) == CK_OK, ""); const int invalid_type = 8; + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) ASSERT(ck_check_list_type(invalid_type) != CK_OK, ""); } diff --git a/libs/json/src/json.c b/libs/json/src/json.c index d1019645..730c46c5 100644 --- a/libs/json/src/json.c +++ b/libs/json/src/json.c @@ -31,7 +31,7 @@ static void start_array(json_parser_t *parser); static int parse_object_name(json_parser_t *parser); static int parse_value(json_parser_t *parser); static char *parse_string(json_parser_t *parser); -static int json_strlen(const char *str); +static size_t json_strlen(const char *str); static json_object_t *get_parent(json_object_t *object); static bool is_empty(json_object_t *object); static void new_child_object(json_parser_t *parser); @@ -326,7 +326,7 @@ static char *parse_string(json_parser_t *parser) { return str; } -static int json_strlen(const char *str) { +static size_t json_strlen(const char *str) { const char *ptr = str; ptr++; // Skip first quote @@ -341,7 +341,7 @@ static int json_strlen(const char *str) { } } - return (int)(ptr - str - 1); + return ptr - str - 1; } static float parse_float(json_parser_t *parser, int significand) { diff --git a/libs/rover/include/rover-assignments.h b/libs/rover/include/rover-assignments.h new file mode 100644 index 00000000..59fb8606 --- /dev/null +++ b/libs/rover/include/rover-assignments.h @@ -0,0 +1,29 @@ +#ifndef ROVER_ASSIGNMENTS_H +#define ROVER_ASSIGNMENTS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef struct { + uint16_t envelope; + uint8_t city; + uint8_t folder; +} rover_assignment_t; + +typedef struct { + rover_assignment_t* assignments; + size_t assignment_count; +} rover_kingdom_t; + +rover_kingdom_t* get_rover_kingdom(void); +void init_rover_kingdom(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* end of include guard: ROVER_ASSIGNMENTS_H */ diff --git a/libs/rover/include/rover.h b/libs/rover/include/rover.h index 4d7ed4ff..26c95a34 100644 --- a/libs/rover/include/rover.h +++ b/libs/rover/include/rover.h @@ -7,57 +7,6 @@ extern "C" { #include -// City IDs -#define ROVER_BATTERY_MONITOR_ID 1 -#define ROVER_SERVO_ID 2 -#define ROVER_MOTOR_ID 3 -#define ROVER_SBUS_RECEIVER_ID 4 -#define ROVER_WHEEL_FRONT_LEFT_ID 5 -#define ROVER_WHEEL_FRONT_RIGHT_ID 6 -#define ROVER_WHEEL_REAR_LEFT_ID 7 -#define ROVER_WHEEL_REAR_RIGHT_ID 8 - -// Kingdom base number -#define ROVER_BASE_NUMBER 0x400 - -// Envelope IDs -// Control messages -#define ROVER_STEERING_ENVELOPE 0x100 -#define ROVER_THROTTLE_ENVELOPE 0x101 - -// Report messages -#define ROVER_BATTERY_CELL_VOLTAGES_ENVELOPE 0x200 -#define ROVER_BATTERY_REGULATED_OUTPUT_ENVELOPE 0x201 -#define ROVER_BATTERY_BATTERY_OUTPUT_ENVELOPE 0x202 -#define ROVER_SERVO_VOLTAGE_ENVELOPE 0x203 -#define ROVER_SERVO_CURRENT_ENVELOPE 0x204 -#define ROVER_BATTERY_VOLTAGE_ENVELOPE 0x205 -#define ROVER_SERVO_POSITION_ENVELOPE 0x206 - -#define ROVER_FRONT_LEFT_SPEED 0x210 -#define ROVER_FRONT_RIGHT_SPEED 0x211 -#define ROVER_REAR_LEFT_SPEED 0x212 -#define ROVER_REAR_RIGHT_SPEED 0x213 - -// Settings messages -#define ROVER_BATTERY_JUMPER_CONFIG_ENVELOPE 0x300 -#define ROVER_BATTERY_REG_OUT_VOLTAGE_ENVELOPE 0x301 -#define ROVER_BATTERY_OUTPUT_ON_OFF_ENVELOPE 0x302 -#define ROVER_BATTERY_REPORT_FREQUENCY_ENVELOPE 0x303 -#define ROVER_BATTERY_LOW_VOLTAGE_CUTOFF_ENVELOPE 0x304 -#define ROVER_SERVO_SET_VOLTAGE_ENVELOPE 0x305 -#define ROVER_SERVO_PWM_CONFIG_ENVELOPE 0x306 -#define ROVER_SERVO_REPORT_FREQUENCY_ENVELOPE 0x307 -#define ROVER_MOTOR_PWM_CONFIG_ENVELOPE 0x308 -#define ROVER_SERVO_REVERSE_ENVELOPE 0x309 -#define ROVER_MOTOR_REVERSE_ENVELOPE 0x30A -#define ROVER_SERVO_FAILSAFE_ENVELOPE 0x30B -#define ROVER_MOTOR_FAILSAFE_ENVELOPE 0x30C -#define ROVER_SERVO_SET_SUBTRIM_ENVELOPE 0x30D -#define ROVER_MOTOR_SET_SUBTRIM_ENVELOPE 0x30E -#define ROVER_BATTERY_VBAT_OUT_OVERCURRENT_THRESHOLD_ENVELOPE 0x30F -#define ROVER_BATTERY_REG_OUT_OVERCURRENT_THRESHOLD_ENVELOPE 0x310 - void init_king_task(uint32_t priority); #ifdef __cplusplus diff --git a/libs/rover/meson.build b/libs/rover/meson.build index 8a8b5547..0d1ac52c 100644 --- a/libs/rover/meson.build +++ b/libs/rover/meson.build @@ -1,6 +1,24 @@ rover_inc = include_directories('include') -rover_helpers_src = files('src' / 'rover-helpers.c') +rover_assignments_c = custom_target( + 'rover-assignments.c', + input: files('src' / 'gen-rover-sources.py'), + output: 'rover-assignments.c', + command: [python, '@INPUT@', '--source', '@OUTPUT@'], +) + +rover_defs_h = custom_target( + 'rover-defs.h', + input: files('src' / 'gen-rover-sources.py'), + output: 'rover-defs.h', + command: [python, '@INPUT@', '--header', '@OUTPUT@'], +) + +rover_helpers_src = [ + files('src' / 'rover-helpers.c'), + rover_assignments_c, + rover_defs_h, +] rover_src = [files('src' / 'rover.c'), rover_helpers_src] diff --git a/libs/rover/src/gen-rover-sources.py b/libs/rover/src/gen-rover-sources.py new file mode 100644 index 00000000..f423610b --- /dev/null +++ b/libs/rover/src/gen-rover-sources.py @@ -0,0 +1,158 @@ +import argparse +import io +from pathlib import Path + +from rover import rover + +source_file_template = """#include "rover-assignments.h" +#include "rover-defs.h" + +#define ASSIGNMENT_COUNT {assignment_count} + +static rover_assignment_t assignments[ASSIGNMENT_COUNT]; + +static rover_kingdom_t kingdom = {{ + .assignments = assignments, + .assignment_count = ASSIGNMENT_COUNT, +}}; + +rover_kingdom_t *get_rover_kingdom(void) {{ + return &kingdom; +}} + +void init_rover_kingdom(void) {{ +{source_contents} +}} +""" + +header_file_template = """#ifndef {header_def} +#define {header_def} + +#ifdef __cplusplus +extern "C" {{ +#endif + +{header_contents} + +#ifdef __cplusplus +}} +#endif + +#endif /* {header_def} */ +""" + +rover_prefix = "ROVER" +city_prefix = f"{rover_prefix}_CITY" +envelope_prefix = f"{rover_prefix}_ENVELOPE" +folder_prefix = f"{rover_prefix}_FOLDER" + + +def main(): + parser = argparse.ArgumentParser(description="Generate Rover system sources") + parser.add_argument( + "--header", type=argparse.FileType("w"), help="output header file" + ) + parser.add_argument( + "--source", type=argparse.FileType("w"), help="output source file" + ) + + args = parser.parse_args() + + source_file = args.source + header_file = args.header + + if header_file: + header_contents = generate_header() + header_name = Path(header_file.name).name + header_def = header_name.upper().replace("-", "_").replace(".", "_") + header_file.write( + header_file_template.format( + header_def=header_def, + header_contents=header_contents, + ) + ) + + if source_file: + source_contents = generate_source() + source_file.write( + source_file_template.format( + assignment_count=len(rover.APP_ASSIGNMENTS), + source_contents=source_contents, + ) + ) + + +def generate_source(): + output = io.StringIO() + output.write(" size_t index = 0;\n") + + for a in rover.APP_ASSIGNMENTS: + output.write(f"\n assignments[index].city = {city_prefix}_{a.city.name};\n") + output.write( + f" assignments[index].envelope = {envelope_prefix}_{a.envelope.name};\n" + ) + output.write( + f" assignments[index].folder = {folder_prefix}_{a.folder.prefix()}_{a.folder.name};\n" + ) + output.write(" index++;\n") + + contents = output.getvalue() + output.close() + return contents + + +def generate_header(): + output = io.StringIO() + output.write(f"#define {rover_prefix}_BASE_NUMBER 0x{rover.BASE_NUMBER:X}\n") + + output.write(f"\n#define {city_prefix}_COUNT {len(rover.City)}\n") + for city in rover.City: + output.write(f"#define {city_prefix}_{city.name} {city}\n") + + output.write(f"\n#define {envelope_prefix}_COUNT {len(rover.Envelope)}\n") + for envelope in rover.Envelope: + output.write(f"#define {envelope_prefix}_{envelope.name} 0x{envelope:X}\n") + + output.write( + f"\n#define {folder_prefix}_BOOTLOADER_COUNT {len(rover.BootloaderFolder)}\n" + ) + for folder in rover.BootloaderFolder: + output.write(f"#define {folder_prefix}_BOOTLOADER_{folder.name} {folder}\n") + + output.write(f"\n#define {folder_prefix}_SERVO_COUNT {len(rover.ServoFolder)}\n") + for folder in rover.ServoFolder: + output.write(f"#define {folder_prefix}_SERVO_{folder.name} {folder}\n") + + output.write( + f"\n#define {folder_prefix}_SBUS_RECEIVER_COUNT {len(rover.SbusReceiverFolder)}\n" + ) + for folder in rover.SbusReceiverFolder: + output.write(f"#define {folder_prefix}_SBUS_RECEIVER_{folder.name} {folder}\n") + + output.write( + f"\n#define {folder_prefix}_BATTERY_MONITOR_COUNT {len(rover.BatteryMonitorFolder)}\n" + ) + for folder in rover.BatteryMonitorFolder: + output.write( + f"#define {folder_prefix}_BATTERY_MONITOR_{folder.name} {folder}\n" + ) + + output.write(f"\n#define {folder_prefix}_BRAKE_COUNT {len(rover.BrakeFolder)}\n") + for folder in rover.BrakeFolder: + output.write(f"#define {folder_prefix}_BRAKE_{folder.name} {folder}\n") + + output.write( + f"\n#define {folder_prefix}_OBSTACLE_DETECTOR_COUNT {len(rover.ObstacleDetectorFolder)}\n" + ) + for folder in rover.ObstacleDetectorFolder: + output.write( + f"#define {folder_prefix}_OBSTACLE_DETECTOR_{folder.name} {folder}\n" + ) + + contents = output.getvalue() + output.close() + return contents + + +if __name__ == "__main__": + main() diff --git a/libs/rover/src/rover-helpers.c b/libs/rover/src/rover-helpers.c index 6bc8b3e7..b8032f34 100644 --- a/libs/rover/src/rover-helpers.c +++ b/libs/rover/src/rover-helpers.c @@ -2,33 +2,14 @@ #include -#include "rover.h" +#include "rover-assignments.h" +#include "rover-defs.h" // CK #include "king.h" #include "mayor.h" #include "postmaster.h" -#define ASSIGNMENT_COUNT 30 - -typedef struct { - uint16_t envelope; - uint8_t city; - uint8_t folder; -} rover_assignment_t; - -typedef struct { - rover_assignment_t *assignments; - uint8_t assignment_count; -} rover_kingdom_t; - -static rover_assignment_t assignments[ASSIGNMENT_COUNT]; - -static rover_kingdom_t kingdom = { - .assignments = assignments, - .assignment_count = ASSIGNMENT_COUNT, -}; - static ck_envelope_t kings_envelope = { .envelope_no = 0, .enable = true, @@ -37,8 +18,6 @@ static ck_envelope_t kings_envelope = { .has_extended_id = false, }; -static void init_assignments(void); - ck_err_t send_default_letter(void) { // Spoof a default letter reception ck_err_t err = ck_default_letter_received(); @@ -57,51 +36,17 @@ ck_err_t send_default_letter(void) { return CK_OK; } -ck_err_t set_rover_base_number(void) { - ck_page_t page; - ck_kp1_args_t kp1_args = { - .address = 0, - .base_no = ROVER_BASE_NUMBER, - .has_extended_id = false, - .mayor_response_no = 0, - }; - ck_err_t err = ck_create_kings_page_1(&kp1_args, &page); - if (err != CK_OK) { - printf("Error: failed to create king's page: %d.\r\n", err); - return err; - } - - ck_letter_t letter = { - .envelope = kings_envelope, - .page = page, - }; - - // Send base number to self - err = ck_process_kings_letter(&letter); - if (err != CK_OK) { - printf("Error: couldn't send base number letter to self: %d.\r\n", err); - return err; - } - - err = ck_send_letter(&letter); - if (err != CK_OK) { - printf("Error: couldn't send base number letter: %d.\r\n", err); - return err; - } - - return CK_OK; -} - ck_err_t assign_rover_envelopes(const ck_id_t *own_id) { - init_assignments(); + init_rover_kingdom(); + rover_kingdom_t *kingdom = get_rover_kingdom(); - for (uint8_t i = 0; i < kingdom.assignment_count; i++) { + for (size_t i = 0; i < kingdom->assignment_count; i++) { ck_page_t page; ck_kp2_args_t kp2_args = { - .address = kingdom.assignments[i].city, + .address = kingdom->assignments[i].city, .envelope.enable = true, - .envelope.envelope_no = kingdom.assignments[i].envelope, - .folder_no = kingdom.assignments[i].folder, + .envelope.envelope_no = kingdom->assignments[i].envelope, + .folder_no = kingdom->assignments[i].folder, .envelope_action = CK_ENVELOPE_ASSIGN, }; ck_err_t err = ck_create_kings_page_2(&kp2_args, &page); @@ -117,7 +62,7 @@ ck_err_t assign_rover_envelopes(const ck_id_t *own_id) { // Letters meant for us need to be processed internally, // since we can't receive the messages we send. - if (kingdom.assignments[i].city == own_id->city_address) { + if (kingdom->assignments[i].city == own_id->city_address) { err = ck_process_kings_letter(&letter); if (err != CK_OK) { printf("Error: failed to assign envelope to self: %d.\r\n", err); @@ -135,10 +80,45 @@ ck_err_t assign_rover_envelopes(const ck_id_t *own_id) { return CK_OK; } +ck_err_t set_rover_base_number(void) { + ck_page_t page; + ck_kp1_args_t kp1_args = { + .address = 0, + .base_no = ROVER_BASE_NUMBER, + .has_extended_id = false, + .mayor_response_no = 0, + }; + ck_err_t err = ck_create_kings_page_1(&kp1_args, &page); + if (err != CK_OK) { + printf("Error: failed to create king's page: %d.\r\n", err); + return err; + } + + ck_letter_t letter = { + .envelope = kings_envelope, + .page = page, + }; + + // Send base number to self + err = ck_process_kings_letter(&letter); + if (err != CK_OK) { + printf("Error: couldn't send base number letter to self: %d.\r\n", err); + return err; + } + + err = ck_send_letter(&letter); + if (err != CK_OK) { + printf("Error: couldn't send base number letter: %d.\r\n", err); + return err; + } + + return CK_OK; +} + // Only setting so far is the reversal of the servo. ck_err_t configure_rover_settings(void) { ck_envelope_t reverse_envelope = { - .envelope_no = ROVER_SERVO_REVERSE_ENVELOPE, + .envelope_no = ROVER_ENVELOPE_SERVO_REVERSE_DIRECTION, .enable = true, .has_extended_id = false, .is_compressed = false, @@ -198,163 +178,3 @@ ck_err_t start_communication(void) { bool someone_else_is_king(void) { return ck_get_comm_mode() != CK_COMM_MODE_SILENT; } - -// NOLINTBEGIN(*-magic-numbers) -static void init_assignments(void) { - int index = 0; - - assignments[index].city = ROVER_SERVO_ID; - assignments[index].envelope = ROVER_STEERING_ENVELOPE; - assignments[index].folder = 9; - index++; - - assignments[index].city = ROVER_MOTOR_ID; - assignments[index].envelope = ROVER_THROTTLE_ENVELOPE; - assignments[index].folder = 9; - index++; - - assignments[index].city = ROVER_SBUS_RECEIVER_ID; - assignments[index].envelope = ROVER_STEERING_ENVELOPE; - assignments[index].folder = 2; - index++; - - assignments[index].city = ROVER_SBUS_RECEIVER_ID; - assignments[index].envelope = ROVER_THROTTLE_ENVELOPE; - assignments[index].folder = 3; - index++; - - assignments[index].city = ROVER_BATTERY_MONITOR_ID; - assignments[index].envelope = ROVER_BATTERY_CELL_VOLTAGES_ENVELOPE; - assignments[index].folder = 2; - index++; - - assignments[index].city = ROVER_BATTERY_MONITOR_ID; - assignments[index].envelope = ROVER_BATTERY_REGULATED_OUTPUT_ENVELOPE; - assignments[index].folder = 3; - index++; - - assignments[index].city = ROVER_BATTERY_MONITOR_ID; - assignments[index].envelope = ROVER_BATTERY_BATTERY_OUTPUT_ENVELOPE; - assignments[index].folder = 4; - index++; - - assignments[index].city = ROVER_SERVO_ID; - assignments[index].envelope = ROVER_SERVO_VOLTAGE_ENVELOPE; - assignments[index].folder = 5; - index++; - - // TODO: Remove this when the power board is standard on the Rover. - assignments[index].city = ROVER_SERVO_ID; - assignments[index].envelope = ROVER_BATTERY_VOLTAGE_ENVELOPE; - assignments[index].folder = 4; - index++; - - assignments[index].city = ROVER_SERVO_ID; - assignments[index].envelope = ROVER_SERVO_CURRENT_ENVELOPE; - assignments[index].folder = 3; - index++; - - assignments[index].city = ROVER_BATTERY_MONITOR_ID; - assignments[index].envelope = ROVER_BATTERY_JUMPER_CONFIG_ENVELOPE; - assignments[index].folder = 5; - index++; - - assignments[index].city = ROVER_BATTERY_MONITOR_ID; - assignments[index].envelope = ROVER_BATTERY_REG_OUT_VOLTAGE_ENVELOPE; - assignments[index].folder = 6; - index++; - - assignments[index].city = ROVER_BATTERY_MONITOR_ID; - assignments[index].envelope = ROVER_BATTERY_OUTPUT_ON_OFF_ENVELOPE; - assignments[index].folder = 7; - index++; - - assignments[index].city = ROVER_BATTERY_MONITOR_ID; - assignments[index].envelope = ROVER_BATTERY_REPORT_FREQUENCY_ENVELOPE; - assignments[index].folder = 8; - index++; - - assignments[index].city = ROVER_BATTERY_MONITOR_ID; - assignments[index].envelope = ROVER_BATTERY_LOW_VOLTAGE_CUTOFF_ENVELOPE; - assignments[index].folder = 9; - index++; - - assignments[index].city = ROVER_BATTERY_MONITOR_ID; - assignments[index].envelope = - ROVER_BATTERY_VBAT_OUT_OVERCURRENT_THRESHOLD_ENVELOPE; - assignments[index].folder = 10; - index++; - - assignments[index].city = ROVER_BATTERY_MONITOR_ID; - assignments[index].envelope = - ROVER_BATTERY_REG_OUT_OVERCURRENT_THRESHOLD_ENVELOPE; - assignments[index].folder = 11; - index++; - - assignments[index].city = ROVER_SERVO_ID; - assignments[index].envelope = ROVER_SERVO_SET_VOLTAGE_ENVELOPE; - assignments[index].folder = 7; - index++; - - assignments[index].city = ROVER_SERVO_ID; - assignments[index].envelope = ROVER_SERVO_PWM_CONFIG_ENVELOPE; - assignments[index].folder = 8; - index++; - - assignments[index].city = ROVER_SERVO_ID; - assignments[index].envelope = ROVER_SERVO_REPORT_FREQUENCY_ENVELOPE; - assignments[index].folder = 11; - index++; - - assignments[index].city = ROVER_MOTOR_ID; - assignments[index].envelope = ROVER_MOTOR_PWM_CONFIG_ENVELOPE; - assignments[index].folder = 8; - index++; - - assignments[index].city = ROVER_SERVO_ID; - assignments[index].envelope = ROVER_SERVO_REVERSE_ENVELOPE; - assignments[index].folder = 12; - index++; - - assignments[index].city = ROVER_MOTOR_ID; - assignments[index].envelope = ROVER_MOTOR_REVERSE_ENVELOPE; - assignments[index].folder = 12; - index++; - - assignments[index].city = ROVER_SERVO_ID; - assignments[index].envelope = ROVER_SERVO_FAILSAFE_ENVELOPE; - assignments[index].folder = 13; - index++; - - assignments[index].city = ROVER_MOTOR_ID; - assignments[index].envelope = ROVER_MOTOR_FAILSAFE_ENVELOPE; - assignments[index].folder = 13; - index++; - - assignments[index].city = ROVER_SBUS_RECEIVER_ID; - assignments[index].envelope = ROVER_SERVO_SET_SUBTRIM_ENVELOPE; - assignments[index].folder = 4; - index++; - - assignments[index].city = ROVER_SBUS_RECEIVER_ID; - assignments[index].envelope = ROVER_MOTOR_SET_SUBTRIM_ENVELOPE; - assignments[index].folder = 5; - index++; - - assignments[index].city = ROVER_SERVO_ID; - assignments[index].envelope = ROVER_SERVO_SET_SUBTRIM_ENVELOPE; - assignments[index].folder = 10; - index++; - - assignments[index].city = ROVER_MOTOR_ID; - assignments[index].envelope = ROVER_MOTOR_SET_SUBTRIM_ENVELOPE; - assignments[index].folder = 10; - index++; - - // Disable for now as it's a work in progress. - // assignments[index].city = ROVER_SERVO_ID; - // assignments[index].envelope = ROVER_SERVO_POSITION_ENVELOPE; - // assignments[index].folder = 2; - // index++; -} -// NOLINTEND(*-magic-numbers) diff --git a/libs/rover/tests/meson.build b/libs/rover/tests/meson.build index 203cab98..6b36b2c8 100644 --- a/libs/rover/tests/meson.build +++ b/libs/rover/tests/meson.build @@ -1,26 +1,12 @@ -# Build native lib for unit testing -rover_native_lib = library( - 'rover-native', - rover_helpers_src, - include_directories: rover_inc, - dependencies: can_kingdom_native_dep, - native: true, -) - -rover_native_dep = declare_dependency( - dependencies: can_kingdom_native_dep, - link_with: rover_native_lib, - include_directories: rover_inc, -) - rover_test_suite = 'rover' -rover_test_src = files('rover-test.c') +rover_test_src = [files('rover-test.c'), rover_helpers_src] rover_test = executable( 'rover-test', rover_test_src, - dependencies: [rover_native_dep, test_utils_dep], + include_directories: rover_inc, + dependencies: [can_kingdom_native_dep, test_utils_dep], native: true, ) diff --git a/libs/rover/tests/rover-test.c b/libs/rover/tests/rover-test.c index 811b0b7e..75c4981f 100644 --- a/libs/rover/tests/rover-test.c +++ b/libs/rover/tests/rover-test.c @@ -1,6 +1,5 @@ -#include "rover.h" - #include "mayor.h" +#include "rover-defs.h" #include "rover-helpers.h" #include "test.h" diff --git a/libs/stm32-common/include/FreeRTOSConfig.h b/libs/stm32-common/include/FreeRTOSConfig.h index a477d9d7..1c1ab476 100644 --- a/libs/stm32-common/include/FreeRTOSConfig.h +++ b/libs/stm32-common/include/FreeRTOSConfig.h @@ -141,8 +141,7 @@ header file. */ #define configASSERT(x) \ if ((x) == 0) { \ taskDISABLE_INTERRUPTS(); \ - for (;;) \ - ; \ + for (;;); \ } /* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS diff --git a/libs/stm32-common/src/device-id.c b/libs/stm32-common/src/device-id.c index 5da55881..6bc69f42 100644 --- a/libs/stm32-common/src/device-id.c +++ b/libs/stm32-common/src/device-id.c @@ -7,7 +7,7 @@ #include "error.h" #include "json.h" #include "jsondb.h" -#include "rover.h" +#include "rover-defs.h" #include "stm32f3xx_ll_utils.h" // FreeRTOS diff --git a/meson.build b/meson.build index 823c779e..cce10703 100644 --- a/meson.build +++ b/meson.build @@ -21,8 +21,7 @@ math_dep = cc_native.find_library('m') objcopy = find_program('objcopy') clang_tidy = find_program('clang-tidy') cat = find_program('cat') -# Try to find python3.exe first, in case we're running in WSL. -python = find_program('python3.exe', 'python3', required: false) +python = find_program('python3', required: true) stm32f3_hal_dep = dependency('stm32f3-hal') freertos_dep = dependency('freertos') @@ -57,29 +56,33 @@ run_target( ], ) +check_src_script = files('scripts' / 'check-src.sh') + run_target( 'check', command: [ 'bash', - meson.project_source_root() / 'scripts' / 'check-src.sh', + check_src_script, meson.project_source_root(), meson.project_build_root(), ], ) -generate_system_conf_script = files('rover_py' / 'generate-system-conf.py') +gen_system_conf_script = files('scripts' / 'gen-system-conf.py') config_dir = custom_target( 'config', output: 'config', command: [ python, - generate_system_conf_script, + gen_system_conf_script, '--force', '--battery-monitor-bin', battery_monitor_bin, '--brake-bin', brake_bin, + '--obstacle-detector-bin', + obstacle_detector_bin, '--sbus-receiver-bin', sbus_receiver_bin, '--servo-bin', @@ -90,8 +93,8 @@ config_dir = custom_target( build_always_stale: true, ) +release_script = files('scripts' / 'create-release.sh') release_files += fs.copyfile('rover.dbc') -release_script = fs.copyfile('scripts' / 'create-release.sh') flasher_path = meson.project_source_root() / 'rover_py' release_dir = 'release' @@ -115,10 +118,13 @@ release_tgt = custom_target( depends: config_dir, ) +# Try to find python3.exe first, in case we're running in WSL. +python_flash = find_program('python3.exe', 'python3', required: false) + run_target( 'flash', command: [ - python, + python_flash, release_dir / 'fw_update.py', 'system', '--config', diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 00000000..62824512 --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,10 @@ +{ + "venv": ".venv", + "exclude": [ + "subprojects", + "**/__pycache__", + "**/node_modules", + "**/.*" + ], + "reportUnnecessaryTypeIgnoreComment": true +} diff --git a/requirements.txt b/requirements.txt index 718e4482..e0bfd99b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,12 +19,12 @@ Jinja2==3.1.3 keyboard==0.13.5 kiwisolver==1.4.4 MarkupSafe==2.1.3 -matplotlib==3.7.2 +matplotlib==3.9.2 meson==1.5.2 mypy-extensions==1.0.0 ninja==1.11.1.1 nodeenv==1.9.1 -numpy==1.25.2 +numpy==1.26.4 packaging==23.1 pathspec==0.11.2 pillow==10.2.0 @@ -39,8 +39,9 @@ pyright==1.1.382.post1 python-dateutil==2.8.2 requests==2.31.0 scipy==1.12.0 +setuptools==75.1.0 shellcheck-py==0.10.0.1 -shfmt-py==3.7.0.1 +shfmt_py==3.7.0.1 six==1.16.0 snowballstemmer==2.2.0 Sphinx==6.2.1 diff --git a/rover.dbc b/rover.dbc index 0c11da5a..08edb210 100644 --- a/rover.dbc +++ b/rover.dbc @@ -105,6 +105,18 @@ BO_ 531 WHEEL_REAR_RIGHT_SPEED: 8 Vector__XXX SG_ RPM : 0|32@1- (1,0) [0|0] "" Vector__XXX SG_ SPEED : 32|32@1- (1,0) [0|0] "km/h" Vector__XXX +BO_ 532 OBSTACLE_DETECTOR_FRONT_DISTANCE: 8 Vector__XXX + SG_ SENSOR_LEFT : 0|16@1+ (1,0) [0|0] "mm" Vector__XXX + SG_ SENSOR_MID_LEFT : 16|16@1+ (1,0) [0|0] "mm" Vector__XXX + SG_ SENSOR_MID_RIGHT : 32|16@1+ (1,0) [0|0] "mm" Vector__XXX + SG_ SENSOR_RIGHT : 48|16@1+ (1,0) [0|0] "mm" Vector__XXX + +BO_ 533 OBSTACLE_DETECTOR_REAR_DISTANCE: 8 Vector__XXX + SG_ SENSOR_LEFT : 0|16@1+ (1,0) [0|0] "mm" Vector__XXX + SG_ SENSOR_MID_LEFT : 16|16@1+ (1,0) [0|0] "mm" Vector__XXX + SG_ SENSOR_MID_RIGHT : 32|16@1+ (1,0) [0|0] "mm" Vector__XXX + SG_ SENSOR_RIGHT : 48|16@1+ (1,0) [0|0] "mm" Vector__XXX + BO_ 768 BATTERY_JUMPER_CONFIG: 1 Vector__XXX SG_ JUMPER_CONFIG : 0|8@1+ (1,0) [0|0] "" Vector__XXX @@ -190,6 +202,12 @@ BO_ 792 WHEEL_REAR_RIGHT_REPORT_FREQUENCY: 2 Vector__XXX BO_ 793 BATTERY_CELL_CALIBRATION: 2 Vector__XXX SG_ CELL_CALIBRATION_VOLTAGE : 0|16@1+ (1,0) [0|0] "mV" Vector__XXX +BO_ 794 OBSTACLE_DETECTOR_FRONT_REPORT_FREQUENCY: 2 Vector__XXX + SG_ REPORT_PERIOD : 0|16@1+ (1,0) [0|0] "ms" Vector__XXX + +BO_ 795 OBSTACLE_DETECTOR_REAR_REPORT_FREQUENCY: 2 Vector__XXX + SG_ REPORT_PERIOD : 0|16@1+ (1,0) [0|0] "ms" Vector__XXX + BO_ 1792 BOOTLOADER_COMMAND_ACK: 5 Vector__XXX SG_ ACK_TYPE : 0|8@1+ (1,0) [0|0] "" Vector__XXX SG_ COMMAND_ID : 8|32@1+ (1,0) [0|0] "" Vector__XXX diff --git a/rover_py/flasher/flasher.py b/rover_py/flasher/flasher.py index 29ca84c0..c43babd5 100644 --- a/rover_py/flasher/flasher.py +++ b/rover_py/flasher/flasher.py @@ -306,11 +306,10 @@ def __restart_all(self): ) def __assign_bootloader_envelopes(self, node_id): - for assignment in rover.BOOTLOADER_ASSIGNMENTS: - envelope = assignment[0] - folder = assignment[1] + bootloader_assignments = rover.generate_bootloader_assignments(node_id) + for assignment in bootloader_assignments: self.ch.writeWait( - rover.assign_envelope(node_id, envelope, folder), + rover.assign_envelope(node_id, assignment.envelope, assignment.folder), self.default_timeout_ms, ) diff --git a/rover_py/live_demo.py b/rover_py/live_demo.py index 4cbe7244..6be5f1ed 100644 --- a/rover_py/live_demo.py +++ b/rover_py/live_demo.py @@ -127,11 +127,23 @@ def send_can_messages(): ch.setBusOutputControl(canlib.Driver.NORMAL) ch.busOn() - rover.start(ch) + ch.write(servo.set_failsafe(servo.FAILSAFE_OFF, city=rover.City.SERVO)) + ch.write(servo.set_failsafe(servo.FAILSAFE_OFF, city=rover.City.MOTOR)) + ch.write( + rover.set_action_mode( + city=rover.City.SBUS_RECEIVER, mode=rover.ActionMode.FREEZE + ) + ) while True: if restart_rover: - rover.start(ch) + ch.write(servo.set_failsafe(servo.FAILSAFE_OFF, city=rover.City.SERVO)) + ch.write(servo.set_failsafe(servo.FAILSAFE_OFF, city=rover.City.MOTOR)) + ch.write( + rover.set_action_mode( + city=rover.City.SBUS_RECEIVER, mode=rover.ActionMode.FREEZE + ) + ) restart_rover = False if keyboard.is_pressed("up"): @@ -150,8 +162,8 @@ def send_can_messages(): if steering < steering_max: steering += step - ch.writeWait(servo.set_throttle_pulse_frame(throttle), -1) - ch.writeWait(servo.set_steering_pulse_frame(steering), -1) + ch.write(servo.set_throttle_pulse_frame(throttle)) + ch.write(servo.set_steering_pulse_frame(steering)) time.sleep(0.01) @@ -191,7 +203,7 @@ def parse_frame(db, frame): def animate(_): - ax1, ax2, ax3 = fig.get_axes() # pyright: ignore [reportAttributeAccessIssue] + ax1, ax2, ax3 = fig.get_axes() # Clear current data ax1.cla() @@ -291,7 +303,13 @@ def get_screen_dpi(): subplots = fig.subplots(1, 3) fig.subplots_adjust(left=0.05, right=0.95, wspace=0.2) -ani = FuncAnimation(fig, animate, interval=100, blit=False, save_count=3000) +ani = FuncAnimation( + fig, + animate, # pyright: ignore [reportArgumentType] + interval=100, + blit=False, + save_count=3000, +) canvas = FigureCanvasTkAgg(fig, master=root) canvas.get_tk_widget().pack( diff --git a/rover_py/pyproject.toml b/rover_py/pyproject.toml new file mode 100644 index 00000000..a7727810 --- /dev/null +++ b/rover_py/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "rover_py" +version = "0.0.1" +dependencies = [ + "canlib>=1.27.606", +] +authors = [ + { name="CanEduDev", email="support@canedudev.com" }, +] +description = "Helpers for interacting with the CanEduDev Rover" +requires-python = ">=3.11" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] + +[project.urls] +Homepage = "https://github.com/canedudev/rover" +Issues = "https://github.com/canedudev/rover/issues" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["rover"] diff --git a/rover_py/rover/rover.py b/rover_py/rover/rover.py index eb53d959..94447c20 100644 --- a/rover_py/rover/rover.py +++ b/rover_py/rover/rover.py @@ -7,7 +7,7 @@ # City addresses -@enum.verify(enum.UNIQUE) +@enum.unique class City(enum.IntEnum): ALL_CITIES = 0 BATTERY_MONITOR = 1 @@ -18,13 +18,15 @@ class City(enum.IntEnum): WHEEL_FRONT_RIGHT = 6 WHEEL_REAR_LEFT = 7 WHEEL_REAR_RIGHT = 8 + OBSTACLE_DETECTOR_FRONT = 9 + OBSTACLE_DETECTOR_REAR = 10 # For AD systems AD_BATTERY_MONITOR = 100 # CAN Message IDs -@enum.verify(enum.UNIQUE) +@enum.unique class Envelope(enum.IntEnum): # Control messages STEERING = 0x100 @@ -41,6 +43,8 @@ class Envelope(enum.IntEnum): WHEEL_FRONT_RIGHT_SPEED = 0x211 WHEEL_REAR_LEFT_SPEED = 0x212 WHEEL_REAR_RIGHT_SPEED = 0x213 + OBSTACLE_DETECTOR_FRONT_DISTANCE = 0x214 + OBSTACLE_DETECTOR_REAR_DISTANCE = 0x215 # Settings messages BATTERY_JUMPER_CONFIG = 0x300 @@ -75,6 +79,9 @@ class Envelope(enum.IntEnum): BATTERY_CELL_CALIBRATION = 0x319 + OBSTACLE_DETECTOR_FRONT_REPORT_FREQUENCY = 0x31A + OBSTACLE_DETECTOR_REAR_REPORT_FREQUENCY = 0x31B + # Bootloader specific envelopes BOOTLOADER_COMMAND_ACK = 0x700 BOOTLOADER_ENTER = 0x701 @@ -98,7 +105,7 @@ class Envelope(enum.IntEnum): AD_BATTERY_CELL_CALIBRATION = 0x611 -@enum.verify(enum.UNIQUE) +@enum.unique class BootloaderFolder(enum.IntEnum): COMMAND_ACK = 2 FLASH_PROGRAM_TX = 3 @@ -110,8 +117,11 @@ class BootloaderFolder(enum.IntEnum): FLASH_PROGRAM_RX = 9 FLASH_CONFIG_RX = 10 + def prefix(self): + return "BOOTLOADER" + -@enum.verify(enum.UNIQUE) +@enum.unique class ServoFolder(enum.IntEnum): POSITION = 2 CURRENT = 3 @@ -126,16 +136,22 @@ class ServoFolder(enum.IntEnum): REVERSE_DIRECTION = 12 FAILSAFE = 13 + def prefix(self): + return "SERVO" + -@enum.verify(enum.UNIQUE) +@enum.unique class SbusReceiverFolder(enum.IntEnum): STEERING = 2 THROTTLE = 3 STEERING_SUBTRIM = 4 THROTTLE_SUBTRIM = 5 + def prefix(self): + return "SBUS_RECEIVER" -@enum.verify(enum.UNIQUE) + +@enum.unique class BatteryMonitorFolder(enum.IntEnum): CELL_VOLTAGES = 2 REGULATED_OUTPUT = 3 @@ -149,296 +165,388 @@ class BatteryMonitorFolder(enum.IntEnum): REG_OUT_OVERCURRENT_THRESHOLD = 11 CELL_CALIBRATION = 12 + def prefix(self): + return "BATTERY_MONITOR" + -@enum.verify(enum.UNIQUE) +@enum.unique class BrakeFolder(enum.IntEnum): WHEEL_SPEED = 2 WHEEL_PARAMETERS = 3 REPORT_FREQUENCY = 4 + def prefix(self): + return "BRAKE" -# List of assignments in the form (city, envelope, folder) -APP_ASSIGNMENTS = [ - # Control messages - ( + +@enum.unique +class ObstacleDetectorFolder(enum.IntEnum): + OBJECT_DISTANCE = 2 + REPORT_FREQUENCY = 3 + + def prefix(self): + return "OBSTACLE_DETECTOR" + + +class Assignment: + def __init__(self, city, envelope, folder): + self.city = city + self.envelope = envelope + self.folder = folder + + +SERVO_ASSIGNMENTS = [ + Assignment( City.SERVO, Envelope.STEERING, ServoFolder.CONTROL, ), - ( + Assignment( + City.SERVO, + Envelope.SERVO_VOLTAGE, + ServoFolder.VOLTAGE, + ), + Assignment( + City.SERVO, + Envelope.SERVO_CURRENT, + ServoFolder.CURRENT, + ), + Assignment( + City.SERVO, + Envelope.SERVO_SET_VOLTAGE, + ServoFolder.SET_VOLTAGE, + ), + Assignment( + City.SERVO, + Envelope.SERVO_PWM_CONFIG, + ServoFolder.PWM_CONFIG, + ), + Assignment( + City.SERVO, + Envelope.SERVO_REPORT_FREQUENCY, + ServoFolder.REPORT_FREQUENCY, + ), + Assignment( + City.SERVO, + Envelope.SERVO_REVERSE_DIRECTION, + ServoFolder.REVERSE_DIRECTION, + ), + Assignment( + City.SERVO, + Envelope.SERVO_FAILSAFE, + ServoFolder.FAILSAFE, + ), + Assignment( + City.SERVO, + Envelope.STEERING_SUBTRIM, + ServoFolder.SET_SUBTRIM, + ), +] + +MOTOR_ASSIGMENTS = [ + Assignment( City.MOTOR, Envelope.THROTTLE, ServoFolder.CONTROL, ), - ( + Assignment( + City.MOTOR, + Envelope.MOTOR_PWM_CONFIG, + ServoFolder.PWM_CONFIG, + ), + Assignment( + City.MOTOR, + Envelope.MOTOR_REVERSE_DIRECTION, + ServoFolder.REVERSE_DIRECTION, + ), + Assignment( + City.MOTOR, + Envelope.MOTOR_FAILSAFE, + ServoFolder.FAILSAFE, + ), + Assignment( + City.MOTOR, + Envelope.THROTTLE_SUBTRIM, + ServoFolder.SET_SUBTRIM, + ), +] + +SBUS_RECEIVER_ASSIGNMENTS = [ + Assignment( City.SBUS_RECEIVER, Envelope.STEERING, SbusReceiverFolder.STEERING, ), - ( + Assignment( City.SBUS_RECEIVER, Envelope.THROTTLE, SbusReceiverFolder.THROTTLE, ), - # Report messages - ( + Assignment( + City.SBUS_RECEIVER, + Envelope.STEERING_SUBTRIM, + SbusReceiverFolder.STEERING_SUBTRIM, + ), + Assignment( + City.SBUS_RECEIVER, + Envelope.THROTTLE_SUBTRIM, + SbusReceiverFolder.THROTTLE_SUBTRIM, + ), +] + +BATTERY_MONITOR_ASSIGNMENTS = [ + Assignment( City.BATTERY_MONITOR, Envelope.BATTERY_CELL_VOLTAGES, BatteryMonitorFolder.CELL_VOLTAGES, ), - ( + Assignment( City.BATTERY_MONITOR, Envelope.BATTERY_REGULATED_OUTPUT, BatteryMonitorFolder.REGULATED_OUTPUT, ), - ( + Assignment( City.BATTERY_MONITOR, Envelope.BATTERY_OUTPUT, BatteryMonitorFolder.BATTERY_OUTPUT, ), - ( - City.SERVO, - Envelope.SERVO_VOLTAGE, - ServoFolder.VOLTAGE, - ), - ( - City.SERVO, - Envelope.SERVO_CURRENT, - ServoFolder.CURRENT, - ), - ( - City.WHEEL_FRONT_LEFT, - Envelope.WHEEL_FRONT_LEFT_SPEED, - BrakeFolder.WHEEL_SPEED, - ), - ( - City.WHEEL_FRONT_RIGHT, - Envelope.WHEEL_FRONT_RIGHT_SPEED, - BrakeFolder.WHEEL_SPEED, - ), - ( - City.WHEEL_REAR_LEFT, - Envelope.WHEEL_REAR_LEFT_SPEED, - BrakeFolder.WHEEL_SPEED, - ), - ( - City.WHEEL_REAR_RIGHT, - Envelope.WHEEL_REAR_RIGHT_SPEED, - BrakeFolder.WHEEL_SPEED, - ), - # Settings messages - ( + Assignment( City.BATTERY_MONITOR, Envelope.BATTERY_JUMPER_CONFIG, BatteryMonitorFolder.JUMPER_CONFIG, ), - ( + Assignment( City.BATTERY_MONITOR, Envelope.BATTERY_REG_OUT_VOLTAGE, BatteryMonitorFolder.REG_OUT_VOLTAGE, ), - ( + Assignment( City.BATTERY_MONITOR, Envelope.BATTERY_OUTPUT_ON_OFF, BatteryMonitorFolder.OUTPUT_ON_OFF, ), - ( + Assignment( City.BATTERY_MONITOR, Envelope.BATTERY_REPORT_FREQUENCY, BatteryMonitorFolder.REPORT_FREQUENCY, ), - ( + Assignment( City.BATTERY_MONITOR, Envelope.BATTERY_LOW_VOLTAGE_CUTOFF, BatteryMonitorFolder.LOW_VOLTAGE_CUTOFF, ), - ( + Assignment( City.BATTERY_MONITOR, Envelope.BATTERY_VBAT_OUT_OVERCURRENT_THRESHOLD, BatteryMonitorFolder.VBAT_OUT_OVERCURRENT_THRESHOLD, ), - ( + Assignment( City.BATTERY_MONITOR, Envelope.BATTERY_REG_OUT_OVERCURRENT_THRESHOLD, BatteryMonitorFolder.REG_OUT_OVERCURRENT_THRESHOLD, ), - ( - City.SERVO, - Envelope.SERVO_SET_VOLTAGE, - ServoFolder.SET_VOLTAGE, - ), - ( - City.SERVO, - Envelope.SERVO_PWM_CONFIG, - ServoFolder.PWM_CONFIG, - ), - ( - City.SERVO, - Envelope.SERVO_REPORT_FREQUENCY, - ServoFolder.REPORT_FREQUENCY, - ), - ( - City.MOTOR, - Envelope.MOTOR_PWM_CONFIG, - ServoFolder.PWM_CONFIG, - ), - ( - City.SERVO, - Envelope.SERVO_REVERSE_DIRECTION, - ServoFolder.REVERSE_DIRECTION, - ), - ( - City.MOTOR, - Envelope.MOTOR_REVERSE_DIRECTION, - ServoFolder.REVERSE_DIRECTION, - ), - ( - City.SERVO, - Envelope.SERVO_FAILSAFE, - ServoFolder.FAILSAFE, - ), - ( - City.MOTOR, - Envelope.MOTOR_FAILSAFE, - ServoFolder.FAILSAFE, - ), - ( - City.SERVO, - Envelope.STEERING_SUBTRIM, - ServoFolder.SET_SUBTRIM, - ), - ( - City.MOTOR, - Envelope.THROTTLE_SUBTRIM, - ServoFolder.SET_SUBTRIM, - ), - ( - City.SBUS_RECEIVER, - Envelope.STEERING_SUBTRIM, - SbusReceiverFolder.STEERING_SUBTRIM, + Assignment( + City.BATTERY_MONITOR, + Envelope.BATTERY_CELL_CALIBRATION, + BatteryMonitorFolder.CELL_CALIBRATION, ), - ( - City.SBUS_RECEIVER, - Envelope.THROTTLE_SUBTRIM, - SbusReceiverFolder.THROTTLE_SUBTRIM, +] + +WHEEL_FRONT_LEFT_ASSIGNMENTS = [ + Assignment( + City.WHEEL_FRONT_LEFT, + Envelope.WHEEL_FRONT_LEFT_SPEED, + BrakeFolder.WHEEL_SPEED, ), - ( + Assignment( City.WHEEL_FRONT_LEFT, Envelope.WHEEL_FRONT_LEFT_WHEEL_PARAMETERS, BrakeFolder.WHEEL_PARAMETERS, ), - ( + Assignment( City.WHEEL_FRONT_LEFT, Envelope.WHEEL_FRONT_LEFT_REPORT_FREQUENCY, BrakeFolder.REPORT_FREQUENCY, ), - ( +] + +WHEEL_FRONT_RIGHT_ASSIGNMENTS = [ + Assignment( + City.WHEEL_FRONT_RIGHT, + Envelope.WHEEL_FRONT_RIGHT_SPEED, + BrakeFolder.WHEEL_SPEED, + ), + Assignment( City.WHEEL_FRONT_RIGHT, Envelope.WHEEL_FRONT_RIGHT_WHEEL_PARAMETERS, BrakeFolder.WHEEL_PARAMETERS, ), - ( + Assignment( City.WHEEL_FRONT_RIGHT, Envelope.WHEEL_FRONT_RIGHT_REPORT_FREQUENCY, BrakeFolder.REPORT_FREQUENCY, ), - ( +] + +WHEEL_REAR_LEFT_ASSIGNMENTS = [ + Assignment( + City.WHEEL_REAR_LEFT, + Envelope.WHEEL_REAR_LEFT_SPEED, + BrakeFolder.WHEEL_SPEED, + ), + Assignment( City.WHEEL_REAR_LEFT, Envelope.WHEEL_REAR_LEFT_WHEEL_PARAMETERS, BrakeFolder.WHEEL_PARAMETERS, ), - ( + Assignment( City.WHEEL_REAR_LEFT, Envelope.WHEEL_REAR_LEFT_REPORT_FREQUENCY, BrakeFolder.REPORT_FREQUENCY, ), - ( +] + +WHEEL_REAR_RIGHT_ASSIGNMENTS = [ + Assignment( + City.WHEEL_REAR_RIGHT, + Envelope.WHEEL_REAR_RIGHT_SPEED, + BrakeFolder.WHEEL_SPEED, + ), + Assignment( City.WHEEL_REAR_RIGHT, Envelope.WHEEL_REAR_RIGHT_WHEEL_PARAMETERS, BrakeFolder.WHEEL_PARAMETERS, ), - ( + Assignment( City.WHEEL_REAR_RIGHT, Envelope.WHEEL_REAR_RIGHT_REPORT_FREQUENCY, BrakeFolder.REPORT_FREQUENCY, ), - ( - City.BATTERY_MONITOR, - Envelope.BATTERY_CELL_CALIBRATION, - BatteryMonitorFolder.CELL_CALIBRATION, - ), - # AD system - ( +] + +AD_BATTERY_MONITOR_ASSIGNMENTS = [ + Assignment( City.AD_BATTERY_MONITOR, Envelope.AD_BATTERY_CELL_VOLTAGES, BatteryMonitorFolder.CELL_VOLTAGES, ), - ( + Assignment( City.AD_BATTERY_MONITOR, Envelope.AD_BATTERY_REGULATED_OUTPUT, BatteryMonitorFolder.REGULATED_OUTPUT, ), - ( + Assignment( City.AD_BATTERY_MONITOR, Envelope.AD_BATTERY_OUTPUT, BatteryMonitorFolder.BATTERY_OUTPUT, ), - ( + Assignment( City.AD_BATTERY_MONITOR, Envelope.AD_BATTERY_JUMPER_CONFIG, BatteryMonitorFolder.JUMPER_CONFIG, ), - ( + Assignment( City.AD_BATTERY_MONITOR, Envelope.AD_BATTERY_REG_OUT_VOLTAGE, BatteryMonitorFolder.REG_OUT_VOLTAGE, ), - ( + Assignment( City.AD_BATTERY_MONITOR, Envelope.AD_BATTERY_OUTPUT_ON_OFF, BatteryMonitorFolder.OUTPUT_ON_OFF, ), - ( + Assignment( City.AD_BATTERY_MONITOR, Envelope.AD_BATTERY_REPORT_FREQUENCY, BatteryMonitorFolder.REPORT_FREQUENCY, ), - ( + Assignment( City.AD_BATTERY_MONITOR, Envelope.AD_BATTERY_LOW_VOLTAGE_CUTOFF, BatteryMonitorFolder.LOW_VOLTAGE_CUTOFF, ), - ( + Assignment( City.AD_BATTERY_MONITOR, Envelope.AD_BATTERY_VBAT_OUT_OVERCURRENT_THRESHOLD, BatteryMonitorFolder.VBAT_OUT_OVERCURRENT_THRESHOLD, ), - ( + Assignment( City.AD_BATTERY_MONITOR, Envelope.AD_BATTERY_REG_OUT_OVERCURRENT_THRESHOLD, BatteryMonitorFolder.REG_OUT_OVERCURRENT_THRESHOLD, ), - ( + Assignment( City.AD_BATTERY_MONITOR, Envelope.AD_BATTERY_CELL_CALIBRATION, BatteryMonitorFolder.CELL_CALIBRATION, ), ] -BOOTLOADER_ASSIGNMENTS = [ - (Envelope.BOOTLOADER_COMMAND_ACK, BootloaderFolder.COMMAND_ACK), - (Envelope.BOOTLOADER_ENTER, BootloaderFolder.ENTER), - (Envelope.BOOTLOADER_EXIT, BootloaderFolder.EXIT), - (Envelope.BOOTLOADER_FLASH_ERASE, BootloaderFolder.FLASH_ERASE), - (Envelope.BOOTLOADER_FORMAT_FS, BootloaderFolder.FORMAT_FS), - (Envelope.BOOTLOADER_FLASH_PROGRAM, BootloaderFolder.FLASH_PROGRAM_TX), - (Envelope.BOOTLOADER_FLASH_PROGRAM, BootloaderFolder.FLASH_PROGRAM_RX), - (Envelope.BOOTLOADER_FLASH_CONFIG, BootloaderFolder.FLASH_CONFIG_TX), - (Envelope.BOOTLOADER_FLASH_CONFIG, BootloaderFolder.FLASH_CONFIG_RX), +OBSTACLE_DETECTOR_FRONT_ASSIGNMENTS = [ + Assignment( + City.OBSTACLE_DETECTOR_FRONT, + Envelope.OBSTACLE_DETECTOR_FRONT_DISTANCE, + ObstacleDetectorFolder.OBJECT_DISTANCE, + ), + Assignment( + City.OBSTACLE_DETECTOR_FRONT, + Envelope.OBSTACLE_DETECTOR_FRONT_REPORT_FREQUENCY, + ObstacleDetectorFolder.REPORT_FREQUENCY, + ), ] +OBSTACLE_DETECTOR_REAR_ASSIGNMENTS = [ + Assignment( + City.OBSTACLE_DETECTOR_REAR, + Envelope.OBSTACLE_DETECTOR_REAR_DISTANCE, + ObstacleDetectorFolder.OBJECT_DISTANCE, + ), + Assignment( + City.OBSTACLE_DETECTOR_REAR, + Envelope.OBSTACLE_DETECTOR_REAR_REPORT_FREQUENCY, + ObstacleDetectorFolder.REPORT_FREQUENCY, + ), +] -@enum.verify(enum.UNIQUE) +# List of assignments in the form (city, envelope, folder) +APP_ASSIGNMENTS = ( + SERVO_ASSIGNMENTS + + MOTOR_ASSIGMENTS + + SBUS_RECEIVER_ASSIGNMENTS + + BATTERY_MONITOR_ASSIGNMENTS + + WHEEL_FRONT_LEFT_ASSIGNMENTS + + WHEEL_FRONT_RIGHT_ASSIGNMENTS + + WHEEL_REAR_LEFT_ASSIGNMENTS + + WHEEL_REAR_RIGHT_ASSIGNMENTS + + OBSTACLE_DETECTOR_FRONT_ASSIGNMENTS + + OBSTACLE_DETECTOR_REAR_ASSIGNMENTS + + AD_BATTERY_MONITOR_ASSIGNMENTS +) + + +def generate_bootloader_assignments(id): + return [ + Assignment(id, Envelope.BOOTLOADER_COMMAND_ACK, BootloaderFolder.COMMAND_ACK), + Assignment(id, Envelope.BOOTLOADER_ENTER, BootloaderFolder.ENTER), + Assignment(id, Envelope.BOOTLOADER_EXIT, BootloaderFolder.EXIT), + Assignment(id, Envelope.BOOTLOADER_FLASH_ERASE, BootloaderFolder.FLASH_ERASE), + Assignment(id, Envelope.BOOTLOADER_FORMAT_FS, BootloaderFolder.FORMAT_FS), + Assignment( + id, Envelope.BOOTLOADER_FLASH_PROGRAM, BootloaderFolder.FLASH_PROGRAM_TX + ), + Assignment( + id, Envelope.BOOTLOADER_FLASH_PROGRAM, BootloaderFolder.FLASH_PROGRAM_RX + ), + Assignment( + id, Envelope.BOOTLOADER_FLASH_CONFIG, BootloaderFolder.FLASH_CONFIG_TX + ), + Assignment( + id, Envelope.BOOTLOADER_FLASH_CONFIG, BootloaderFolder.FLASH_CONFIG_RX + ), + ] + + +@enum.unique class CommMode(enum.IntEnum): KEEP_CURRENT = 0 SILENT = 1 @@ -446,7 +554,7 @@ class CommMode(enum.IntEnum): COMMUNICATE = 3 -@enum.verify(enum.UNIQUE) +@enum.unique class ActionMode(enum.IntEnum): KEEP_CURRENT = 0 RUN = 1 diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh index d6c4d5a4..41f2c38e 100755 --- a/scripts/bootstrap.sh +++ b/scripts/bootstrap.sh @@ -14,6 +14,7 @@ sudo apt-get -qq install --no-upgrade -y \ gcc-arm-none-eabi \ libc6-dev-armhf-cross \ python3-pip \ + python3-venv \ zip echo "Downloading yamlfmt..." @@ -36,7 +37,7 @@ if [[ -z ${VIRTUAL_ENV} ]]; then fi echo "Populating virtualenv..." -pip3 install -q -r requirements.txt +pip install -q -r requirements.txt -e rover_py echo "Setting up build dir..." meson setup --cross-file stm32f302ret6.ini --wipe build diff --git a/scripts/check-src.sh b/scripts/check-src.sh index dad8a95e..5d4b0b01 100644 --- a/scripts/check-src.sh +++ b/scripts/check-src.sh @@ -23,11 +23,12 @@ main() { black --quiet . meson fmt -i -r . ninja --quiet -C "${BUILD_DIR}" clang-format + pip freeze --exclude-editable >requirements.txt # Check echo "Linting..." shfmt -f . | grep -v ^subprojects | xargs shellcheck -o all - pyright rover_py + pyright . ninja --quiet -C "${BUILD_DIR}" clang-tidy check_git_index diff --git a/rover_py/generate-system-conf.py b/scripts/gen-system-conf.py similarity index 81% rename from rover_py/generate-system-conf.py rename to scripts/gen-system-conf.py index f376b84b..8d9d8cbe 100644 --- a/rover_py/generate-system-conf.py +++ b/scripts/gen-system-conf.py @@ -18,7 +18,13 @@ def main(): def parse_args(): parser = argparse.ArgumentParser() - apps = ["battery-monitor", "brake", "sbus-receiver", "servo"] + apps = [ + "battery-monitor", + "brake", + "obstacle-detector", + "sbus-receiver", + "servo", + ] for app in apps: parser.add_argument( @@ -51,6 +57,7 @@ def gen_system_base(args): battery_monitor = Path(args.battery_monitor_bin) brake = Path(args.brake_bin) + obstacle_detector = Path(args.obstacle_detector_bin) sbus_receiver = Path(args.sbus_receiver_bin) servo = Path(args.servo_bin) @@ -94,6 +101,14 @@ def gen_system_base(args): "id": rover.City.WHEEL_REAR_RIGHT, "binary": brake.name, }, + "obstacle-detector-front": { + "id": rover.City.OBSTACLE_DETECTOR_FRONT, + "binary": obstacle_detector.name, + }, + "obstacle-detector-rear": { + "id": rover.City.OBSTACLE_DETECTOR_REAR, + "binary": obstacle_detector.name, + }, "ad-battery-monitor": { "id": rover.City.AD_BATTERY_MONITOR, "binary": battery_monitor.name, @@ -113,13 +128,15 @@ def gen_node_config(system): "assignments": [], } for assignment in rover.APP_ASSIGNMENTS: - assignment_to_config(system, *assignment) + assignment_to_config(system, assignment) -def assignment_to_config(system, city, envelope, folder): +def assignment_to_config(system, assignment): for _, node in system.items(): - if node["id"] == city: - node["config"]["assignments"] += [{"folder": folder, "envelope": envelope}] + if node["id"] == assignment.city: + node["config"]["assignments"] += [ + {"folder": assignment.folder, "envelope": assignment.envelope} + ] return