From aab096d385a5d633cdf06098fadee536fd09003d Mon Sep 17 00:00:00 2001 From: superkor Date: Fri, 23 Jan 2026 18:51:06 -0500 Subject: [PATCH] updates to topology with flatbuffers --- .../Movement_generated.h | 8 +- .../Topology_generated.h | 8 +- components/movements/TopologyManager.cpp | 210 ++++++++++++++---- .../movements/include/TopologyManager.h | 27 ++- main/README.md | 10 + main/main.cpp | 2 +- main/main_preprogrammed_test.cpp | 48 ++++ main/main_rmt_test.cpp | 2 +- 8 files changed, 245 insertions(+), 70 deletions(-) create mode 100644 main/main_preprogrammed_test.cpp diff --git a/components/flatbuffers/include/flatbuffers_generated/Movement_generated.h b/components/flatbuffers/include/flatbuffers_generated/Movement_generated.h index a8acf9a..b6e8010 100644 --- a/components/flatbuffers/include/flatbuffers_generated/Movement_generated.h +++ b/components/flatbuffers/include/flatbuffers_generated/Movement_generated.h @@ -8,10 +8,10 @@ // Ensure the included flatbuffers.h is the same version as when this file was // generated, otherwise it may not be compatible. -static_assert(FLATBUFFERS_VERSION_MAJOR == 25 && - FLATBUFFERS_VERSION_MINOR == 12 && - FLATBUFFERS_VERSION_REVISION == 19, - "Non-compatible flatbuffers version included"); +// static_assert(FLATBUFFERS_VERSION_MAJOR == 25 && +// FLATBUFFERS_VERSION_MINOR == 12 && +// FLATBUFFERS_VERSION_REVISION == 19, +// "Non-compatible flatbuffers version included"); namespace Movement { diff --git a/components/flatbuffers/include/flatbuffers_generated/Topology_generated.h b/components/flatbuffers/include/flatbuffers_generated/Topology_generated.h index 6dad170..5a3bced 100644 --- a/components/flatbuffers/include/flatbuffers_generated/Topology_generated.h +++ b/components/flatbuffers/include/flatbuffers_generated/Topology_generated.h @@ -8,10 +8,10 @@ // Ensure the included flatbuffers.h is the same version as when this file was // generated, otherwise it may not be compatible. -static_assert(FLATBUFFERS_VERSION_MAJOR == 25 && - FLATBUFFERS_VERSION_MINOR == 12 && - FLATBUFFERS_VERSION_REVISION == 19, - "Non-compatible flatbuffers version included"); +// static_assert(FLATBUFFERS_VERSION_MAJOR == 25 && +// FLATBUFFERS_VERSION_MINOR == 12 && +// FLATBUFFERS_VERSION_REVISION == 19, +// "Non-compatible flatbuffers version included"); namespace Topology { diff --git a/components/movements/TopologyManager.cpp b/components/movements/TopologyManager.cpp index edce60a..c010f9e 100644 --- a/components/movements/TopologyManager.cpp +++ b/components/movements/TopologyManager.cpp @@ -17,70 +17,182 @@ TopologyManager::~TopologyManager(){ nvs_close(handle); } -// esp_err_t TopologyManager::add_board_to_topology(struct NeighbourBlob& blob){ -// if (!ready){ -// return ESP_ERR_INVALID_STATE; -// } +/** + * @brief Creates a `Topology::NeighbourBlob` based on the input parameters and adds the blob to the internal topology map + * + * @param connections This is a vector of pairs (uint8_t: `channel number`, uint16_t: `board_id`) representing the connection from the `curr_board_id` to `board_id` via `channel number` + * @param curr_board_id This is the board id, from its perspective, has the connections to the other boards in the vector `connections` + * @return esp_err_t + * + * @note + * Connections to fail: + * + * 1. Any of the `board_id` in the vector is `PC_ADDR` or `BROADCAST_ADDR` - Reserved addresses + * + * 2. Any of the `board_id` in the vector is `curr_board_id` - No loopback connections are allowed + * + * 3. Any of the `channel` in the vector is greater than or equal to `MAX_CHANNELS` - Channels are 0-indexed and there are at most 4 channels + * + * 4. All `board_id` is not unique in the vector - Should be a 1:1 connection between the boards + */ +esp_err_t TopologyManager::add_board_to_topology(std::vector>& connections, uint16_t curr_board_id){ + if (!ready){ + return ESP_ERR_INVALID_STATE; + } -// if (blob.num_connections > MAX_CHANNELS || blob.curr_board_id == PC_ADDR || blob.curr_board_id == BROADCAST_ADDR){ -// return ESP_ERR_INVALID_ARG; -// } + if (connections.size() == 0 || connections.size() > MAX_CHANNELS){ + return ESP_ERR_INVALID_ARG; + } -// for (uint8_t i = 0; i < blob.num_connections; i++){ -// ChannelBoardConn connection = blob.neighbour_connections[i]; -// if (connection.channel > MAX_CHANNELS || connection.board_id == PC_ADDR -// || connection == BROADCAST_ADDR || connection.board_id == blob.curr_board_id){ -// return ESP_ERR_INVALID_ARG; -// } -// } + if (curr_board_id == PC_ADDR || curr_board_id == BROADCAST_ADDR){ + return ESP_ERR_INVALID_ARG; + } -// topology[blob.curr_board_id] = blob; + //verify the connections in the vector + std::unordered_set check_set; + + std::vector conns; + + for (const std::pair& pair : connections){ + if (pair.second >= MAX_CHANNELS || pair.first == curr_board_id || pair.first == PC_ADDR + || pair.first == BROADCAST_ADDR || check_set.find(pair.first) != check_set.end()){ + return ESP_ERR_INVALID_ARG; + } + + check_set.insert(pair.first); + + conns.push_back(builder.build_connections(pair.first, pair.second)); + } + + topology[curr_board_id] = conns; -// return ESP_OK; -// } + return ESP_OK; +} -// esp_err_t TopologyManager::remove_board_from_topology(uint16_t board_id){ -// if (!ready){ -// return ESP_ERR_INVALID_STATE; -// } +esp_err_t TopologyManager::remove_board_from_topology(uint16_t board_id){ + if (!ready){ + return ESP_ERR_INVALID_STATE; + } -// if (topology.find(board_id) == topology.end()){ -// return ESP_ERR_NOT_FOUND; -// } + if (topology.find(board_id) == topology.end()){ + return ESP_ERR_NOT_FOUND; + } -// topology.erase(board_id); + topology.erase(board_id); -// return ESP_OK; -// } + return ESP_OK; +} -// esp_err_t TopologyManager::get_board_in_topology(struct NeighbourBlob& blob){ -// if (!ready){ -// return ESP_ERR_INVALID_STATE; -// } +esp_err_t TopologyManager::get_board_in_topology(std::vector>& connections, uint16_t curr_board_id){ + if (!ready){ + return ESP_ERR_INVALID_STATE; + } -// if (topology.find(board_id) == topology.end()){ -// return ESP_ERR_NOT_FOUND; -// } + if (topology.find(curr_board_id) == topology.end()){ + return ESP_ERR_NOT_FOUND; + } -// blob = topology.find(board_id); + std::vector conn = topology[curr_board_id]; -// return ESP_OK; -// } + connections.clear(); -// esp_err_t TopologyManager::verify_topology(){ -// if (!ready){ -// return ESP_ERR_INVALID_STATE; -// } -// return ESP_OK; -// } + for (const Topology::ChannelBoardConn c : conn){ + connections.push_back(std::pair(c.channel(), c.board_id())); + } -// esp_err_t TopologyManager::get_curr_topology(struct Topology& topology){ -// if (!ready){ -// return ESP_ERR_INVALID_STATE; -// } + return ESP_OK; +} -// return ESP_OK; -// } +esp_err_t TopologyManager::verify_topology(){ + if (!ready){ + return ESP_ERR_INVALID_STATE; + } + + if (topology.size() == 0){ + return ESP_ERR_INVALID_STATE; + } + + /** + * Conditions to fail: + * 1. Any board that is referenced in the topology does not have a reciprocal connection (eg. A -> B and B -> A) + * 2. The topology contains 2 separate graphs (a board in the topology should be in the same graph with some sort of path to all other boards) + */ + + //condition 1 + + for (const auto& pair : topology){ + for (const Topology::ChannelBoardConn& conn : pair.second){ + if (topology.find(conn.board_id()) == topology.end()){ + return ESP_ERR_INVALID_STATE; //could not find reciprocal board + } + + std::vector topology_board_conn = topology[conn.board_id()]; + bool found = false; + for (const Topology::ChannelBoardConn& c : topology_board_conn){ + if (c.board_id() == pair.first){ + found = true; + break; + } + } + if (!found){ + return ESP_ERR_INVALID_STATE; //reciprocal board does not have a connection back to `pair.first` + } + + } + } + + //condition 2 - dfs + std::unordered_set visited; + std::stack backtrack; + uint32_t count = 0; + + backtrack.push(topology.begin()->first); + + while (!backtrack.empty()){ + uint16_t curr_node = backtrack.top(); + backtrack.pop(); + + if (visited.find(curr_node) != visited.end()) { + continue; + } + + visited.insert(curr_node); + count++; + + const std::vector& conns = topology[curr_node]; + + for (const Topology::ChannelBoardConn& conn : conns) { + uint16_t next = conn.board_id(); + if (visited.find(next) != visited.end()) { + backtrack.push(next); + } + } + } + + if (count != topology.size()){ + return ESP_ERR_INVALID_STATE; + } + + + return ESP_OK; +} + +esp_err_t TopologyManager::get_curr_topology(std::unordered_map>>& topology){ + if (!ready){ + return ESP_ERR_INVALID_STATE; + } + + topology.clear(); + + for (const auto& [key, value] : this->topology){ + topology[key] = std::vector>(); + for (const Topology::ChannelBoardConn c : value){ + topology[key].push_back(std::pair(c.channel(), c.board_id())); + } + } + + return ESP_OK; +} // esp_err_t TopologyManager::write_nvs_topology(){ // if (!ready){ diff --git a/components/movements/include/TopologyManager.h b/components/movements/include/TopologyManager.h index f036f79..9bcab08 100644 --- a/components/movements/include/TopologyManager.h +++ b/components/movements/include/TopologyManager.h @@ -1,31 +1,36 @@ #include "MovementManager.h" + +#ifdef MOVEMENTS #include "nvs_flash.h" #include +#include // move these to constants header file? #include "RMTManager.h" #include "DataLinkManager.h" #include "Frames.h" - -#ifdef MOVEMENTS -#pragma once +#include "TopologyBuilder.h" +#include +#include +#include class TopologyManager { public: TopologyManager(); ~TopologyManager(); - // esp_err_t add_board_to_topology(struct NeighbourBlob& blob); - // esp_err_t remove_board_from_topology(uint16_t board_id); - // esp_err_t get_board_in_topology(struct NeighbourBlob& blob); - // esp_err_t verify_topology(); - // esp_err_t get_curr_topology(struct Topology& topology); - // esp_err_t write_nvs_topology(); - // esp_err_t get_nvs_topology(struct Topology& topology); + esp_err_t add_board_to_topology(std::vector>& connections, uint16_t curr_board_id); + esp_err_t remove_board_from_topology(uint16_t board_id); + esp_err_t get_board_in_topology(std::vector>& connections, uint16_t curr_board_id); + esp_err_t verify_topology(); + esp_err_t get_curr_topology(std::unordered_map>>& topology); + esp_err_t write_nvs_topology(); + esp_err_t get_nvs_topology(std::unordered_map>& topology); private: - // std::unordered_map topology; + std::unordered_map> topology; nvs_handle_t handle; bool ready = false; + Flatbuffers::TopologyBuilder builder; }; #endif //MOVEMENTS \ No newline at end of file diff --git a/main/README.md b/main/README.md index 44aa224..215a013 100644 --- a/main/README.md +++ b/main/README.md @@ -12,6 +12,8 @@ To run the sample code, add the compiler variable `RMT_TEST=1` when compiling: ` With `main_rmt_test.cpp`, flash one or more boards, ensuring that `BOARD_ID` are unique and `RECEIVER_BOARD_ID` is a board that exists and is connected on the same network. +Please note that `SRC_BOARD` and `RECEIVER_BOARD` should be an 8 bit value (that is not 0x0 or 0xFF). + ### Example Usage: Board A: `idf.py -B ./build_A build -D SRC_BOARD=1 -D RECEIVER_BOARD=69 -D RMT_TEST=1 flash monitor -p COM3` @@ -32,6 +34,14 @@ When monitoring both boards, you should see `Received message '%.*s' on channel The specific message being sent can vary depending on the setup (determined at compile time). Simply changing the `snprintf` on the `send_buf` and/or `FrameType` argument of `send()` will change the message/frames being sent (from the perspective of the user). +# Preprogrammed Movements Sample Code + +See `main_preprogrammed_test.cpp`. + +To run the sample code, add the compiler variable `PP_MOVE=1` when compiling. + +You can use the `SRC_BOARD` compiler variable to set a custom board id. + # cat ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⡷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ diff --git a/main/main.cpp b/main/main.cpp index 8118ebb..04521cc 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1,4 +1,4 @@ -#if !defined(RMT_TEST) || (defined(RMT_TEST) && RMT_TEST == 0) +#if (!defined(RMT_TEST) || (defined(RMT_TEST) && RMT_TEST == 0)) && (!defined(PP_MOVE) || (defined(PP_MOVE) && PP_MOVE == 0)) // #include // #include diff --git a/main/main_preprogrammed_test.cpp b/main/main_preprogrammed_test.cpp new file mode 100644 index 0000000..392e1e6 --- /dev/null +++ b/main/main_preprogrammed_test.cpp @@ -0,0 +1,48 @@ +#if (defined(RMT_TEST) && RMT_TEST == 0) && (!defined(PP_MOVE) || (defined(PP_MOVE) && PP_MOVE == 1)) +#include +#include + +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_flash.h" +#include "nvs_flash.h" + +#include "constants/datalink.h" +#include "TopologyManager.h" + +#include +#include +#include +#include "driver/gptimer.h" +#include "esp_log.h" + +#if !defined(SRC_BOARD) +const uint8_t BOARD_ID = 1; +#else +const uint8_t BOARD_ID = SRC_BOARD +#endif + +extern "C" [[noreturn]] void app_main(void) { + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + esp_netif_init(); + esp_event_loop_create_default(); + + printf("finished esp init\n"); + + printf("Hello world!\n"); + + TopologyManager obj = TopologyManager(); + + while(true){ + //do nothing + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} +#endif \ No newline at end of file diff --git a/main/main_rmt_test.cpp b/main/main_rmt_test.cpp index 7bc05da..47cf2b7 100644 --- a/main/main_rmt_test.cpp +++ b/main/main_rmt_test.cpp @@ -1,4 +1,4 @@ -#if defined(RMT_TEST) && RMT_TEST == 1 +#if (defined(RMT_TEST) && RMT_TEST == 1) && (!defined(PP_MOVE) || (defined(PP_MOVE) && PP_MOVE == 0)) //Used for link layer testing (change name to main.cpp to use) #include #include