diff --git a/README.md b/README.md index 10dc64f..c6cfea9 100644 --- a/README.md +++ b/README.md @@ -127,55 +127,6 @@ Format Example `!3;` Description: This command will tell the ESP32 to re-enable its idle animation. It will respond with a `!3;` when it recieves this command. -**HERE IS A GUIDE TO GET YOU STARTED ON YOUR [MAYBE] FIRST REPOSITORY**: - - - -Obviously, this is not what your README should say on-submission. Change it. Yes, the whole thing. - -This is where a description of your project will go. README.md files support [Markdown syntax](https://www.markdownguide.org/basic-syntax/). - -## Setup - -**Oh yeah, by the way, don't forget to set up `git lfs` on your machine.** - -Tell everyone what your project needs in order to run. This might involve many components or software. Maybe some cool libraries? - -### Hardware Required - -- Some polyphonic headset on OS version `3.14159265358979323846233832795028841971` with room-temperature hyperconductors -- Some macrocontroller with specific hardware revision `4.2` - - With tank wheels -- Some computer with a holoported underdisplay cap -- Some fancy smart device that is worn and knows your favorite toothpaste - -### Software Dependencies - -- Division Game Engine version `2024.1.26` -- Michaelsoft Binbows `XD` - -## Run - -1. Open the thing - - You know, that thing over there - - No, not that one - - Go back - - Yes, that one -2. Click the button and type the following text: - -```shell -# install the sotware -sudo apt install -y cmatrix -# run the trap -cmatrix -``` - -3. After the process completes and you don't even see the code, anymore, you are ready. Here is what it looks like: - -```js -"b" + "a" + +"a" + "a"; // 'baNaNa' -``` - ## Shout-Outs - Thank you to all the organizers at Reality Hack and the Hardware track. - Lucas De Bonet for creating `the Singularity` which allowed us to connect the board to Quest headsets. diff --git a/lib/Board/BoardManager.h b/lib/Board/BoardManager.h index 54624d3..dccdd70 100644 --- a/lib/Board/BoardManager.h +++ b/lib/Board/BoardManager.h @@ -133,7 +133,10 @@ void BoardManager::Update(){ // create a cube slice array buffer BOARD_TYPES::Cube* sliceBuffer[BOARD_DIMS.z]; // have the board slice get read into our buffer - this->board.SliceBoard(sliceVector, sliceBuffer); + uint32_t sliceSize = this->board.SliceBoard(sliceVector, sliceBuffer); + if(sliceSize < BOARD_DIMS.z){ + return; + } // send the board slice to the driver to update its LED colors this->driver.UpdateStackLEDs(stackIndex, sliceBuffer, BOARD_DIMS.z); } @@ -146,9 +149,14 @@ void BoardManager::updateStackColors(const V3D &column){ V3D sliceVector{column.x, column.y, BOARD_TYPES::Z}; // create a buffer for slice board to write the cube slice into BOARD_TYPES::Cube * cubeSlice[BOARD_DIMS.z]; - this->board.SliceBoard(column, cubeSlice); + uint32_t sliceSize = this->board.SliceBoard(column, cubeSlice); uint32_t numCubes{this->getColumnHeight(static_cast(column.z))}; + + if(sliceSize < numCubes){ + return; + } + this->driver.UpdateStackLEDs(BOARD_DIMS.x, cubeSlice, numCubes); } @@ -168,6 +176,11 @@ void BoardManager::SetColumnColors(const V3D &column, cons uint32_t sliceLength{this->board.SliceBoard(column, slicedBoard)}; uint32_t maxIndex{std::min(numColors, columnHeight)}; + + if(sliceLength < maxIndex){ + return; + } + for(uint32_t i = 0; i < maxIndex; i++){ slicedBoard[i]->color = color[i]; } diff --git a/lib/CommandHandler/CommandHandler.cpp b/lib/CommandHandler/CommandHandler.cpp new file mode 100644 index 0000000..9d49114 --- /dev/null +++ b/lib/CommandHandler/CommandHandler.cpp @@ -0,0 +1,47 @@ +#include "CommandHandler.h" + +CommandHandler::CommandHandler(){ + for(uint32_t i = 0; i < MAX_COMMAND_SIZE; i++){ + commandCallbacks[i] = nullptr; + commandCallbackIDs[i] = -1; + } +} + +bool CommandHandler::RegisterCommand(uint32_t commandID, CommandCallback callback){ + for(uint32_t i = 0; i < MAX_COMMAND_SIZE; i++){ + if(commandCallbacks[i] == nullptr){ + commandCallbacks[i] = callback; + commandCallbackIDs[i] = commandID; + return true; + } + } + return false; +} + +bool CommandHandler::RemoveCommand(uint32_t commandID){ + for(uint32_t i = 0; i < MAX_COMMAND_SIZE; i++){ + if(commandCallbackIDs[i] == commandID){ + commandCallbacks[i] = nullptr; + commandCallbackIDs[i] = -1; + return true; + } + } + return false; +} + +CommandHandler::CommandStatus CommandHandler::ProcessCommand(uint32_t * command, uint32_t commandSize){ + if(commandSize == 0){ + return CommandStatus::INVALID; + } + + uint32_t commandID{command[0]}; + // get a pointer to the second element in the array because the first element is the command ID + uint32_t * args{&(command[1])}; + + for(uint32_t i = 0; i < MAX_COMMAND_SIZE; i++){ + if(commandCallbacks[i] != nullptr && commandCallbackIDs[i] == commandID){ + return commandCallbacks[i](args, commandSize-1); + } + } + return CommandStatus::INVALID; +} \ No newline at end of file diff --git a/lib/CommandHandler/CommandHandler.h b/lib/CommandHandler/CommandHandler.h new file mode 100644 index 0000000..1486ed7 --- /dev/null +++ b/lib/CommandHandler/CommandHandler.h @@ -0,0 +1,47 @@ +#pragma once +#include + +class CommandHandler{ +public: + // create an enum for command return values + enum CommandStatus{ + SUCCESS, + FAILURE, + INVALID + }; + + // create a typedef for a command callback function which takes an array pointer and an array size as an argument and returns a CommandStatus + typedef CommandStatus (*CommandCallback)(uint32_t * command, uint32_t commandSize); + + CommandHandler(); + ~CommandHandler() = default; + + /** + * @brief Register a command callback function to a command ID + * @param commandID The command ID to register the callback to + * @param callback The callback function to register + * @return true if the callback was registered successfully, false otherwise + */ + bool RegisterCommand(uint32_t commandID, CommandCallback callback); + + /** + * @brief Remove a command callback function from a command ID + * @param commandID The command ID to remove the callback from + * @return true if the callback was removed successfully, false otherwise + */ + bool RemoveCommand(uint32_t commandID); + + /** + * @brief Process a command + * @param command The command to process + */ + CommandStatus ProcessCommand(uint32_t * command, uint32_t commandSize); + +private: + static constexpr uint32_t MAX_COMMAND_SIZE{10}; + + // an array of command callbacks + CommandCallback commandCallbacks[MAX_COMMAND_SIZE]; + // an array of command callback IDs + uint32_t commandCallbackIDs[MAX_COMMAND_SIZE]; +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index b0986f2..412b232 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,6 +15,7 @@ #include "BluetoothSerial.h" #include "SerialMessage.h" #include "GlobalPrint.h" +#include "CommandHandler.h" #include "BoardManager.h" #include "BoardDriver.h" @@ -36,6 +37,8 @@ std::array*, 2> animations = { &RotatingCubes::rotating, }; +CommandHandler commandHandler{}; + // BluetoothSerial SerialBT; // BluetoothSerialMessage serialMessageBT(&SerialBT); SerialMessage serialMessage(&Serial); @@ -73,7 +76,6 @@ void SetupBluetoothModule(){ delay(100); } - void printBoardState(){ GlobalPrint::Print("!0,"); String boardString; @@ -82,63 +84,63 @@ void printBoardState(){ GlobalPrint::Println(";"); } -void SetStackColor(uint32_t * args, int argsLength){ - uint32_t stackNum = args[1]; +void SetStackColor(uint32_t * args, uint32_t argsLength){ + uint32_t stackNum = args[0]; uint32_t X_COORD{stackNum}; while(X_COORD > BOARD_DIMENSIONS.x - 1){ X_COORD -= BOARD_DIMENSIONS.x; } uint32_t Y_COORD{(stackNum - X_COORD) / BOARD_DIMENSIONS.y}; + Serial.println("StackNum: " + String(stackNum)); + Serial.println("X: " + String(X_COORD) + " Y: " + String(Y_COORD)); + uint32_t numColors = (argsLength - 1) / 3; - uint32_t numColors = (argsLength - 2) / 3; + // nothing to do if no colors were given + if(numColors == 0){ + return; + } + + Serial.println("Num Colors: " + String(numColors)); V3D colors[numColors]; for(int i = 0; i < numColors; i++){ - uint32_t red = args[2 + (i * 3)]; - uint32_t green = args[3 + (i * 3)]; - uint32_t blue = args[4 + (i * 3)]; + uint32_t red = args[1 + (i * 3)]; + uint32_t green = args[2 + (i * 3)]; + uint32_t blue = args[3 + (i * 3)]; colors[i] = V3D{red, green, blue}; + Serial.println("Color: " + String(red) + "," + String(green) + "," + String(blue)); } boardManager.SetColumnColors(V3D{X_COORD, Y_COORD, BOARD_TYPES::PLANE_NORMAL::Z}, colors, numColors); } -void parseData(Message &message){ - int32_t * args{message.GetArgs()}; - uint32_t argsLength{message.GetPopulatedArgs()}; - uint32_t command = args[0]; - switch(command){ - case Commands::BoardState:{ - printBoardState(); - break; - } - case Commands::PING:{ - GlobalPrint::Println("!" + String(Commands::PING) + ";"); - boardManager.PrintColorState(); - break; - } - case Commands::SetStackColors:{ - GlobalPrint::Println("!2;"); - animator.isEnabled = false; - V3D black{}; - boardManager.FillColor(black); - SetStackColor(reinterpret_cast(args), argsLength); - break; - } - case Commands::GoToIdle:{ - GlobalPrint::Println("!3;"); - animator.isEnabled = true; - break; - } - default:{ - GlobalPrint::Println("INVALID COMMAND"); - break; - } - } - - // now that we have run the command we can clear the data for the next command. - serialMessage.ClearNewData(); +// command handling functions +CommandHandler::CommandStatus BoardStateCommandHandler(uint32_t * /*args*/, uint32_t /*argsLength*/){ + printBoardState(); + return CommandHandler::CommandStatus::SUCCESS; } +CommandHandler::CommandStatus PingCommandHandler(uint32_t * /*args*/, uint32_t /*argsLength*/){ + GlobalPrint::Println("!" + String(Commands::PING) + ";"); + return CommandHandler::CommandStatus::SUCCESS; +} + +CommandHandler::CommandStatus SetColorCommandHandler(uint32_t * args, uint32_t argsLength){ + GlobalPrint::Println("!2;"); + animator.isEnabled = false; + V3D black{}; + boardManager.FillColor(black); + SetStackColor(args, argsLength); + return CommandHandler::CommandStatus::SUCCESS; +} + +CommandHandler::CommandStatus GoToIdleCommandHandler(uint32_t * /*args*/, uint32_t /*argsLength*/){ + GlobalPrint::Println("!3;"); + animator.isEnabled = true; + return CommandHandler::CommandStatus::SUCCESS; +} + + + // -------------------------------------------------- // ----------------- FREERTOS TASKS ----------------- // -------------------------------------------------- @@ -148,12 +150,14 @@ void UpdateCommunication(void * params){ // DO serial processing serialMessage.Update(); if(serialMessage.IsNewData()){ - parseData(serialMessage); + // We reinterpret cast the args to a uint32_t pointer because we know that the args will always be positive + commandHandler.ProcessCommand(reinterpret_cast(serialMessage.GetArgs()), serialMessage.GetPopulatedArgs()); + serialMessage.ClearNewData(); } // serialMessageBT.Update(); // if(serialMessageBT.IsNewData()){ - // parseData(serialMessageBT.GetArgs(), serialMessageBT.GetArgsLength()); - // serialMessageBT.ClearNewData(); + // commandHandler.ProcessCommand(reinterpret_cast(serialMessage.GetArgs()), serialMessage.GetPopulatedArgs()); + // serialMessage.ClearNewData(); // } vTaskDelay(3); } @@ -214,6 +218,12 @@ void setup() { SetupBluetoothModule(); Serial.begin(9600); + // Register all of our commands with the command handler + commandHandler.RegisterCommand(Commands::BoardState, BoardStateCommandHandler); + commandHandler.RegisterCommand(Commands::PING, PingCommandHandler); + commandHandler.RegisterCommand(Commands::SetStackColors, SetColorCommandHandler); + commandHandler.RegisterCommand(Commands::GoToIdle, GoToIdleCommandHandler); + Serial.println("Configuring communication methods"); serialMessage.Init(9600); // SerialBT.begin("blockPartyBT");