diff --git a/.vscode/settings.json b/.vscode/settings.json
index caa4ff15bf38b4658d34a4bf0af6745b62d54e44..68d8988bcafbddb302ab2276396aab2f81057fc8 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -118,19 +118,30 @@
                 "specialfunctions": "cpp"
         },
         "cSpell.words": [
+                "abom",
                 "ADCPRE",
+                "addfilter",
                 "ADON",
                 "aelf",
                 "airbrakes",
                 "Airbrakes",
+                "Alain",
                 "Alessandro",
+                "ALST",
                 "AMSL",
+                "apbclk",
                 "atthr",
                 "AVDD",
+                "awum",
                 "Baro",
+                "bittiming",
                 "boardcore",
                 "Boardcorev",
                 "boudrate",
+                "Canbus",
+                "canprotocol",
+                "Carlucci",
+                "compid",
                 "Corigliano",
                 "CORTEXM",
                 "cpitch",
@@ -153,6 +164,8 @@
                 "Fatttr",
                 "fedetft's",
                 "fiprintf",
+                "FMPIE",
+                "FOVR",
                 "Gatttr",
                 "getdetahstate",
                 "GNSS",
@@ -169,10 +182,15 @@
                 "Hatt",
                 "HIFCR",
                 "HISR",
+                "hppw",
                 "HSCMAND",
                 "HSCMRNN",
                 "HWMAPPING",
                 "IDLEIE",
+                "Impli",
+                "Implii",
+                "INAK",
+                "INRQ",
                 "irqn",
                 "irqv",
                 "JEOC",
@@ -182,13 +200,17 @@
                 "Kalman",
                 "Katt",
                 "kbps",
+                "LBKM",
                 "ldrex",
                 "leds",
                 "LIFCR",
                 "LISR",
                 "logdecoder",
                 "Luca",
+                "Mandelli",
                 "Matteo",
+                "Mavlink",
+                "mavlinkdriver",
                 "MEKF",
                 "microcontrollers",
                 "MINC",
@@ -198,6 +220,7 @@
                 "mosi",
                 "MPXHZ",
                 "Musso",
+                "nart",
                 "NATT",
                 "NBAR",
                 "NDTR",
@@ -213,13 +236,21 @@
                 "PINC",
                 "Pitot",
                 "Plin",
+                "prescaler",
                 "Qgbw",
                 "Qget",
                 "Qhandle",
                 "Qput",
                 "Qwait",
                 "Qwakeup",
+                "RDHR",
+                "RDLR",
+                "RDTR",
+                "rflm",
+                "RFOM",
                 "Riccardo",
+                "RQCP",
+                "RXIRQ",
                 "RXNE",
                 "RXNEIE",
                 "sats",
@@ -239,11 +270,22 @@
                 "syaw",
                 "TCIE",
                 "TCIF",
+                "TDHR",
+                "TDLR",
+                "TDTR",
                 "TEIE",
                 "Terraneo",
                 "testsuite",
+                "THID",
+                "TMEIE",
+                "tparam",
                 "TSCPP",
                 "TSVREFE",
+                "Tweakable",
+                "txfp",
+                "TXIRQ",
+                "TXOK",
+                "TXRQ",
                 "Ublox",
                 "UBXACK",
                 "UBXGPS",
@@ -261,4 +303,4 @@
         ],
         "cSpell.language": "en",
         "cSpell.enabled": true
-}
\ No newline at end of file
+}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ad794b6f0220f7347b1ac0fb5ae15c7b63e1db63..d29e49129200e02632cb2652bf9001e68da5ab70 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -52,7 +52,7 @@ add_executable(runcam-settings src/entrypoints/runcam-settings.cpp)
 sbs_target(runcam-settings stm32f407vg_stm32f4discovery)
 
 add_executable(sdcard-benchmark src/entrypoints/sdcard-benchmark.cpp)
-sbs_target(sdcard-benchmark stm32f205rc_skyward_ciuti)
+sbs_target(sdcard-benchmark stm32f429zi_skyward_death_stack_x)
 
 #-----------------------------------------------------------------------------#
 #                                    Tests                                    #
@@ -159,14 +159,18 @@ sbs_target(test-ada stm32f429zi_skyward_death_stack_v3)
 #-----------------------------------------------------------------------------#
 #                               Tests - Drivers                               #
 #-----------------------------------------------------------------------------#
-add_executable(test-can-protocol src/tests/drivers/canbus/test-can-protocol.cpp)
-sbs_target(test-can-protocol stm32f429zi_stm32f4discovery)
 
-add_executable(test-canbus-loopback src/tests/drivers/canbus/test-canbus-loopback.cpp)
-sbs_target(test-canbus-loopback stm32f429zi_stm32f4discovery)
+add_executable(test-can-2way src/tests/drivers/canbus/CanDriver/test-can-2way.cpp)
+sbs_target(test-can-2way stm32f429zi_skyward_pyxis_auxiliary)
 
-add_executable(test-canbus-2way src/tests/drivers/canbus/test-canbus-2way.cpp)
-sbs_target(test-canbus-2way stm32f429zi_stm32f4discovery)
+add_executable(test-can-filters src/tests/drivers/canbus/CanDriver/test-can-filters.cpp)
+sbs_target(test-can-filters stm32f429zi_skyward_pyxis_auxiliary)
+
+add_executable(test-can-loopback src/tests/drivers/canbus/CanDriver/test-can-loopback.cpp)
+sbs_target(test-can-loopback stm32f429zi_skyward_death_stack_x)
+
+add_executable(test-can-protocol src/tests/drivers/canbus/CanProtocol/test-can-protocol.cpp)
+sbs_target(test-can-protocol stm32f429zi_skyward_death_stack_x)
 
 add_executable(test-dma-spi src/tests/drivers/dma/test-dma-spi.cpp)
 sbs_target(test-dma-spi stm32f429zi_stm32f4discovery)
diff --git a/cmake/boardcore.cmake b/cmake/boardcore.cmake
index 21fc12ef98d913aca0e8accc6c7f91959a210015..3453618b78c814bded59867f2d416255aad4744b 100644
--- a/cmake/boardcore.cmake
+++ b/cmake/boardcore.cmake
@@ -45,8 +45,9 @@ foreach(OPT_BOARD ${BOARDS})
         # Drivers
         ${SBS_BASE}/src/shared/drivers/adc/InternalTemp.cpp
         ${SBS_BASE}/src/shared/drivers/adc/InternalADC.cpp
-        ${SBS_BASE}/src/shared/drivers/canbus/Canbus.cpp
-        ${SBS_BASE}/src/shared/drivers/canbus/CanInterrupt.cpp
+        ${SBS_BASE}/src/shared/drivers/canbus/CanDriver/CanDriver.cpp
+        ${SBS_BASE}/src/shared/drivers/canbus/CanDriver/CanInterrupt.cpp
+        ${SBS_BASE}/src/shared/drivers/canbus/CanProtocol/CanProtocol.cpp
         ${SBS_BASE}/src/shared/drivers/i2c/stm32f2_f4_i2c.cpp
         ${SBS_BASE}/src/shared/drivers/interrupt/external_interrupts.cpp
         ${SBS_BASE}/src/shared/drivers/timer/PWM.cpp
diff --git a/src/shared/drivers/canbus/BusLoadEstimation.h b/src/shared/drivers/canbus/CanDriver/BusLoadEstimation.h
similarity index 98%
rename from src/shared/drivers/canbus/BusLoadEstimation.h
rename to src/shared/drivers/canbus/CanDriver/BusLoadEstimation.h
index 66bf37394c23a1fc14617028d1b350e0c830a968..d635553acec07638bffa7aa20876123429fbe2de 100644
--- a/src/shared/drivers/canbus/BusLoadEstimation.h
+++ b/src/shared/drivers/canbus/CanDriver/BusLoadEstimation.h
@@ -27,7 +27,7 @@
 
 #include <cstdint>
 
-#include "CanData.h"
+#include "CanDriverData.h"
 
 using miosix::FastMutex;
 using miosix::Lock;
diff --git a/src/shared/drivers/canbus/Canbus.cpp b/src/shared/drivers/canbus/CanDriver/CanDriver.cpp
similarity index 94%
rename from src/shared/drivers/canbus/Canbus.cpp
rename to src/shared/drivers/canbus/CanDriver/CanDriver.cpp
index b7d70591a2b90d79f7ba675bd98c6858e89192ad..dc9469bddda098bdeac1b050a113bc04e734128c 100644
--- a/src/shared/drivers/canbus/Canbus.cpp
+++ b/src/shared/drivers/canbus/CanDriver/CanDriver.cpp
@@ -20,7 +20,7 @@
  * THE SOFTWARE.
  */
 
-#include "Canbus.h"
+#include "CanDriver.h"
 
 #include <kernel/scheduler/scheduler.h>
 #include <utils/ClockUtils.h>
@@ -53,26 +53,23 @@ CanbusDriver::CanbusDriver(CAN_TypeDef* can, CanbusConfig config,
     ClockUtils::enablePeripheralClock(can);
 
     // Enter init mode
-    can->MCR &= (~CAN_MCR_SLEEP);
+    can->MCR &= ~CAN_MCR_SLEEP;
     can->MCR |= CAN_MCR_INRQ;
 
     while ((can->MSR & CAN_MSR_INAK) == 0)
         ;
 
+    // Automatic wakeup when a new packet is available
     if (config.awum)
-    {
-        can->MCR |=
-            CAN_MCR_AWUM;  // Automatic wakeup when a new packet is available
-    }
+        can->MCR |= CAN_MCR_AWUM;
 
+    // Automatically recover from Bus-Off mode
     if (config.abom)
-    {
-        can->MCR |= CAN_MCR_ABOM;  // Automatically recover from Bus-Off mode
-    }
+        can->MCR |= CAN_MCR_ABOM;
+
+    // Disable automatic retransmission
     if (config.nart)
-    {
-        can->MCR |= CAN_MCR_NART;  // Disable automatic retransmission
-    }
+        can->MCR |= CAN_MCR_NART;
 
     // Bit timing configuration
     can->BTR &= ~CAN_BTR_BRP;
@@ -86,21 +83,15 @@ CanbusDriver::CanbusDriver(CAN_TypeDef* can, CanbusConfig config,
     can->BTR |= ((bitTiming.SJW - 1) & 0x3) << 24;
 
     if (config.loopback)
-    {
         can->BTR |= CAN_BTR_LBKM;
-    }
 
     // Enter filter initialization mode
     can->FMR |= CAN_FMR_FINIT;
 
     if (can == CAN1)
-    {
         canDrivers[0] = this;
-    }
     else
-    {
         canDrivers[1] = this;
-    }
 
     // Enable interrupts
     can->IER |= CAN_IER_FMPIE0 | CAN_IER_FMPIE1 | CAN_IER_TMEIE;
@@ -139,7 +130,7 @@ CanbusDriver::BitTiming CanbusDriver::calcBitTiming(AutoBitTiming autoBt)
                      1 << 10),
             1);
 
-        // Given N, calculate BS1 and BS2 that statusult in a sample time as
+        // Given N, calculate BS1 and BS2 that result in a sample time as
         // close as possible to the target one
         cfgIter.BS1 =
             std::min(std::max((int)roundf(autoBt.samplePoint * N - 1), 1),
@@ -190,9 +181,7 @@ CanbusDriver::BitTiming CanbusDriver::calcBitTiming(AutoBitTiming autoBt)
 void CanbusDriver::init()
 {
     if (isInit)
-    {
         return;
-    }
 
     PrintLogger ls = l.getChild("init");
 
@@ -203,9 +192,7 @@ void CanbusDriver::init()
 
     LOG_DEBUG(ls, "Waiting for canbus synchronization...");
     while ((can->MSR & CAN_MSR_INAK) > 0)
-    {
         Thread::sleep(1);
-    }
 
     LOG_INFO(ls, "Canbus synchronized! Init done!");
 
@@ -221,6 +208,7 @@ bool CanbusDriver::addFilter(FilterBank filter)
         LOG_ERR(ls, "Cannot add filter: canbus already initialized");
         return false;
     }
+
     if (filterIndex == NUM_FILTER_BANKS)
     {
         LOG_ERR(ls, "Cannot add filter: no more filter banks available");
@@ -256,6 +244,7 @@ uint32_t CanbusDriver::send(CanPacket packet)
 
     {
         miosix::FastInterruptDisableLock d;
+
         // Wait until there is an empty mailbox available to use
         while ((can->TSR & CAN_TSR_TME) == 0)
         {
@@ -312,13 +301,9 @@ uint32_t CanbusDriver::send(CanPacket packet)
     for (uint8_t i = 0; i < packet.length; ++i)
     {
         if (i < 4)
-        {
             mailbox->TDLR |= packet.data[i] << i * 8;
-        }
         else
-        {
             mailbox->TDHR |= packet.data[i] << (i - 4) * 8;
-        }
     }
 
     // Finally send the packet
@@ -336,13 +321,9 @@ void CanbusDriver::handleRXInterrupt(int fifo)
 
     mailbox = &can->sFIFOMailBox[fifo];
     if (fifo == 0)
-    {
         RFR = &can->RF0R;
-    }
     else
-    {
         RFR = &can->RF1R;
-    }
 
     status.fifoOverrun = (*RFR & CAN_RF0R_FOVR0) > 0;
     status.fifoFull    = (*RFR & CAN_RF0R_FULL0) > 0;
@@ -365,25 +346,18 @@ void CanbusDriver::handleRXInterrupt(int fifo)
         p.rtr = (mailbox->RIR & CAN_RI0R_RTR) > 0;
 
         if (p.ext)
-        {
             p.id = (mailbox->RIR >> 3) & 0x1FFFFFFF;
-        }
         else
-        {
             p.id = (mailbox->RIR >> 21) & 0x7FF;
-        }
+
         p.length = mailbox->RDTR & CAN_RDT0R_DLC;
 
         for (uint8_t i = 0; i < p.length; i++)
         {
             if (i < 4)  // Low register
-            {
                 p.data[i] = (mailbox->RDLR >> (i * 8)) & 0xFF;
-            }
             else  // High register
-            {
                 p.data[i] = (mailbox->RDHR >> ((i - 4) * 8)) & 0xFF;
-            }
         }
 
         *RFR |= CAN_RF0R_RFOM0;
diff --git a/src/shared/drivers/canbus/Canbus.h b/src/shared/drivers/canbus/CanDriver/CanDriver.h
similarity index 93%
rename from src/shared/drivers/canbus/Canbus.h
rename to src/shared/drivers/canbus/CanDriver/CanDriver.h
index b841096f29ade3f3ec47fb8af18198709075e4bf..f9d9b493268bd321000865bcfa0e05692ccc51f4 100644
--- a/src/shared/drivers/canbus/Canbus.h
+++ b/src/shared/drivers/canbus/CanDriver/CanDriver.h
@@ -26,7 +26,7 @@
 #include <miosix.h>
 #include <utils/collections/IRQCircularBuffer.h>
 
-#include "CanData.h"
+#include "CanDriverData.h"
 #include "Filters.h"
 
 using miosix::Thread;
@@ -38,8 +38,8 @@ namespace Canbus
 {
 
 /**
- * @brief Low level canbus driver, with support for both peripherals (CAN1 and
- * CAN2) on stm32f4 micros.
+ * @brief Low level CanBus driver, with support for both peripherals (CAN1 and
+ * CAN2) on stm32f4 microcontrollers.
  */
 class CanbusDriver
 {
@@ -56,7 +56,7 @@ class CanbusDriver
 
 public:
     /**
-     * @brief Configuration struct for basic canbus operation.
+     * @brief Configuration struct for basic CanBus operation.
      */
     struct CanbusConfig
     {
@@ -81,7 +81,7 @@ public:
     {
         /**
          * @brief Canbus baud rate in bps (BITS PER SECOND). CANOpen standard
-         * values are preferred but not mandatory: 1000 kpbs, 500 kbps, 250
+         * values are preferred but not mandatory: 1000 kbps, 500 kbps, 250
          * kbps, 125 kbps, 100 kbps, 83.333 kbps, 50 kbps, 25 kbps and 10 kbps.
          */
         uint32_t baudRate;
@@ -93,7 +93,7 @@ public:
     };
 
     /**
-     * @brief Struct specifing exact bit timing registers values.
+     * @brief Struct specifying exact bit timing registers values.
      */
     struct BitTiming
     {
@@ -133,7 +133,7 @@ public:
     ~CanbusDriver();
 
     /**
-     * @brief Exits initialization mode and starts canbus operation.
+     * @brief Exits initialization mode and starts CanBus operation.
      */
     void init();
 
@@ -173,7 +173,7 @@ public:
     }
 
     /**
-     * @brief Returns the canbus peripheral assigned to this instance.
+     * @brief Returns the CanBus peripheral assigned to this instance.
      */
     CAN_TypeDef* getCAN() { return can; }
 
diff --git a/src/shared/drivers/canbus/CanData.h b/src/shared/drivers/canbus/CanDriver/CanDriverData.h
similarity index 96%
rename from src/shared/drivers/canbus/CanData.h
rename to src/shared/drivers/canbus/CanDriver/CanDriverData.h
index 8123c8514b1e25e58ea88eb04d453bb742c7b509..97e583073d3f823b35b79ea7dde0df4c7e2c599d 100644
--- a/src/shared/drivers/canbus/CanData.h
+++ b/src/shared/drivers/canbus/CanDriver/CanDriverData.h
@@ -54,7 +54,7 @@ struct CanPacket
     uint32_t timestamp = 0;
 
     uint32_t id;
-    bool ext = false;
+    bool ext = false;  ///< Whether to use extended packet id
 
     bool rtr = false;
 
diff --git a/src/shared/drivers/canbus/CanInterrupt.cpp b/src/shared/drivers/canbus/CanDriver/CanInterrupt.cpp
similarity index 99%
rename from src/shared/drivers/canbus/CanInterrupt.cpp
rename to src/shared/drivers/canbus/CanDriver/CanInterrupt.cpp
index e0312b326dc917837e84f4faca875d6f22fa62c8..4ed47a191f8c8afa1a0716b7f1b0bfc08642cf47 100644
--- a/src/shared/drivers/canbus/CanInterrupt.cpp
+++ b/src/shared/drivers/canbus/CanDriver/CanInterrupt.cpp
@@ -26,7 +26,7 @@
 #include <kernel/scheduler/scheduler.h>
 #include <miosix.h>
 
-#include "Canbus.h"
+#include "CanDriver.h"
 
 namespace Boardcore
 {
@@ -98,9 +98,7 @@ void __attribute__((used)) CAN_RXIRQHandlerImpl(int canDev, int fifo)
     (void)canDev;
 
     if (canDrivers[canDev])
-    {
         canDrivers[canDev]->handleRXInterrupt(fifo);
-    }
 }
 
 void __attribute__((used)) CAN_TXIRQHandlerImpl(int canDev)
diff --git a/src/shared/drivers/canbus/CanInterrupt.h b/src/shared/drivers/canbus/CanDriver/CanInterrupt.h
similarity index 100%
rename from src/shared/drivers/canbus/CanInterrupt.h
rename to src/shared/drivers/canbus/CanDriver/CanInterrupt.h
diff --git a/src/shared/drivers/canbus/Filters.h b/src/shared/drivers/canbus/CanDriver/Filters.h
similarity index 100%
rename from src/shared/drivers/canbus/Filters.h
rename to src/shared/drivers/canbus/CanDriver/Filters.h
diff --git a/src/shared/drivers/canbus/CanProtocol.h b/src/shared/drivers/canbus/CanProtocol.h
deleted file mode 100644
index ae2384a0bc15a1bdc64ec462a05b755b7f755c4f..0000000000000000000000000000000000000000
--- a/src/shared/drivers/canbus/CanProtocol.h
+++ /dev/null
@@ -1,236 +0,0 @@
-/* Copyright (c) 2022 Skyward Experimental Rocketry
- * Author: Federico Mandelli
- *
- * 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 <ActiveObject.h>
-#include <utils/Debug.h>
-#include <utils/collections/IRQCircularBuffer.h>
-
-#include "Canbus.h"
-
-#define NPACKET 3  // equals the number of boards in the can system
-
-/**
- * @brief Struct that contains how the canId is composed
- */
-struct CanIDMask
-{
-    uint32_t priority    = 0x1E000000;
-    uint32_t type        = 0x1F80000;
-    uint32_t source      = 0x78000;
-    uint32_t destination = 0x7800;
-    uint32_t idType      = 0x780;
-    uint32_t firstPacket = 0x40;
-    uint32_t leftToSend  = 0x3F;
-} idMask;
-
-namespace Boardcore
-{
-namespace Canbus
-{
-
-/**
- * @brief Generic struct that contains a logical can packet
- * i.e. 1 accelerometer packet 3*4byte (acc: x,y,z)+timestamp, will be 4
- * canPacket but a single canData.
- */
-struct CanData
-{
-    int32_t canId =
-        -1;  // the id of the can packet without the last 7 bits (sequence bit)
-    uint8_t len;
-    uint8_t nRec = 0;
-    uint64_t payload[32];
-} data[NPACKET];
-
-/**
- * @brief Canbus protocol, given an initialized can this class takes care of
- * sending the multiple packet of CanData with the corresponding id and
- * receiving single CanPacket that are then reframed as one Candata.
- */
-class CanProtocol : public ActiveObject
-{
-private:
-    miosix::FastMutex
-        mutex;          // todo add mutex and create get data in can protocol
-    CanbusDriver* can;  // the physical can
-    IRQCircularBuffer<CanData, NPACKET>
-        buffer;  // the buffer used to send data from CanProtocol to CanHandler
-
-public:
-    /**
-     * @brief Construct a new CanProtocol object
-     * @param can CanbusDriver pointer.
-     */
-    CanProtocol(CanbusDriver* can) { this->can = can; }
-
-    CanData
-    getPacket()  // return the packet, if buffer is empty return an empty packet
-    {
-        CanData temp;
-        miosix::Lock<miosix::FastMutex> l(mutex);
-        if (!buffer.isEmpty())
-        {
-            temp = buffer.pop();
-        }
-
-        return temp;
-    }
-
-    bool isEmpty()
-    {
-        miosix::Lock<miosix::FastMutex> l(mutex);
-        return buffer.isEmpty();
-    }
-
-    void waitEmpty() { buffer.waitUntilNotEmpty(); }
-
-    uint8_t byteForInt(uint64_t number)
-    {
-        uint8_t i;
-        for (i = 1; i <= 8; i++)
-        {
-            number = number >> 8;
-            if (number == 0)
-                return i;
-        }
-        return i;
-    }
-    /**
-     * @brief Takes a canData, it splits it into single canpacket with the
-     * correct sequential id
-     * @param toSend = containing the id e the data of the packet to send
-     * @warning requires toSend to be not empty
-     */
-    void sendCan(CanData toSend)  //@requires toSen to not be empty
-    {
-        CanPacket packet;
-        uint8_t tempLen = toSend.len - 1;
-        uint32_t tempId = toSend.canId;
-        packet.ext      = true;
-        packet.id       = (tempId << 7) | idMask.firstPacket |
-                    (63 - (tempLen & idMask.leftToSend));
-        packet.length = byteForInt(toSend.payload[0]);
-        for (int k = 0; k < packet.length; k++)
-        {
-            packet.data[k] = toSend.payload[0] >> (8 * k);
-        }
-        tempLen--;
-        can->send(packet);
-        for (int i = 1; i < toSend.len; i++)
-        {
-            tempId    = toSend.canId;
-            packet.id = (tempId << 7) | !(idMask.firstPacket) |
-                        (63 - (tempLen & idMask.leftToSend));
-            packet.length = byteForInt(toSend.payload[i]);
-            for (int k = 0; k < packet.length; k++)
-            {
-                packet.data[k] = toSend.payload[i] >> (8 * k);
-            }
-            can->send(packet);
-            tempLen--;
-        }
-    }
-
-protected:
-    /**
-     * @brief Keeps listening on hte canbus for packets, once received it checks
-     * if they are expected (that id is already present in data), if they are
-     * they are added to the list. once we receive the correct amount of packet
-     * we send it to can handler.
-     */
-    void run() override  // for now if a packet is missed/received in the wrong
-                         // order the whole packet will be lost once we receive
-                         // a new first packet without warning canhandler
-    {
-        uint32_t sourceId;
-        CanPacket packet;
-        // Infinite loop
-        while (true)
-        {
-            can->getRXBuffer().waitUntilNotEmpty();
-            if (!can->getRXBuffer().isEmpty())
-            {
-
-                packet   = can->getRXBuffer().pop().packet;
-                sourceId = (packet.id & idMask.source) >> 15;
-                if (sourceId >= 0 &&
-                    sourceId < NPACKET)  // check for maximum size
-                {
-                    if (data[sourceId].canId == -1 ||
-                        (((data[sourceId].canId << 7) & idMask.source) >> 15) ==
-                            sourceId)
-                    {
-                        uint32_t leftToSend =
-                            63 - (packet.id & idMask.leftToSend);
-
-                        if ((packet.id & idMask.firstPacket) >>
-                            6)  // it is a first
-                                // packet of a data;
-                        {
-
-                            data[sourceId].len = leftToSend + 1;
-                            data[sourceId].canId =
-                                packet.id >> 7;  // discard the sequence number
-                        }
-                        if ((data[sourceId].len - (data[sourceId].nRec + 1)) ==
-                            leftToSend)
-                        {
-
-                            uint64_t tempPayload = 0;
-                            for (int f = 0; f < packet.length; f++)
-                            {
-                                uint64_t tempData = packet.data[f];
-                                tempPayload =
-                                    tempPayload | (tempData << (f * 8));
-                            }
-                            if (data[sourceId].len - leftToSend - 1 >= 0 &&
-                                data[sourceId].len - leftToSend - 1 <
-                                    32)  // check for index
-                            {
-
-                                data[sourceId].payload[data[sourceId].len -
-                                                       leftToSend - 1] =
-                                    tempPayload;
-                                data[sourceId].nRec++;
-                            }
-                        }
-
-                        if (data[sourceId].nRec == data[sourceId].len &&
-                            data[sourceId].nRec != 0)
-                        {
-                            miosix::Lock<miosix::FastMutex> l(mutex);
-                            buffer.put(data[sourceId]);
-                            // empties the struct
-                            data[sourceId].canId = -1;
-                            data[sourceId].nRec  = 0;
-                            data[sourceId].len   = 0;
-                        }
-                    }
-                }
-            }
-        }
-    }
-};
-}  // namespace Canbus
-}  // namespace Boardcore
diff --git a/src/shared/drivers/canbus/CanProtocol/CanProtocol.cpp b/src/shared/drivers/canbus/CanProtocol/CanProtocol.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f8454775b2842f3feb099b0d088397d50832149d
--- /dev/null
+++ b/src/shared/drivers/canbus/CanProtocol/CanProtocol.cpp
@@ -0,0 +1,283 @@
+/* Copyright (c) 2022 Skyward Experimental Rocketry
+ * Author: Federico Mandelli
+ *
+ * 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 "CanProtocol.h"
+
+using namespace miosix;
+
+namespace Boardcore
+{
+
+namespace Canbus
+{
+
+CanProtocol::CanProtocol(CanbusDriver* can, MsgHandler onReceive)
+    : can(can), onReceive(onReceive)
+{
+}
+
+bool CanProtocol::start()
+{
+    stopFlag = false;
+
+    // Start sender (joinable thread)
+    if (!sndStarted)
+    {
+        sndThread = miosix::Thread::create(
+            sndLauncher, skywardStack(4 * 1024), miosix::MAIN_PRIORITY,
+            reinterpret_cast<void*>(this), miosix::Thread::JOINABLE);
+
+        if (sndThread != nullptr)
+            sndStarted = true;
+        else
+            LOG_ERR(logger, "Could not start sender!");
+    }
+
+    // Start receiver
+    if (!rcvStarted)
+    {
+        rcvThread = miosix::Thread::create(rcvLauncher, skywardStack(4 * 1024),
+                                           miosix::MAIN_PRIORITY,
+                                           reinterpret_cast<void*>(this));
+
+        if (rcvThread != nullptr)
+            rcvStarted = true;
+        else
+            LOG_ERR(logger, "Could not start receiver!");
+    }
+
+    if (sndStarted && rcvStarted)
+        LOG_DEBUG(logger, "Sender and receiver started");
+
+    return sndStarted && rcvStarted;
+}
+
+bool CanProtocol::isStarted() { return sndStarted && rcvStarted; }
+
+void CanProtocol::stop()
+{
+    stopFlag = true;
+
+    // Wait for sender to stop
+    sndThread->join();
+}
+
+bool CanProtocol::enqueueMsg(const CanMessage& msg)
+{
+    // Append the message to the queue
+    outQueue.put(msg);
+
+    // Update stats
+    // updateQueueStats(appended);
+
+    // Return always true because the circular buffer overrides current packets
+    // and can't get full.
+    return true;
+}
+
+bool CanProtocol::addFilter(uint8_t src, uint64_t dst)
+{
+    if (src > 0xF || dst > 0xF)
+        return false;
+
+    // The filter mask will cover only the source and destination bits
+    uint32_t mask = static_cast<uint32_t>(CanProtocolIdMask::SOURCE) |
+                    static_cast<uint32_t>(CanProtocolIdMask::DESTINATION);
+
+    uint32_t id =
+        src << static_cast<uint8_t>(CanProtocolShiftInformation::SOURCE) |
+        dst << static_cast<uint8_t>(CanProtocolShiftInformation::DESTINATION);
+
+    Mask32FilterBank filterBank(id, mask, 1, 1, 0, 0, 0);
+
+    return can->addFilter(filterBank);
+}
+
+void CanProtocol::sendMessage(const CanMessage& msg)
+{
+    CanPacket packet    = {};
+    uint32_t leftToSend = msg.length - 1;
+
+    // Create the id for the first packet
+    packet.ext = true;  // Use extended packet id
+
+    // The number of left to send packets
+    packet.id = static_cast<uint32_t>(msg.id) |
+                ((static_cast<uint32_t>(0x3F) - leftToSend) &
+                 static_cast<uint32_t>(CanProtocolIdMask::LEFT_TO_SEND));
+    packet.length = byteForUint64(msg.payload[0]);
+
+    // Splits payload[0] in the right number of uint8_t
+    for (int i = 0; i < packet.length; i++)
+        packet.data[i] = msg.payload[0] >> (8 * i);
+
+    // Send the first packet
+    can->send(packet);
+    leftToSend--;
+
+    // Prepare the remaining packets
+    for (int i = 1; i < msg.length; i++)
+    {
+        packet.id =
+            static_cast<uint32_t>(msg.id) |
+            static_cast<uint32_t>(CanProtocolIdMask::FIRST_PACKET_FLAG) |
+            ((static_cast<uint32_t>(0x3F) - leftToSend) &
+             static_cast<uint32_t>(CanProtocolIdMask::LEFT_TO_SEND));
+        packet.length = byteForUint64(msg.payload[i]);
+
+        // Splits payload[i] in the right number of uint8_t
+        for (int k = 0; k < packet.length; k++)
+            packet.data[k] = msg.payload[i] >> (8 * k);
+
+        can->send(packet);
+        leftToSend--;
+    }
+}
+
+void CanProtocol::runReceiver()
+{
+    CanMessage msg;
+    uint8_t nReceived = 0;
+
+    while (!stopFlag)
+    {
+        // Wait for the next packet
+        can->getRXBuffer().waitUntilNotEmpty();
+
+        // If the buffer is not empty retrieve the packet
+        if (!can->getRXBuffer().isEmpty())
+        {
+            CanPacket pkt = can->getRXBuffer().pop().packet;
+
+            uint8_t leftToReceive =
+                static_cast<uint32_t>(0x3F) -
+                (pkt.id &
+                 static_cast<uint32_t>(CanProtocolIdMask::LEFT_TO_SEND));
+
+            // Check if the packet is the first in the sequence, if this is the
+            // case then the previous message is overriden
+            if ((pkt.id & static_cast<uint32_t>(
+                              CanProtocolIdMask::FIRST_PACKET_FLAG)) == 0)
+            {
+                // If it is we save the id (without the sequence number) and the
+                // message length
+                msg.id = pkt.id & static_cast<uint32_t>(
+                                      CanProtocolIdMask::MESSAGE_INFORMATION);
+                msg.length = leftToReceive + 1;
+
+                // Reset the number of received packets
+                nReceived = 0;
+            }
+
+            // Accept the packet only if it has the expected id
+            // clang-format off
+            if (msg.id != -1 &&
+                (pkt.id & static_cast<uint32_t>(CanProtocolIdMask::MESSAGE_INFORMATION)) ==
+                (msg.id & static_cast<uint32_t>(CanProtocolIdMask::MESSAGE_INFORMATION)))
+            // clang-format on
+            {
+                // Check if the packet is expected in the sequence. The received
+                // packet must have the expected left to send value
+
+                if (msg.length - nReceived - 1 == leftToReceive)
+                {
+                    uint64_t payload = 0;
+
+                    // Assemble the packet data into a uint64_t
+                    for (uint8_t i = 0; i < pkt.length; i++)
+                        payload |= pkt.data[i] << (i * 8);
+
+                    // Add the data to the message
+                    msg.payload[pkt.length - leftToReceive - 1] = payload;
+                    nReceived++;
+                }
+            }
+
+            // If we have received the right number of packet call onReceive and
+            // reset the message
+            if (nReceived == msg.length && nReceived != 0)
+            {
+                onReceive(msg);
+
+                // Reset the packet
+                msg.id     = -1;
+                msg.length = 0;
+            }
+        }
+    }
+}
+
+void CanProtocol::runSender()
+{
+    LOG_DEBUG(logger, "Sender is running");
+    CanMessage msg;
+
+    while (!stopFlag)
+    {
+        outQueue.waitUntilNotEmpty();
+
+        if (!outQueue.isEmpty())
+        {
+            // Get the first packet in the queue, without removing it
+            msg = outQueue.pop();
+
+            LOG_DEBUG(logger, "Sending message, length: {}", msg.length);
+
+            sendMessage(msg);
+
+            // updateSenderStats();
+        }
+        else
+        {
+            // Wait before sending something else
+            miosix::Thread::sleep(50);
+        }
+    }
+}
+
+void CanProtocol::rcvLauncher(void* arg)
+{
+    reinterpret_cast<CanProtocol*>(arg)->runReceiver();
+}
+
+void CanProtocol::sndLauncher(void* arg)
+{
+    reinterpret_cast<CanProtocol*>(arg)->runSender();
+}
+
+uint8_t CanProtocol::byteForUint64(uint64_t number)
+{
+    uint8_t i;
+
+    for (i = 1; i <= 8; i++)
+    {
+        number >>= 8;
+        if (number == 0)
+            return i;
+    }
+
+    return i;
+}
+
+}  // namespace Canbus
+
+}  // namespace Boardcore
diff --git a/src/shared/drivers/canbus/CanProtocol/CanProtocol.h b/src/shared/drivers/canbus/CanProtocol/CanProtocol.h
new file mode 100644
index 0000000000000000000000000000000000000000..a8792d7c08016fbbd3f9192d2bd3927bb1a4b962
--- /dev/null
+++ b/src/shared/drivers/canbus/CanProtocol/CanProtocol.h
@@ -0,0 +1,197 @@
+/* Copyright (c) 2022 Skyward Experimental Rocketry
+ * Author: Federico Mandelli
+ *
+ * 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 <diagnostic/PrintLogger.h>
+#include <drivers/canbus/CanDriver/CanDriver.h>
+#include <utils/Debug.h>
+#include <utils/collections/SyncCircularBuffer.h>
+
+#include "CanProtocolData.h"
+#include "CanProtocolTypes.h"
+
+namespace Boardcore
+{
+
+namespace Canbus
+{
+
+/**
+ * @brief Canbus protocol implementation.
+ *
+ * Given a can interface this class takes care of sending messages segmented
+ * into multiple packets, and receiving single packets that are then reframed
+ * into messages.
+ *
+ * This driver has been implemented following the MavlinkDriver.
+ */
+class CanProtocol
+{
+    using MsgHandler = std::function<void(CanMessage data)>;
+
+public:
+    /**
+     * @brief Construct a new CanProtocol object.
+     *
+     * @param can Pointer to a CanbusDriver object.
+     */
+    CanProtocol(CanbusDriver* can, MsgHandler onReceive);
+
+    /**
+     * @brief Start the receiving and sending threads.
+     *
+     * @return False if at least one could not start.
+     */
+    bool start();
+
+    /**
+     * @brief Tells whether the driver was started.
+     */
+    bool isStarted();
+
+    /**
+     * @brief Stops sender and receiver threads.
+     */
+    void stop();
+
+    /**
+     * @brief Adds a filter to the can peripheral to receive only messages from
+     * the given source and targeted to the given destination.
+     *
+     * @param src Message source.
+     * @param dst Message destination.
+     * @return True if the filter was added successfully.
+     */
+    bool addFilter(uint8_t src, uint64_t dst);
+
+    /**
+     * @brief Non-blocking send function, puts the messages in a queue.
+     * Message is discarded if the queue is full.
+     *
+     * @param msg Message to send (CanMessage struct).
+     * @return True if the message could be enqueued.
+     */
+    bool enqueueMsg(const CanMessage& msg);
+
+    /**
+     * @brief Non-blocking send function for a generic data type.
+     *
+     * @warning There must be a function called with this prototype:
+     *   CanMessage toCanMessage(const T& t);
+     *
+     * @param t The class to be logged.
+     */
+    template <typename T>
+    bool enqueueData(uint8_t priority, uint8_t primaryType, uint8_t source,
+                     uint8_t destination, uint8_t secondaryType, const T& t);
+
+private:
+    /**
+     * @brief Blocking send function, puts the CanMessage object on the bus.
+     *
+     * Takes a CanMessage object, splits it into multiple CanPackets with the
+     * correct sequential id.
+     *
+     * @param msg Contains the id and the data of the packet to send.
+     */
+    void sendMessage(const CanMessage& msg);
+
+    /**
+     * @brief Receiver thread: waits for one packet at a time from the can
+     * driver and tries to parse a message.
+     *
+     * If the message is successfully parsed, the onReceive function is
+     * executed.
+     *
+     * For now if a packet is received in the wrong order or if a packet with a
+     * different id is received, the current (incomplete) message will be lost.
+     * Once we receive a new first packet, currently saved data are reset.
+     */
+    void runReceiver();
+
+    /**
+     * @brief Sender Thread: Periodically flushes the message queue and sends
+     * all the enqueued messages.
+     */
+    void runSender();
+
+    /**
+     * @brief Calls the run member function.
+     *
+     * @param arg The object pointer cast to void*.
+     */
+    static void rcvLauncher(void* arg);
+
+    /**
+     * @brief Calls the run member function.
+     *
+     * @param arg The object pointer cast to void*.
+     */
+    static void sndLauncher(void* arg);
+
+    /**
+     * @brief Count the number of bytes needed to encode a uint64_t number.
+     */
+    uint8_t byteForUint64(uint64_t number);
+
+    CanbusDriver* can;     ///< Device used to send and receive packets.
+    MsgHandler onReceive;  ///< Function executed when a message is ready.
+
+    // Threads
+    bool stopFlag   = false;
+    bool sndStarted = false;
+    bool rcvStarted = false;
+
+    miosix::Thread* sndThread = nullptr;
+    miosix::Thread* rcvThread = nullptr;
+
+    SyncCircularBuffer<CanMessage, 10> outQueue;
+
+    PrintLogger logger = Logging::getLogger("canprotocol");
+};
+
+template <typename T>
+bool CanProtocol::enqueueData(uint8_t priority, uint8_t primaryType,
+                              uint8_t source, uint8_t destination,
+                              uint8_t secondaryType, const T& t)
+{
+    if (priority > 0xF || primaryType > 0x3F || source > 0xF ||
+        destination > 0xF || secondaryType > 0xF)
+        return false;
+
+    CanMessage msg = toCanMessage(t);
+
+    // clang-format off
+    msg.id =  priority      << static_cast<uint32_t>(CanProtocolShiftInformation::PRIORITY);
+    msg.id |= primaryType   << static_cast<uint32_t>(CanProtocolShiftInformation::PRIMARY_TYPE);
+    msg.id |= source        << static_cast<uint32_t>(CanProtocolShiftInformation::SOURCE);
+    msg.id |= destination   << static_cast<uint32_t>(CanProtocolShiftInformation::DESTINATION);
+    msg.id |= secondaryType << static_cast<uint32_t>(CanProtocolShiftInformation::SECONDARY_TYPE);
+    // clang-format off
+
+    return enqueueMsg(msg);
+}
+
+}  // namespace Canbus
+
+}  // namespace Boardcore
diff --git a/src/shared/drivers/canbus/CanProtocol/CanProtocolData.h b/src/shared/drivers/canbus/CanProtocol/CanProtocolData.h
new file mode 100644
index 0000000000000000000000000000000000000000..1a2b3b62e9835dddd237d74915342fc7e618ca4e
--- /dev/null
+++ b/src/shared/drivers/canbus/CanProtocol/CanProtocolData.h
@@ -0,0 +1,184 @@
+/* Copyright (c) 2022 Skyward Experimental Rocketry
+ * Author: Federico Mandelli
+ *
+ * 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 <stdint.h>
+
+namespace Boardcore
+{
+
+namespace Canbus
+{
+
+/**
+ * The CanProtocol allows to transmit arbitrarily sized messages over the CanBus
+ * overcoming the 8 byte limitation of each single packet.
+ *
+ * Our CanProtocol uses the extended can packet, the 29 bits id is divided such
+ * as:
+ * - Priority           4 bit \
+ * - Primary type       6 bit |
+ * - Source             4 bit | 22 bits - Message informations
+ * - Destination        4 bit |
+ * - Secondary type     4 bit /
+ * - First packet flag  1 bit \ 7 bits - Sequential informations
+ * - Remaining packets  6 bit /
+ * shiftNameOfField the number of shift needed to reach that field
+ *
+ * The id is split into 2 parts:
+ * - Message information: Common to every packet of a given message
+ * - Sequential information: Used to distinguish between packets
+ *
+ * The sender splits into multiple packets a message that is then recomposed on
+ * the receiver end. The message informations are encoded into the packets id,
+ * therefore they have an effect on packets priorities.
+ */
+/**
+ * The CanProtocol allows to transmit arbitrarily sized messages over the CanBus
+ * overcoming the 8 byte limitation of each single packet.
+ *
+ * Our CanProtocol uses the extended can packet, the 29 bits id is divided such
+ * as:
+ * - Priority           4 bit \
+ * - Primary type       6 bit |
+ * - Source             4 bit | 22 bits - Message informations
+ * - Destination        4 bit |
+ * - Secondary type     4 bit /
+ * - First packet flag  1 bit \ 7 bits - Sequential informations
+ * - Remaining packets  6 bit /
+ * shiftNameOfField the number of shift needed to reach that field
+ *
+ * The id is split into 2 parts:
+ * - Message information: Common to every packet of a given message
+ * - Sequential information: Used to distinguish between packets
+ *
+ * The sender splits into multiple packets a message that is then recomposed on
+ * the receiver end. The message informations are encoded into the packets id,
+ * therefore they have an effect on packets priorities.
+ */
+
+/**
+ * @brief Masks of the elements composing can packets ids.
+ */
+enum class CanProtocolIdMask : uint32_t
+{
+    PRIORITY       = 0x1E000000,
+    PRIMARY_TYPE   = 0x01F80000,
+    SOURCE         = 0x00078000,
+    DESTINATION    = 0x00003800,
+    SECONDARY_TYPE = 0x00000780,
+
+    MESSAGE_INFORMATION = 0x1FFFFF80,
+
+    FIRST_PACKET_FLAG = 0x00000040,
+    LEFT_TO_SEND      = 0x0000003F,
+
+    SEQUENTIAL_INFORMATION = 0x0000007F
+};
+
+enum CanProtocolShiftInformation : uint8_t
+{
+    // Shift values for message informations
+    PRIORITY       = 25,
+    PRIMARY_TYPE   = 19,
+    SOURCE         = 15,
+    DESTINATION    = 11,
+    SECONDARY_TYPE = 7,
+
+    // Shift values for sequential informations
+    FIRST_PACKET_FLAG = 6,
+    LEFT_TO_SEND      = 0,
+
+    // Position of the message infos relative to the entire can packet id
+    SEQUENTIAL_INFORMATION = 7
+};
+
+/**
+ * @brief Generic struct that contains a can protocol message.
+ *
+ * For example an accelerometer message could have:
+ * - 4 bytes for the timestamp
+ * - 3x4 bytes for float values
+ * This message would be divided into 2 can packets.
+ *
+ * Note that the maximum size for a message is 520 bytes since the remaining
+ * packet information is 6 bit wide.
+ */
+struct CanMessage
+{
+    int32_t id     = -1;  ///< Id of the message without sequential infos.
+    uint8_t length = 0;   ///< Length of the message content.
+    uint64_t payload[65];
+
+    uint8_t getPriority()
+    {
+        return id >>
+               static_cast<uint8_t>(CanProtocolShiftInformation::PRIORITY);
+    }
+
+    uint8_t getPrimaryType()
+    {
+        return (id | static_cast<uint32_t>(CanProtocolIdMask::PRIMARY_TYPE)) >>
+               static_cast<uint8_t>(CanProtocolShiftInformation::PRIMARY_TYPE);
+    }
+
+    uint8_t getSource()
+    {
+        return (id | static_cast<uint32_t>(CanProtocolIdMask::SOURCE)) >>
+               static_cast<uint8_t>(CanProtocolShiftInformation::SOURCE);
+    }
+
+    uint8_t getDestination()
+    {
+        return (id | static_cast<uint32_t>(CanProtocolIdMask::DESTINATION)) >>
+               static_cast<uint8_t>(CanProtocolShiftInformation::DESTINATION);
+    }
+
+    uint8_t getSecondaryType()
+    {
+        return (id |
+                static_cast<uint32_t>(CanProtocolIdMask::SECONDARY_TYPE)) >>
+               static_cast<uint8_t>(CanProtocolShiftInformation::PRIMARY_TYPE);
+    }
+};
+
+inline bool operator==(const CanMessage& lhs, const CanMessage& rhs)
+{
+    if (lhs.id != rhs.id || lhs.length != rhs.length)
+        return false;
+
+    for (int i = 0; i < lhs.length; i++)
+        if (lhs.payload[i] != rhs.payload[i])
+            return false;
+
+    return true;
+}
+
+inline bool operator!=(const CanMessage& lhs, const CanMessage& rhs)
+{
+    return !(lhs == rhs);
+}
+
+}  // namespace Canbus
+
+}  // namespace Boardcore
diff --git a/src/shared/drivers/canbus/CanProtocol/CanProtocolTypes.h b/src/shared/drivers/canbus/CanProtocol/CanProtocolTypes.h
new file mode 100644
index 0000000000000000000000000000000000000000..b84db80a9c1e530dc9666af6c2e9951a7e3ed7f6
--- /dev/null
+++ b/src/shared/drivers/canbus/CanProtocol/CanProtocolTypes.h
@@ -0,0 +1,49 @@
+/* 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 <sensors/analog/Pitot/PitotData.h>
+
+#include <cstring>
+
+#include "CanProtocolData.h"
+
+namespace Boardcore
+{
+
+inline Canbus::CanMessage toCanMessage(const PitotData& data)
+{
+    Canbus::CanMessage message;
+
+    uint32_t tmp;
+    memcpy(&tmp, &(data.airspeed), sizeof(tmp));
+
+    message.id         = -1;
+    message.length     = 1;
+    message.payload[0] = (data.timestamp & ~0x3) << 30;
+    message.payload[0] |= tmp;
+
+    return message;
+}
+
+}  // namespace Boardcore
diff --git a/src/shared/logger/Logger.h b/src/shared/logger/Logger.h
index 496b1cf311900f722a7070447cb9234f864791f4..7d404ae83eb064d15f9ef793e0f3cac9867eb11b 100644
--- a/src/shared/logger/Logger.h
+++ b/src/shared/logger/Logger.h
@@ -124,7 +124,7 @@ public:
      */
     void logStats();
 
-public:
+private:
     Logger();
 
     static std::string getFileName(int logNumber);
diff --git a/src/shared/radio/MavlinkDriver/MavlinkDriver.h b/src/shared/radio/MavlinkDriver/MavlinkDriver.h
index 1bf8c8047e51277aa25155f5221b9ac51625ffa6..81607862cfe14f10b7de94b1faf00bd2fcce23bb 100644
--- a/src/shared/radio/MavlinkDriver/MavlinkDriver.h
+++ b/src/shared/radio/MavlinkDriver/MavlinkDriver.h
@@ -22,15 +22,12 @@
 
 #pragma once
 
-#include <diagnostic/PrintLogger.h>
-
 #include <vector>
 
 /**
  * This object includes only the protocol header (`protocol.h`). To use this
  * driver, you should include YOUR OWN implementation of the messages definition
- * (`mavlink.h`) before including this header. To create your implementation you
- * can use Skyward's *Mavlink Editor*.
+ * (`mavlink.h`) before including this header.
  */
 #ifndef MAVLINK_H
 #error \
@@ -38,6 +35,7 @@
 implementation before including MavlinkDriver.h"
 #endif
 
+#include <diagnostic/PrintLogger.h>
 #include <diagnostic/SkywardStack.h>
 #include <diagnostic/StackLogger.h>
 #include <mavlink_lib/mavlink_types.h>
@@ -84,7 +82,8 @@ public:
 
     /**
      * @brief  Start the receiving and sending threads.
-     * @return false if at least one could not start.
+     *
+     * @return False if at least one could not start.
      */
     bool start();
 
@@ -116,6 +115,17 @@ public:
      */
     bool enqueueRaw(uint8_t* msg, size_t size);
 
+    /**
+     * @brief Synchronized status getter.
+     */
+    MavlinkStatus getStatus();
+
+    /**
+     * @brief Setter for the sleep after send value.
+     */
+    void setSleepAfterSend(uint16_t newSleepTime);
+
+private:
     /**
      * @brief Receiver thread: reads one char at a time from the transceiver and
      * tries to parse a mavlink message.
@@ -134,21 +144,10 @@ public:
      */
     void runSender();
 
-    /**
-     * @brief Synchronized status getter.
-     */
-    MavlinkStatus getStatus();
-
-    /**
-     * @brief Setter for the sleep after send value.
-     */
-    void setSleepAfterSend(uint16_t newSleepTime);
-
-private:
     /**
      * @brief Calls the run member function.
      *
-     * @param arg the object pointer cast to void*
+     * @param arg The object pointer cast to void*.
      */
     static void rcvLauncher(void* arg)
     {
@@ -158,7 +157,7 @@ private:
     /**
      * @brief Calls the run member function.
      *
-     * @param arg the object pointer cast to void*
+     * @param arg The object pointer cast to void*.
      */
     static void sndLauncher(void* arg)
     {
@@ -169,8 +168,8 @@ private:
 
     void updateSenderStats(size_t msgCount, bool sent);
 
-    Transceiver* device;   ///< transceiver used to send and receive
-    MavHandler onReceive;  ///< function executed on message rcv
+    Transceiver* device;   ///< Transceiver used to send and receive packets.
+    MavHandler onReceive;  ///< Function executed when a message is ready.
 
     // Tweakable params
     uint16_t sleepAfterSend;
@@ -273,7 +272,7 @@ bool MavlinkDriver<PktLength, OutQueueSize, MavMsgLength>::enqueueMsg(
     uint8_t msgTempBuf[MAVLINK_NUM_NON_PAYLOAD_BYTES + MavMsgLength];
     int msgLen = mavlink_msg_to_send_buffer(msgTempBuf, &msg);
 
-    // Append message to the queue
+    // Append the message to the queue
     bool appended = outQueue.put(msgTempBuf, msgLen);
 
     // Update stats
diff --git a/src/tests/drivers/canbus/SimpleCanManager.h b/src/tests/drivers/canbus/CanDriver/SimpleCanManager.h
similarity index 95%
rename from src/tests/drivers/canbus/SimpleCanManager.h
rename to src/tests/drivers/canbus/CanDriver/SimpleCanManager.h
index f7ff5a59d5c65a83500948b128f2f0714c344701..141c5a60e2fe12c72607cd171f35231313874559 100644
--- a/src/tests/drivers/canbus/SimpleCanManager.h
+++ b/src/tests/drivers/canbus/CanDriver/SimpleCanManager.h
@@ -21,16 +21,16 @@
  */
 
 #pragma once
+
+#include <ActiveObject.h>
+#include <drivers/canbus/CanDriver/BusLoadEstimation.h>
+#include <drivers/canbus/CanDriver/CanDriver.h>
 #include <miosix.h>
+#include <utils/collections/SyncCircularBuffer.h>
 
 #include <cstdlib>
 #include <functional>
 
-#include "ActiveObject.h"
-#include "drivers/canbus/BusLoadEstimation.h"
-#include "drivers/canbus/Canbus.h"
-#include "utils/collections/SyncCircularBuffer.h"
-
 using std::function;
 
 class SimpleCanManager
diff --git a/src/tests/drivers/canbus/test-canbus-2way.cpp b/src/tests/drivers/canbus/CanDriver/test-can-2way.cpp
similarity index 96%
rename from src/tests/drivers/canbus/test-canbus-2way.cpp
rename to src/tests/drivers/canbus/CanDriver/test-can-2way.cpp
index 91b69f5c2b9589fc67bfe5c843f0ed3eadad35b9..1a22bdfc874f306cfa4222838a19cd6b7ac02628 100644
--- a/src/tests/drivers/canbus/test-canbus-2way.cpp
+++ b/src/tests/drivers/canbus/CanDriver/test-can-2way.cpp
@@ -25,17 +25,17 @@
 // considered lost. It may also receive requests from other canbus devices, to
 // which it will respond
 
+#include <ActiveObject.h>
+#include <diagnostic/PrintLogger.h>
+#include <drivers/canbus/CanDriver/BusLoadEstimation.h>
+#include <drivers/canbus/CanDriver/CanDriver.h>
 #include <utils/Debug.h>
 #include <utils/Stats/Stats.h>
+#include <utils/collections/CircularBuffer.h>
 
 #include <string>
 
-#include "ActiveObject.h"
 #include "SimpleCanManager.h"
-#include "diagnostic/PrintLogger.h"
-#include "drivers/canbus/BusLoadEstimation.h"
-#include "drivers/canbus/Canbus.h"
-#include "utils/collections/CircularBuffer.h"
 
 constexpr uint32_t BAUD_RATE         = 500 * 1000;
 constexpr float SAMPLE_POINT         = 87.5f / 100.0f;
diff --git a/src/tests/drivers/canbus/test-canbus-filters.cpp b/src/tests/drivers/canbus/CanDriver/test-can-filters.cpp
similarity index 97%
rename from src/tests/drivers/canbus/test-canbus-filters.cpp
rename to src/tests/drivers/canbus/CanDriver/test-can-filters.cpp
index 64c8868840454afd28abcb7506592713ceb08095..b6c3f6b667ffcac3ff1021d99634f5430ec08b78 100644
--- a/src/tests/drivers/canbus/test-canbus-filters.cpp
+++ b/src/tests/drivers/canbus/CanDriver/test-can-filters.cpp
@@ -22,8 +22,8 @@
 
 #include <ActiveObject.h>
 #include <diagnostic/PrintLogger.h>
-#include <drivers/canbus/BusLoadEstimation.h>
-#include <drivers/canbus/Canbus.h>
+#include <drivers/canbus/CanDriver/BusLoadEstimation.h>
+#include <drivers/canbus/CanDriver/CanDriver.h>
 
 #include <string>
 
diff --git a/src/tests/drivers/canbus/test-canbus-loopback.cpp b/src/tests/drivers/canbus/CanDriver/test-can-loopback.cpp
similarity index 96%
rename from src/tests/drivers/canbus/test-canbus-loopback.cpp
rename to src/tests/drivers/canbus/CanDriver/test-can-loopback.cpp
index d01ac89a1eee286837069dde8cbd8c8eef46e28e..06559020e6eaee2e933d2ce66696ec2a1c5baf44 100644
--- a/src/tests/drivers/canbus/test-canbus-loopback.cpp
+++ b/src/tests/drivers/canbus/CanDriver/test-can-loopback.cpp
@@ -20,12 +20,12 @@
  * THE SOFTWARE.
  */
 
-#include <string>
+#include <ActiveObject.h>
+#include <diagnostic/PrintLogger.h>
+#include <drivers/canbus/CanDriver/BusLoadEstimation.h>
+#include <drivers/canbus/CanDriver/CanDriver.h>
 
-#include "ActiveObject.h"
-#include "diagnostic/PrintLogger.h"
-#include "drivers/canbus/BusLoadEstimation.h"
-#include "drivers/canbus/Canbus.h"
+#include <string>
 
 using std::string;
 using namespace Boardcore;
diff --git a/src/tests/drivers/canbus/CanProtocol/test-can-protocol.cpp b/src/tests/drivers/canbus/CanProtocol/test-can-protocol.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2fcc424502a10db5c3936d2924a264120219e2cd
--- /dev/null
+++ b/src/tests/drivers/canbus/CanProtocol/test-can-protocol.cpp
@@ -0,0 +1,88 @@
+/* Copyright (c) 2022 Skyward Experimental Rocketry
+ * Author: Federico Mandelli
+ *
+ * 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/canbus/CanProtocol/CanProtocol.h>
+#include <drivers/timer/TimestampTimer.h>
+#include <scheduler/TaskScheduler.h>
+
+using namespace std;
+using namespace miosix;
+using namespace Boardcore;
+using namespace Canbus;
+
+void print(CanMessage data)
+{
+    printf("Received packet:\n");
+    printf("\tpriority:       %d\n", data.getPriority());
+    printf("\tprimary type:   %d\n", data.getPrimaryType());
+    printf("\tsource:         %d\n", data.getSource());
+    printf("\tdestination:    %d\n", data.getDestination());
+    printf("\tsecondary type: %d\n", data.getSecondaryType());
+    printf("\n");
+}
+
+int main()
+{
+    // Prepare the cab driver
+    CanbusDriver::CanbusConfig config;
+    config.loopback = true;
+    CanbusDriver::AutoBitTiming bitTiming;
+    bitTiming.baudRate    = 500 * 1000;
+    bitTiming.samplePoint = 87.5f / 100.0f;
+    CanbusDriver* driver  = new CanbusDriver(CAN1, config, bitTiming);
+
+    // Prepare the can driver
+    CanProtocol protocol(driver, print);
+
+    // Add a filter to allow every message
+    Mask32FilterBank f2(0, 0, 1, 1, 0, 0, 0);
+    driver->addFilter(f2);
+    driver->init();
+
+    // Start the protocol
+    protocol.start();
+
+    CanMessage msg1;
+    msg1.id         = 0x200;
+    msg1.length     = 2;
+    msg1.payload[0] = 0xffffffffffffffff;
+    msg1.payload[1] = 0x0123456789ABCDEF;
+
+    TaskScheduler scheduler;
+
+    scheduler.addTask([&]() { protocol.enqueueMsg(msg1); }, 1000);
+    scheduler.addTask(
+        [&]()
+        {
+            PitotData data{TimestampTimer::getTimestamp(), 23};
+
+            protocol.enqueueData(0xF, 0xA, 0x1, 0x2, 0xB, data);
+        },
+        2000);
+
+    scheduler.start();
+
+    printf("Started\n");
+
+    while (true)
+        Thread::sleep(1000);
+}
diff --git a/src/tests/drivers/canbus/test-can-protocol.cpp b/src/tests/drivers/canbus/test-can-protocol.cpp
deleted file mode 100644
index 9f8eef1ed68be96fccad479a789df19a701d2ba5..0000000000000000000000000000000000000000
--- a/src/tests/drivers/canbus/test-can-protocol.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-
-
-/* Copyright (c) 2022 Skyward Experimental Rocketry
- * Author: Federico Mandelli
- *
- * 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/canbus/CanProtocol.h>
-
-#include <thread>
-
-#include "drivers/canbus/BusLoadEstimation.h"
-#include "drivers/canbus/Canbus.h"
-#include "utils/collections/CircularBuffer.h"
-
-constexpr uint32_t BAUD_RATE         = 500 * 1000;
-constexpr float SAMPLE_POINT         = 87.5f / 100.0f;
-constexpr uint32_t MSG_DEADLINE      = 100;  // ms
-constexpr uint32_t MSG_LOST_DEADLINE = 400;  // ms
-
-using std::string;
-using namespace Boardcore;
-using namespace Boardcore::Canbus;
-using namespace miosix;
-
-#ifdef _ARCH_CORTEXM3_STM32
-using CanRX = Gpio<GPIOA_BASE, 11>;
-using CanTX = Gpio<GPIOA_BASE, 12>;
-#else
-using CanRX = Gpio<GPIOA_BASE, 11>;
-using CanTX = Gpio<GPIOA_BASE, 12>;
-#endif
-
-#define SLP 100
-miosix::FastMutex mutex;
-CanData toSend1;
-CanData toSend2;
-void sendData(CanProtocol* protocol, CanData* toSend)
-{
-    while (true)
-    {
-
-        TRACE("send\n");
-        {
-            miosix::Lock<miosix::FastMutex> l(mutex);
-            (*protocol).sendCan(*toSend);
-        }
-        Thread::sleep(SLP);
-    }
-}
-bool equal(CanData* first, CanData* second)
-{
-    if ((*first).canId != (*second).canId || (*first).len != (*second).len)
-    {
-        return false;
-    }
-    for (int i = 0; i < (*first).len; i++)
-    {
-        if ((*first).payload[i] != (*second).payload[i])
-            return false;
-    }
-    return true;
-}
-
-int main()
-{
-    {
-        miosix::FastInterruptDisableLock dLock;
-
-#ifdef _ARCH_CORTEXM3_STM32
-        CanRX::mode(Mode::ALTERNATE);
-        CanTX::mode(Mode::ALTERNATE);
-#else
-        CanRX::mode(Mode::ALTERNATE);
-        CanTX::mode(Mode::ALTERNATE);
-
-        CanRX::alternateFunction(9);
-        CanTX::alternateFunction(9);
-#endif
-    }
-    CanbusDriver::CanbusConfig cfg{};
-    CanbusDriver::AutoBitTiming bt;
-    bt.baudRate     = BAUD_RATE;
-    bt.samplePoint  = SAMPLE_POINT;
-    CanbusDriver* c = new CanbusDriver(CAN1, cfg, bt);
-    CanProtocol protocol(c);
-    // Allow every message
-    Mask32FilterBank f2(0, 0, 0, 0, 0, 0, 0);
-
-    c->addFilter(f2);
-    c->init();
-    protocol.start();
-
-    toSend1.canId      = 0x200;
-    toSend1.len        = 8;
-    toSend1.payload[0] = 0xffffffffffffffff;
-    toSend1.payload[1] = 2;
-    toSend1.payload[2] = 78022;
-    toSend1.payload[3] = 0xfffffffffffff;
-    toSend1.payload[4] = 23;
-    toSend1.payload[5] = 3234;
-    toSend1.payload[6] = 12;
-    toSend1.payload[7] = 0;
-    std::thread firstSend(sendData, &protocol, &toSend1);
-
-    Thread::sleep(10);
-    toSend2.canId      = 0x100;
-    toSend2.len        = 4;
-    toSend2.payload[0] = 0xffffff;
-    toSend2.payload[1] = 2;
-    toSend2.payload[2] = 0x123ff;
-    toSend2.payload[3] = 1;
-    std::thread secondSend(sendData, &protocol, &toSend2);
-    TRACE("start \n");
-    for (;;)
-    {
-
-        protocol.waitEmpty();
-        CanData temp = protocol.getPacket();
-        TRACE("received packet \n");
-        if ((!equal(&temp, &toSend1) && !equal(&temp, &toSend2)))
-        {
-            TRACE("Error\n");
-            TRACE("Received  %lu\n", temp.canId);
-            TRACE("Received %d\n", temp.len);
-            for (int i = 0; i < temp.len; i++)
-            {
-                TRACE("Received payload %d:  %llu,\n", i, temp.payload[i]);
-            }
-        }
-        else
-        {
-            TRACE("OK :) id  %lu\n", temp.canId);
-        }
-    }
-}