diff --git a/CMakeLists.txt b/CMakeLists.txt index e50a959a0d54b57a185d1331bb1f1b49add26e68..239e46323be079d1091eab9766450382a3565995 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,10 @@ add_executable(rig-entry src/entrypoints/RIG/rig-entry.cpp ${RIG_COMPUTER}) target_include_directories(rig-entry PRIVATE ${OBSW_INCLUDE_DIRS}) sbs_target(rig-entry stm32f429zi_skyward_rig) +add_executable(con_rig-entry src/entrypoints/con_RIG/con_rig-entry.cpp ${CON_RIG_COMPUTER}) +target_include_directories(con_rig-entry PRIVATE ${OBSW_INCLUDE_DIRS}) +sbs_target(con_rig-entry stm32f429zi_stm32f4discovery) + #-----------------------------------------------------------------------------# # Test entrypoints # #-----------------------------------------------------------------------------# - diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index 65ae83089d526ae7967dc9be87e3a8175b094772..da4dc6d1f4720e9ad6e5cbb1ccb6dd009373b949 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -67,3 +67,9 @@ set(RIG_COMPUTER src/boards/RIG/CanHandler/CanHandler.cpp src/boards/RIG/StatesMonitor/StatesMonitor.cpp ) + +set(CON_RIG_COMPUTER + src/boards/con_RIG/Buttons/Buttons.cpp + src/boards/con_RIG/Radio/Radio.cpp + src/boards/con_RIG/BoardScheduler.cpp +) diff --git a/src/boards/con_RIG/BoardScheduler.cpp b/src/boards/con_RIG/BoardScheduler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a9371f3e4239f586392b3cbc45219f8cbe623320 --- /dev/null +++ b/src/boards/con_RIG/BoardScheduler.cpp @@ -0,0 +1,66 @@ +/* Copyright (c) 2023 Skyward Experimental Rocketry + * Authors: Matteo Pignataro + * + * 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 "BoardScheduler.h" + +using namespace Boardcore; + +namespace con_RIG +{ +// TODO: UPDATE THE SCHEDULER PRIORITY PARAMETER ONCE MERGED NEW TASK SCHEDULER +BoardScheduler::BoardScheduler() +{ + scheduler1 = new TaskScheduler(miosix::PRIORITY_MAX - 4); + scheduler2 = new TaskScheduler(miosix::PRIORITY_MAX - 3); + scheduler3 = new TaskScheduler(miosix::PRIORITY_MAX - 2); + scheduler4 = new TaskScheduler(miosix::PRIORITY_MAX - 1); +} + +TaskScheduler* BoardScheduler::getScheduler(miosix::Priority priority) +{ + switch (priority.get()) + { + case miosix::PRIORITY_MAX: + return scheduler4; + case miosix::PRIORITY_MAX - 1: + return scheduler3; + case miosix::PRIORITY_MAX - 2: + return scheduler2; + case miosix::MAIN_PRIORITY: + return scheduler1; + default: + return scheduler1; + } +} + +bool BoardScheduler::start() +{ + return scheduler1->start() && scheduler2->start() && scheduler3->start() && + scheduler4->start(); +} + +bool BoardScheduler::isStarted() +{ + return scheduler1->isRunning() && scheduler2->isRunning() && + scheduler3->isRunning() && scheduler4->isRunning(); +} +} // namespace con_RIG \ No newline at end of file diff --git a/src/boards/con_RIG/BoardScheduler.h b/src/boards/con_RIG/BoardScheduler.h new file mode 100644 index 0000000000000000000000000000000000000000..9fe83776e8aee7421c1b264b1299833fcc6e85fe --- /dev/null +++ b/src/boards/con_RIG/BoardScheduler.h @@ -0,0 +1,64 @@ +/* Copyright (c) 2022 Skyward Experimental Rocketry + * Author: Alberto Nidasio + * + * 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/ModuleManager/ModuleManager.hpp> + +namespace con_RIG +{ + +/** + * @brief Class that wraps the 4 main task schedulers of the entire OBSW. + * There is a task scheduler for every miosix priority + */ +class BoardScheduler : public Boardcore::Module +{ +public: + BoardScheduler(); + + /** + * @brief Get the Scheduler object relative to the requested priority + * + * @param priority The task scheduler priority + * @return Boardcore::TaskScheduler& Reference to the requested task + * scheduler. + * @note Min priority scheduler is returned in case of non valid priority. + */ + Boardcore::TaskScheduler* getScheduler(miosix::Priority priority); + + [[nodiscard]] bool start(); + + /** + * @brief Returns if all the schedulers are up and running + */ + bool isStarted(); + +private: + Boardcore::TaskScheduler* scheduler1; + Boardcore::TaskScheduler* scheduler2; + Boardcore::TaskScheduler* scheduler3; + Boardcore::TaskScheduler* scheduler4; +}; +} // namespace con_RIG diff --git a/src/boards/con_RIG/Buses.h b/src/boards/con_RIG/Buses.h new file mode 100644 index 0000000000000000000000000000000000000000..7fb368dade3952e30df1c26807f0d43db6e648ca --- /dev/null +++ b/src/boards/con_RIG/Buses.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2022 Skyward Experimental Rocketry + * Author: Alberto Nidasio + * + * 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/ModuleManager/ModuleManager.hpp> + +namespace con_RIG +{ + +struct Buses : public Boardcore::Module +{ + Boardcore::SPIBus spi1; + Boardcore::SPIBus spi2; + +public: + Buses() : spi1(SPI1), spi2(SPI2) {} +}; + +} // namespace con_RIG diff --git a/src/boards/con_RIG/Buttons/Buttons.cpp b/src/boards/con_RIG/Buttons/Buttons.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5a604346d62281d9ae92554c123d1359200584ea --- /dev/null +++ b/src/boards/con_RIG/Buttons/Buttons.cpp @@ -0,0 +1,184 @@ +/* Copyright (c) 2022 Skyward Experimental Rocketry + * Author: Giacomo Caironi + * + * 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 <con_RIG/BoardScheduler.h> +#include <con_RIG/Configs/ButtonsConfig.h> +#include <con_RIG/Radio/Radio.h> + +using namespace std; +using namespace miosix; +using namespace Boardcore; +using namespace con_RIG::Config::Buttons; + +namespace con_RIG +{ + +Buttons::Buttons(TaskScheduler* sched) : scheduler(sched) +{ + resetState(); + state.arm_switch = false; + remoteArm = 0; +} + +bool Buttons::start() +{ + return scheduler->addTask([&]() { periodicStatusCheck(); }, + BUTTON_SAMPLE_PERIOD); +} + +Buttons::~Buttons() +{ + // Delete all the buttons +} + +mavlink_conrig_state_tc_t Buttons::getState() { return state; } + +void Buttons::resetState() +{ + state.ignition_btn = false; + state.filling_valve_btn = false; + state.venting_valve_btn = false; + state.release_pressure_btn = false; + state.quick_connector_btn = false; + state.start_tars_btn = false; +} + +void Buttons::periodicStatusCheck() +{ + // TODO: This should be in bsp + using GpioIgnitionBtn = Gpio<GPIOB_BASE, 4>; + using GpioFillingValveBtn = Gpio<GPIOE_BASE, 6>; + using GpioVentingValveBtn = Gpio<GPIOE_BASE, 4>; + using GpioReleasePressureBtn = Gpio<GPIOG_BASE, 9>; + using GpioQuickConnectorBtn = Gpio<GPIOD_BASE, 7>; + using GpioStartTarsBtn = Gpio<GPIOD_BASE, 5>; + using GpioArmedSwitch = Gpio<GPIOE_BASE, 2>; + + state.arm_switch = GpioArmedSwitch::getPin().value(); + + if (!GpioIgnitionBtn::getPin().value() && state.arm_switch) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.ignition_btn = true; + } + else + { + guard++; + } + } + else if (GpioFillingValveBtn::getPin().value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.filling_valve_btn = true; + } + else + { + guard++; + } + } + else if (GpioVentingValveBtn::getPin().value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.venting_valve_btn = true; + } + else + { + guard++; + } + } + else if (GpioReleasePressureBtn::getPin().value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.release_pressure_btn = true; + } + else + { + guard++; + } + } + else if (GpioQuickConnectorBtn::getPin().value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.quick_connector_btn = true; + } + else + { + guard++; + } + } + else if (GpioStartTarsBtn::getPin().value()) + { + if (guard > Config::Buttons::GUARD_THRESHOLD) + { + guard = 0; + state.start_tars_btn = true; + } + else + { + guard++; + } + } + else + { + // Reset all the states and guard + guard = 0; + resetState(); + } + + // Set the internal button state in Radio module + ModuleManager::getInstance().get<Radio>()->setInternalState(state); + + // printf("%d %d %d %d %d %d %d\n", state.ignition, state.filling_valve, + // state.venting_valve, state.release_filling_line_pressure, + // state.detach_quick_connector, state.startup_tars, state.armed); +} + +// 1 if rocket is armed, 0 if disarmed +void Buttons::setRemoteArmState(int armed) +{ + using armed_led = Gpio<GPIOC_BASE, 13>; + + if (armed) + { + remoteArm = true; + armed_led::high(); + } + else + { + remoteArm = false; + armed_led::low(); + } +} + +} // namespace con_RIG diff --git a/src/boards/con_RIG/Buttons/Buttons.h b/src/boards/con_RIG/Buttons/Buttons.h new file mode 100644 index 0000000000000000000000000000000000000000..221a456a461406f078b717046f8c9892b0e23911 --- /dev/null +++ b/src/boards/con_RIG/Buttons/Buttons.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2022 Skyward Experimental Rocketry + * Author: Giacomo Caironi + * + * 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/Mavlink.h> +#include <diagnostic/PrintLogger.h> +#include <scheduler/TaskScheduler.h> + +#include <utils/ModuleManager/ModuleManager.hpp> + +namespace con_RIG +{ + +class Buttons : public Boardcore::Module +{ + +public: + explicit Buttons(Boardcore::TaskScheduler* sched); + + ~Buttons(); + + bool start(); + + mavlink_conrig_state_tc_t getState(); + + void resetState(); + + void setRemoteArmState(int state); + +private: + mavlink_conrig_state_tc_t state; + void periodicStatusCheck(); + std::atomic<bool> remoteArm{false}; + + // Counter guard to avoid spurious triggers + uint8_t guard = 0; + + Boardcore::TaskScheduler* scheduler = nullptr; + + Boardcore::PrintLogger logger = Boardcore::Logging::getLogger("buttons"); +}; + +} // namespace con_RIG diff --git a/src/boards/con_RIG/Configs/ButtonsConfig.h b/src/boards/con_RIG/Configs/ButtonsConfig.h new file mode 100644 index 0000000000000000000000000000000000000000..671d55fd46c5834d0b651aebcd60b93a1cf4d9a4 --- /dev/null +++ b/src/boards/con_RIG/Configs/ButtonsConfig.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2022 Skyward Experimental Rocketry + * Author: Giacomo Caironi + * + * 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. + */ + +/** + * This class specifies the sensors constants that the sensor manager + * needs to know about every device. For example the sample time is + * essential to understand how much time a sensor should wait before + * another sample. + */ + +#pragma once + +namespace con_RIG +{ +namespace Config +{ +namespace Buttons +{ + +static constexpr uint32_t BUTTON_SAMPLE_PERIOD = 20; // 50Hz + +constexpr uint8_t CHECK_BUTTON_STATE_TASK_ID = 150; + +static constexpr uint32_t BUZZER_PERIOD = 100; +static constexpr uint32_t BUZZER_DELAY = 3000; +constexpr uint8_t BUZZER_ON_TASK_ID = 160; +constexpr uint8_t BUZZER_OFF_TASK_ID = 161; + +constexpr uint8_t GUARD_THRESHOLD = + 5; // 5 samples to trigger the guard and activate a single button + +} // namespace Buttons +} // namespace Config + +} // namespace con_RIG diff --git a/src/boards/con_RIG/Configs/RadioConfig.h b/src/boards/con_RIG/Configs/RadioConfig.h new file mode 100644 index 0000000000000000000000000000000000000000..1785b129073302133e26f895f8e378aa72f7dc74 --- /dev/null +++ b/src/boards/con_RIG/Configs/RadioConfig.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2022 Skyward Experimental Rocketry + * Author: Matteo Pignataro + * + * 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/Mavlink.h> + +namespace con_RIG +{ + +namespace Config +{ +namespace Radio +{ + +// Mavlink driver template parameters +constexpr uint32_t RADIO_PKT_LENGTH = 255; +constexpr uint32_t RADIO_OUT_QUEUE_SIZE = 10; +constexpr uint32_t RADIO_MAV_MSG_LENGTH = MAVLINK_MAX_DIALECT_PAYLOAD_SIZE; + +// Mavlink driver parameters +constexpr size_t MAV_OUT_BUFFER_MAX_AGE = 200; + +// Mavlink ids +constexpr uint8_t MAV_SYSTEM_ID = 171; +constexpr uint8_t MAV_COMPONENT_ID = 96; + +// Periodic telemetries frequency +constexpr uint32_t PING_GSE_PERIOD = 500; // [ms] + +// Periodic telemetries tasks ids +constexpr uint8_t PING_GSE_TASK_ID = 200; + +constexpr uint32_t MAVLINK_QUEUE_SIZE = 3; + +} // namespace Radio +} // namespace Config +} // namespace con_RIG diff --git a/src/boards/con_RIG/Radio/Radio.cpp b/src/boards/con_RIG/Radio/Radio.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f4c70e239c2f4d7cf5b09c7a6b8f6549122297b9 --- /dev/null +++ b/src/boards/con_RIG/Radio/Radio.cpp @@ -0,0 +1,280 @@ +/* Copyright (c) 2022 Skyward Experimental Rocketry + * Author: Giacomo Caironi + * + * 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/Mavlink.h> +#include <con_RIG/BoardScheduler.h> +#include <con_RIG/Buses.h> +#include <con_RIG/Buttons/Buttons.h> +#include <diagnostic/SkywardStack.h> +#include <drivers/interrupt/external_interrupts.h> +#include <events/EventBroker.h> +#include <radio/SX1278/SX1278Frontends.h> +#include <radio/Xbee/ATCommands.h> + +#include <thread> + +using namespace std; +using namespace miosix; +using namespace placeholders; +using namespace Boardcore; +using namespace con_RIG::Config::Radio; + +void __attribute__((used)) EXTI1_IRQHandlerImpl() +{ + ModuleManager& modules = ModuleManager::getInstance(); + if (modules.get<con_RIG::Radio>()->transceiver != nullptr) + { + modules.get<con_RIG::Radio>()->transceiver->handleDioIRQ(); + } +} + +void __attribute__((used)) EXTI12_IRQHandlerImpl() +{ + ModuleManager& modules = ModuleManager::getInstance(); + if (modules.get<con_RIG::Radio>()->transceiver != nullptr) + { + modules.get<con_RIG::Radio>()->transceiver->handleDioIRQ(); + } +} + +void __attribute__((used)) EXTI13_IRQHandlerImpl() +{ + ModuleManager& modules = ModuleManager::getInstance(); + if (modules.get<con_RIG::Radio>()->transceiver != nullptr) + { + modules.get<con_RIG::Radio>()->transceiver->handleDioIRQ(); + } +} + +namespace con_RIG +{ + +void Radio::handleMavlinkMessage(MavDriver* driver, + const mavlink_message_t& msg) +{ + switch (msg.msgid) + { + case MAVLINK_MSG_ID_GSE_TM: + { + int arming_state = mavlink_msg_gse_tm_get_arming_state(&msg); + ModuleManager::getInstance().get<Buttons>()->setRemoteArmState( + arming_state); + messageReceived += + arming_state == 1 + ? 10 + : 2; // The beep increments in speed as the state is armed + } + 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(internalStateMutex); + // Reset the internal button state + buttonState.ignition_btn = false; + buttonState.filling_valve_btn = false; + buttonState.venting_valve_btn = false; + buttonState.release_pressure_btn = false; + buttonState.quick_connector_btn = false; + buttonState.start_tars_btn = false; + buttonState.arm_switch = false; + } + } + } + mavlinkWriteToUsart(msg); +} + +void Radio::mavlinkWriteToUsart(const mavlink_message_t& msg) +{ + uint8_t temp_buf[MAVLINK_NUM_NON_PAYLOAD_BYTES + + MAVLINK_MAX_DIALECT_PAYLOAD_SIZE]; + int len = mavlink_msg_to_send_buffer(temp_buf, &msg); + + auto serial = miosix::DefaultConsole::instance().get(); + serial->writeBlock(temp_buf, len, 0); +} + +void Radio::sendMessages() +{ + mavlink_message_t msg; + + { + Lock<FastMutex> lock(internalStateMutex); + mavlink_msg_conrig_state_tc_encode(Config::Radio::MAV_SYSTEM_ID, + Config::Radio::MAV_COMPONENT_ID, + &msg, &buttonState); + } + + { + Lock<FastMutex> lock(mutex); + for (uint8_t i = 0; i < message_queue_index; i++) + { + mavDriver->enqueueMsg(message_queue[i]); + } + message_queue_index = 0; + + // The last is the button state message + mavDriver->enqueueMsg(msg); + } +} + +void Radio::loopReadFromUsart() +{ + mavlink_status_t status; + mavlink_message_t msg; + int chan = 0; // TODO: what is this? + uint8_t byte; + + while (true) + { + auto serial = miosix::DefaultConsole::instance().get(); + int len = serial->readBlock(&byte, 1, 0); + + if (len && mavlink_parse_char(chan, byte, &msg, &status)) + { + if (message_queue_index == MAVLINK_QUEUE_SIZE - 1) + { + mavlink_message_t nack_msg; + mavlink_nack_tm_t nack_tm; + nack_tm.recv_msgid = msg.msgid; + nack_tm.seq_ack = msg.seq; + mavlink_msg_nack_tm_encode(Config::Radio::MAV_SYSTEM_ID, + Config::Radio::MAV_COMPONENT_ID, + &nack_msg, &nack_tm); + mavlinkWriteToUsart(nack_msg); + } + else + { + Lock<FastMutex> lock(mutex); + message_queue[message_queue_index] = msg; + message_queue_index += 1; + } + } + } +} + +void Radio::setInternalState(mavlink_conrig_state_tc_t state) +{ + Lock<FastMutex> lock(internalStateMutex); + // The OR operator is introduced to make sure that the receiver + // understood the command + buttonState.ignition_btn = state.ignition_btn || buttonState.ignition_btn; + buttonState.filling_valve_btn = + state.filling_valve_btn || buttonState.filling_valve_btn; + buttonState.venting_valve_btn = + state.venting_valve_btn || buttonState.venting_valve_btn; + buttonState.release_pressure_btn = + state.release_pressure_btn || buttonState.release_pressure_btn; + buttonState.quick_connector_btn = + state.quick_connector_btn || buttonState.quick_connector_btn; + buttonState.start_tars_btn = + state.start_tars_btn || buttonState.start_tars_btn; + buttonState.arm_switch = state.arm_switch || buttonState.arm_switch; +} + +bool Radio::start() +{ + // TODO: constants should be in bps + using dio0 = Gpio<GPIOB_BASE, 1>; + using dio1 = Gpio<GPIOD_BASE, 12>; + using dio3 = Gpio<GPIOD_BASE, 13>; + using txEn = Gpio<GPIOG_BASE, 2>; + using rxEn = Gpio<GPIOG_BASE, 3>; + using cs = Gpio<GPIOF_BASE, 6>; + + ModuleManager& modules = ModuleManager::getInstance(); + + // Config the transceiver + SX1278Lora::Config config{}; + config.power = 2; + config.ocp = 0; // Over current protection + config.coding_rate = SX1278Lora::Config::Cr::CR_1; + config.spreading_factor = SX1278Lora::Config::Sf::SF_7; + + std::unique_ptr<SX1278::ISX1278Frontend> frontend = + std::make_unique<EbyteFrontend>(txEn::getPin(), rxEn::getPin()); + + transceiver = + new SX1278Lora(modules.get<Buses>()->spi1, cs::getPin(), dio0::getPin(), + dio1::getPin(), dio3::getPin(), + SPI::ClockDivider::DIV_64, std::move(frontend)); + + SX1278Lora::Error error = transceiver->init(config); + + // Config mavDriver + mavDriver = new MavDriver(transceiver, + bind(&Radio::handleMavlinkMessage, this, _1, _2), + 0, MAV_OUT_BUFFER_MAX_AGE); + + // In case of failure the mav driver must be created at least + if (error != SX1278Lora::Error::NONE) + { + return false; + } + + scheduler->addTask([&]() { sendMessages(); }, PING_GSE_PERIOD, + TaskScheduler::Policy::RECOVER); + + receiverLooper = std::thread([=]() { loopReadFromUsart(); }); + beeperLooper = std::thread( + [&]() + { + using buzzer = Gpio<GPIOB_BASE, 7>; + while (true) + { + Thread::sleep(100); + // Doesn't matter the precision, the important thing is + // the beep, this is because an atomic is used + if (messageReceived > 5) + { + messageReceived = 0; + buzzer::low(); + Thread::sleep(100); + buzzer::high(); + } + } + }); + + return mavDriver->start(); +} + +bool Radio::isStarted() { return mavDriver->isStarted(); } + +MavlinkStatus Radio::getMavlinkStatus() { return mavDriver->getStatus(); } + +Radio::Radio(TaskScheduler* sched) : scheduler(sched) +{ + Lock<FastMutex> lock(internalStateMutex); + + buttonState.ignition_btn = false; + buttonState.filling_valve_btn = false; + buttonState.venting_valve_btn = false; + buttonState.release_pressure_btn = false; + buttonState.quick_connector_btn = false; + buttonState.start_tars_btn = false; + buttonState.arm_switch = false; +} + +} // namespace con_RIG diff --git a/src/boards/con_RIG/Radio/Radio.h b/src/boards/con_RIG/Radio/Radio.h new file mode 100644 index 0000000000000000000000000000000000000000..6765e51d69905c2149557e4ad66102c32ae91d6e --- /dev/null +++ b/src/boards/con_RIG/Radio/Radio.h @@ -0,0 +1,83 @@ +/* Copyright (c) 2022 Skyward Experimental Rocketry + * Author: Giacomo Caironi + * + * 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/Mavlink.h> +#include <con_RIG/Configs/RadioConfig.h> +#include <diagnostic/PrintLogger.h> +#include <radio/MavlinkDriver/MavlinkDriver.h> +#include <radio/SX1278/SX1278Lora.h> +#include <scheduler/TaskScheduler.h> + +#include <cstdint> +#include <thread> +#include <utils/ModuleManager/ModuleManager.hpp> + +namespace con_RIG +{ + +using MavDriver = Boardcore::MavlinkDriver<Config::Radio::RADIO_PKT_LENGTH, + Config::Radio::RADIO_OUT_QUEUE_SIZE, + Config::Radio::RADIO_MAV_MSG_LENGTH>; + +class Radio : public Boardcore::Module +{ +public: + explicit Radio(Boardcore::TaskScheduler* sched); + + bool start(); + + bool isStarted(); + + Boardcore::MavlinkStatus getMavlinkStatus(); + + void sendMessages(); + + void loopReadFromUsart(); + + void setInternalState(mavlink_conrig_state_tc_t state); + + Boardcore::SX1278Lora* transceiver = nullptr; + MavDriver* mavDriver = nullptr; + +private: + void handleMavlinkMessage(MavDriver* driver, const mavlink_message_t& msg); + + void mavlinkWriteToUsart(const mavlink_message_t& msg); + + mavlink_message_t message_queue[Config::Radio::MAVLINK_QUEUE_SIZE]; + uint8_t message_queue_index = 0; + miosix::FastMutex mutex; + miosix::FastMutex internalStateMutex; + + // Button internal state + mavlink_conrig_state_tc_t buttonState; + + std::thread receiverLooper; + std::thread beeperLooper; + std::atomic<uint8_t> messageReceived{0}; + Boardcore::TaskScheduler* scheduler = nullptr; + Boardcore::PrintLogger logger = Boardcore::Logging::getLogger("radio"); +}; + +} // namespace con_RIG diff --git a/src/entrypoints/con_RIG/con_rig-entry.cpp b/src/entrypoints/con_RIG/con_rig-entry.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5e305fb8b3c396681e7f254e750a3a6368eb3df9 --- /dev/null +++ b/src/entrypoints/con_RIG/con_rig-entry.cpp @@ -0,0 +1,189 @@ +/* Copyright (c) 2022 Skyward Experimental Rocketry + * Author: Giacomo Caironi + * + * 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 <con_RIG/BoardScheduler.h> +#include <con_RIG/Buses.h> +#include <con_RIG/Buttons/Buttons.h> +#include <con_RIG/Configs/ButtonsConfig.h> +#include <con_RIG/Radio/Radio.h> +#include <diagnostic/CpuMeter/CpuMeter.h> +#include <diagnostic/PrintLogger.h> +#include <events/EventBroker.h> +#include <miosix.h> + +#include <thread> +#include <utils/ModuleManager/ModuleManager.hpp> + +using namespace miosix; +using namespace Boardcore; +using namespace con_RIG; + +// TODO delete with hwmapping and bsp +void initPins() +{ + // SPI1 + RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; + using sck = Gpio<GPIOA_BASE, 5>; + using miso = Gpio<GPIOA_BASE, 6>; + using mosi = Gpio<GPIOA_BASE, 7>; + using cs = Gpio<GPIOF_BASE, 6>; + + using dio0 = Gpio<GPIOB_BASE, 1>; + using dio1 = Gpio<GPIOD_BASE, 12>; + using dio3 = Gpio<GPIOD_BASE, 13>; + + using txEn = Gpio<GPIOG_BASE, 2>; + using rxEn = Gpio<GPIOG_BASE, 3>; + using nrst = Gpio<GPIOB_BASE, 0>; + + sck::mode(Mode::ALTERNATE); + miso::mode(Mode::ALTERNATE); + mosi::mode(Mode::ALTERNATE); + + sck::alternateFunction(5); + miso::alternateFunction(5); + mosi::alternateFunction(5); + + cs::mode(Mode::OUTPUT); + dio0::mode(Mode::INPUT_PULL_UP); + dio1::mode(Mode::INPUT_PULL_UP); + dio3::mode(Mode::INPUT_PULL_UP); + txEn::mode(Mode::OUTPUT); + rxEn::mode(Mode::OUTPUT); + nrst::mode(Mode::OUTPUT); + + cs::high(); + nrst::high(); + + // USART 1 + using tx = Gpio<GPIOA_BASE, 9>; + using rx = Gpio<GPIOA_BASE, 10>; + + tx::mode(Mode::ALTERNATE); + rx::mode(Mode::ALTERNATE); + + tx::alternateFunction(7); + rx::alternateFunction(7); + + // Buttons and switch + using GpioIgnitionBtn = Gpio<GPIOB_BASE, 4>; + using GpioFillingValveBtn = Gpio<GPIOE_BASE, 6>; + using GpioVentingValveBtn = Gpio<GPIOE_BASE, 4>; + using GpioReleasePressureBtn = Gpio<GPIOG_BASE, 9>; + using GpioQuickConnectorBtn = Gpio<GPIOD_BASE, 7>; + using GpioStartTarsBtn = Gpio<GPIOD_BASE, 5>; + using GpioArmedSwitch = Gpio<GPIOE_BASE, 2>; + + GpioIgnitionBtn::mode(Mode::INPUT); + GpioFillingValveBtn::mode(Mode::INPUT); + GpioVentingValveBtn::mode(Mode::INPUT); + GpioReleasePressureBtn::mode(Mode::INPUT); + GpioQuickConnectorBtn::mode(Mode::INPUT); + GpioStartTarsBtn::mode(Mode::INPUT); + GpioArmedSwitch::mode(Mode::INPUT); + + // Actuators + using armed_led = Gpio<GPIOC_BASE, 13>; + using buzzer = Gpio<GPIOB_BASE, 7>; + using redLed = Gpio<GPIOG_BASE, 14>; // Red LED + + armed_led::mode(Mode::OUTPUT); + buzzer::mode(Mode::OUTPUT); + redLed::mode(Mode::OUTPUT); + + buzzer::high(); +} + +int main() +{ + + bool initResult = true; + ModuleManager& modules = ModuleManager::getInstance(); + PrintLogger logger = Logging::getLogger("main"); + + initPins(); + + BoardScheduler* scheduler = new BoardScheduler(); + Buses* buses = new Buses(); + Radio* radio = new Radio(scheduler->getScheduler(PRIORITY_MAX)); + Buttons* buttons = new Buttons(scheduler->getScheduler(PRIORITY_MAX - 1)); + + if (!modules.insert<BoardScheduler>(scheduler)) + { + initResult = false; + LOG_ERR(logger, "Error initializing BoardScheduler module"); + } + + if (!modules.insert<Buses>(buses)) + { + initResult = false; + LOG_ERR(logger, "Error initializing Buses module"); + } + + if (!modules.insert<Radio>(radio)) + { + initResult = false; + LOG_ERR(logger, "Error initializing Radio module"); + } + + if (!modules.insert<Buttons>(buttons)) + { + initResult = false; + LOG_ERR(logger, "Error initializing Buttons module"); + } + + if (!modules.get<Radio>()->start()) + { + initResult = false; + LOG_ERR(logger, "Error starting the radio"); + } + + if (!modules.get<Buttons>()->start()) + { + initResult = false; + LOG_ERR(logger, "Error starting the buttons"); + } + + if (!modules.get<BoardScheduler>()->start()) + { + initResult = false; + LOG_ERR(logger, "Error starting the General Purpose Scheduler"); + } + + if (initResult) + { + // POST OK + // EventBroker::getInstance().post(FMM_INIT_OK, TOPIC_FMM); + } + else + { + using redLed = Gpio<GPIOG_BASE, 14>; // Red LED + redLed::high(); + } + + // Periodical statistics + while (true) + { + Thread::sleep(1000); + CpuMeter::resetCpuStats(); + } +}