9 Commits

Author SHA1 Message Date
Quinn
3954624413 Create THIRD-PARTY-LICENSES.md 2024-11-11 17:53:34 -05:00
Quinn
5c3f2730ec Create LICENSE 2024-11-11 17:45:40 -05:00
Quinn
ff9bb57665 Merge pull request #24 from Block-Party-VR/develop
Develop
2024-10-29 19:56:16 -04:00
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
7 changed files with 211 additions and 97 deletions

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Quinn Henthorne, Y. Jenny Wang, Stanton Nash
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

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.

25
THIRD-PARTY-LICENSES.md Normal file
View File

@@ -0,0 +1,25 @@
# Open Source Software Used in the Project
[BlockParty](https://codeberg.org/reality-hack-2024/BlockParty)
MIT License
Copyright (c) 2024 Reality-Hack-Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

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"
@@ -36,6 +37,8 @@ std::array<std::vector<AnimationFrame>*, 2> animations = {
&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()};
uint32_t command = args[0];
switch(command){
case Commands::BoardState:{
printBoardState(); printBoardState();
break; return CommandHandler::CommandStatus::SUCCESS;
} }
case Commands::PING:{
CommandHandler::CommandStatus PingCommandHandler(uint32_t * /*args*/, uint32_t /*argsLength*/){
GlobalPrint::Println("!" + String(Commands::PING) + ";"); GlobalPrint::Println("!" + String(Commands::PING) + ";");
boardManager.PrintColorState(); return CommandHandler::CommandStatus::SUCCESS;
break;
} }
case Commands::SetStackColors:{
CommandHandler::CommandStatus SetColorCommandHandler(uint32_t * args, uint32_t argsLength){
GlobalPrint::Println("!2;"); GlobalPrint::Println("!2;");
animator.isEnabled = false; animator.isEnabled = false;
V3D<uint32_t> black{}; V3D<uint32_t> black{};
boardManager.FillColor(black); boardManager.FillColor(black);
SetStackColor(reinterpret_cast<uint32_t *>(args), argsLength); SetStackColor(args, argsLength);
break; return CommandHandler::CommandStatus::SUCCESS;
}
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. CommandHandler::CommandStatus GoToIdleCommandHandler(uint32_t * /*args*/, uint32_t /*argsLength*/){
serialMessage.ClearNewData(); 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");