diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9c5e17a2f5c3d03fc51072d89b42dff9a51730d0..e7546c9d5ef2d8bcc8348a4d8120254a897d6ce3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -77,6 +77,14 @@ add_executable(sx1278fsk-ebyterig-serial src/entrypoints/sx1278-serial.cpp)
 target_compile_definitions(sx1278fsk-ebyterig-serial PRIVATE SX1278_IS_FSK)
 sbs_target(sx1278fsk-ebyterig-serial stm32f429zi_skyward_rig)
 
+add_executable(sx1278fsk-s-mav-raw src/entrypoints/sx1278-mav-raw.cpp)
+target_compile_definitions(sx1278fsk-s-mav-raw PRIVATE SX1278_IS_FSK SX1278_IS_SENDER)
+sbs_target(sx1278fsk-s-mav-raw stm32f429zi_skyward_groundstation_v2)
+
+add_executable(sx1278fsk-r-mav-raw src/entrypoints/sx1278-mav-raw.cpp)
+target_compile_definitions(sx1278fsk-r-mav-raw PRIVATE SX1278_IS_FSK SX1278_IS_RECEIVER)
+sbs_target(sx1278fsk-r-mav-raw stm32f429zi_skyward_groundstation_v2)
+
 #-----------------------------------------------------------------------------#
 #                                    Tests                                    #
 #-----------------------------------------------------------------------------#
diff --git a/src/entrypoints/sx1278-mav-raw.cpp b/src/entrypoints/sx1278-mav-raw.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..01f8532ae4cab97968b7fdee52528294694327e9
--- /dev/null
+++ b/src/entrypoints/sx1278-mav-raw.cpp
@@ -0,0 +1,337 @@
+/* Copyright (c) 2023 Skyward Experimental Rocketry
+ * Author: Davide Mor, Federico Lolli
+ *
+ * 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 <drivers/interrupt/external_interrupts.h>
+#include <filesystem/console/console_device.h>
+
+// SX1278 includes
+#include <mavlink_lib/gemini/mavlink.h>
+#include <radio/SX1278/SX1278Frontends.h>
+#include <radio/SX1278/SX1278Fsk.h>
+#include <radio/SX1278/SX1278Lora.h>
+
+#include <iostream>
+#include <thread>
+
+using namespace miosix;
+
+// Uncomment the following line to enable Lora mode
+// Or use SBS to define it for you
+// #define SX1278_IS_LORA
+
+#if defined _BOARD_STM32F429ZI_SKYWARD_GS_V2
+#include "interfaces-impl/hwmapping.h"
+
+// Uncomment the following line to enable Ebyte module
+// #define SX1278_IS_EBYTE
+// Uncomment the following line to ebable Skyward433 module
+// #define SX1278_IS_SKYWARD433
+
+using cs   = peripherals::ra01::pc13::cs;
+using dio0 = peripherals::ra01::pc13::dio0;
+using dio1 = peripherals::ra01::pc13::dio1;
+using dio3 = peripherals::ra01::pc13::dio3;
+
+using sck  = interfaces::spi4::sck;
+using miso = interfaces::spi4::miso;
+using mosi = interfaces::spi4::mosi;
+
+#ifdef SX1278_IS_EBYTE
+using txen = Gpio<GPIOE_BASE, 4>;
+using rxen = Gpio<GPIOD_BASE, 4>;
+#endif
+
+#define SX1278_SPI SPI4
+
+#define SX1278_IRQ_DIO0 EXTI6_IRQHandlerImpl
+#define SX1278_IRQ_DIO1 EXTI4_IRQHandlerImpl
+#define SX1278_IRQ_DIO3 EXTI11_IRQHandlerImpl
+
+#elif defined _BOARD_STM32F429ZI_SKYWARD_RIG
+#include "interfaces-impl/hwmapping.h"
+
+#define SX1278_IS_EBYTE
+
+using cs   = radio::cs;
+using dio0 = radio::dio0;
+using dio1 = radio::dio1;
+using dio3 = radio::dio3;
+
+using sck  = radio::sck;
+using miso = radio::miso;
+using mosi = radio::mosi;
+
+using txen = radio::txEn;
+using rxen = radio::rxEn;
+
+#define SX1278_SPI SPI4
+
+#define SX1278_IRQ_DIO0 EXTI5_IRQHandlerImpl
+#define SX1278_IRQ_DIO1 EXTI12_IRQHandlerImpl
+#define SX1278_IRQ_DIO3 EXTI13_IRQHandlerImpl
+
+#elif defined _BOARD_STM32F767ZI_GEMINI_GS
+#include "interfaces-impl/hwmapping.h"
+#include "mavlink_lib/gemini/mavlink_msg_payload_flight_tm.h"
+
+// #define SX1278_IS_SKYWARD433
+// #define SX1278_IS_EBYTE
+
+// Comment to use SX1278_2
+// #define SX1278_1
+
+#ifdef SX1278_1
+using cs   = miosix::radio1::cs;
+using dio0 = miosix::radio1::dio0;
+using dio1 = miosix::radio1::dio1;
+using dio3 = miosix::radio1::dio3;
+
+using sck  = miosix::radio1::spi::sck;
+using miso = miosix::radio1::spi::miso;
+using mosi = miosix::radio1::spi::mosi;
+
+using txen = miosix::radio1::txen;
+using rxen = miosix::radio1::rxen;
+
+#define SX1278_NRST
+using rst  = miosix::radio1::nrst;
+
+#define SX1278_SPI MIOSIX_RADIO1_SPI
+
+#define SX1278_IRQ_DIO0 MIOSIX_RADIO1_DIO0_IRQ
+#define SX1278_IRQ_DIO1 MIOSIX_RADIO1_DIO1_IRQ
+#define SX1278_IRQ_DIO3 MIOSIX_RADIO1_DIO3_IRQ
+#else
+using cs   = miosix::radio2::cs;
+using dio0 = miosix::radio2::dio0;
+using dio1 = miosix::radio2::dio1;
+using dio3 = miosix::radio2::dio3;
+
+using sck  = miosix::radio2::spi::sck;
+using miso = miosix::radio2::spi::miso;
+using mosi = miosix::radio2::spi::mosi;
+
+using txen = miosix::radio2::txen;
+using rxen = miosix::radio2::rxen;
+
+#define SX1278_NRST
+using rst  = miosix::radio2::nrst;
+
+#define SX1278_SPI MIOSIX_RADIO2_SPI
+
+#define SX1278_IRQ_DIO0 MIOSIX_RADIO2_DIO0_IRQ
+#define SX1278_IRQ_DIO1 MIOSIX_RADIO2_DIO1_IRQ
+#define SX1278_IRQ_DIO3 MIOSIX_RADIO2_DIO3_IRQ
+#endif
+
+#else
+#error "Target not supported"
+#endif
+
+#ifdef SX1278_IS_LORA
+static constexpr size_t SX1278_MTU = Boardcore::SX1278Lora::MTU;
+Boardcore::SX1278Lora* sx1278      = nullptr;
+#else
+static constexpr size_t SX1278_MTU = Boardcore::SX1278Fsk::MTU;
+Boardcore::SX1278Fsk* sx1278       = nullptr;
+#endif
+
+volatile int dio0_cnt = 0;
+volatile int dio1_cnt = 0;
+volatile int dio3_cnt = 0;
+
+#ifdef SX1278_IRQ_DIO0
+void __attribute__((used)) SX1278_IRQ_DIO0()
+{
+    dio0_cnt++;
+    if (sx1278)
+        sx1278->handleDioIRQ();
+}
+#endif
+
+#ifdef SX1278_IRQ_DIO1
+void __attribute__((used)) SX1278_IRQ_DIO1()
+{
+    dio1_cnt++;
+    if (sx1278)
+        sx1278->handleDioIRQ();
+}
+#endif
+
+#ifdef SX1278_IRQ_DIO3
+void __attribute__((used)) SX1278_IRQ_DIO3()
+{
+    dio3_cnt++;
+    if (sx1278)
+        sx1278->handleDioIRQ();
+}
+#endif
+
+void initBoard()
+{
+#ifdef SX1278_IS_EBYTE
+    rxen::mode(Mode::OUTPUT);
+    txen::mode(Mode::OUTPUT);
+    rxen::low();
+    txen::low();
+#endif
+
+#ifdef SX1278_NRST
+    rst::mode(miosix::Mode::OUTPUT);
+    rst::high();
+#endif
+}
+
+constexpr size_t PACKET_SIZE = 158;
+
+/** @brief Number of packets to send */
+constexpr size_t MSG_NUM = 580;
+
+/** @brief End of transmission byte. Used to signal the end of a packet. */
+constexpr uint8_t EOT = 0x04;
+
+/**
+ * @brief Read a packet from the serial port
+ * @warning This function will parse raw bytes coming from
+ * serial into the struct
+ * @return mavlink_payload_flight_tm_t
+ */
+mavlink_payload_flight_tm_t readPacketFromSerial()
+{
+    mavlink_payload_flight_tm_t tm;
+    uint8_t* ptr_to_tm = (uint8_t*)&tm;
+    uint8_t serial_buffer[PACKET_SIZE];
+
+    auto serial = DefaultConsole::instance().get();
+    serial->writeBlock(&EOT, 1, 0);
+    serial->readBlock(serial_buffer, PACKET_SIZE, 0);
+
+    // this may be shrunk to the above statement (needs further testing)
+    memcpy(ptr_to_tm, serial_buffer, sizeof(mavlink_payload_flight_tm_t));
+
+    return tm;
+}
+
+void recvLoop()
+{
+    uint8_t msg[SX1278_MTU];
+    while (1)
+    {
+        int len = sx1278->receive(msg, sizeof(msg));
+        if (len > 0)
+        {
+            mavlink_payload_flight_tm_t tm;
+            memcpy(&tm, msg, sizeof(mavlink_payload_flight_tm_t));
+            auto serial = miosix::DefaultConsole::instance().get();
+            // serial->writeBlock(msg, len, 0);
+            std::cout << "[sx1278] Received packet - time: " << tm.timestamp
+                      << std::endl;
+        }
+    }
+}
+
+void sendLoop()
+{
+    uint8_t msg[SX1278_MTU];
+    for (size_t i = 0; i < MSG_NUM; i++)
+    {
+        mavlink_payload_flight_tm_t tm = readPacketFromSerial();
+        std::cout << "[sx1278] Sending packet " << i << std::endl;
+        memcpy(msg, &tm, sizeof(mavlink_payload_flight_tm_t));
+        sx1278->send(msg, sizeof(mavlink_payload_flight_tm_t));
+    }
+}
+
+Boardcore::SPIBus sx1278_bus(SX1278_SPI);
+
+int main()
+{
+    initBoard();
+
+#if defined SX1278_IS_EBYTE
+    printf("[sx1278] Confuring Ebyte frontend...\n");
+    std::unique_ptr<Boardcore::SX1278::ISX1278Frontend> frontend(
+        new Boardcore::EbyteFrontend(txen::getPin(), rxen::getPin()));
+#elif defined SX1278_IS_SKYWARD433
+    printf("[sx1278] Confuring Skyward 433 frontend...\n");
+    std::unique_ptr<Boardcore::SX1278::ISX1278Frontend> frontend(
+        new Boardcore::Skyward433Frontend());
+#else
+    printf("[sx1278] Confuring RA01 frontend...\n");
+    std::unique_ptr<Boardcore::SX1278::ISX1278Frontend> frontend(
+        new Boardcore::RA01Frontend());
+#endif
+
+#ifdef SX1278_IS_LORA
+    // Run default configuration
+    Boardcore::SX1278Lora::Config config;
+    Boardcore::SX1278Lora::Error err;
+
+    sx1278 = new Boardcore::SX1278Lora(sx1278_bus, cs::getPin(), dio0::getPin(),
+                                       dio1::getPin(), dio3::getPin(),
+                                       Boardcore::SPI::ClockDivider::DIV_64,
+                                       std::move(frontend));
+
+    printf("\n[sx1278] Configuring sx1278 lora...\n");
+    if ((err = sx1278->init(config)) != Boardcore::SX1278Lora::Error::NONE)
+    {
+        printf("[sx1278] sx1278->init error\n");
+        return -1;
+    }
+
+    printf("\n[sx1278] Initialization complete!\n");
+#else
+    // Run default configuration
+    Boardcore::SX1278Fsk::Config config;
+    Boardcore::SX1278Fsk::Error err;
+
+    config.freq_rf    = 434000000;
+    config.enable_crc = false;
+
+    sx1278 = new Boardcore::SX1278Fsk(sx1278_bus, cs::getPin(), dio0::getPin(),
+                                      dio1::getPin(), dio3::getPin(),
+                                      Boardcore::SPI::ClockDivider::DIV_256,
+                                      std::move(frontend));
+
+    printf("\n[sx1278] Configuring sx1278 fsk...\n");
+    if ((err = sx1278->init(config)) != Boardcore::SX1278Fsk::Error::NONE)
+    {
+        // FIXME: Why does clang-format put this line up here?
+        printf("[sx1278] sx1278->init error\n");
+        return false;
+    }
+
+    printf("\n[sx1278] Initialization complete!\n");
+#endif
+
+#if defined SX1278_IS_SENDER
+    sendLoop();
+#elif defined SX1278_IS_RECEIVER
+    recvLoop();
+#else
+    // Actually spawn threads
+    std::thread send([]() { sendLoop(); });
+    recvLoop();
+#endif
+
+    return 0;
+}