// Other peoples libraries #include #include #include #include #include #include // Static Defines #include "PINOUT.h" #include "BOARD-DEFINITIONS.h" #include "COMMANDS.h" // project specific libraries #include "BluetoothSerial.h" #include "SerialMessage.h" #include "GlobalPrint.h" #include "CommandHandler.h" #include "BoardManager.h" #include "BoardDriver.h" #include "BoardTypes.h" #include "Animator.h" #include "TestFrames.h" #include "Animation.h" // -------------------------------------------------- // ----------------- VARIABLES ---------------------- // -------------------------------------------------- TaskHandle_t updateCommunicaitonTask; TaskHandle_t updateBoardTask; // WARNING! This array size should always be equal to the number of entries in it!! std::array*, 2> animations = { &RisingCubes::rising, &RotatingCubes::rotating, }; CommandHandler commandHandler{}; // BluetoothSerial SerialBT; // BluetoothSerialMessage serialMessageBT(&SerialBT); SerialMessage serialMessage(&Serial); Adafruit_NeoPixel pixelController{BOARD_HEIGHT*2, STACK1_LED_PIN, NEO_GRB + NEO_KHZ800}; Animator animator{}; BoardDriver boardDriver{stacks, pixelController}; BoardManager boardManager{boardDriver, animator}; // -------------------------------------------------- // ----------------- FUNCTIONS ---------------------- // -------------------------------------------------- /** * @brief Send programming commands to the serial to bluetooth adapter so * it is set up as expected for the VR headset * @post the serial baud rate will be set to 9600 */ void SetupBluetoothModule(){ Serial.begin(38400); Serial.print("AT+UART=9600,0,0\r\n"); // set baud rate to 9600 delay(100); Serial.print("AT+NAME=blockPartyBT-v01\r\n"); // set name to blockPartyBT-v0.1 delay(100); Serial.print("AT+PSWD=1234\r\n"); // set password to 1234 delay(100); Serial.print("AT+ROLE=0\r\n"); // set to slave delay(100); // exit at mode and go into pairing mode Serial.print("AT+INIT\r\n"); Serial.begin(9600); delay(100); } void printBoardState(){ GlobalPrint::Print("!0,"); String boardString; boardManager.Board2StackString(boardString); GlobalPrint::Print(boardString); GlobalPrint::Println(";"); } 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; // 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[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); } // 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 ----------------- // -------------------------------------------------- void UpdateCommunication(void * params){ Serial.println("Spawning UpdateCommunication task"); for(;;){ // DO serial processing serialMessage.Update(); if(serialMessage.IsNewData()){ // 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()){ // commandHandler.ProcessCommand(reinterpret_cast(serialMessage.GetArgs()), serialMessage.GetPopulatedArgs()); // serialMessage.ClearNewData(); // } vTaskDelay(3); } Serial.println("UpdateCommunication task has ended unexpectedly!"); } void UpdateBoard(void * params){ Serial.println("Spawning UpdateBoard task"); auto updateTickRate{std::chrono::milliseconds(8)}; auto boardStateTimer{std::chrono::milliseconds(0)}; auto boardStateMaxUpdatePeriod{std::chrono::milliseconds(34)}; // this is a little slower than 30fps unsigned long accurateTimer{millis()}; auto changeAnimationTimer{std::chrono::milliseconds(0)}; uint8_t currentAnimation{0}; for(;;){ auto actualTimePassed{std::chrono::milliseconds(millis() - accurateTimer)}; accurateTimer = millis(); if(boardStateTimer >= boardStateMaxUpdatePeriod && boardManager.HasBoardChanged()){ printBoardState(); boardManager.ClearBoardChanged(); boardStateTimer = std::chrono::milliseconds(0); } if(changeAnimationTimer >= std::chrono::duration_cast(std::chrono::minutes(1))){ changeAnimationTimer = std::chrono::milliseconds(0); currentAnimation++; if(currentAnimation >= animations.size()){ currentAnimation = 0; } animator.StartAnimation(animations[currentAnimation]); } animator.RunAnimation(actualTimePassed); boardManager.Update(); boardStateTimer += actualTimePassed; changeAnimationTimer += actualTimePassed; vTaskDelay(updateTickRate.count()); } Serial.println("UpdateBoard task has ended unexpectedly!"); } // -------------------------------------------------- // ----------------- SETUP AND LOOP ----------------- // -------------------------------------------------- void setup() { // delay a little bit to get the serial monitor a chance to capture the next log messages. delay(1000); Serial.begin(9600); Serial.println("Beginning Setup"); Serial.println("Configuring Bluetooth Adapter"); 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"); xTaskCreate(UpdateCommunication, "UpdateCommunication", 10000, NULL, 0, &updateCommunicaitonTask); Serial.println("Beginning Board Initializaiton"); boardManager.Init(); animator.SetLoop(true); animator.StartAnimation(animations[0]); xTaskCreate(UpdateBoard, "UpdateBoard", 10000, NULL, 0, &updateBoardTask); Serial.println("Setup Complete"); } void loop() { // delete the loop task because we don't use it vTaskDelete(NULL); }