diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 1ada274691a4637589ee76449b7d4aed76b64196..38b527054c5069aa39290fed1d6ccfad693ef268 100755 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -218,6 +218,28 @@ "${workspaceFolder}/skyward-boardcore/src/bsps/stm32f429zi_con_rig" ] }, + { + "name": "stm32f767zi_conrig_v2", + "cStandard": "c11", + "cppStandard": "c++14", + "compilerPath": "/opt/arm-miosix-eabi/bin/arm-miosix-eabi-g++", + "defines": [ + "${defaultDefines}", + "_MIOSIX_BOARDNAME=stm32f767zi_conrig_v2", + "_BOARD_STM32F767ZI_CONRIG_V2", + "_ARCH_CORTEXM7_STM32F7", + "HSE_VALUE=25000000", + "SYSCLK_FREQ_216MHz=216000000", + "__ENABLE_XRAM", + "V_DDA_VOLTAGE=3.3f" + ], + "includePath": [ + "${defaultIncludePaths}", + "${workspaceFolder}/skyward-boardcore/libs/miosix-kernel/miosix/arch/cortexM7_stm32f7/common", + "${workspaceFolder}/skyward-boardcore/src/bsps/stm32f767zi_conrig_v2/config", + "${workspaceFolder}/skyward-boardcore/src/bsps/stm32f767zi_conrig_v2" + ] + }, { "name": "stm32f429zi_stm32f4discovery", "cStandard": "c11", diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e28274d6c43e8bb72a37d6435f9592bb23fd2f3..d44bbc9def4d34462530edbaf8486b6c115a3ff3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,10 @@ add_executable(con_rig-entry src/ConRIG/con_rig-entry.cpp ${CON_RIG_COMPUTER}) target_include_directories(con_rig-entry PRIVATE ${OBSW_INCLUDE_DIRS}) sbs_target(con_rig-entry stm32f429zi_con_rig) +add_executable(conrig-v2-entry src/ConRIGv2/conrig-v2-entry.cpp ${CONRIG_V2_COMPUTER}) +target_include_directories(conrig-v2-entry PRIVATE ${OBSW_INCLUDE_DIRS}) +sbs_target(conrig-v2-entry stm32f767zi_conrig_v2) + add_executable(rovie-groundstation-entry src/Groundstation/Rovie/rovie-groundstation-entry.cpp ${GROUNDSTATION_COMMON} ${GROUNDSTATION_ROVIE} diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index 27364e888ef6b87ce7fafb589120f6ef41ea96e8..31286e428d19780d9c6f8c07aaed992cff7e5c6e 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -73,6 +73,12 @@ set(CON_RIG_COMPUTER src/ConRIG/Serial/Serial.cpp ) +set(CONRIG_V2_COMPUTER + src/ConRIGv2/Buttons/Buttons.cpp + src/ConRIGv2/Radio/Radio.cpp + src/ConRIGv2/Hub/Hub.cpp +) + set(PAYLOAD_COMPUTER src/Payload/Actuators/Actuators.cpp src/Payload/CanHandler/CanHandler.cpp diff --git a/src/ConRIG/BoardScheduler.h b/src/ConRIG/BoardScheduler.h index 3736c89c1ef83790d652f590b52f9d31f407fadb..741441282d47314da6a6880e50b7d6f2b96b8ee6 100644 --- a/src/ConRIG/BoardScheduler.h +++ b/src/ConRIG/BoardScheduler.h @@ -1,5 +1,5 @@ -/* Copyright (c) 2022 Skyward Experimental Rocketry - * Author: Alberto Nidasio +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Ettore Pane * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/ConRIGv2/BoardScheduler.h b/src/ConRIGv2/BoardScheduler.h new file mode 100644 index 0000000000000000000000000000000000000000..9ce95f48a3f0d6e3269ad8cef7af12581b7d50f2 --- /dev/null +++ b/src/ConRIGv2/BoardScheduler.h @@ -0,0 +1,70 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Ettore Pane + * + * 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 <scheduler/TaskScheduler.h> +#include <utils/DependencyManager/DependencyManager.h> + +namespace ConRIGv2 +{ + +/** + * @brief Class that wraps the main task schedulers of the entire OBSW. + */ +class BoardScheduler : public Boardcore::Injectable +{ +public: + BoardScheduler() + : radio{miosix::PRIORITY_MAX - 1}, buttons{miosix::PRIORITY_MAX - 2} + { + } + + [[nodiscard]] bool start() + { + if (!radio.start()) + { + LOG_ERR(logger, "Failed to start radio scheduler"); + return false; + } + + if (!buttons.start()) + { + LOG_ERR(logger, "Failed to start buttons scheduler"); + return false; + } + + return true; + } + + Boardcore::TaskScheduler& getRadioScheduler() { return radio; } + + Boardcore::TaskScheduler& getButtonsScheduler() { return buttons; } + +private: + Boardcore::PrintLogger logger = + Boardcore::Logging::getLogger("boardscheduler"); + + Boardcore::TaskScheduler radio; + Boardcore::TaskScheduler buttons; +}; +} // namespace ConRIGv2 diff --git a/src/ConRIGv2/Buses.h b/src/ConRIGv2/Buses.h new file mode 100644 index 0000000000000000000000000000000000000000..e5502a21d86ba8b912e361facfa50edeca334bbf --- /dev/null +++ b/src/ConRIGv2/Buses.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Ettore Pane + * + * 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/spi/SPIBus.h> +#include <drivers/usart/USART.h> +#include <miosix.h> +#include <utils/DependencyManager/DependencyManager.h> + +namespace ConRIGv2 +{ + +struct Buses : public Boardcore::Injectable +{ +private: + Boardcore::SPIBus spi1{SPI1}; + Boardcore::SPIBus spi6{SPI6}; + + Boardcore::USART usart2{USART2, 115200}; + Boardcore::USART uart4{UART4, 115200}; + +public: + Boardcore::SPIBus& getRadio() { return spi6; } + + Boardcore::USART& getUsart2() { return usart2; } + Boardcore::USART& getUsart4() { return uart4; } + + Boardcore::SPIBus& getEthernet() { return spi1; } +}; + +} // namespace ConRIGv2 diff --git a/src/ConRIGv2/Buttons/Buttons.cpp b/src/ConRIGv2/Buttons/Buttons.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e7056ab79167ecda0e60e0254917e566461ca41e --- /dev/null +++ b/src/ConRIGv2/Buttons/Buttons.cpp @@ -0,0 +1,245 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Ettore Pane + * + * 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 "Buttons.h" + +#include <ConRIGv2/BoardScheduler.h> +#include <ConRIGv2/Configs/ButtonsConfig.h> +#include <ConRIGv2/Radio/Radio.h> +#include <interfaces-impl/hwmapping.h> + +using namespace std; +using namespace miosix; +using namespace Boardcore; +using namespace ConRIGv2; + +Buttons::Buttons() +{ + resetState(); + state.arm_switch = false; +} + +bool Buttons::start() +{ + TaskScheduler& scheduler = getModule<BoardScheduler>()->getRadioScheduler(); + + return scheduler.addTask([this]() { periodicStatusCheck(); }, + Config::Buttons::BUTTON_SAMPLE_PERIOD) != 0; +} + +mavlink_conrig_state_tc_t Buttons::getState() { return state; } + +void Buttons::resetState() +{ + // Preserve the arm switch state + auto armSwitch = state.arm_switch; + state = {}; + state.arm_switch = armSwitch; +} + +void Buttons::periodicStatusCheck() +{ + state.arm_switch = btns::arm::value(); + + if (!btns::ignition::value() && state.arm_switch) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.ignition_btn = true; + LOG_DEBUG(logger, "Ignition button pressed"); + } + else + { + guard++; + } + } + else if (btns::ox_filling::value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.ox_filling_btn = true; + LOG_DEBUG(logger, "Ox filling button pressed"); + } + else + { + guard++; + } + } + else if (btns::ox_release::value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.ox_release_btn = true; + LOG_DEBUG(logger, "Ox release button pressed"); + } + else + { + guard++; + } + } + else if (btns::ox_detach::value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.ox_detach_btn = true; + LOG_DEBUG(logger, "Ox detach button pressed"); + } + else + { + guard++; + } + } + else if (btns::n2_3way::value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.ox_detach_btn = true; + LOG_DEBUG(logger, "n2 3way button pressed"); + } + else + { + guard++; + } + } + else if (btns::n2_filling::value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.n2_filling_btn = true; + LOG_DEBUG(logger, "N2 filling button pressed"); + } + else + { + guard++; + } + } + else if (btns::n2_release::value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.n2_release_btn = true; + LOG_DEBUG(logger, "N2 release button pressed"); + } + else + { + guard++; + } + } + else if (btns::n2_detach::value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.n2_detach_btn = true; + LOG_DEBUG(logger, "N2 detach button pressed"); + } + else + { + guard++; + } + } + else if (btns::nitrogen::value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.nitrogen_btn = true; + LOG_DEBUG(logger, "Nitrogen button pressed"); + } + else + { + guard++; + } + } + else if (btns::ox_venting::value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.ox_venting_btn = true; + LOG_DEBUG(logger, "Ox venting button pressed"); + } + else + { + guard++; + } + } + else if (btns::n2_quenching::value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.n2_quenching_btn = true; + LOG_DEBUG(logger, "N2 quenching button pressed"); + } + else + { + guard++; + } + } + else if (btns::tars3::value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.tars3_btn = true; + LOG_DEBUG(logger, "Tars3 button pressed"); + } + else + { + guard++; + } + } + else if (btns::tars3m::value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.tars3m_btn = true; + LOG_DEBUG(logger, "Tars3m button pressed"); + } + else + { + guard++; + } + } + else + { + // Reset all the states and guard + guard = 0; + resetState(); + } + + // Set the internal button state in Radio module + getModule<Radio>()->setButtonsState(state); +} + +void Buttons::enableIgnition() { ui::armedLed::high(); } + +void Buttons::disableIgnition() { ui::armedLed::low(); } diff --git a/src/ConRIGv2/Buttons/Buttons.h b/src/ConRIGv2/Buttons/Buttons.h new file mode 100644 index 0000000000000000000000000000000000000000..5d2c26d0e205475718628903b21934478dfb8c6e --- /dev/null +++ b/src/ConRIGv2/Buttons/Buttons.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Ettore Pane + * + * 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 <ConRIGv2/BoardScheduler.h> +#include <common/MavlinkOrion.h> +#include <diagnostic/PrintLogger.h> +#include <scheduler/TaskScheduler.h> +#include <utils/DependencyManager/DependencyManager.h> + +namespace ConRIGv2 +{ + +class Radio; + +class Buttons : public Boardcore::InjectableWithDeps<BoardScheduler, Radio> +{ +public: + Buttons(); + + [[nodiscard]] bool start(); + + mavlink_conrig_state_tc_t getState(); + + void enableIgnition(); + void disableIgnition(); + +private: + void resetState(); + + void periodicStatusCheck(); + + mavlink_conrig_state_tc_t state; + + // Counter guard to avoid spurious triggers + uint8_t guard = 0; + + Boardcore::PrintLogger logger = Boardcore::Logging::getLogger("buttons"); +}; + +} // namespace ConRIGv2 diff --git a/src/ConRIGv2/Configs/ButtonsConfig.h b/src/ConRIGv2/Configs/ButtonsConfig.h new file mode 100644 index 0000000000000000000000000000000000000000..8ede7d4000d939e8f5805b6f48b6af9842d6d2c3 --- /dev/null +++ b/src/ConRIGv2/Configs/ButtonsConfig.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Ettore Pane + * + * 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 <units/Frequency.h> + +namespace ConRIGv2 +{ +namespace Config +{ +namespace Buttons +{ + +/* linter off */ using namespace Boardcore::Units::Frequency; + +constexpr Hertz BUTTON_SAMPLE_PERIOD = 50_hz; + +constexpr uint8_t GUARD_THRESHOLD = + 5; // 5 samples to trigger the guard and activate a single button + +} // namespace Buttons +} // namespace Config + +} // namespace ConRIGv2 diff --git a/src/ConRIGv2/Configs/RadioConfig.h b/src/ConRIGv2/Configs/RadioConfig.h new file mode 100644 index 0000000000000000000000000000000000000000..8f2695e6784a4d18966dbb0ad481efeb2eebe4bb --- /dev/null +++ b/src/ConRIGv2/Configs/RadioConfig.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Ettore Pane + * + * 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 <common/MavlinkOrion.h> +#include <units/Frequency.h> + +namespace ConRIGv2 +{ + +namespace Config +{ +namespace Radio +{ + +/* linter off */ using namespace Boardcore::Units::Frequency; + +constexpr unsigned int MAV_OUT_QUEUE_SIZE = 20; +constexpr unsigned int MAV_MAX_LENGTH = MAVLINK_MAX_DIALECT_PAYLOAD_SIZE; + +constexpr unsigned int CIRCULAR_BUFFER_SIZE = 10; + +constexpr uint16_t MAV_SLEEP_AFTER_SEND = 0; +constexpr size_t MAV_OUT_BUFFER_MAX_AGE = 10; + +// Mavlink ids +constexpr uint8_t MAV_SYSTEM_ID = 171; +constexpr uint8_t MAV_COMPONENT_ID = 96; + +// Periodic telemetries frequency +constexpr Hertz PING_GSE_PERIOD = 2_hz; + +} // namespace Radio +} // namespace Config +} // namespace ConRIGv2 diff --git a/src/ConRIGv2/Hub/Hub.cpp b/src/ConRIGv2/Hub/Hub.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1b12bec437c5b8d7a844f1d1bc9512633b10c226 --- /dev/null +++ b/src/ConRIGv2/Hub/Hub.cpp @@ -0,0 +1,171 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Ettore Pane + * + * 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 "Hub.h" + +#include <ConRIGv2/Buses.h> +#include <ConRIGv2/Radio/Radio.h> +#include <Groundstation/Common/Config/EthernetConfig.h> +#include <interfaces-impl/hwmapping.h> + +using namespace Boardcore; +using namespace miosix; + +static std::weak_ptr<Wiz5500> gEthernet; + +void __attribute__((used)) MIOSIX_ETHERNET_IRQ() +{ + if (auto ethernet = gEthernet.lock()) + ethernet->handleINTn(); +} + +namespace ConRIGv2 +{ +WizIp generateRandomIp() +{ + WizIp ip = Groundstation::IP_BASE; + ip.d = (rand() % 253) + 1; // Generate in range 1-254 + + return ip; +} + +WizMac generateRandomMac() +{ + WizMac mac = Groundstation::MAC_BASE; + mac.e = (rand() % 253) + 1; // Generate in range 1-254 + mac.f = (rand() % 253) + 1; // Generate in range 1-254 + + return mac; +} + +Boardcore::Wiz5500::PhyState Hub::getEthernetState() +{ + return wiz5500->getPhyState(); +} + +bool Hub::start() +{ + auto* buses = getModule<Buses>(); + + serial2 = std::make_unique<SerialTransceiver>(buses->getUsart2()); + serial4 = std::make_unique<SerialTransceiver>(buses->getUsart4()); + + mavDriver2 = std::make_unique<SerialMavDriver>( + serial2.get(), [this](auto channel, const mavlink_message_t& msg) + { dispatchToRIG(msg); }, 0, 10); + + mavDriver4 = std::make_unique<SerialMavDriver>( + serial4.get(), [this](auto channel, const mavlink_message_t& msg) + { dispatchToRIG(msg); }, 0, 10); + + if (!mavDriver2->start()) + { + LOG_ERR(logger, "Error starting the MAVLink driver for USART2"); + return false; + } + + if (!mavDriver4->start()) + { + LOG_ERR(logger, "Error starting the MAVLink driver for USART4"); + return false; + } + + if (!initEthernet()) + { + LOG_ERR(logger, "Error starting the Ethernet driver"); + return false; + } + + mavDriverEth = std::make_unique<EthernetMavDriver>( + ethernet.get(), + [this](EthernetMavDriver* channel, const mavlink_message_t& msg) + { dispatchToRIG(msg); }, 0, 10); + + if (!mavDriverEth->start()) + return false; + + return true; +} + +bool Hub::initEthernet() +{ + auto* buses = getModule<Buses>(); + + // Initialize the Wiz5500 + wiz5500 = std::make_unique<Wiz5500>( + buses->getEthernet(), ethernet::cs::getPin(), ethernet::intr::getPin(), + SPI::ClockDivider::DIV_64); + + // Store the global ethernet instance for the interrupt handler + gEthernet = wiz5500; + + // Check SPI communication + if (!wiz5500->checkVersion()) + { + LOG_ERR(logger, "Error checking the Wiz5500 version"); + return false; + } + + // Reset the device + wiz5500->reset(); + + // Setup ip and other stuff + wiz5500->setSubnetMask(Groundstation::SUBNET); + wiz5500->setGatewayIp(Groundstation::GATEWAY); + wiz5500->setSourceIp(generateRandomIp()); + wiz5500->setSourceMac(generateRandomMac()); + + wiz5500->setOnIpConflict([this]() + { wiz5500->setSourceIp(generateRandomIp()); }); + + // Ok now open the UDP socket + if (!wiz5500->openUdp(0, Groundstation::RECV_PORT, {255, 255, 255, 255}, + Groundstation::SEND_PORT, 500)) + { + LOG_ERR(logger, "Error opening the UDP socket"); + return false; + } + + // Initialize Ethernet Transceiver + ethernet = std::make_unique<UdpTransceiver>(this->wiz5500, 0); + + return true; +} + +void Hub::dispatchToPorts(const mavlink_message_t& msg) +{ + if (mavDriver2 && mavDriver2->isStarted()) + mavDriver2->enqueueMsg(msg); + + if (mavDriver4 && mavDriver4->isStarted()) + mavDriver4->enqueueMsg(msg); + + if (mavDriverEth && mavDriverEth->isStarted()) + mavDriverEth->enqueueMsg(msg); +} + +void Hub::dispatchToRIG(const mavlink_message_t& msg) +{ + if (msg.sysid == MAV_SYSID_RIG) + getModule<Radio>()->enqueueMessage(msg); +} +} // namespace ConRIGv2 diff --git a/src/ConRIGv2/Hub/Hub.h b/src/ConRIGv2/Hub/Hub.h new file mode 100644 index 0000000000000000000000000000000000000000..ada16a0a7960b7c27dc79d25f2aeb183a4598549 --- /dev/null +++ b/src/ConRIGv2/Hub/Hub.h @@ -0,0 +1,81 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Ettore Pane + * + * 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 <common/MavlinkOrion.h> +#include <drivers/WIZ5500/WIZ5500.h> +#include <radio/MavlinkDriver/MavlinkDriver.h> +#include <radio/SerialTransceiver/SerialTransceiver.h> +#include <radio/UdpTransceiver/UdpTransceiver.h> +#include <utils/DependencyManager/DependencyManager.h> + +#include <memory> + +namespace ConRIGv2 +{ +class Buses; +class Radio; + +using SerialMavDriver = + Boardcore::MavlinkDriver<1024, 10, MAVLINK_MAX_DIALECT_PAYLOAD_SIZE>; +using EthernetMavDriver = + Boardcore::MavlinkDriver<1024, 10, MAVLINK_MAX_DIALECT_PAYLOAD_SIZE>; + +/** + * @brief Central hub connecting all outgoing and incoming modules. + */ +class Hub : public Boardcore::InjectableWithDeps<Buses, Radio> +{ +public: + bool start(); + + /** + * @brief Dispatch messages to external ports (serial, ethernet). + */ + void dispatchToPorts(const mavlink_message_t& msg); + + /** + * @brief Dispatch messages to the RIG. + */ + void dispatchToRIG(const mavlink_message_t& msg); + + Boardcore::Wiz5500::PhyState getEthernetState(); + +private: + bool initEthernet(); + + std::unique_ptr<Boardcore::SerialTransceiver> serial2; + std::unique_ptr<SerialMavDriver> mavDriver2; + + std::unique_ptr<Boardcore::SerialTransceiver> serial4; + std::unique_ptr<SerialMavDriver> mavDriver4; + + std::shared_ptr<Boardcore::Wiz5500> wiz5500; + std::unique_ptr<Boardcore::UdpTransceiver> ethernet; + std::unique_ptr<EthernetMavDriver> mavDriverEth; + + bool started = false; + + Boardcore::PrintLogger logger = Boardcore::Logging::getLogger("Hub"); +}; +} // namespace ConRIGv2 diff --git a/src/ConRIGv2/Radio/Radio.cpp b/src/ConRIGv2/Radio/Radio.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8e7ebb8436e7f309b9cc74e4bb1e0665e72c250a --- /dev/null +++ b/src/ConRIGv2/Radio/Radio.cpp @@ -0,0 +1,245 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Ettore Pane + * + * 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 "Radio.h" + +#include <common/MavlinkOrion.h> +#include <common/Radio.h> +#include <diagnostic/SkywardStack.h> +#include <drivers/interrupt/external_interrupts.h> +#include <events/EventBroker.h> +#include <interfaces-impl/hwmapping.h> +#include <radio/SX1278/SX1278Frontends.h> + +#include <thread> + +using namespace miosix; +using namespace Boardcore; +using namespace ConRIGv2; +using namespace Common; + +SX1278Lora* gRadio{nullptr}; + +void handleDioIRQ() +{ + SX1278Lora* instance = gRadio; + if (instance) + instance->handleDioIRQ(); +} + +void setIRQRadio(SX1278Lora* radio) +{ + FastInterruptDisableLock dl; + gRadio = radio; +} + +void __attribute__((used)) MIOSIX_RADIO_DIO0_IRQ() { handleDioIRQ(); } +void __attribute__((used)) MIOSIX_RADIO_DIO1_IRQ() { handleDioIRQ(); } +void __attribute__((used)) MIOSIX_RADIO_DIO3_IRQ() { handleDioIRQ(); } + +void Radio::handleMessage(const mavlink_message_t& msg) +{ + switch (msg.msgid) + { + case MAVLINK_MSG_ID_GSE_TM: + { + int armingState = mavlink_msg_gse_tm_get_arming_state(&msg); + messagesReceived += 1; + + isArmed = armingState == 1; + if (armingState == 1) + getModule<Buttons>()->enableIgnition(); + else + getModule<Buttons>()->disableIgnition(); + + break; + } + + case MAVLINK_MSG_ID_ACK_TM: + { + int id = mavlink_msg_ack_tm_get_recv_msgid(&msg); + // we assume this ack is about the last sent message + if (id == MAVLINK_MSG_ID_CONRIG_STATE_TC) + { + Lock<FastMutex> lock{buttonsMutex}; + // Reset the internal button state + buttonState = {}; + } + + break; + } + } + + getModule<Hub>()->dispatchToPorts(msg); +} + +bool Radio::enqueueMessage(const mavlink_message_t& msg) +{ + Lock<FastMutex> lock{queueMutex}; + if (messageQueue.isFull()) + { + return false; + } + else + { + messageQueue.put(msg); + return true; + } +} + +void Radio::sendPeriodicPing() +{ + mavlink_message_t msg; + + { + Lock<FastMutex> lock{buttonsMutex}; + mavlink_msg_conrig_state_tc_encode(Config::Radio::MAV_SYSTEM_ID, + Config::Radio::MAV_COMPONENT_ID, + &msg, &buttonState); + } + + // Flush the queue + { + Lock<FastMutex> lock{queueMutex}; + // TODO(davide.mor): Maybe implement a maximum per ping? + for (size_t i = 0; i < messageQueue.count(); i++) + { + try + { + mavDriver->enqueueMsg(messageQueue.pop()); + } + catch (...) + { + // This shouldn't happen, but still try to prevent it + } + } + } + + // Finally make sure we always send the periodic ping + mavDriver->enqueueMsg(msg); +} + +void Radio::buzzerOn() +{ + buzzer.enableChannel(TimerUtils::Channel::MIOSIX_BUZZER_CHANNEL); +} + +void Radio::buzzerOff() +{ + buzzer.disableChannel(TimerUtils::Channel::MIOSIX_BUZZER_CHANNEL); +} + +void Radio::buzzerTask() +{ + constexpr int beepPeriod = 5; // seconds + + if ((!isArmed && messagesReceived > beepPeriod * 2) || + (isArmed && messagesReceived > 0)) + { + messagesReceived = 0; + buzzerOn(); + } + else + { + buzzerOff(); + } +} + +void Radio::setButtonsState(const mavlink_conrig_state_tc_t& state) +{ + Lock<FastMutex> lock{buttonsMutex}; + // The OR operator is introduced to make sure that the receiver + // understood the command + buttonState.ox_filling_btn |= state.ox_filling_btn; + buttonState.ox_release_btn |= state.ox_release_btn; + buttonState.n2_filling_btn |= state.n2_filling_btn; + buttonState.n2_release_btn |= state.n2_release_btn; + buttonState.n2_detach_btn |= state.n2_detach_btn; + buttonState.ox_venting_btn |= state.ox_venting_btn; + buttonState.nitrogen_btn |= state.nitrogen_btn; + buttonState.ox_detach_btn |= state.ox_detach_btn; + buttonState.n2_quenching_btn |= state.n2_quenching_btn; + buttonState.n2_3way_btn |= state.n2_3way_btn; + buttonState.tars3_btn |= state.tars3_btn; + buttonState.tars3m_btn |= state.tars3m_btn; + buttonState.ignition_btn |= state.ignition_btn; + buttonState.arm_switch |= state.arm_switch; +} + +bool Radio::start() +{ + // Setup the frontend + std::unique_ptr<SX1278::ISX1278Frontend> frontend = + std::make_unique<EbyteFrontend>(radio::txEn::getPin(), + radio::rxEn::getPin()); + + // Setup transceiver + radio = std::make_unique<SX1278Lora>( + getModule<Buses>()->getRadio(), radio::cs::getPin(), + radio::dio0::getPin(), radio::dio1::getPin(), radio::dio3::getPin(), + SPI::ClockDivider::DIV_64, std::move(frontend)); + + // Store the global radio instance + setIRQRadio(radio.get()); + + // Initialize radio + auto result = radio->init(RIG_RADIO_CONFIG); + if (result != SX1278Lora::Error::NONE) + { + LOG_ERR(logger, "Failed to initialize RIG radio"); + return false; + } + + // Initialize mavdriver + mavDriver = std::make_unique<MavDriver>( + radio.get(), [this](MavDriver*, const mavlink_message_t& msg) + { handleMessage(msg); }, Config::Radio::MAV_SLEEP_AFTER_SEND, + Config::Radio::MAV_OUT_BUFFER_MAX_AGE); + + if (!mavDriver->start()) + { + LOG_ERR(logger, "Failed to initialize ConRIGv2 mav driver"); + return false; + } + + TaskScheduler& scheduler = getModule<BoardScheduler>()->getRadioScheduler(); + + if (scheduler.addTask([this]() { sendPeriodicPing(); }, + Config::Radio::PING_GSE_PERIOD, + TaskScheduler::Policy::RECOVER) == 0) + { + LOG_ERR(logger, "Failed to add ping task"); + return false; + } + + scheduler.addTask([this]() { buzzerTask(); }, 50, + TaskScheduler::Policy::RECOVER); + + return true; +} + +MavlinkStatus Radio::getMavlinkStatus() { return mavDriver->getStatus(); } + +Radio::Radio() : buzzer(MIOSIX_BUZZER_TIM, 523), buttonState() +{ + buzzer.setDutyCycle(TimerUtils::Channel::MIOSIX_BUZZER_CHANNEL, 0.5); +} diff --git a/src/ConRIGv2/Radio/Radio.h b/src/ConRIGv2/Radio/Radio.h new file mode 100644 index 0000000000000000000000000000000000000000..399eb42fba46ada9bbf87528b16a6d8b04091493 --- /dev/null +++ b/src/ConRIGv2/Radio/Radio.h @@ -0,0 +1,95 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Ettore Pane + * + * 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 <ConRIGv2/BoardScheduler.h> +#include <ConRIGv2/Buses.h> +#include <ConRIGv2/Buttons/Buttons.h> +#include <ConRIGv2/Configs/RadioConfig.h> +#include <ConRIGv2/Hub/Hub.h> +#include <common/MavlinkOrion.h> +#include <diagnostic/PrintLogger.h> +#include <drivers/timer/PWM.h> +#include <radio/MavlinkDriver/MavlinkDriver.h> +#include <radio/SX1278/SX1278Lora.h> +#include <scheduler/TaskScheduler.h> +#include <utils/DependencyManager/DependencyManager.h> +#include <utils/collections/CircularBuffer.h> + +#include <cstdint> +#include <thread> + +namespace ConRIGv2 +{ + +using MavDriver = Boardcore::MavlinkDriver<Boardcore::SX1278Lora::MTU, + Config::Radio::MAV_OUT_QUEUE_SIZE, + Config::Radio::MAV_MAX_LENGTH>; + +class Radio + : public Boardcore::InjectableWithDeps<Buses, BoardScheduler, Buttons, Hub> +{ +public: + Radio(); + + [[nodiscard]] bool start(); + + Boardcore::MavlinkStatus getMavlinkStatus(); + + void setButtonsState(const mavlink_conrig_state_tc_t& state); + + bool enqueueMessage(const mavlink_message_t& msg); + +private: + void sendPeriodicPing(); + void buzzerTask(); + void handleMessage(const mavlink_message_t& msg); + + std::unique_ptr<Boardcore::SX1278Lora> radio; + std::unique_ptr<MavDriver> mavDriver; + + void buzzerOn(); + void buzzerOff(); + + Boardcore::PWM buzzer; + + std::atomic<uint32_t> buzzerCounter{0}; + std::atomic<uint32_t> buzzerOverflow{100}; + + Boardcore::CircularBuffer<mavlink_message_t, + Config::Radio::CIRCULAR_BUFFER_SIZE> + messageQueue; + + miosix::FastMutex queueMutex; + miosix::FastMutex buttonsMutex; + + // Button internal state + mavlink_conrig_state_tc_t buttonState; + + std::atomic<uint8_t> messagesReceived{0}; + std::atomic<bool> isArmed{false}; + + Boardcore::PrintLogger logger = Boardcore::Logging::getLogger("radio"); +}; + +} // namespace ConRIGv2 diff --git a/src/ConRIGv2/conrig-v2-entry.cpp b/src/ConRIGv2/conrig-v2-entry.cpp new file mode 100644 index 0000000000000000000000000000000000000000..de3105b2eb48d0eb0de1f5153543be3b8c81bbca --- /dev/null +++ b/src/ConRIGv2/conrig-v2-entry.cpp @@ -0,0 +1,109 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Ettore Pane + * + * 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 <ConRIGv2/BoardScheduler.h> +#include <ConRIGv2/Buses.h> +#include <ConRIGv2/Buttons/Buttons.h> +#include <ConRIGv2/Configs/ButtonsConfig.h> +#include <ConRIGv2/Hub/Hub.h> +#include <ConRIGv2/Radio/Radio.h> +#include <diagnostic/CpuMeter/CpuMeter.h> +#include <diagnostic/PrintLogger.h> +#include <events/EventBroker.h> +#include <interfaces-impl/hwmapping.h> +#include <miosix.h> +#include <utils/DependencyManager/DependencyManager.h> + +#include <iostream> +#include <thread> + +using namespace miosix; +using namespace Boardcore; +using namespace ConRIGv2; + +int main() +{ + PrintLogger logger = Logging::getLogger("main"); + DependencyManager manager; + + Buses* buses = new Buses(); + BoardScheduler* scheduler = new BoardScheduler(); + Radio* radio = new Radio(); + Buttons* buttons = new Buttons(); + Hub* hub = new Hub(); + + bool initResult = manager.insert<BoardScheduler>(scheduler) && + manager.insert<Buses>(buses) && + manager.insert<Radio>(radio) && + manager.insert<Hub>(hub) && + manager.insert<Buttons>(buttons) && manager.inject(); + + manager.graphviz(std::cout); + + if (!initResult) + { + LOG_ERR(logger, "Failed to inject dependencies"); + return 0; + } + + if (!radio->start()) + { + initResult = false; + LOG_ERR(logger, "Error starting the radio"); + } + + if (!hub->start()) + { + initResult = false; + LOG_ERR(logger, "Error starting the mavlink dispatcher hub"); + } + + if (!buttons->start()) + { + initResult = false; + LOG_ERR(logger, "Error starting the buttons"); + } + + if (!scheduler->start()) + { + initResult = false; + LOG_ERR(logger, "Error starting the General Purpose Scheduler"); + } + + if (!initResult) + { + LOG_ERR(logger, "Init failure!"); + led2On(); // Led RED + } + else + { + LOG_INFO(logger, "All good!"); + led4On(); // Led GREEN + } + + // Periodical statistics + while (true) + { + Thread::sleep(1000); + CpuMeter::resetCpuStats(); + } +}