6 Commits

Author SHA1 Message Date
Quinn
17af6e4faf Merge pull request #23 from Block-Party-VR/21-implement-callbacks-for-processing-commands
Rework command processing to use callbacks instead of polling
2024-10-29 19:55:35 -04:00
331cbe4f82 Fixed off by one error and updated readme 2024-10-29 19:54:41 -04:00
a5652102a0 Moved the lambdas to actual functions to avoid them dissapearing after setup. 2024-09-25 18:47:12 -04:00
Quinn
1df45a2d2b Merge pull request #22 from Block-Party-VR/master
Updating develop with patch from master
2024-09-24 11:47:32 -04:00
59bafb2549 Implimented a callback approach to processing functions 2024-09-23 22:50:37 -04:00
495330dbf4 renabled a missing animation 2024-09-12 13:16:56 -04:00
5 changed files with 166 additions and 98 deletions

View File

@@ -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. 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**:
<https://docs.codeberg.org/getting-started/first-repository/#2.-clone-the-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 ## Shout-Outs
- Thank you to all the organizers at Reality Hack and the Hardware track. - 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. - Lucas De Bonet for creating `the Singularity` which allowed us to connect the board to Quest headsets.

View File

@@ -133,7 +133,10 @@ void BoardManager<BOARD_DIMS>::Update(){
// create a cube slice array buffer // create a cube slice array buffer
BOARD_TYPES::Cube* sliceBuffer[BOARD_DIMS.z]; BOARD_TYPES::Cube* sliceBuffer[BOARD_DIMS.z];
// have the board slice get read into our buffer // 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 // send the board slice to the driver to update its LED colors
this->driver.UpdateStackLEDs(stackIndex, sliceBuffer, BOARD_DIMS.z); this->driver.UpdateStackLEDs(stackIndex, sliceBuffer, BOARD_DIMS.z);
} }
@@ -146,9 +149,14 @@ void BoardManager<BOARD_DIMS>::updateStackColors(const V3D<uint32_t> &column){
V3D<uint32_t> sliceVector{column.x, column.y, BOARD_TYPES::Z}; V3D<uint32_t> sliceVector{column.x, column.y, BOARD_TYPES::Z};
// create a buffer for slice board to write the cube slice into // create a buffer for slice board to write the cube slice into
BOARD_TYPES::Cube * cubeSlice[BOARD_DIMS.z]; 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<BOARD_TYPES::PLANE_NORMAL>(column.z))}; uint32_t numCubes{this->getColumnHeight(static_cast<BOARD_TYPES::PLANE_NORMAL>(column.z))};
if(sliceSize < numCubes){
return;
}
this->driver.UpdateStackLEDs(BOARD_DIMS.x, cubeSlice, numCubes); this->driver.UpdateStackLEDs(BOARD_DIMS.x, cubeSlice, numCubes);
} }
@@ -168,6 +176,11 @@ void BoardManager<BOARD_DIMS>::SetColumnColors(const V3D<uint32_t> &column, cons
uint32_t sliceLength{this->board.SliceBoard(column, slicedBoard)}; uint32_t sliceLength{this->board.SliceBoard(column, slicedBoard)};
uint32_t maxIndex{std::min(numColors, columnHeight)}; uint32_t maxIndex{std::min(numColors, columnHeight)};
if(sliceLength < maxIndex){
return;
}
for(uint32_t i = 0; i < maxIndex; i++){ for(uint32_t i = 0; i < maxIndex; i++){
slicedBoard[i]->color = color[i]; slicedBoard[i]->color = color[i];
} }

View File

@@ -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;
}

View File

@@ -0,0 +1,47 @@
#pragma once
#include <cstdint>
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];
};

View File

@@ -15,6 +15,7 @@
#include "BluetoothSerial.h" #include "BluetoothSerial.h"
#include "SerialMessage.h" #include "SerialMessage.h"
#include "GlobalPrint.h" #include "GlobalPrint.h"
#include "CommandHandler.h"
#include "BoardManager.h" #include "BoardManager.h"
#include "BoardDriver.h" #include "BoardDriver.h"
@@ -31,11 +32,13 @@ TaskHandle_t updateCommunicaitonTask;
TaskHandle_t updateBoardTask; TaskHandle_t updateBoardTask;
// WARNING! This array size should always be equal to the number of entries in it!! // WARNING! This array size should always be equal to the number of entries in it!!
std::array<std::vector<AnimationFrame>*, 1> animations = { std::array<std::vector<AnimationFrame>*, 2> animations = {
&RisingCubes::rising, &RisingCubes::rising,
// &RotatingCubes::rotating, &RotatingCubes::rotating,
}; };
CommandHandler commandHandler{};
// BluetoothSerial SerialBT; // BluetoothSerial SerialBT;
// BluetoothSerialMessage serialMessageBT(&SerialBT); // BluetoothSerialMessage serialMessageBT(&SerialBT);
SerialMessage<SERIAL_CHAR_LENGTH, SERIAL_ARG_LENGTH> serialMessage(&Serial); SerialMessage<SERIAL_CHAR_LENGTH, SERIAL_ARG_LENGTH> serialMessage(&Serial);
@@ -73,7 +76,6 @@ void SetupBluetoothModule(){
delay(100); delay(100);
} }
void printBoardState(){ void printBoardState(){
GlobalPrint::Print("!0,"); GlobalPrint::Print("!0,");
String boardString; String boardString;
@@ -82,63 +84,63 @@ void printBoardState(){
GlobalPrint::Println(";"); GlobalPrint::Println(";");
} }
void SetStackColor(uint32_t * args, int argsLength){ void SetStackColor(uint32_t * args, uint32_t argsLength){
uint32_t stackNum = args[1]; uint32_t stackNum = args[0];
uint32_t X_COORD{stackNum}; uint32_t X_COORD{stackNum};
while(X_COORD > BOARD_DIMENSIONS.x - 1){ while(X_COORD > BOARD_DIMENSIONS.x - 1){
X_COORD -= BOARD_DIMENSIONS.x; X_COORD -= BOARD_DIMENSIONS.x;
} }
uint32_t Y_COORD{(stackNum - X_COORD) / BOARD_DIMENSIONS.y}; 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<uint32_t> colors[numColors]; V3D<uint32_t> colors[numColors];
for(int i = 0; i < numColors; i++){ for(int i = 0; i < numColors; i++){
uint32_t red = args[2 + (i * 3)]; uint32_t red = args[1 + (i * 3)];
uint32_t green = args[3 + (i * 3)]; uint32_t green = args[2 + (i * 3)];
uint32_t blue = args[4 + (i * 3)]; uint32_t blue = args[3 + (i * 3)];
colors[i] = V3D<uint32_t>{red, green, blue}; colors[i] = V3D<uint32_t>{red, green, blue};
Serial.println("Color: " + String(red) + "," + String(green) + "," + String(blue));
} }
boardManager.SetColumnColors(V3D<uint32_t>{X_COORD, Y_COORD, BOARD_TYPES::PLANE_NORMAL::Z}, colors, numColors); boardManager.SetColumnColors(V3D<uint32_t>{X_COORD, Y_COORD, BOARD_TYPES::PLANE_NORMAL::Z}, colors, numColors);
} }
void parseData(Message<SERIAL_CHAR_LENGTH, SERIAL_ARG_LENGTH> &message){ // command handling functions
int32_t * args{message.GetArgs()}; CommandHandler::CommandStatus BoardStateCommandHandler(uint32_t * /*args*/, uint32_t /*argsLength*/){
uint32_t argsLength{message.GetPopulatedArgs()}; printBoardState();
uint32_t command = args[0]; return CommandHandler::CommandStatus::SUCCESS;
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<uint32_t> black{};
boardManager.FillColor(black);
SetStackColor(reinterpret_cast<uint32_t *>(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();
} }
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<uint32_t> 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 ----------------- // ----------------- FREERTOS TASKS -----------------
// -------------------------------------------------- // --------------------------------------------------
@@ -148,12 +150,14 @@ void UpdateCommunication(void * params){
// DO serial processing // DO serial processing
serialMessage.Update(); serialMessage.Update();
if(serialMessage.IsNewData()){ 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<uint32_t *>(serialMessage.GetArgs()), serialMessage.GetPopulatedArgs());
serialMessage.ClearNewData();
} }
// serialMessageBT.Update(); // serialMessageBT.Update();
// if(serialMessageBT.IsNewData()){ // if(serialMessageBT.IsNewData()){
// parseData(serialMessageBT.GetArgs(), serialMessageBT.GetArgsLength()); // commandHandler.ProcessCommand(reinterpret_cast<uint32_t *>(serialMessage.GetArgs()), serialMessage.GetPopulatedArgs());
// serialMessageBT.ClearNewData(); // serialMessage.ClearNewData();
// } // }
vTaskDelay(3); vTaskDelay(3);
} }
@@ -214,6 +218,12 @@ void setup() {
SetupBluetoothModule(); SetupBluetoothModule();
Serial.begin(9600); 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"); Serial.println("Configuring communication methods");
serialMessage.Init(9600); serialMessage.Init(9600);
// SerialBT.begin("blockPartyBT"); // SerialBT.begin("blockPartyBT");