From e9d79aacce34d6c4551f4170f0ab15ebe02495be Mon Sep 17 00:00:00 2001
From: Emilio Corigliano <emilio.corigliano@skywarder.eu>
Date: Wed, 2 Oct 2024 15:17:04 +0200
Subject: [PATCH] [AutomatedAntennas] Implemented Actuators and Buses classes
 and base of test-steps

---
 CMakeLists.txt                                |   4 +
 cmake/dependencies.cmake                      |   4 +
 src/boards/AutomatedAntennas/Actuators.cpp    | 214 ++++++++++++++++++
 src/boards/AutomatedAntennas/Actuators.h      |  75 ++++++
 .../AutomatedAntennas/ActuatorsConfig.h       |  63 ++++++
 src/boards/AutomatedAntennas/Buses.h          |  55 +++++
 .../AutomatedAntennas/test-steps.cpp          | 120 ++++++++++
 7 files changed, 535 insertions(+)
 create mode 100644 src/boards/AutomatedAntennas/Actuators.cpp
 create mode 100644 src/boards/AutomatedAntennas/Actuators.h
 create mode 100644 src/boards/AutomatedAntennas/ActuatorsConfig.h
 create mode 100644 src/boards/AutomatedAntennas/Buses.h
 create mode 100644 src/entrypoints/Groundstation/AutomatedAntennas/test-steps.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 94185b780..b447dd861 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -108,3 +108,7 @@ sbs_target(nokia-groundstation-entry stm32f429zi_skyward_groundstation_v2)
 add_executable(automated-antennas-entry src/entrypoints/Groundstation/AutomatedAntennas/automated-antennas-entry.cpp)
 target_include_directories(automated-antennas-entry PRIVATE ${OBSW_INCLUDE_DIRS})
 sbs_target(automated-antennas-entry stm32f407vg_stm32f4discovery)
+
+add_executable(test-steps src/entrypoints/Groundstation/AutomatedAntennas/test-steps.cpp ${ANTENNAS})
+target_include_directories(test-steps PRIVATE ${OBSW_INCLUDE_DIRS})
+sbs_target(test-steps stm32f767zi_nucleo)
diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake
index 1e3364643..bd8e4077e 100644
--- a/cmake/dependencies.cmake
+++ b/cmake/dependencies.cmake
@@ -137,4 +137,8 @@ set(GROUNDSTATION_BASE
     src/boards/Groundstation/Common/Ports/EthernetBase.cpp
     src/boards/Groundstation/Common/Radio/RadioBase.cpp
     src/boards/Groundstation/Common/HubBase.cpp
+)
+
+set(ANTENNAS
+    src/boards/AutomatedAntennas/Actuators.cpp
 )
\ No newline at end of file
diff --git a/src/boards/AutomatedAntennas/Actuators.cpp b/src/boards/AutomatedAntennas/Actuators.cpp
new file mode 100644
index 000000000..1274feba2
--- /dev/null
+++ b/src/boards/AutomatedAntennas/Actuators.cpp
@@ -0,0 +1,214 @@
+/* Copyright (c) 2023 Skyward Experimental Rocketry
+ * Author: Emilio Corigliano
+ *
+ * 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.
+ */
+
+#include "Actuators.h"
+
+#include "ActuatorsConfig.h"
+#include "logger/Logger.h"
+
+using namespace miosix;
+using namespace Boardcore;
+
+namespace Antennas
+{
+// TIM1_CH4 PA11 AF1
+//      |
+// TIM3_CH2 PC7  AF2
+
+// TIM4_CH1 PD12 AF2
+//      |
+// TIM8_CH4 PC9  AF3
+
+GpioPin stepPin1      = GpioPin(GPIOA_BASE, 11);  // tim1_ch4
+GpioPin countPin1     = GpioPin(GPIOC_BASE, 7);   // tim3_ch2
+GpioPin directionPin1 = GpioPin(GPIOA_BASE, 12);
+GpioPin enablePin1    = GpioPin(GPIOA_BASE, 8);
+
+GpioPin stepPin2      = GpioPin(GPIOD_BASE, 12);  // tim4_ch1
+GpioPin countPin2     = GpioPin(GPIOC_BASE, 9);   // tim8_ch4
+GpioPin directionPin2 = GpioPin(GPIOD_BASE, 13);
+GpioPin enablePin2    = GpioPin(GPIOD_BASE, 3);
+
+GpioPin ledRGB = GpioPin(GPIOG_BASE, 14);
+
+CountedPWM countedPwmX(Config::StepperConfig::SERVO1_PULSE_TIM,
+                       Config::StepperConfig::SERVO1_PULSE_CH,
+                       Config::StepperConfig::SERVO1_PULSE_ITR,
+                       Config::StepperConfig::SERVO1_COUNT_TIM,
+                       Config::StepperConfig::SERVO1_COUNT_CH,
+                       Config::StepperConfig::SERVO1_COUNT_ITR);
+
+CountedPWM countedPwmY(Config::StepperConfig::SERVO2_PULSE_TIM,
+                       Config::StepperConfig::SERVO2_PULSE_CH,
+                       Config::StepperConfig::SERVO2_PULSE_ITR,
+                       Config::StepperConfig::SERVO2_COUNT_TIM,
+                       Config::StepperConfig::SERVO2_COUNT_CH,
+                       Config::StepperConfig::SERVO2_COUNT_ITR);
+
+Actuators::Actuators()
+    : stepperX(countedPwmX, stepPin1, directionPin1, 1, 1.8, false, 8,
+               Stepper::PinConfiguration::COMMON_CATHODE, enablePin1),
+      stepperY(countedPwmY, stepPin2, directionPin2, 1, 1.8, false, 8,
+               Stepper::PinConfiguration::COMMON_CATHODE, enablePin2)
+{
+    stepPin1.mode(Mode::ALTERNATE);
+    stepPin1.alternateFunction(1);
+    stepPin2.mode(Mode::ALTERNATE);
+    stepPin2.alternateFunction(2);
+
+    directionPin1.mode(Mode::OUTPUT);
+    enablePin1.mode(Mode::OUTPUT);
+    directionPin2.mode(Mode::OUTPUT);
+    enablePin2.mode(Mode::OUTPUT);
+#ifdef NO_SD_LOGGING
+    countPin1.mode(Mode::ALTERNATE);
+    countPin1.alternateFunction(2);
+    countPin2.mode(Mode::ALTERNATE);
+    countPin2.alternateFunction(3);
+#endif
+}
+
+/**
+ * @brief Enables all the stepperPWM
+ */
+void Actuators::start()
+{
+    stepperX.enable();
+    stepperY.enable();
+}
+
+void Actuators::setSpeed(StepperList axis, float speed)
+{
+    switch (axis)
+    {
+        case StepperList::HORIZONTAL:
+            stepperX.setSpeed(speed);
+            break;
+        case StepperList::VERTICAL:
+            stepperY.setSpeed(speed);
+            break;
+        default:
+            assert(false && "Non existent stepper");
+            break;
+    }
+}
+
+int16_t Actuators::getCurrentPosition(StepperList axis)
+{
+    switch (axis)
+    {
+        case StepperList::HORIZONTAL:
+            return stepperX.getCurrentPosition();
+            break;
+        case StepperList::VERTICAL:
+            return stepperY.getCurrentPosition();
+            break;
+        default:
+            assert(false && "Non existent stepper");
+            break;
+    }
+    return 0;
+}
+
+float Actuators::getCurrentDegPosition(StepperList axis)
+{
+    switch (axis)
+    {
+        case StepperList::HORIZONTAL:
+            return stepperX.getCurrentDegPosition();
+            break;
+        case StepperList::VERTICAL:
+            return stepperY.getCurrentDegPosition();
+            break;
+        default:
+            assert(false && "Non existent stepper");
+            break;
+    }
+    return 0;
+}
+
+void Actuators::moveDeg(StepperList axis, float degrees)
+{
+    switch (axis)
+    {
+        case StepperList::HORIZONTAL:
+            if (stepperXActive)  // Check for emergency stop
+            {
+                stepperX.moveDeg(degrees);
+                Logger::getInstance().log(stepperX.getState(degrees));
+            }
+            break;
+        case StepperList::VERTICAL:
+            if (stepperYActive)  // Check for emergency stop
+            {
+                stepperY.moveDeg(degrees);
+                Logger::getInstance().log(stepperY.getState(degrees));
+            }
+            break;
+        default:
+            assert(false && "Non existent stepper");
+            break;
+    }
+}
+
+void Actuators::setPositionDeg(StepperList axis, float positionDeg)
+{
+    float currentDegPosition;
+    switch (axis)
+    {
+        case StepperList::HORIZONTAL:
+            currentDegPosition = stepperX.getCurrentDegPosition();
+            break;
+        case StepperList::VERTICAL:
+            currentDegPosition = stepperY.getCurrentDegPosition();
+            break;
+        default:
+            assert(false && "Non existent stepper");
+            return;
+    }
+
+    moveDeg(axis, positionDeg - currentDegPosition);
+}
+
+void Actuators::emergencyStop()
+{
+    // Do not preempt during this method
+    PauseKernelLock pkLock;
+    stepperXActive = false;  // Disable actuation of horizontal stepper
+    stepperYActive = false;  // Disable actuation of vertical stepper
+    stepperX.disable();      // Disable the horizontal movement
+    stepperY.enable();       // Don't make the antenna fall
+    countedPwmX.stop();      // Terminate current stepper actuation
+    countedPwmY.stop();      // Terminate current stepper actuation
+}
+
+void Actuators::emergencyStopRecovery()
+{
+    // Do not preempt during this method
+    PauseKernelLock pkLock;
+    stepperXActive = true;  // Re-enable actuation of horizontal stepper
+    stepperYActive = true;  // Re-enable actuation of vertical stepper
+    stepperX.enable();      // Re-enable horizontal movement
+    stepperY.enable();      // Re-enable vertical movement
+}
+
+}  // namespace Antennas
\ No newline at end of file
diff --git a/src/boards/AutomatedAntennas/Actuators.h b/src/boards/AutomatedAntennas/Actuators.h
new file mode 100644
index 000000000..3dc8ce071
--- /dev/null
+++ b/src/boards/AutomatedAntennas/Actuators.h
@@ -0,0 +1,75 @@
+/* Copyright (c) 2023 Skyward Experimental Rocketry
+ * Author: Emilio Corigliano
+ *
+ * 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.
+ */
+#pragma once
+
+#include <utils/ModuleManager/ModuleManager.hpp>
+
+#include "actuators/stepper/StepperPWM.h"
+
+// TIM1_CH4 PA11 AF1
+//      |
+// TIM3_CH2 PC7  AF2
+
+// TIM4_CH1 PD12 AF2
+//      |
+// TIM8_CH4 PC9  AF3
+
+namespace Antennas
+{
+class Actuators : public Boardcore::Module
+{
+public:
+    enum StepperList
+    {
+        HORIZONTAL,  // Stepper moving horizontally (x axis)
+        VERTICAL     // Stepper moving vertically (y axis)
+    };
+
+    Actuators();
+
+    /**
+     * @brief Enables all the servos PWMs
+     */
+    void start();
+
+    void emergencyStop();
+    void emergencyStopRecovery();
+
+    void setSpeed(StepperList axis, float speed);
+
+    void move(StepperList axis, int16_t steps);
+    void moveDeg(StepperList axis, float degrees);
+    void setPosition(StepperList axis, int16_t steps);
+    void setPositionDeg(StepperList axis, float degrees);
+
+    int16_t getCurrentPosition(StepperList axis);
+    float getCurrentDegPosition(StepperList axis);
+
+private:
+    Boardcore::StepperPWM& getServo(StepperList servo);
+
+    Boardcore::StepperPWM stepperX;
+    Boardcore::StepperPWM stepperY;
+    bool stepperXActive = true;  // Whether stepper on X axis can accept steps
+    bool stepperYActive = true;  // Whether stepper on Y axis can accept steps
+};
+}  // namespace Antennas
\ No newline at end of file
diff --git a/src/boards/AutomatedAntennas/ActuatorsConfig.h b/src/boards/AutomatedAntennas/ActuatorsConfig.h
new file mode 100644
index 000000000..43d52bea5
--- /dev/null
+++ b/src/boards/AutomatedAntennas/ActuatorsConfig.h
@@ -0,0 +1,63 @@
+/* Copyright (c) 2023 Skyward Experimental Rocketry
+ * Author: Emilio Corigliano
+ *
+ * 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.
+ */
+#pragma once
+#include <drivers/timer/PWM.h>
+#include <drivers/timer/TimerUtils.h>
+
+namespace Antennas
+{
+namespace Config
+{
+namespace StepperConfig
+{
+// TIM1_CH4 PA11 AF1
+//      |
+// TIM3_CH2 PC7  AF2
+
+// TIM4_CH1 PD12 AF2
+//      |
+// TIM8_CH4 PC9  AF3
+static TIM_TypeDef* const SERVO1_PULSE_TIM = TIM1;
+static TIM_TypeDef* const SERVO1_COUNT_TIM = TIM3;
+static TIM_TypeDef* const SERVO2_PULSE_TIM = TIM4;
+static TIM_TypeDef* const SERVO2_COUNT_TIM = TIM8;
+
+constexpr Boardcore::TimerUtils::Channel SERVO1_PULSE_CH =
+    Boardcore::TimerUtils::Channel::CHANNEL_4;
+constexpr Boardcore::TimerUtils::Channel SERVO1_COUNT_CH =
+    Boardcore::TimerUtils::Channel::CHANNEL_2;
+constexpr Boardcore::TimerUtils::Channel SERVO2_PULSE_CH =
+    Boardcore::TimerUtils::Channel::CHANNEL_1;
+constexpr Boardcore::TimerUtils::Channel SERVO2_COUNT_CH =
+    Boardcore::TimerUtils::Channel::CHANNEL_4;
+
+constexpr Boardcore::TimerUtils::TriggerSource SERVO1_PULSE_ITR =
+    Boardcore::TimerUtils::TriggerSource::ITR2;
+constexpr Boardcore::TimerUtils::TriggerSource SERVO1_COUNT_ITR =
+    Boardcore::TimerUtils::TriggerSource::ITR0;
+constexpr Boardcore::TimerUtils::TriggerSource SERVO2_PULSE_ITR =
+    Boardcore::TimerUtils::TriggerSource::ITR3;
+constexpr Boardcore::TimerUtils::TriggerSource SERVO2_COUNT_ITR =
+    Boardcore::TimerUtils::TriggerSource::ITR2;
+}  // namespace StepperConfig
+}  // namespace Config
+}  // namespace Antennas
\ No newline at end of file
diff --git a/src/boards/AutomatedAntennas/Buses.h b/src/boards/AutomatedAntennas/Buses.h
new file mode 100644
index 000000000..b21a2521a
--- /dev/null
+++ b/src/boards/AutomatedAntennas/Buses.h
@@ -0,0 +1,55 @@
+/* Copyright (c) 2023 Skyward Experimental Rocketry
+ * Author: Emilio Corigliano
+ *
+ * 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.
+ */
+#pragma once
+
+#include <drivers/usart/USART.h>
+
+#include <utils/ModuleManager/ModuleManager.hpp>
+
+namespace Antennas
+{
+
+miosix::GpioPin usart2_tx = miosix::GpioPin(GPIOA_BASE, 2);
+miosix::GpioPin usart2_rx = miosix::GpioPin(GPIOA_BASE, 3);
+miosix::GpioPin uart4_tx  = miosix::GpioPin(GPIOA_BASE, 0);
+miosix::GpioPin uart4_rx  = miosix::GpioPin(GPIOA_BASE, 1);
+
+class Buses : public Boardcore::Module
+{
+public:
+    Boardcore::USART usart2;
+    Boardcore::USART uart4;
+
+    Buses() : usart2(USART2, 115200), uart4(UART4, 115200)
+    {
+        usart2_tx.mode(miosix::Mode::ALTERNATE);
+        usart2_tx.alternateFunction(7);
+        usart2_rx.mode(miosix::Mode::ALTERNATE);
+        usart2_rx.alternateFunction(7);
+
+        uart4_tx.mode(miosix::Mode::ALTERNATE);
+        uart4_tx.alternateFunction(8);
+        uart4_rx.mode(miosix::Mode::ALTERNATE);
+        uart4_rx.alternateFunction(8);
+    }
+};
+}  // namespace Antennas
\ No newline at end of file
diff --git a/src/entrypoints/Groundstation/AutomatedAntennas/test-steps.cpp b/src/entrypoints/Groundstation/AutomatedAntennas/test-steps.cpp
new file mode 100644
index 000000000..b48cec020
--- /dev/null
+++ b/src/entrypoints/Groundstation/AutomatedAntennas/test-steps.cpp
@@ -0,0 +1,120 @@
+/* Copyright (c) 2023 Skyward Experimental Rocketry
+ * Author: Emilio Corigliano
+ *
+ * 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.
+ */
+
+#include <AutomatedAntennas/Actuators.h>
+#include <AutomatedAntennas/Buses.h>
+#include <diagnostic/CpuMeter/CpuMeter.h>
+#include <diagnostic/PrintLogger.h>
+#include <diagnostic/StackLogger.h>
+#include <drivers/timer/TimestampTimer.h>
+#include <events/EventBroker.h>
+#include <miosix.h>
+#include <scheduler/TaskScheduler.h>
+
+#include <utils/ModuleManager/ModuleManager.hpp>
+
+#include "actuators/stepper/StepperPWM.h"
+
+using namespace miosix;
+using namespace Boardcore;
+using namespace Antennas;
+
+/**
+ * Test used to characterize the automated antennas.
+ */
+
+int main()
+{
+    bool initResult          = true;
+    PrintLogger logger       = Logging::getLogger("automated_antennas");
+    ModuleManager& modules   = ModuleManager::getInstance();
+    TaskScheduler* scheduler = new TaskScheduler();
+
+    // Starting singleton
+    {
+        scheduler->start();
+#ifndef NO_SD_LOGGING
+        printf("Starting Logger\n");
+        if (!Logger::getInstance().start())
+        {
+            initResult = false;
+            printf("Error initializing the Logger\n");
+        }
+        else
+        {
+            printf("Logger started successfully\n");
+        }
+#endif
+    }
+
+    // Initialize the modules
+    {
+        if (!modules.insert<Buses>(new Buses()))
+        {
+            initResult = false;
+            LOG_ERR(logger, "Error inserting the Buses module");
+        }
+
+        if (!modules.insert<Actuators>(new Actuators()))
+        {
+            initResult = false;
+            LOG_ERR(logger, "Error inserting the Actuators module");
+        }
+    }
+
+    modules.get<Actuators>()->start();
+
+    if (!initResult)
+    {
+        printf("Errors present during module insertion\n");
+        return -1;
+    }
+
+    // Periodically statistics
+    for (auto i = 0; i < 50; i++)
+    {
+        modules.get<Actuators>()->moveDeg(Actuators::StepperList::VERTICAL, 90);
+        Thread::sleep(100);
+        Logger::getInstance().log(CpuMeter::getCpuStats());
+        CpuMeter::resetCpuStats();
+        Logger::getInstance().logStats();
+        StackLogger::getInstance().log();
+    }
+
+    for (auto i = 0; i < 50; i++)
+    {
+        modules.get<Actuators>()->moveDeg(Actuators::StepperList::HORIZONTAL,
+                                          90);
+        Thread::sleep(100);
+        Logger::getInstance().log(CpuMeter::getCpuStats());
+        CpuMeter::resetCpuStats();
+        Logger::getInstance().logStats();
+        StackLogger::getInstance().log();
+    }
+
+    printf("Logging finished\n");
+    Logger::getInstance().stop();
+    printf("Logging stopped\n");
+
+    while (1)
+        ;
+}
-- 
GitLab