diff --git a/src/shared/sensors/ADS131M04/ADS131M04.cpp b/src/shared/sensors/ADS131M04/ADS131M04.cpp
index e8619ac47f4c7afcd999fe4a49959df38edbd19e..bb614f6cef4029e39f0d869011422141108158b9 100644
--- a/src/shared/sensors/ADS131M04/ADS131M04.cpp
+++ b/src/shared/sensors/ADS131M04/ADS131M04.cpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2020 Skyward Experimental Rocketry
+/* Copyright (c) 2023 Skyward Experimental Rocketry
  * Author: Alberto Nidasio
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -27,7 +27,7 @@
 #include <utils/CRC.h>
 
 using namespace miosix;
-using namespace Boardcore::ADS131M04RegisterBitMasks;
+using namespace Boardcore::ADS131M04Defs;
 
 namespace Boardcore
 {
@@ -38,7 +38,16 @@ ADS131M04::ADS131M04(SPIBusInterface &bus, miosix::GpioPin cs,
 {
 }
 
-ADS131M04::ADS131M04(SPISlave spiSlave) : spiSlave(spiSlave) {}
+ADS131M04::ADS131M04(SPISlave spiSlave) : spiSlave(spiSlave)
+{
+    // Initialize the channel configurations
+    for (int i = 0; i < CHANNELS_NUM; i++)
+    {
+        channelsPGAGain[i] = PGA::PGA_1;
+        channelsOffset[i]  = 0;
+        channelsGain[i]    = 1.0;
+    }
+}
 
 SPIBusConfig ADS131M04::getDefaultSPIConfig()
 {
@@ -48,28 +57,22 @@ SPIBusConfig ADS131M04::getDefaultSPIConfig()
     return spiConfig;
 }
 
-void ADS131M04::setOversamplingRatio(OversamplingRatio ratio)
-{
-    changeRegister(Registers::REG_CLOCK, static_cast<uint16_t>(ratio),
-                   REG_CLOCK_OSR);
-}
-
 bool ADS131M04::init() { return true; }
 
 bool ADS131M04::reset()
 {
-    // Write all the communication frame which is maximum 6 24bit words
-    uint8_t data[18] = {0};
+    SPITransaction transaction(spiSlave);
 
-    // Prepare the command
-    data[1] = static_cast<uint16_t>(Commands::RESET);
+    uint8_t data[ADS131M04Defs::FULL_FRAME_SIZE] = {0};
+    sendCommand(transaction, Command::RESET, data);
 
-    SPITransaction transaction(spiSlave);
-    transaction.write(data, sizeof(data));
+    // The reset command takes typically 5us to reload the register contents.
+    // We wait 10us to be sure
     miosix::delayUs(10);
-    transaction.read(data, 3);
 
-    uint16_t response = data[0] << 8 | data[1];
+    // To read the response we need to read the full 3 bytes word. The response
+    // code is contained in the first 2 bytes, the last byte is padding
+    uint16_t response = transaction.read24() >> 8;
 
     // Check for the correct response
     if (response != RESET_CMD_RESPONSE)
@@ -84,242 +87,189 @@ bool ADS131M04::reset()
 
 void ADS131M04::calibrateOffset()
 {
-    // Reset all offsets and gains
-    for (int i = 0; i < 8; i++)
+    // Reset all offsets and gains, otherwise the calibration would be wrong
+    for (int i = 0; i < CHANNELS_NUM; i++)
     {
-        setChannelOffset(static_cast<Channel>(i), 0);
+        setChannelOffsetImpl(static_cast<Channel>(i), 0);
+        setChannelGainCalibrationImpl(static_cast<Channel>(i), 1.0);
     }
 
     // Sample the channels and average the samples
-    int32_t averageValues[4] = {0};
-    for (int i = 0; i < 250; i++)
+    int32_t averageValues[CHANNELS_NUM] = {0};
+    int realSampleCount                 = 0;
+    for (int i = 0; i < CALIBRATION_SAMPLES; i++)
     {
+        // Wait for a sample to be ready
         Thread::sleep(4);
 
-        uint8_t data[18] = {0};
-
-        data[0] = static_cast<uint16_t>(Commands::NULL_CMD) >> 8;
-        data[1] = static_cast<uint16_t>(Commands::NULL_CMD);
-
-        SPITransaction transaction(spiSlave);
-        transaction.transfer(data, sizeof(data));
-
-        for (int j = 0; j < 4; j++)
+        // Sample the channels
+        int32_t rawValues[CHANNELS_NUM];
+        if (!readSamples(rawValues))
         {
-            int32_t tmp = static_cast<uint32_t>(data[j * 3 + 3]) << 16 |
-                          static_cast<uint32_t>(data[j * 3 + 4]) << 8 |
-                          static_cast<uint32_t>(data[j * 3 + 5]);
-
-            // Check for the sign bit
-            if (tmp & 0x00800000)
-                tmp |= 0xFF000000;  // Extend the sign bit
+            // If the CRC failed we skip this sample
+            continue;
+        }
 
-            averageValues[j] += tmp;
+        for (int j = 0; j < CHANNELS_NUM; j++)
+        {
+            averageValues[j] += rawValues[j];
         }
-    }
-    for (int i = 0; i < 4; i++)
-        averageValues[i] /= 250;
 
-    // Configure the offset registers
-    for (int i = 0; i < 4; i++)
+        realSampleCount++;
+    }
+    for (int i = 0; i < CHANNELS_NUM; i++)
     {
+        // Compute the average
+        averageValues[i] /= realSampleCount;
+        LOG_INFO(logger, "Channel {} average offset: {}", i,
+                 averageValues[i] * getLSBSizeFromGain(channelsPGAGain[i]));
+
+        // The new offset is the average value
         setChannelOffset(static_cast<Channel>(i), averageValues[i]);
+
+        // Reset the gain as it was before
+        setChannelGainCalibrationImpl(static_cast<Channel>(i), channelsGain[i]);
     }
 }
 
+void ADS131M04::setOversamplingRatio(OversamplingRatio ratio)
+{
+    changeRegister(Register::REG_CLOCK, static_cast<uint16_t>(ratio),
+                   RegClockMasks::OSR);
+}
+
 void ADS131M04::setChannelPGA(Channel channel, PGA gain)
 {
+    setChannelPGAImpl(channel, gain);
     channelsPGAGain[static_cast<int>(channel)] = gain;
-
-    int shift = static_cast<int>(channel) * 4;
-    changeRegister(Registers::REG_GAIN, static_cast<uint16_t>(gain) << shift,
-                   REG_GAIN_PGAGAIN0 << shift);
 }
 
 void ADS131M04::setChannelOffset(Channel channel, uint32_t offset)
 {
-    // Set the correct registers based on the selected channel
-    Registers regMSB, regLSB;
-    switch (static_cast<int>(channel))
-    {
-        case 0:
-            regMSB = Registers::REG_CH0_OCAL_MSB;
-            regLSB = Registers::REG_CH0_OCAL_LSB;
-            break;
-        case 1:
-            regMSB = Registers::REG_CH1_OCAL_MSB;
-            regLSB = Registers::REG_CH1_OCAL_LSB;
-            break;
-        case 2:
-            regMSB = Registers::REG_CH2_OCAL_MSB;
-            regLSB = Registers::REG_CH2_OCAL_LSB;
-            break;
-        default:
-            regMSB = Registers::REG_CH3_OCAL_MSB;
-            regLSB = Registers::REG_CH3_OCAL_LSB;
-            break;
-    }
-
-    writeRegister(regMSB, offset >> 8);
-    writeRegister(regLSB, offset << 16);
+    setChannelOffsetImpl(channel, offset);
+    channelsOffset[static_cast<int>(channel)] = offset;
 }
 
 void ADS131M04::setChannelGainCalibration(Channel channel, double gain)
 {
-    // Check gain value
-    if (gain < 0 || gain > 2)
-        return;
-
-    // Set the correct registers based on the selected channel
-    Registers regMSB, regLSB;
-    switch (static_cast<int>(channel))
-    {
-        case 0:
-            regMSB = Registers::REG_CH0_GCAL_MSB;
-            regLSB = Registers::REG_CH0_GCAL_LSB;
-            break;
-        case 1:
-            regMSB = Registers::REG_CH1_GCAL_MSB;
-            regLSB = Registers::REG_CH1_GCAL_LSB;
-            break;
-        case 2:
-            regMSB = Registers::REG_CH2_GCAL_MSB;
-            regLSB = Registers::REG_CH2_GCAL_LSB;
-            break;
-        default:
-            regMSB = Registers::REG_CH3_GCAL_MSB;
-            regLSB = Registers::REG_CH3_GCAL_LSB;
-            break;
-    }
-
-    // Compute the correct gain register parameter
-    uint32_t rawGain = gain * 8388608;  // 2^23
-
-    writeRegister(regMSB, rawGain >> 16);
-    writeRegister(regLSB, rawGain << 8);
-}
-
-void ADS131M04::setChannelInput(Channel channel, Input input)
-{
-    // Set the correct register based on the selected channel
-    Registers reg;
-    switch (static_cast<int>(channel))
-    {
-        case 0:
-            reg = Registers::REG_CH0_CFG;
-            break;
-        case 1:
-            reg = Registers::REG_CH1_CFG;
-            break;
-        case 2:
-            reg = Registers::REG_CH2_CFG;
-            break;
-        default:
-            reg = Registers::REG_CH3_CFG;
-            break;
-    }
-
-    uint16_t value = readRegister(reg);
-    value &= REG_CHx_CFG_MUX;
-    value |= static_cast<uint16_t>(input);
-    writeRegister(reg, value);
+    setChannelGainCalibrationImpl(channel, gain);
+    channelsGain[static_cast<int>(channel)] = gain;
 }
 
 void ADS131M04::enableChannel(Channel channel)
 {
-    changeRegister(Registers::REG_CLOCK, 1 << (static_cast<int>(channel) + 8),
+    changeRegister(Register::REG_CLOCK, 1 << (static_cast<int>(channel) + 8),
                    1 << (static_cast<int>(channel) + 8));
 }
 
 void ADS131M04::disableChannel(Channel channel)
 {
-    changeRegister(Registers::REG_CLOCK, 0,
+    changeRegister(Register::REG_CLOCK, 0,
                    1 << (static_cast<int>(channel) + 8));
 }
 
 void ADS131M04::enableGlobalChopMode()
 {
-    changeRegister(Registers::REG_CFG, ADS131M04RegisterBitMasks::REG_CFG_GC_EN,
-                   ADS131M04RegisterBitMasks::REG_CFG_GC_EN);
+    changeRegister(Register::REG_CFG, RegConfigurationMasks::GC_EN,
+                   RegConfigurationMasks::GC_EN);
 }
 
 void ADS131M04::disableGlobalChopMode()
 {
-    changeRegister(Registers::REG_CFG, 0,
-                   ADS131M04RegisterBitMasks::REG_CFG_GC_EN);
-}
-
-ADCData ADS131M04::getVoltage(Channel channel)
-{
-    return {lastSample.timestamp, static_cast<uint8_t>(channel),
-            lastSample.voltage[static_cast<uint8_t>(channel)]};
+    changeRegister(Register::REG_CFG, 0, RegConfigurationMasks::GC_EN);
 }
 
 bool ADS131M04::selfTest()
 {
-    static constexpr float V_REF              = 1.2;
-    static constexpr float TEST_SIGNAL_FACTOR = 2 / 15;
-    static constexpr float TEST_SIGNAL_SLACK  = 0.1;  // Not defined in DS
-
-    float voltage[4] = {0};
+    float voltage[CHANNELS_NUM] = {0};
+    bool success                = true;
 
-    // Connect all channels to the positive DC test signal
-    for (int i = 0; i < 4; i++)
+    // Reset PGA, offsets and gains and connect the positive test signal
+    for (int i = 0; i < CHANNELS_NUM; i++)
     {
+        setChannelPGAImpl(static_cast<Channel>(i), PGA::PGA_1);
+        setChannelOffsetImpl(static_cast<Channel>(i), 0);
+        setChannelGainCalibrationImpl(static_cast<Channel>(i), 1.0);
         setChannelInput(static_cast<Channel>(i), Input::POSITIVE_DC_TEST);
-        setChannelPGA(static_cast<Channel>(i), PGA::PGA_1);
     }
 
     // Take some samples
-    for (int i = 0; i < 100; i++)
+    for (int i = 0; i < SELF_TEST_SAMPLES; i++)
     {
         Thread::sleep(4);
         auto newSample = sampleImpl();
 
-        for (int j = 0; j < 4; j++)
+        for (int j = 0; j < CHANNELS_NUM; j++)
         {
             voltage[j] += newSample.voltage[j];
         }
     }
 
-    // Check values
-    for (int i = 0; i < 4; i++)
+    // Check the values
+    for (int i = 0; i < CHANNELS_NUM; i++)
     {
-        voltage[i] /= 100;
+        // Compute the average
+        voltage[i] /= SELF_TEST_SAMPLES;
+
+        // Check if the value is in the acceptable range
         if (voltage[i] < V_REF * TEST_SIGNAL_FACTOR - TEST_SIGNAL_SLACK)
-            return false;
+        {
+            LOG_ERR(logger,
+                    "Self test failed on channel {} on positive test signal, "
+                    "value was {}",
+                    i, voltage[i]);
+            success = false;
+        }
     }
 
     // Connect all channels to the negative DC test signal
-    for (int i = 0; i < 4; i++)
+    for (int i = 0; i < CHANNELS_NUM; i++)
     {
         setChannelInput(static_cast<Channel>(i), Input::NEGATIVE_DC_TEST);
     }
 
     // Take some samples
-    for (int i = 0; i < 100; i++)
+    for (int i = 0; i < SELF_TEST_SAMPLES; i++)
     {
         Thread::sleep(4);
         auto newSample = sampleImpl();
 
-        for (int j = 0; j < 4; j++)
+        for (int j = 0; j < CHANNELS_NUM; j++)
         {
             voltage[j] += newSample.voltage[j];
         }
     }
 
-    // Check values
-    for (int i = 0; i < 4; i++)
+    // Check the values
+    for (int i = 0; i < CHANNELS_NUM; i++)
     {
-        voltage[i] /= 100;
+        // Compute the average
+        voltage[i] /= SELF_TEST_SAMPLES;
+
+        // Check if the value is in the acceptable range
         if (voltage[i] > -V_REF * TEST_SIGNAL_FACTOR + TEST_SIGNAL_SLACK)
-            return false;
+        {
+            LOG_ERR(logger,
+                    "Self test failed on channel {} on negative test signal, "
+                    "value was {}",
+                    i, voltage[i]);
+            success = false;
+        }
     }
 
-    // Reset channel connections
-    for (int i = 0; i < 8; i++)
+    // If any channel failed we return false
+    if (!success)
     {
-        setChannelInput(static_cast<Channel>(i), Input::POSITIVE_DC_TEST);
+        return false;
+    }
+
+    // Reset channels previous configuration
+    for (int i = 0; i < CHANNELS_NUM; i++)
+    {
+        setChannelPGAImpl(static_cast<Channel>(i), channelsPGAGain[i]);
+        setChannelOffsetImpl(static_cast<Channel>(i), channelsOffset[i]);
+        setChannelGainCalibrationImpl(static_cast<Channel>(i), channelsGain[i]);
+        setChannelInput(static_cast<Channel>(i), Input::DEFAULT);
     }
 
     return true;
@@ -327,27 +277,184 @@ bool ADS131M04::selfTest()
 
 ADS131M04Data ADS131M04::sampleImpl()
 {
-    // Send the NULL command and read response
-    uint8_t data[18] = {0};
+    // Read the samples and check if the CRC is correct
+    int32_t rawValues[CHANNELS_NUM];
+    if (!readSamples(rawValues))
+    {
+        // The CRC failed, return the last sample
+        return lastSample;
+    }
 
-    data[0] = static_cast<uint16_t>(Commands::NULL_CMD) >> 8;
-    data[1] = static_cast<uint16_t>(Commands::NULL_CMD);
+    // Convert the raw values to voltages
+    ADS131M04Data adcData;
+    adcData.timestamp = TimestampTimer::getTimestamp();
+    for (int i = 0; i < CHANNELS_NUM; i++)
+    {
+        adcData.voltage[i] =
+            rawValues[i] * getLSBSizeFromGain(channelsPGAGain[i]);
+    }
 
-    SPITransaction transaction(spiSlave);
-    transaction.transfer(data, sizeof(data));
+    return adcData;
+}
 
-    // Extract each channel value
-    int32_t rawValue[4];
-    for (int i = 0; i < 4; i++)
+void ADS131M04::setChannelInput(Channel channel, Input input)
+{
+    Register reg = getChannelConfigRegister(channel);
+    changeRegister(reg, static_cast<uint16_t>(input), RegChannelMasks::CFG_MUX);
+}
+
+void ADS131M04::setChannelPGAImpl(Channel channel, PGA gain)
+{
+    int shift = static_cast<int>(channel) * 4;
+    changeRegister(Register::REG_GAIN, static_cast<uint16_t>(gain) << shift,
+                   RegGainMasks::PGA_GAIN_0 << shift);
+}
+
+void ADS131M04::setChannelOffsetImpl(Channel channel, uint32_t offset)
+{
+    // The offset is a 24 bit value. Its two most significant bytes are stored
+    // in the MSB register, and the least significant in the LSB register, like
+    // this:
+    // +----------+-------+
+    // |          | 23-16 |
+    // | OCAL_MSB +-------+
+    // |          | 15-8  |
+    // +----------+-------+
+    // |          |  7-0  |
+    // | OCAL_LSB +-------+
+    // |          |       |
+    // +----------+-------+
+    writeRegister(getChannelOffsetRegisterMSB(channel), offset >> 8);
+    writeRegister(getChannelOffsetRegisterLSB(channel), offset << 8);
+}
+
+void ADS131M04::setChannelGainCalibrationImpl(Channel channel, double gain)
+{
+    // If the user passes a value outside the range [0, 2] we cap it.
+    if (gain < 0)
+    {
+        gain = 0;
+    }
+    else if (gain > 2)
     {
-        rawValue[i] = static_cast<uint32_t>(data[i * 3 + 3]) << 16 |
-                      static_cast<uint32_t>(data[i * 3 + 4]) << 8 |
-                      static_cast<uint32_t>(data[i * 3 + 5]);
+        gain = 2;
     }
 
+    // The ADS131M04 corrects for gain errors by multiplying the ADC conversion
+    // result using the gain calibration registers.
+    // The contents of the gain calibration registers are interpreted by the
+    // dives as 24-bit unsigned values corresponding to linear steps from 0 to
+    // 2-(1/2^23).
+    // So effectively the register value is a fixed point number with 1bit for
+    // the integer part and 23 bits for the fractional part.
+    // So to convert the gain value to the register value we multiply it by 2^23
+    uint32_t rawGain = gain * (1 << 23);
+
+    // The gain is a 24 bit value. Its two most significant bytes are stored
+    // in the MSB register, and the least significant in the LSB register, like
+    // this:
+    // +----------+-------+
+    // |          | 23-16 |
+    // | GCAL_MSB +-------+
+    // |          | 15-8  |
+    // +----------+-------+
+    // |          |  7-0  |
+    // | GCAL_LSB +-------+
+    // |          |       |
+    // +----------+-------+
+    writeRegister(getChannelGainRegisterMSB(channel), rawGain >> 8);
+    writeRegister(getChannelGainRegisterLSB(channel), rawGain << 8);
+}
+
+Register ADS131M04::getChannelConfigRegister(Channel channel)
+{
+    switch (static_cast<int>(channel))
+    {
+        case 0:
+            return Register::REG_CH0_CFG;
+        case 1:
+            return Register::REG_CH1_CFG;
+        case 2:
+            return Register::REG_CH2_CFG;
+        default:
+            return Register::REG_CH3_CFG;
+    }
+}
+
+Register ADS131M04::getChannelOffsetRegisterMSB(Channel channel)
+{
+    switch (static_cast<int>(channel))
+    {
+        case 0:
+            return Register::REG_CH0_OCAL_MSB;
+        case 1:
+            return Register::REG_CH1_OCAL_MSB;
+        case 2:
+            return Register::REG_CH2_OCAL_MSB;
+        default:
+            return Register::REG_CH3_OCAL_MSB;
+    }
+}
+
+Register ADS131M04::getChannelOffsetRegisterLSB(Channel channel)
+{
+    switch (static_cast<int>(channel))
+    {
+        case 0:
+            return Register::REG_CH0_OCAL_LSB;
+        case 1:
+            return Register::REG_CH1_OCAL_LSB;
+        case 2:
+            return Register::REG_CH2_OCAL_LSB;
+        default:
+            return Register::REG_CH3_OCAL_LSB;
+    }
+}
+
+Register ADS131M04::getChannelGainRegisterMSB(Channel channel)
+{
+    switch (static_cast<int>(channel))
+    {
+        case 0:
+            return Register::REG_CH0_GCAL_MSB;
+        case 1:
+            return Register::REG_CH1_GCAL_MSB;
+        case 2:
+            return Register::REG_CH2_GCAL_MSB;
+        default:
+            return Register::REG_CH3_GCAL_MSB;
+    }
+}
+
+Register ADS131M04::getChannelGainRegisterLSB(Channel channel)
+{
+    switch (static_cast<int>(channel))
+    {
+        case 0:
+            return Register::REG_CH0_GCAL_LSB;
+        case 1:
+            return Register::REG_CH1_GCAL_LSB;
+        case 2:
+            return Register::REG_CH2_GCAL_LSB;
+        default:
+            return Register::REG_CH3_GCAL_LSB;
+    }
+}
+
+bool ADS131M04::readSamples(int32_t rawValues[CHANNELS_NUM])
+{
+    // Send the NULL command and read response
+    uint8_t data[FULL_FRAME_SIZE] = {0};
+
+    data[0] = (static_cast<uint16_t>(Command::NULL_CMD) & 0xff00) >> 8;
+    data[1] = (static_cast<uint16_t>(Command::NULL_CMD) & 0x00ff);
+
+    SPITransaction transaction(spiSlave);
+    transaction.transfer(data, sizeof(data));
+
     // Extract and verify the CRC
     uint16_t dataCrc =
-        static_cast<uint32_t>(data[15]) << 8 | static_cast<uint32_t>(data[16]);
+        static_cast<uint16_t>(data[15]) << 8 | static_cast<uint16_t>(data[16]);
     uint16_t calculatedCrc = CRCUtils::crc16(data, sizeof(data) - 3);
 
     if (dataCrc != calculatedCrc)
@@ -356,36 +463,30 @@ ADS131M04Data ADS131M04::sampleImpl()
         LOG_ERR(logger, "Failed CRC check during sensor sampling");
 
         // Return and don't convert the corrupted data
-        return lastSample;
+        return false;
     }
 
-    // Set the two complement
-    for (int i = 0; i < 4; i++)
+    // Extract each channel value
+    for (int i = 0; i < CHANNELS_NUM; i++)
     {
-        // Check for the sign bit
-        if (rawValue[i] & 0x00800000)
-            rawValue[i] |= 0xFF000000;  // Extend the sign bit
-    }
+        rawValues[i] = static_cast<uint32_t>(data[i * 3 + 3]) << 16 |
+                       static_cast<uint32_t>(data[i * 3 + 4]) << 8 |
+                       static_cast<uint32_t>(data[i * 3 + 5]);
 
-    // Convert values
-    ADS131M04Data adcData;
-    adcData.timestamp = TimestampTimer::getTimestamp();
-    for (int i = 0; i < 4; i++)
-    {
-        adcData.voltage[i] =
-            rawValue[i] *
-            PGA_LSB_SIZE[static_cast<uint16_t>(channelsPGAGain[i])];
+        // Extend the sign bit with a double shift
+        rawValues[i] <<= 8;
+        rawValues[i] >>= 8;
     }
 
-    return adcData;
+    return true;
 }
 
-uint16_t ADS131M04::readRegister(Registers reg)
+uint16_t ADS131M04::readRegister(Register reg)
 {
     uint8_t data[3] = {0};
 
     // Prepare the command
-    data[0] = static_cast<uint16_t>(Commands::RREG) >> 8 |
+    data[0] = static_cast<uint16_t>(Command::RREG) >> 8 |
               static_cast<uint16_t>(reg) >> 1;
     data[1] = static_cast<uint16_t>(reg) << 7;
 
@@ -396,12 +497,14 @@ uint16_t ADS131M04::readRegister(Registers reg)
     return data[0] << 8 | data[1];
 }
 
-void ADS131M04::writeRegister(Registers reg, uint16_t data)
+void ADS131M04::writeRegister(Register reg, uint16_t data)
 {
+    // The write command uses two communication words (3 bytes each), one for
+    // the register address and one for the data to write
     uint8_t writeCommand[6] = {0};
 
     // Prepare the command
-    writeCommand[0] = static_cast<uint16_t>(Commands::WREG) >> 8 |
+    writeCommand[0] = static_cast<uint16_t>(Command::WREG) >> 8 |
                       static_cast<uint16_t>(reg) >> 1;
     writeCommand[1] = static_cast<uint16_t>(reg) << 7;
     writeCommand[3] = data >> 8;
@@ -410,29 +513,47 @@ void ADS131M04::writeRegister(Registers reg, uint16_t data)
     SPITransaction transaction(spiSlave);
     transaction.write(writeCommand, sizeof(writeCommand));
 
-    // Check response
-    transaction.read(writeCommand, 3);
-    uint16_t response = writeCommand[0] << 8 | writeCommand[1];
-    if (response != (0x4000 | (static_cast<uint16_t>(reg) << 7)))
+    // The response contains a fixed part and the register address.
+    uint16_t response = transaction.read24() >> 8;
+    if (response != (WRITE_CMD_RESPONSE | (static_cast<uint16_t>(reg) << 7)))
     {
         lastError = SensorErrors::COMMAND_FAILED;
         LOG_ERR(logger, "Write command failed, response was {:X}", response);
     }
 }
 
-void ADS131M04::changeRegister(Registers reg, uint16_t newValue, uint16_t mask)
+void ADS131M04::changeRegister(Register reg, uint16_t newValue, uint16_t mask)
 {
     // Read the clock register
     uint16_t regValue = readRegister(reg);
 
-    // Remove the OSR configuration
+    // Remove the target configuration
     regValue &= ~mask;
 
-    // Set the OSR
+    // Set the new value
     regValue |= newValue;
 
     // Write the new value
     writeRegister(reg, regValue);
 }
 
+void ADS131M04::sendCommand(SPITransaction &transaction, Command command,
+                            uint8_t data[FULL_FRAME_SIZE])
+{
+    // All commands (a part from read and write) needs the full 10 words
+    // communication frame. Each word is (by default) 3 bytes long
+
+    // The command is 16 bits long and goes in the most significant bytes of the
+    // first communication word
+    data[0] = (static_cast<uint16_t>(command) & 0xff00) >> 8;
+    data[1] = (static_cast<uint16_t>(command) & 0x00ff);
+
+    transaction.write(data, FULL_FRAME_SIZE);
+}
+
+float ADS131M04::getLSBSizeFromGain(PGA gain)
+{
+    return PGA_LSB_SIZE[static_cast<uint16_t>(gain)];
+}
+
 }  // namespace Boardcore
diff --git a/src/shared/sensors/ADS131M04/ADS131M04.h b/src/shared/sensors/ADS131M04/ADS131M04.h
index 4f4615ac1a44c0adc702a62537720b9727627a98..5140345736481fa8ab203cf70f9f6c6f2c840eac 100644
--- a/src/shared/sensors/ADS131M04/ADS131M04.h
+++ b/src/shared/sensors/ADS131M04/ADS131M04.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2021 Skyward Experimental Rocketry
+/* Copyright (c) 2023 Skyward Experimental Rocketry
  * Author: Alberto Nidasio
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -27,6 +27,7 @@
 #include <sensors/Sensor.h>
 
 #include "ADS131M04Data.h"
+#include "ADS131M04Defs.h"
 
 namespace Boardcore
 {
@@ -74,57 +75,7 @@ namespace Boardcore
 class ADS131M04 : public Sensor<ADS131M04Data>
 {
 public:
-    /**
-     * @brief ADC's oversampling ratio configurations.
-     *
-     * The OSR determins the output data rate, depending on the master clock
-     * frequency.
-     *
-     * ODR = f_CLK / 2 / OSR
-     *
-     * On Skyward's boards an 8.192MHz clock is used.
-     */
-    enum class OversamplingRatio : uint16_t
-    {
-        OSR_128   = 0,         // ODR is 32KHz
-        OSR_256   = 0x1 << 2,  // ODR is 16KHz
-        OSR_512   = 0x2 << 2,  // ODR is 8KHz
-        OSR_1024  = 0x3 << 2,  // ODR is 4KHz
-        OSR_2048  = 0x4 << 2,  // ODR is 2KHz
-        OSR_4096  = 0x5 << 2,  // ODR is 1KHz
-        OSR_8192  = 0x6 << 2,  // ODR is 500Hz
-        OSR_16256 = 0x7 << 2   // ODR is 250Hz
-    };
-
-    enum class PGA : uint16_t
-    {
-        PGA_1   = 0,    ///< Full scale resolution is ±1.2V
-        PGA_2   = 0x1,  ///< Full scale resolution is ±600mV
-        PGA_4   = 0x2,  ///< Full scale resolution is ±300mV
-        PGA_8   = 0x3,  ///< Full scale resolution is ±150mV
-        PGA_16  = 0x4,  ///< Full scale resolution is ±75mV
-        PGA_32  = 0x5,  ///< Full scale resolution is ±37.5mV
-        PGA_64  = 0x6,  ///< Full scale resolution is ±18.75mV
-        PGA_128 = 0x7   ///< Full scale resolution is ±9.375mV
-    };
-
-    enum class Channel : uint8_t
-    {
-        CHANNEL_0 = 0,
-        CHANNEL_1 = 1,
-        CHANNEL_2 = 2,
-        CHANNEL_3 = 3
-    };
-
-    enum class Input : uint8_t
-    {
-        DEFAULT          = 0,  // AINxP and AINxN (default)
-        SHORTED          = 1,  // ADC inputs shorted
-        POSITIVE_DC_TEST = 2,  // Positive DC test signal
-        NEGATIVE_DC_TEST = 3   // Negative DC test signal
-    };
-
-    ADS131M04(SPIBusInterface &bus, miosix::GpioPin cs,
+    ADS131M04(SPIBusInterface& bus, miosix::GpioPin cs,
               SPIBusConfig config = getDefaultSPIConfig());
 
     explicit ADS131M04(SPISlave spiSlave);
@@ -146,16 +97,16 @@ public:
      */
     void calibrateOffset();
 
-    void setOversamplingRatio(OversamplingRatio ratio);
+    void setOversamplingRatio(ADS131M04Defs::OversamplingRatio ratio);
 
-    void setChannelPGA(Channel channel, PGA gain);
+    void setChannelPGA(ADS131M04Defs::Channel channel, ADS131M04Defs::PGA gain);
 
     /**
      * @brief Sets the channel offset.
      *
      * Note that the device offset is a 24bit two complement.
      */
-    void setChannelOffset(Channel channel, uint32_t offset);
+    void setChannelOffset(ADS131M04Defs::Channel channel, uint32_t offset);
 
     /**
      * @brief Sets the channel gain calibration.
@@ -170,152 +121,69 @@ public:
      *
      * @param gain Must be between 0 and 2.
      */
-    void setChannelGainCalibration(Channel channel, double gain);
-
-    void setChannelInput(Channel channel, Input input);
+    void setChannelGainCalibration(ADS131M04Defs::Channel channel, double gain);
 
-    void enableChannel(Channel channel);
+    void enableChannel(ADS131M04Defs::Channel channel);
 
-    void disableChannel(Channel channel);
+    void disableChannel(ADS131M04Defs::Channel channel);
 
     void enableGlobalChopMode();
 
     void disableGlobalChopMode();
 
-    ADCData getVoltage(Channel channel);
-
     bool selfTest() override;
 
-protected:
+private:
     ADS131M04Data sampleImpl() override;
 
-private:
-    enum class Registers : uint16_t
-    {
-        // Device settings and indicators
-        REG_ID     = 0,
-        REG_STATUS = 0x1,
-
-        // Global settings across channels
-        REG_MODE        = 0x2,
-        REG_CLOCK       = 0x3,
-        REG_GAIN        = 0x4,
-        REG_CFG         = 0x6,
-        REG_THRSHLD_MSB = 0x7,
-        REG_THRSHLD_LSB = 0x8,
-
-        // Channel specific settings
-        REG_CH0_CFG      = 0x9,
-        REG_CH0_OCAL_MSB = 0xA,
-        REG_CH0_OCAL_LSB = 0xB,
-        REG_CH0_GCAL_MSB = 0xC,
-        REG_CH0_GCAL_LSB = 0xD,
-        REG_CH1_CFG      = 0xE,
-        REG_CH1_OCAL_MSB = 0xF,
-        REG_CH1_OCAL_LSB = 0x10,
-        REG_CH1_GCAL_MSB = 0x11,
-        REG_CH1_GCAL_LSB = 0x12,
-        REG_CH2_CFG      = 0x13,
-        REG_CH2_OCAL_MSB = 0x14,
-        REG_CH2_OCAL_LSB = 0x15,
-        REG_CH2_GCAL_MSB = 0x16,
-        REG_CH2_GCAL_LSB = 0x17,
-        REG_CH3_CFG      = 0x18,
-        REG_CH3_OCAL_MSB = 0x19,
-        REG_CH3_OCAL_LSB = 0x1A,
-        REG_CH3_GCAL_MSB = 0x1B,
-        REG_CH3_GCAL_LSB = 0x1C,
-
-        // Register map CRC
-        REG_REGMAP_CRC = 0x3E
-    };
-
-    uint16_t readRegister(Registers reg);
-
-    void writeRegister(Registers reg, uint16_t data);
-
-    void changeRegister(Registers reg, uint16_t newValue, uint16_t mask);
-
-    enum class Commands : uint16_t
-    {
-        NULL_CMD = 0,
-        RESET    = 0x11,
-        STANDBY  = 0x22,
-        WAKEUP   = 0x33,
-        LOCK     = 0x555,
-        UNLOCK   = 0x655,
-        RREG     = 0xA000,
-        WREG     = 0x6000
-    };
+    void setChannelInput(ADS131M04Defs::Channel channel,
+                         ADS131M04Defs::Input input);
 
-    SPISlave spiSlave;
+    void setChannelPGAImpl(ADS131M04Defs::Channel channel,
+                           ADS131M04Defs::PGA gain);
 
-    PGA channelsPGAGain[4] = {PGA::PGA_1, PGA::PGA_1, PGA::PGA_1, PGA::PGA_1};
+    void setChannelOffsetImpl(ADS131M04Defs::Channel channel, uint32_t offset);
 
-    PrintLogger logger = Logging::getLogger("ads131m04");
+    void setChannelGainCalibrationImpl(ADS131M04Defs::Channel channel,
+                                       double gain);
 
-    static constexpr uint16_t RESET_CMD_RESPONSE = 0xFF24;
+    ADS131M04Defs::Register getChannelConfigRegister(
+        ADS131M04Defs::Channel channel);
 
-    ///< Digit value in mV for each pga configurations
-    const float PGA_LSB_SIZE[8] = {143.0511e-9, 71.5256e-9, 35.7628e-9,
-                                   17.8814e-9,  8.9407e-9,  4.4703e-9,
-                                   2.2352e-9,   1.1176e-9};
-};
+    ADS131M04Defs::Register getChannelOffsetRegisterMSB(
+        ADS131M04Defs::Channel channel);
 
-namespace ADS131M04RegisterBitMasks
-{
+    ADS131M04Defs::Register getChannelOffsetRegisterLSB(
+        ADS131M04Defs::Channel channel);
 
-// Status register
-constexpr uint16_t REG_STATUS_LOCK     = 1 << 15;
-constexpr uint16_t REG_STATUS_F_RESYNC = 1 << 14;
-constexpr uint16_t REG_STATUS_REG_MAP  = 1 << 13;
-constexpr uint16_t REG_STATUS_CRC_ERR  = 1 << 12;
-constexpr uint16_t REG_STATUS_CRC_TYPE = 1 << 11;
-constexpr uint16_t REG_STATUS_RESET    = 1 << 10;
-constexpr uint16_t REG_STATUS_WLENGTH  = 3 << 8;
-constexpr uint16_t REG_STATUS_DRDY3    = 1 << 3;
-constexpr uint16_t REG_STATUS_DRDY2    = 1 << 2;
-constexpr uint16_t REG_STATUS_DRDY1    = 1 << 1;
-constexpr uint16_t REG_STATUS_DRDY0    = 1;
-
-// Mode register
-constexpr uint16_t REG_MODE_REG_CRC_EN = 1 << 13;
-constexpr uint16_t REG_MODE_RX_CRC_EN  = 1 << 12;
-constexpr uint16_t REG_MODE_CRC_TYPE   = 1 << 11;
-constexpr uint16_t REG_MODE_RESET      = 1 << 10;
-constexpr uint16_t REG_MODE_WLENGTH    = 3 << 8;
-constexpr uint16_t REG_MODE_TIMEOUT    = 1 << 4;
-constexpr uint16_t REG_MODE_DRDY_SEL   = 3 << 2;
-constexpr uint16_t REG_MODE_DRDY_HiZ   = 1 << 1;
-constexpr uint16_t REG_MODE_DRDY_FMT   = 1 << 0;
-
-// Clock register
-constexpr uint16_t REG_CLOCK_CH3_EN = 1 << 11;
-constexpr uint16_t REG_CLOCK_CH2_EN = 1 << 10;
-constexpr uint16_t REG_CLOCK_CH1_EN = 1 << 9;
-constexpr uint16_t REG_CLOCK_CH0_EN = 1 << 8;
-constexpr uint16_t REG_CLOCK_OSR    = 7 << 2;
-constexpr uint16_t REG_CLOCK_PWR    = 3;
-
-// Gain register
-constexpr uint16_t REG_GAIN_PGAGAIN3 = 7 << 12;
-constexpr uint16_t REG_GAIN_PGAGAIN2 = 7 << 8;
-constexpr uint16_t REG_GAIN_PGAGAIN1 = 7 << 4;
-constexpr uint16_t REG_GAIN_PGAGAIN0 = 7;
-
-// Configuration register
-constexpr uint16_t REG_CFG_GC_DLY   = 0xF << 9;
-constexpr uint16_t REG_CFG_GC_EN    = 1 << 8;
-constexpr uint16_t REG_CFG_CD_ALLCH = 1 << 7;
-constexpr uint16_t REG_CFG_CD_NUM   = 7 << 4;
-constexpr uint16_t REG_CFG_CD_LEN   = 7 << 1;
-constexpr uint16_t REG_CFG_CD_EN    = 1;
-
-// Channel configuration register
-constexpr uint16_t REG_CHx_CFG_PHASE     = 0x3FF << 6;
-constexpr uint16_t REG_CHx_CFG_DCBLK_DIS = 1 << 2;
-constexpr uint16_t REG_CHx_CFG_MUX       = 3;
-
-}  // namespace ADS131M04RegisterBitMasks
+    ADS131M04Defs::Register getChannelGainRegisterMSB(
+        ADS131M04Defs::Channel channel);
+
+    ADS131M04Defs::Register getChannelGainRegisterLSB(
+        ADS131M04Defs::Channel channel);
+
+    bool readSamples(int32_t rawValues[ADS131M04Defs::CHANNELS_NUM]);
+
+    uint16_t readRegister(ADS131M04Defs::Register reg);
+
+    void writeRegister(ADS131M04Defs::Register reg, uint16_t data);
+
+    void changeRegister(ADS131M04Defs::Register reg, uint16_t newValue,
+                        uint16_t mask);
+
+    void sendCommand(SPITransaction& transaction, ADS131M04Defs::Command cmd,
+                     uint8_t data[ADS131M04Defs::FULL_FRAME_SIZE]);
+
+    float getLSBSizeFromGain(ADS131M04Defs::PGA gain);
+
+    SPISlave spiSlave;
+
+    // Current channels configuration
+    ADS131M04Defs::PGA channelsPGAGain[ADS131M04Defs::CHANNELS_NUM];
+    uint32_t channelsOffset[ADS131M04Defs::CHANNELS_NUM];
+    double channelsGain[ADS131M04Defs::CHANNELS_NUM];
+
+    PrintLogger logger = Logging::getLogger("ads131m04");
+};
 
 }  // namespace Boardcore
diff --git a/src/shared/sensors/ADS131M04/ADS131M04Defs.h b/src/shared/sensors/ADS131M04/ADS131M04Defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..c90555df41eacec8f8ff6a32a75fb391a89dfbfb
--- /dev/null
+++ b/src/shared/sensors/ADS131M04/ADS131M04Defs.h
@@ -0,0 +1,216 @@
+/* Copyright (c) 2023 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 <stdint.h>
+
+namespace Boardcore
+{
+
+namespace ADS131M04Defs
+{
+
+static constexpr int CHANNELS_NUM            = 4;
+static constexpr int CALIBRATION_SAMPLES     = 250;
+static constexpr int SELF_TEST_SAMPLES       = 250;
+static constexpr int FULL_FRAME_SIZE         = 18;
+static constexpr uint16_t RESET_CMD_RESPONSE = 0xFF24;
+static constexpr uint16_t WRITE_CMD_RESPONSE = 0x4000;
+
+///< Digit value in mV for each pga configurations
+constexpr float PGA_LSB_SIZE[8] = {143.0511e-9, 71.5256e-9, 35.7628e-9,
+                                   17.8814e-9,  8.9407e-9,  4.4703e-9,
+                                   2.2352e-9,   1.1176e-9};
+
+static constexpr float V_REF              = 1.2;
+static constexpr float TEST_SIGNAL_FACTOR = 2 / 15;
+static constexpr float TEST_SIGNAL_SLACK  = 0.1;  // Not defined in DS
+
+/**
+ * @brief ADC's oversampling ratio configurations.
+ *
+ * The OSR determins the output data rate, depending on the master clock
+ * frequency.
+ *
+ * ODR = f_CLK / 2 / OSR
+ *
+ * On Skyward's boards an 8.192MHz clock is used.
+ */
+enum class OversamplingRatio : uint16_t
+{
+    OSR_128   = 0,         // ODR is 32KHz
+    OSR_256   = 0x1 << 2,  // ODR is 16KHz
+    OSR_512   = 0x2 << 2,  // ODR is 8KHz
+    OSR_1024  = 0x3 << 2,  // ODR is 4KHz
+    OSR_2048  = 0x4 << 2,  // ODR is 2KHz
+    OSR_4096  = 0x5 << 2,  // ODR is 1KHz
+    OSR_8192  = 0x6 << 2,  // ODR is 500Hz
+    OSR_16256 = 0x7 << 2   // ODR is 250Hz
+};
+
+enum class PGA : uint16_t
+{
+    PGA_1   = 0,    ///< Full scale resolution is ±1.2V
+    PGA_2   = 0x1,  ///< Full scale resolution is ±600mV
+    PGA_4   = 0x2,  ///< Full scale resolution is ±300mV
+    PGA_8   = 0x3,  ///< Full scale resolution is ±150mV
+    PGA_16  = 0x4,  ///< Full scale resolution is ±75mV
+    PGA_32  = 0x5,  ///< Full scale resolution is ±37.5mV
+    PGA_64  = 0x6,  ///< Full scale resolution is ±18.75mV
+    PGA_128 = 0x7   ///< Full scale resolution is ±9.375mV
+};
+
+enum class Channel : uint8_t
+{
+    CHANNEL_0 = 0,
+    CHANNEL_1 = 1,
+    CHANNEL_2 = 2,
+    CHANNEL_3 = 3
+};
+
+enum class Input : uint8_t
+{
+    DEFAULT          = 0,  // AINxP and AINxN (default)
+    SHORTED          = 1,  // ADC inputs shorted
+    POSITIVE_DC_TEST = 2,  // Positive DC test signal
+    NEGATIVE_DC_TEST = 3   // Negative DC test signal
+};
+
+enum class Register : uint16_t
+{
+    // Device settings and indicators
+    REG_ID     = 0,
+    REG_STATUS = 0x1,
+
+    // Global settings across channels
+    REG_MODE        = 0x2,
+    REG_CLOCK       = 0x3,
+    REG_GAIN        = 0x4,
+    REG_CFG         = 0x6,
+    REG_THRSHLD_MSB = 0x7,
+    REG_THRSHLD_LSB = 0x8,
+
+    // Channel specific settings
+    REG_CH0_CFG      = 0x9,
+    REG_CH0_OCAL_MSB = 0xA,
+    REG_CH0_OCAL_LSB = 0xB,
+    REG_CH0_GCAL_MSB = 0xC,
+    REG_CH0_GCAL_LSB = 0xD,
+    REG_CH1_CFG      = 0xE,
+    REG_CH1_OCAL_MSB = 0xF,
+    REG_CH1_OCAL_LSB = 0x10,
+    REG_CH1_GCAL_MSB = 0x11,
+    REG_CH1_GCAL_LSB = 0x12,
+    REG_CH2_CFG      = 0x13,
+    REG_CH2_OCAL_MSB = 0x14,
+    REG_CH2_OCAL_LSB = 0x15,
+    REG_CH2_GCAL_MSB = 0x16,
+    REG_CH2_GCAL_LSB = 0x17,
+    REG_CH3_CFG      = 0x18,
+    REG_CH3_OCAL_MSB = 0x19,
+    REG_CH3_OCAL_LSB = 0x1A,
+    REG_CH3_GCAL_MSB = 0x1B,
+    REG_CH3_GCAL_LSB = 0x1C,
+
+    // Register map CRC
+    REG_REGMAP_CRC = 0x3E
+};
+
+enum class Command : uint16_t
+{
+    NULL_CMD = 0x0000,
+    RESET    = 0x0011,
+    STANDBY  = 0x0022,
+    WAKEUP   = 0x0033,
+    LOCK     = 0x0555,
+    UNLOCK   = 0x0655,
+    RREG     = 0xA000,
+    WREG     = 0x6000
+};
+
+namespace RegStatusMasks
+{
+constexpr uint16_t LOCK     = 0x1 << 15;
+constexpr uint16_t F_RESYNC = 0x1 << 14;
+constexpr uint16_t REG_MAP  = 0x1 << 13;
+constexpr uint16_t CRC_ERR  = 0x1 << 12;
+constexpr uint16_t CRC_TYPE = 0x1 << 11;
+constexpr uint16_t RESET    = 0x1 << 10;
+constexpr uint16_t WLENGTH  = 0x3 << 8;
+constexpr uint16_t DRDY3    = 0x1 << 3;
+constexpr uint16_t DRDY2    = 0x1 << 2;
+constexpr uint16_t DRDY1    = 0x1 << 1;
+constexpr uint16_t DRDY0    = 0x1 << 0;
+}  // namespace RegStatusMasks
+
+namespace RegModeMasks
+{
+constexpr uint16_t REG_CRC_EN = 0x1 << 13;
+constexpr uint16_t RX_CRC_EN  = 0x1 << 12;
+constexpr uint16_t CRC_TYPE   = 0x1 << 11;
+constexpr uint16_t RESET      = 0x1 << 10;
+constexpr uint16_t WLENGTH    = 0x3 << 8;
+constexpr uint16_t TIMEOUT    = 0x1 << 4;
+constexpr uint16_t DRDY_SEL   = 0x3 << 2;
+constexpr uint16_t DRDY_HiZ   = 0x1 << 1;
+constexpr uint16_t DRDY_FMT   = 0x1 << 0;
+}  // namespace RegModeMasks
+
+namespace RegClockMasks
+{
+constexpr uint16_t CH3_EN     = 0x1 << 11;
+constexpr uint16_t CH2_EN     = 0x1 << 10;
+constexpr uint16_t CH1_EN     = 0x1 << 9;
+constexpr uint16_t CH0_EN     = 0x1 << 8;
+constexpr uint16_t OSR        = 0x7 << 2;
+constexpr uint16_t POWER_MODE = 0x3 << 0;
+}  // namespace RegClockMasks
+
+namespace RegGainMasks
+{
+constexpr uint16_t PGA_GAIN_3 = 0x7 << 12;
+constexpr uint16_t PGA_GAIN_2 = 0x7 << 8;
+constexpr uint16_t PGA_GAIN_1 = 0x7 << 4;
+constexpr uint16_t PGA_GAIN_0 = 0x7 << 0;
+}  // namespace RegGainMasks
+
+namespace RegConfigurationMasks
+{
+constexpr uint16_t GC_DLY   = 0xF << 9;
+constexpr uint16_t GC_EN    = 0x1 << 8;
+constexpr uint16_t CD_ALLCH = 0x1 << 7;
+constexpr uint16_t CD_NUM   = 0x7 << 4;
+constexpr uint16_t CD_LEN   = 0x7 << 1;
+constexpr uint16_t CD_EN    = 0x1 << 0;
+}  // namespace RegConfigurationMasks
+
+namespace RegChannelMasks
+{
+constexpr uint16_t CFG_PHASE     = 0x3FF << 6;
+constexpr uint16_t CFG_DCBLK_DIS = 0x001 << 2;
+constexpr uint16_t CFG_MUX       = 0x003 << 0;
+}  // namespace RegChannelMasks
+
+}  // namespace ADS131M04Defs
+
+}  // namespace Boardcore
diff --git a/src/shared/sensors/ADS131M08/ADS131M08.cpp b/src/shared/sensors/ADS131M08/ADS131M08.cpp
index 667ccd2449109ad690b190ed5ed62338e76ba54d..dfcda68abb9c52b4754ca6ca1e71057c1ed1136b 100644
--- a/src/shared/sensors/ADS131M08/ADS131M08.cpp
+++ b/src/shared/sensors/ADS131M08/ADS131M08.cpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2020 Skyward Experimental Rocketry
+/* Copyright (c) 2023 Skyward Experimental Rocketry
  * Author: Alberto Nidasio
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -27,7 +27,7 @@
 #include <utils/CRC.h>
 
 using namespace miosix;
-using namespace Boardcore::ADS131M08RegisterBitMasks;
+using namespace Boardcore::ADS131M08Defs;
 
 namespace Boardcore
 {
@@ -38,7 +38,16 @@ ADS131M08::ADS131M08(SPIBusInterface &bus, miosix::GpioPin cs,
 {
 }
 
-ADS131M08::ADS131M08(SPISlave spiSlave) : spiSlave(spiSlave) {}
+ADS131M08::ADS131M08(SPISlave spiSlave) : spiSlave(spiSlave)
+{
+    // Initialize the channel configurations
+    for (int i = 0; i < CHANNELS_NUM; i++)
+    {
+        channelsPGAGain[i] = PGA::PGA_1;
+        channelsOffset[i]  = 0;
+        channelsGain[i]    = 1.0;
+    }
+}
 
 SPIBusConfig ADS131M08::getDefaultSPIConfig()
 {
@@ -48,28 +57,22 @@ SPIBusConfig ADS131M08::getDefaultSPIConfig()
     return spiConfig;
 }
 
-void ADS131M08::setOversamplingRatio(OversamplingRatio ratio)
-{
-    changeRegister(Registers::REG_CLOCK, static_cast<uint16_t>(ratio),
-                   REG_CLOCK_OSR);
-}
-
 bool ADS131M08::init() { return true; }
 
 bool ADS131M08::reset()
 {
-    // Write all the communication frame which is maximum 6 24bit words
-    uint8_t data[30] = {0};
+    SPITransaction transaction(spiSlave);
 
-    // Prepare the command
-    data[1] = static_cast<uint16_t>(Commands::RESET);
+    uint8_t data[ADS131M08Defs::FULL_FRAME_SIZE] = {0};
+    sendCommand(transaction, Command::RESET, data);
 
-    SPITransaction transaction(spiSlave);
-    transaction.write(data, sizeof(data));
+    // The reset command takes typically 5us to reload the register contents.
+    // We wait 10us to be sure
     miosix::delayUs(10);
-    transaction.read(data, 3);
 
-    uint16_t response = data[0] << 8 | data[1];
+    // To read the response we need to read the full 3 bytes word. The response
+    // code is contained in the first 2 bytes, the last byte is padding
+    uint16_t response = transaction.read24() >> 8;
 
     // Check for the correct response
     if (response != RESET_CMD_RESPONSE)
@@ -84,297 +87,189 @@ bool ADS131M08::reset()
 
 void ADS131M08::calibrateOffset()
 {
-    // Reset all offsets and gains
-    for (int i = 0; i < 8; i++)
+    // Reset all offsets and gains, otherwise the calibration would be wrong
+    for (int i = 0; i < CHANNELS_NUM; i++)
     {
-        setChannelOffset(static_cast<Channel>(i), 0);
+        setChannelOffsetImpl(static_cast<Channel>(i), 0);
+        setChannelGainCalibrationImpl(static_cast<Channel>(i), 1.0);
     }
 
     // Sample the channels and average the samples
-    int32_t averageValues[8] = {0};
-    for (int i = 0; i < 250; i++)
+    int32_t averageValues[CHANNELS_NUM] = {0};
+    int realSampleCount                 = 0;
+    for (int i = 0; i < CALIBRATION_SAMPLES; i++)
     {
+        // Wait for a sample to be ready
         Thread::sleep(4);
 
-        uint8_t data[30] = {0};
-
-        data[0] = static_cast<uint16_t>(Commands::NULL_CMD) >> 8;
-        data[1] = static_cast<uint16_t>(Commands::NULL_CMD);
-
-        SPITransaction transaction(spiSlave);
-        transaction.transfer(data, sizeof(data));
-
-        for (int j = 0; j < 8; j++)
+        // Sample the channels
+        int32_t rawValues[CHANNELS_NUM];
+        if (!readSamples(rawValues))
         {
-            int32_t tmp = static_cast<uint32_t>(data[j * 3 + 3]) << 16 |
-                          static_cast<uint32_t>(data[j * 3 + 4]) << 8 |
-                          static_cast<uint32_t>(data[j * 3 + 5]);
-
-            // Check for the sign bit
-            if (tmp & 0x00800000)
-                tmp |= 0xFF000000;  // Extend the sign bit
+            // If the CRC failed we skip this sample
+            continue;
+        }
 
-            averageValues[j] += tmp;
+        for (int j = 0; j < CHANNELS_NUM; j++)
+        {
+            averageValues[j] += rawValues[j];
         }
-    }
-    for (int i = 0; i < 8; i++)
-        averageValues[i] /= 250;
 
-    // Configure the offset registers
-    for (int i = 0; i < 8; i++)
+        realSampleCount++;
+    }
+    for (int i = 0; i < CHANNELS_NUM; i++)
     {
+        // Compute the average
+        averageValues[i] /= realSampleCount;
+        LOG_INFO(logger, "Channel {} average offset: {}", i,
+                 averageValues[i] * getLSBSizeFromGain(channelsPGAGain[i]));
+
+        // The new offset is the average value
         setChannelOffset(static_cast<Channel>(i), averageValues[i]);
+
+        // Reset the gain as it was before
+        setChannelGainCalibrationImpl(static_cast<Channel>(i), channelsGain[i]);
     }
 }
 
+void ADS131M08::setOversamplingRatio(OversamplingRatio ratio)
+{
+    changeRegister(Register::REG_CLOCK, static_cast<uint16_t>(ratio),
+                   RegClockMasks::OSR);
+}
+
 void ADS131M08::setChannelPGA(Channel channel, PGA gain)
 {
+    setChannelPGAImpl(channel, gain);
     channelsPGAGain[static_cast<int>(channel)] = gain;
-
-    if (channel <= Channel::CHANNEL_3)
-    {
-        int shift = static_cast<int>(channel) * 4;
-        changeRegister(Registers::REG_GAIN_1,
-                       static_cast<uint16_t>(gain) << shift,
-                       REG_GAIN_PGAGAIN0 << shift);
-    }
-    else
-    {
-        int shift = (static_cast<int>(channel) - 4) * 4;
-        changeRegister(Registers::REG_GAIN_2,
-                       static_cast<uint16_t>(gain) << shift,
-                       REG_GAIN_PGAGAIN0 << shift);
-    }
 }
 
 void ADS131M08::setChannelOffset(Channel channel, uint32_t offset)
 {
-    // Set the correct registers based on the selected channel
-    Registers regMSB, regLSB;
-    switch (static_cast<int>(channel))
-    {
-        case 0:
-            regMSB = Registers::REG_CH0_OCAL_MSB;
-            regLSB = Registers::REG_CH0_OCAL_LSB;
-            break;
-        case 1:
-            regMSB = Registers::REG_CH1_OCAL_MSB;
-            regLSB = Registers::REG_CH1_OCAL_LSB;
-            break;
-        case 2:
-            regMSB = Registers::REG_CH2_OCAL_MSB;
-            regLSB = Registers::REG_CH2_OCAL_LSB;
-            break;
-        case 3:
-            regMSB = Registers::REG_CH3_OCAL_MSB;
-            regLSB = Registers::REG_CH3_OCAL_LSB;
-            break;
-        case 4:
-            regMSB = Registers::REG_CH4_OCAL_MSB;
-            regLSB = Registers::REG_CH4_OCAL_LSB;
-            break;
-        case 5:
-            regMSB = Registers::REG_CH5_OCAL_MSB;
-            regLSB = Registers::REG_CH5_OCAL_LSB;
-            break;
-        case 6:
-            regMSB = Registers::REG_CH6_OCAL_MSB;
-            regLSB = Registers::REG_CH6_OCAL_LSB;
-            break;
-        default:
-            regMSB = Registers::REG_CH7_OCAL_MSB;
-            regLSB = Registers::REG_CH7_OCAL_LSB;
-            break;
-    }
-
-    writeRegister(regMSB, offset >> 8);
-    writeRegister(regLSB, offset << 16);
+    setChannelOffsetImpl(channel, offset);
+    channelsOffset[static_cast<int>(channel)] = offset;
 }
 
 void ADS131M08::setChannelGainCalibration(Channel channel, double gain)
 {
-    // Check gain value
-    if (gain < 0 || gain > 2)
-        return;
-
-    // Set the correct registers based on the selected channel
-    Registers regMSB, regLSB;
-    switch (static_cast<int>(channel))
-    {
-        case 0:
-            regMSB = Registers::REG_CH0_GCAL_MSB;
-            regLSB = Registers::REG_CH0_GCAL_LSB;
-            break;
-        case 1:
-            regMSB = Registers::REG_CH1_GCAL_MSB;
-            regLSB = Registers::REG_CH1_GCAL_LSB;
-            break;
-        case 2:
-            regMSB = Registers::REG_CH2_GCAL_MSB;
-            regLSB = Registers::REG_CH2_GCAL_LSB;
-            break;
-        case 3:
-            regMSB = Registers::REG_CH3_GCAL_MSB;
-            regLSB = Registers::REG_CH3_GCAL_LSB;
-            break;
-        case 4:
-            regMSB = Registers::REG_CH4_GCAL_MSB;
-            regLSB = Registers::REG_CH4_GCAL_LSB;
-            break;
-        case 5:
-            regMSB = Registers::REG_CH5_GCAL_MSB;
-            regLSB = Registers::REG_CH5_GCAL_LSB;
-            break;
-        case 6:
-            regMSB = Registers::REG_CH6_GCAL_MSB;
-            regLSB = Registers::REG_CH6_GCAL_LSB;
-            break;
-        default:
-            regMSB = Registers::REG_CH7_GCAL_MSB;
-            regLSB = Registers::REG_CH7_GCAL_LSB;
-            break;
-    }
-
-    // Compute the correct gain register parameter
-    uint32_t rawGain = gain * 8388608;  // 2^23
-
-    writeRegister(regMSB, rawGain >> 16);
-    writeRegister(regLSB, rawGain << 8);
-}
-
-void ADS131M08::setChannelInput(Channel channel, Input input)
-{
-    // Set the correct register based on the selected channel
-    Registers reg;
-    switch (static_cast<int>(channel))
-    {
-        case 0:
-            reg = Registers::REG_CH0_CFG;
-            break;
-        case 1:
-            reg = Registers::REG_CH1_CFG;
-            break;
-        case 2:
-            reg = Registers::REG_CH2_CFG;
-            break;
-        case 3:
-            reg = Registers::REG_CH3_CFG;
-            break;
-        case 4:
-            reg = Registers::REG_CH4_CFG;
-            break;
-        case 5:
-            reg = Registers::REG_CH5_CFG;
-            break;
-        case 6:
-            reg = Registers::REG_CH6_CFG;
-            break;
-        default:
-            reg = Registers::REG_CH7_CFG;
-            break;
-    }
-
-    uint16_t value = readRegister(reg);
-    value &= REG_CHx_CFG_MUX;
-    value |= static_cast<uint16_t>(input);
-    writeRegister(reg, value);
+    setChannelGainCalibrationImpl(channel, gain);
+    channelsGain[static_cast<int>(channel)] = gain;
 }
 
 void ADS131M08::enableChannel(Channel channel)
 {
-    changeRegister(Registers::REG_CLOCK, 1 << (static_cast<int>(channel) + 8),
+    changeRegister(Register::REG_CLOCK, 1 << (static_cast<int>(channel) + 8),
                    1 << (static_cast<int>(channel) + 8));
 }
 
 void ADS131M08::disableChannel(Channel channel)
 {
-    changeRegister(Registers::REG_CLOCK, 0,
+    changeRegister(Register::REG_CLOCK, 0,
                    1 << (static_cast<int>(channel) + 8));
 }
 
 void ADS131M08::enableGlobalChopMode()
 {
-    changeRegister(Registers::REG_CFG, ADS131M08RegisterBitMasks::REG_CFG_GC_EN,
-                   ADS131M08RegisterBitMasks::REG_CFG_GC_EN);
+    changeRegister(Register::REG_CFG, RegConfigurationMasks::GC_EN,
+                   RegConfigurationMasks::GC_EN);
 }
 
 void ADS131M08::disableGlobalChopMode()
 {
-    changeRegister(Registers::REG_CFG, 0,
-                   ADS131M08RegisterBitMasks::REG_CFG_GC_EN);
-}
-
-ADCData ADS131M08::getVoltage(Channel channel)
-{
-    return {lastSample.timestamp, static_cast<uint8_t>(channel),
-            lastSample.voltage[static_cast<uint8_t>(channel)]};
+    changeRegister(Register::REG_CFG, 0, RegConfigurationMasks::GC_EN);
 }
 
 bool ADS131M08::selfTest()
 {
-    static constexpr float V_REF              = 1.2;
-    static constexpr float TEST_SIGNAL_FACTOR = 2 / 15;
-    static constexpr float TEST_SIGNAL_SLACK  = 0.1;  // Not defined in DS
-
-    float voltage[8] = {0};
+    float voltage[CHANNELS_NUM] = {0};
+    bool success                = true;
 
-    // Connect all channels to the positive DC test signal
-    for (int i = 0; i < 8; i++)
+    // Reset PGA, offsets and gains and connect the positive test signal
+    for (int i = 0; i < CHANNELS_NUM; i++)
     {
+        setChannelPGAImpl(static_cast<Channel>(i), PGA::PGA_1);
+        setChannelOffsetImpl(static_cast<Channel>(i), 0);
+        setChannelGainCalibrationImpl(static_cast<Channel>(i), 1.0);
         setChannelInput(static_cast<Channel>(i), Input::POSITIVE_DC_TEST);
-        setChannelPGA(static_cast<Channel>(i), PGA::PGA_1);
     }
 
     // Take some samples
-    for (int i = 0; i < 100; i++)
+    for (int i = 0; i < SELF_TEST_SAMPLES; i++)
     {
         Thread::sleep(4);
         auto newSample = sampleImpl();
 
-        for (int j = 0; j < 8; j++)
+        for (int j = 0; j < CHANNELS_NUM; j++)
         {
             voltage[j] += newSample.voltage[j];
         }
     }
 
-    // Check values
-    for (int i = 0; i < 8; i++)
+    // Check the values
+    for (int i = 0; i < CHANNELS_NUM; i++)
     {
-        voltage[i] /= 100;
+        // Compute the average
+        voltage[i] /= SELF_TEST_SAMPLES;
+
+        // Check if the value is in the acceptable range
         if (voltage[i] < V_REF * TEST_SIGNAL_FACTOR - TEST_SIGNAL_SLACK)
-            return false;
+        {
+            LOG_ERR(logger,
+                    "Self test failed on channel {} on positive test signal, "
+                    "value was {}",
+                    i, voltage[i]);
+            success = false;
+        }
     }
 
     // Connect all channels to the negative DC test signal
-    for (int i = 0; i < 8; i++)
+    for (int i = 0; i < CHANNELS_NUM; i++)
     {
         setChannelInput(static_cast<Channel>(i), Input::NEGATIVE_DC_TEST);
     }
 
     // Take some samples
-    for (int i = 0; i < 100; i++)
+    for (int i = 0; i < SELF_TEST_SAMPLES; i++)
     {
         Thread::sleep(4);
         auto newSample = sampleImpl();
 
-        for (int j = 0; j < 8; j++)
+        for (int j = 0; j < CHANNELS_NUM; j++)
         {
             voltage[j] += newSample.voltage[j];
         }
     }
 
-    // Check values
-    for (int i = 0; i < 8; i++)
+    // Check the values
+    for (int i = 0; i < CHANNELS_NUM; i++)
     {
-        voltage[i] /= 100;
+        // Compute the average
+        voltage[i] /= SELF_TEST_SAMPLES;
+
+        // Check if the value is in the acceptable range
         if (voltage[i] > -V_REF * TEST_SIGNAL_FACTOR + TEST_SIGNAL_SLACK)
-            return false;
+        {
+            LOG_ERR(logger,
+                    "Self test failed on channel {} on negative test signal, "
+                    "value was {}",
+                    i, voltage[i]);
+            success = false;
+        }
     }
 
-    // Reset channel connections
-    for (int i = 0; i < 8; i++)
+    // If any channel failed we return false
+    if (!success)
     {
-        setChannelInput(static_cast<Channel>(i), Input::POSITIVE_DC_TEST);
+        return false;
+    }
+
+    // Reset channels previous configuration
+    for (int i = 0; i < CHANNELS_NUM; i++)
+    {
+        setChannelPGAImpl(static_cast<Channel>(i), channelsPGAGain[i]);
+        setChannelOffsetImpl(static_cast<Channel>(i), channelsOffset[i]);
+        setChannelGainCalibrationImpl(static_cast<Channel>(i), channelsGain[i]);
+        setChannelInput(static_cast<Channel>(i), Input::DEFAULT);
     }
 
     return true;
@@ -382,27 +277,235 @@ bool ADS131M08::selfTest()
 
 ADS131M08Data ADS131M08::sampleImpl()
 {
-    // Send the NULL command and read response
-    uint8_t data[30] = {0};
+    // Read the samples and check if the CRC is correct
+    int32_t rawValues[CHANNELS_NUM];
+    if (!readSamples(rawValues))
+    {
+        // The CRC failed, return the last sample
+        return lastSample;
+    }
+
+    // Convert the raw values to voltages
+    ADS131M08Data adcData;
+    adcData.timestamp = TimestampTimer::getTimestamp();
+    for (int i = 0; i < CHANNELS_NUM; i++)
+    {
+        adcData.voltage[i] =
+            rawValues[i] * getLSBSizeFromGain(channelsPGAGain[i]);
+    }
 
-    data[0] = static_cast<uint16_t>(Commands::NULL_CMD) >> 8;
-    data[1] = static_cast<uint16_t>(Commands::NULL_CMD);
+    return adcData;
+}
 
-    SPITransaction transaction(spiSlave);
-    transaction.transfer(data, sizeof(data));
+void ADS131M08::setChannelInput(Channel channel, Input input)
+{
+    Register reg = getChannelConfigRegister(channel);
+    changeRegister(reg, static_cast<uint16_t>(input), RegChannelMasks::CFG_MUX);
+}
 
-    // Extract each channel value
-    int32_t rawValue[8];
-    for (int i = 0; i < 8; i++)
+void ADS131M08::setChannelPGAImpl(Channel channel, PGA gain)
+{
+    if (channel <= Channel::CHANNEL_3)
     {
-        rawValue[i] = static_cast<uint32_t>(data[i * 3 + 3]) << 16 |
-                      static_cast<uint32_t>(data[i * 3 + 4]) << 8 |
-                      static_cast<uint32_t>(data[i * 3 + 5]);
+        int shift = static_cast<int>(channel) * 4;
+        changeRegister(Register::REG_GAIN_1,
+                       static_cast<uint16_t>(gain) << shift,
+                       RegGainMasks::PGA_GAIN_0 << shift);
+    }
+    else
+    {
+        int shift = (static_cast<int>(channel) - 4) * 4;
+        changeRegister(Register::REG_GAIN_2,
+                       static_cast<uint16_t>(gain) << shift,
+                       RegGainMasks::PGA_GAIN_0 << shift);
+    }
+}
+
+void ADS131M08::setChannelOffsetImpl(Channel channel, uint32_t offset)
+{
+    // The offset is a 24 bit value. Its two most significant bytes are stored
+    // in the MSB register, and the least significant in the LSB register, like
+    // this:
+    // +----------+-------+
+    // |          | 23-16 |
+    // | OCAL_MSB +-------+
+    // |          | 15-8  |
+    // +----------+-------+
+    // |          |  7-0  |
+    // | OCAL_LSB +-------+
+    // |          |       |
+    // +----------+-------+
+    writeRegister(getChannelOffsetRegisterMSB(channel), offset >> 8);
+    writeRegister(getChannelOffsetRegisterLSB(channel), offset << 8);
+}
+
+void ADS131M08::setChannelGainCalibrationImpl(Channel channel, double gain)
+{
+    // If the user passes a value outside the range [0, 2] we cap it.
+    if (gain < 0)
+    {
+        gain = 0;
+    }
+    else if (gain > 2)
+    {
+        gain = 2;
+    }
+
+    // The ADS131M08 corrects for gain errors by multiplying the ADC conversion
+    // result using the gain calibration registers.
+    // The contents of the gain calibration registers are interpreted by the
+    // dives as 24-bit unsigned values corresponding to linear steps from 0 to
+    // 2-(1/2^23).
+    // So effectively the register value is a fixed point number with 1bit for
+    // the integer part and 23 bits for the fractional part.
+    // So to convert the gain value to the register value we multiply it by 2^23
+    uint32_t rawGain = gain * (1 << 23);
+
+    // The gain is a 24 bit value. Its two most significant bytes are stored
+    // in the MSB register, and the least significant in the LSB register, like
+    // this:
+    // +----------+-------+
+    // |          | 23-16 |
+    // | GCAL_MSB +-------+
+    // |          | 15-8  |
+    // +----------+-------+
+    // |          |  7-0  |
+    // | GCAL_LSB +-------+
+    // |          |       |
+    // +----------+-------+
+    writeRegister(getChannelGainRegisterMSB(channel), rawGain >> 8);
+    writeRegister(getChannelGainRegisterLSB(channel), rawGain << 8);
+}
+
+Register ADS131M08::getChannelConfigRegister(Channel channel)
+{
+    switch (static_cast<int>(channel))
+    {
+        case 0:
+            return Register::REG_CH0_CFG;
+        case 1:
+            return Register::REG_CH1_CFG;
+        case 2:
+            return Register::REG_CH2_CFG;
+        case 3:
+            return Register::REG_CH3_CFG;
+        case 4:
+            return Register::REG_CH4_CFG;
+        case 5:
+            return Register::REG_CH5_CFG;
+        case 6:
+            return Register::REG_CH6_CFG;
+        default:
+            return Register::REG_CH7_CFG;
+    }
+}
+
+Register ADS131M08::getChannelOffsetRegisterMSB(Channel channel)
+{
+    switch (static_cast<int>(channel))
+    {
+        case 0:
+            return Register::REG_CH0_OCAL_MSB;
+        case 1:
+            return Register::REG_CH1_OCAL_MSB;
+        case 2:
+            return Register::REG_CH2_OCAL_MSB;
+        case 3:
+            return Register::REG_CH3_OCAL_MSB;
+        case 4:
+            return Register::REG_CH4_OCAL_MSB;
+        case 5:
+            return Register::REG_CH5_OCAL_MSB;
+        case 6:
+            return Register::REG_CH6_OCAL_MSB;
+        default:
+            return Register::REG_CH7_OCAL_MSB;
+    }
+}
+
+Register ADS131M08::getChannelOffsetRegisterLSB(Channel channel)
+{
+    switch (static_cast<int>(channel))
+    {
+        case 0:
+            return Register::REG_CH0_OCAL_LSB;
+        case 1:
+            return Register::REG_CH1_OCAL_LSB;
+        case 2:
+            return Register::REG_CH2_OCAL_LSB;
+        case 3:
+            return Register::REG_CH3_OCAL_LSB;
+        case 4:
+            return Register::REG_CH4_OCAL_LSB;
+        case 5:
+            return Register::REG_CH5_OCAL_LSB;
+        case 6:
+            return Register::REG_CH6_OCAL_LSB;
+        default:
+            return Register::REG_CH7_OCAL_LSB;
     }
+}
+
+Register ADS131M08::getChannelGainRegisterMSB(Channel channel)
+{
+    switch (static_cast<int>(channel))
+    {
+        case 0:
+            return Register::REG_CH0_GCAL_MSB;
+        case 1:
+            return Register::REG_CH1_GCAL_MSB;
+        case 2:
+            return Register::REG_CH2_GCAL_MSB;
+        case 3:
+            return Register::REG_CH3_GCAL_MSB;
+        case 4:
+            return Register::REG_CH4_GCAL_MSB;
+        case 5:
+            return Register::REG_CH5_GCAL_MSB;
+        case 6:
+            return Register::REG_CH6_GCAL_MSB;
+        default:
+            return Register::REG_CH7_GCAL_MSB;
+    }
+}
+
+Register ADS131M08::getChannelGainRegisterLSB(Channel channel)
+{
+    switch (static_cast<int>(channel))
+    {
+        case 0:
+            return Register::REG_CH0_GCAL_LSB;
+        case 1:
+            return Register::REG_CH1_GCAL_LSB;
+        case 2:
+            return Register::REG_CH2_GCAL_LSB;
+        case 3:
+            return Register::REG_CH3_GCAL_LSB;
+        case 4:
+            return Register::REG_CH4_GCAL_LSB;
+        case 5:
+            return Register::REG_CH5_GCAL_LSB;
+        case 6:
+            return Register::REG_CH6_GCAL_LSB;
+        default:
+            return Register::REG_CH7_GCAL_LSB;
+    }
+}
+
+bool ADS131M08::readSamples(int32_t rawValues[CHANNELS_NUM])
+{
+    // Send the NULL command and read response
+    uint8_t data[FULL_FRAME_SIZE] = {0};
+
+    data[0] = (static_cast<uint16_t>(Command::NULL_CMD) & 0xff00) >> 8;
+    data[1] = (static_cast<uint16_t>(Command::NULL_CMD) & 0x00ff);
+
+    SPITransaction transaction(spiSlave);
+    transaction.transfer(data, sizeof(data));
 
     // Extract and verify the CRC
     uint16_t dataCrc =
-        static_cast<uint32_t>(data[27]) << 8 | static_cast<uint32_t>(data[28]);
+        static_cast<uint16_t>(data[27]) << 8 | static_cast<uint16_t>(data[28]);
     uint16_t calculatedCrc = CRCUtils::crc16(data, sizeof(data) - 3);
 
     if (dataCrc != calculatedCrc)
@@ -411,36 +514,30 @@ ADS131M08Data ADS131M08::sampleImpl()
         LOG_ERR(logger, "Failed CRC check during sensor sampling");
 
         // Return and don't convert the corrupted data
-        return lastSample;
+        return false;
     }
 
-    // Set the two complement
-    for (int i = 0; i < 8; i++)
+    // Extract each channel value
+    for (int i = 0; i < CHANNELS_NUM; i++)
     {
-        // Check for the sign bit
-        if (rawValue[i] & 0x00800000)
-            rawValue[i] |= 0xFF000000;  // Extend the sign bit
-    }
+        rawValues[i] = static_cast<uint32_t>(data[i * 3 + 3]) << 16 |
+                       static_cast<uint32_t>(data[i * 3 + 4]) << 8 |
+                       static_cast<uint32_t>(data[i * 3 + 5]);
 
-    // Convert values
-    ADS131M08Data adcData;
-    adcData.timestamp = TimestampTimer::getTimestamp();
-    for (int i = 0; i < 8; i++)
-    {
-        adcData.voltage[i] =
-            rawValue[i] *
-            PGA_LSB_SIZE[static_cast<uint16_t>(channelsPGAGain[i])];
+        // Extend the sign bit with a double shift
+        rawValues[i] <<= 8;
+        rawValues[i] >>= 8;
     }
 
-    return adcData;
+    return true;
 }
 
-uint16_t ADS131M08::readRegister(Registers reg)
+uint16_t ADS131M08::readRegister(Register reg)
 {
     uint8_t data[3] = {0};
 
     // Prepare the command
-    data[0] = static_cast<uint16_t>(Commands::RREG) >> 8 |
+    data[0] = static_cast<uint16_t>(Command::RREG) >> 8 |
               static_cast<uint16_t>(reg) >> 1;
     data[1] = static_cast<uint16_t>(reg) << 7;
 
@@ -451,12 +548,14 @@ uint16_t ADS131M08::readRegister(Registers reg)
     return data[0] << 8 | data[1];
 }
 
-void ADS131M08::writeRegister(Registers reg, uint16_t data)
+void ADS131M08::writeRegister(Register reg, uint16_t data)
 {
+    // The write command uses two communication words (3 bytes each), one for
+    // the register address and one for the data to write
     uint8_t writeCommand[6] = {0};
 
     // Prepare the command
-    writeCommand[0] = static_cast<uint16_t>(Commands::WREG) >> 8 |
+    writeCommand[0] = static_cast<uint16_t>(Command::WREG) >> 8 |
                       static_cast<uint16_t>(reg) >> 1;
     writeCommand[1] = static_cast<uint16_t>(reg) << 7;
     writeCommand[3] = data >> 8;
@@ -465,29 +564,47 @@ void ADS131M08::writeRegister(Registers reg, uint16_t data)
     SPITransaction transaction(spiSlave);
     transaction.write(writeCommand, sizeof(writeCommand));
 
-    // Check response
-    transaction.read(writeCommand, 3);
-    uint16_t response = writeCommand[0] << 8 | writeCommand[1];
-    if (response != (0x4000 | (static_cast<uint16_t>(reg) << 7)))
+    // The response contains a fixed part and the register address.
+    uint16_t response = transaction.read24() >> 8;
+    if (response != (WRITE_CMD_RESPONSE | (static_cast<uint16_t>(reg) << 7)))
     {
         lastError = SensorErrors::COMMAND_FAILED;
         LOG_ERR(logger, "Write command failed, response was {:X}", response);
     }
 }
 
-void ADS131M08::changeRegister(Registers reg, uint16_t newValue, uint16_t mask)
+void ADS131M08::changeRegister(Register reg, uint16_t newValue, uint16_t mask)
 {
     // Read the clock register
     uint16_t regValue = readRegister(reg);
 
-    // Remove the OSR configuration
+    // Remove the target configuration
     regValue &= ~mask;
 
-    // Set the OSR
+    // Set the new value
     regValue |= newValue;
 
     // Write the new value
     writeRegister(reg, regValue);
 }
 
+void ADS131M08::sendCommand(SPITransaction &transaction, Command command,
+                            uint8_t data[FULL_FRAME_SIZE])
+{
+    // All commands (a part from read and write) needs the full 10 words
+    // communication frame. Each word is (by default) 3 bytes long
+
+    // The command is 16 bits long and goes in the most significant bytes of the
+    // first communication word
+    data[0] = (static_cast<uint16_t>(command) & 0xff00) >> 8;
+    data[1] = (static_cast<uint16_t>(command) & 0x00ff);
+
+    transaction.write(data, FULL_FRAME_SIZE);
+}
+
+float ADS131M08::getLSBSizeFromGain(PGA gain)
+{
+    return PGA_LSB_SIZE[static_cast<uint16_t>(gain)];
+}
+
 }  // namespace Boardcore
diff --git a/src/shared/sensors/ADS131M08/ADS131M08.h b/src/shared/sensors/ADS131M08/ADS131M08.h
index 374288a80525f288c71d89e647e67126b11fd1bc..705cf29dd3e7d71ad5a406a53ee0b4d9c5e0f014 100644
--- a/src/shared/sensors/ADS131M08/ADS131M08.h
+++ b/src/shared/sensors/ADS131M08/ADS131M08.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2021 Skyward Experimental Rocketry
+/* Copyright (c) 2023 Skyward Experimental Rocketry
  * Author: Alberto Nidasio
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -27,6 +27,7 @@
 #include <sensors/Sensor.h>
 
 #include "ADS131M08Data.h"
+#include "ADS131M08Defs.h"
 
 namespace Boardcore
 {
@@ -74,61 +75,7 @@ namespace Boardcore
 class ADS131M08 : public Sensor<ADS131M08Data>
 {
 public:
-    /**
-     * @brief ADC's oversampling ratio configurations.
-     *
-     * The OSR determins the output data rate, depending on the master clock
-     * frequency.
-     *
-     * ODR = f_CLK / 2 / OSR
-     *
-     * On Skyward's boards an 8.192MHz clock is used.
-     */
-    enum class OversamplingRatio : uint16_t
-    {
-        OSR_128   = 0,         // ODR is 32KHz
-        OSR_256   = 0x1 << 2,  // ODR is 16KHz
-        OSR_512   = 0x2 << 2,  // ODR is 8KHz
-        OSR_1024  = 0x3 << 2,  // ODR is 4KHz
-        OSR_2048  = 0x4 << 2,  // ODR is 2KHz
-        OSR_4096  = 0x5 << 2,  // ODR is 1KHz
-        OSR_8192  = 0x6 << 2,  // ODR is 500Hz
-        OSR_16256 = 0x7 << 2   // ODR is 250Hz
-    };
-
-    enum class PGA : uint16_t
-    {
-        PGA_1   = 0,    ///< Full scale resolution is ±1.2V
-        PGA_2   = 0x1,  ///< Full scale resolution is ±600mV
-        PGA_4   = 0x2,  ///< Full scale resolution is ±300mV
-        PGA_8   = 0x3,  ///< Full scale resolution is ±150mV
-        PGA_16  = 0x4,  ///< Full scale resolution is ±75mV
-        PGA_32  = 0x5,  ///< Full scale resolution is ±37.5mV
-        PGA_64  = 0x6,  ///< Full scale resolution is ±18.75mV
-        PGA_128 = 0x7   ///< Full scale resolution is ±9.375mV
-    };
-
-    enum class Channel : uint8_t
-    {
-        CHANNEL_0 = 0,
-        CHANNEL_1 = 1,
-        CHANNEL_2 = 2,
-        CHANNEL_3 = 3,
-        CHANNEL_4 = 4,
-        CHANNEL_5 = 5,
-        CHANNEL_6 = 6,
-        CHANNEL_7 = 7
-    };
-
-    enum class Input : uint8_t
-    {
-        DEFAULT          = 0,  // AINxP and AINxN (default)
-        SHORTED          = 1,  // ADC inputs shorted
-        POSITIVE_DC_TEST = 2,  // Positive DC test signal
-        NEGATIVE_DC_TEST = 3   // Negative DC test signal
-    };
-
-    ADS131M08(SPIBusInterface &bus, miosix::GpioPin cs,
+    ADS131M08(SPIBusInterface& bus, miosix::GpioPin cs,
               SPIBusConfig config = getDefaultSPIConfig());
 
     explicit ADS131M08(SPISlave spiSlave);
@@ -150,16 +97,16 @@ public:
      */
     void calibrateOffset();
 
-    void setOversamplingRatio(OversamplingRatio ratio);
+    void setOversamplingRatio(ADS131M08Defs::OversamplingRatio ratio);
 
-    void setChannelPGA(Channel channel, PGA gain);
+    void setChannelPGA(ADS131M08Defs::Channel channel, ADS131M08Defs::PGA gain);
 
     /**
      * @brief Sets the channel offset.
      *
      * Note that the device offset is a 24bit two complement.
      */
-    void setChannelOffset(Channel channel, uint32_t offset);
+    void setChannelOffset(ADS131M08Defs::Channel channel, uint32_t offset);
 
     /**
      * @brief Sets the channel gain calibration.
@@ -174,174 +121,69 @@ public:
      *
      * @param gain Must be between 0 and 2.
      */
-    void setChannelGainCalibration(Channel channel, double gain);
-
-    void setChannelInput(Channel channel, Input input);
+    void setChannelGainCalibration(ADS131M08Defs::Channel channel, double gain);
 
-    void enableChannel(Channel channel);
+    void enableChannel(ADS131M08Defs::Channel channel);
 
-    void disableChannel(Channel channel);
+    void disableChannel(ADS131M08Defs::Channel channel);
 
     void enableGlobalChopMode();
 
     void disableGlobalChopMode();
 
-    ADCData getVoltage(Channel channel);
-
     bool selfTest() override;
 
-protected:
+private:
     ADS131M08Data sampleImpl() override;
 
-private:
-    enum class Registers : uint16_t
-    {
-        // Device settings and indicators
-        REG_ID     = 0,
-        REG_STATUS = 0x1,
-
-        // Global settings across channels
-        REG_MODE        = 0x2,
-        REG_CLOCK       = 0x3,
-        REG_GAIN_1      = 0x4,
-        REG_GAIN_2      = 0x5,
-        REG_CFG         = 0x6,
-        REG_THRSHLD_MSB = 0x7,
-        REG_THRSHLD_LSB = 0x8,
-
-        // Channel specific settings
-        REG_CH0_CFG      = 0x9,
-        REG_CH0_OCAL_MSB = 0xA,
-        REG_CH0_OCAL_LSB = 0xB,
-        REG_CH0_GCAL_MSB = 0xC,
-        REG_CH0_GCAL_LSB = 0xD,
-        REG_CH1_CFG      = 0xE,
-        REG_CH1_OCAL_MSB = 0xF,
-        REG_CH1_OCAL_LSB = 0x10,
-        REG_CH1_GCAL_MSB = 0x11,
-        REG_CH1_GCAL_LSB = 0x12,
-        REG_CH2_CFG      = 0x13,
-        REG_CH2_OCAL_MSB = 0x14,
-        REG_CH2_OCAL_LSB = 0x15,
-        REG_CH2_GCAL_MSB = 0x16,
-        REG_CH2_GCAL_LSB = 0x17,
-        REG_CH3_CFG      = 0x18,
-        REG_CH3_OCAL_MSB = 0x19,
-        REG_CH3_OCAL_LSB = 0x1A,
-        REG_CH3_GCAL_MSB = 0x1B,
-        REG_CH3_GCAL_LSB = 0x1C,
-        REG_CH4_CFG      = 0x1D,
-        REG_CH4_OCAL_MSB = 0x1E,
-        REG_CH4_OCAL_LSB = 0x1F,
-        REG_CH4_GCAL_MSB = 0x20,
-        REG_CH4_GCAL_LSB = 0x21,
-        REG_CH5_CFG      = 0x22,
-        REG_CH5_OCAL_MSB = 0x23,
-        REG_CH5_OCAL_LSB = 0x24,
-        REG_CH5_GCAL_MSB = 0x25,
-        REG_CH5_GCAL_LSB = 0x26,
-        REG_CH6_CFG      = 0x27,
-        REG_CH6_OCAL_MSB = 0x28,
-        REG_CH6_OCAL_LSB = 0x29,
-        REG_CH6_GCAL_MSB = 0x2A,
-        REG_CH6_GCAL_LSB = 0x2B,
-        REG_CH7_CFG      = 0x2C,
-        REG_CH7_OCAL_MSB = 0x2D,
-        REG_CH7_OCAL_LSB = 0x2E,
-        REG_CH7_GCAL_MSB = 0x2F,
-        REG_CH7_GCAL_LSB = 0x30,
-
-        // Register map CRC
-        REG_REGMAP_CRC = 0x3E
-    };
-
-    uint16_t readRegister(Registers reg);
-
-    void writeRegister(Registers reg, uint16_t data);
-
-    void changeRegister(Registers reg, uint16_t newValue, uint16_t mask);
-
-    enum class Commands : uint16_t
-    {
-        NULL_CMD = 0,
-        RESET    = 0x11,
-        STANDBY  = 0x22,
-        WAKEUP   = 0x33,
-        LOCK     = 0x555,
-        UNLOCK   = 0x655,
-        RREG     = 0xA000,
-        WREG     = 0x6000
-    };
+    void setChannelInput(ADS131M08Defs::Channel channel,
+                         ADS131M08Defs::Input input);
 
-    SPISlave spiSlave;
+    void setChannelPGAImpl(ADS131M08Defs::Channel channel,
+                           ADS131M08Defs::PGA gain);
 
-    PGA channelsPGAGain[8] = {PGA::PGA_1, PGA::PGA_1, PGA::PGA_1, PGA::PGA_1,
-                              PGA::PGA_1, PGA::PGA_1, PGA::PGA_1, PGA::PGA_1};
+    void setChannelOffsetImpl(ADS131M08Defs::Channel channel, uint32_t offset);
 
-    PrintLogger logger = Logging::getLogger("ads131m08");
+    void setChannelGainCalibrationImpl(ADS131M08Defs::Channel channel,
+                                       double gain);
 
-    static constexpr uint16_t RESET_CMD_RESPONSE = 0xFF28;
+    ADS131M08Defs::Register getChannelConfigRegister(
+        ADS131M08Defs::Channel channel);
 
-    ///< Digit value in mV for each pga configurations
-    const float PGA_LSB_SIZE[8] = {143.0511e-9, 71.5256e-9, 35.7628e-9,
-                                   17.8814e-9,  8.9407e-9,  4.4703e-9,
-                                   2.2352e-9,   1.1176e-9};
-};
+    ADS131M08Defs::Register getChannelOffsetRegisterMSB(
+        ADS131M08Defs::Channel channel);
 
-namespace ADS131M08RegisterBitMasks
-{
+    ADS131M08Defs::Register getChannelOffsetRegisterLSB(
+        ADS131M08Defs::Channel channel);
 
-// Status register
-constexpr uint16_t REG_STATUS_LOCK     = 1 << 15;
-constexpr uint16_t REG_STATUS_F_RESYNC = 1 << 14;
-constexpr uint16_t REG_STATUS_REG_MAP  = 1 << 13;
-constexpr uint16_t REG_STATUS_CRC_ERR  = 1 << 12;
-constexpr uint16_t REG_STATUS_CRC_TYPE = 1 << 11;
-constexpr uint16_t REG_STATUS_RESET    = 1 << 10;
-constexpr uint16_t REG_STATUS_WLENGTH  = 3 << 8;
-constexpr uint16_t REG_STATUS_DRDY3    = 1 << 3;
-constexpr uint16_t REG_STATUS_DRDY2    = 1 << 2;
-constexpr uint16_t REG_STATUS_DRDY1    = 1 << 1;
-constexpr uint16_t REG_STATUS_DRDY0    = 1;
-
-// Mode register
-constexpr uint16_t REG_MODE_REG_CRC_EN = 1 << 13;
-constexpr uint16_t REG_MODE_RX_CRC_EN  = 1 << 12;
-constexpr uint16_t REG_MODE_CRC_TYPE   = 1 << 11;
-constexpr uint16_t REG_MODE_RESET      = 1 << 10;
-constexpr uint16_t REG_MODE_WLENGTH    = 3 << 8;
-constexpr uint16_t REG_MODE_TIMEOUT    = 1 << 4;
-constexpr uint16_t REG_MODE_DRDY_SEL   = 3 << 2;
-constexpr uint16_t REG_MODE_DRDY_HiZ   = 1 << 1;
-constexpr uint16_t REG_MODE_DRDY_FMT   = 1 << 0;
-
-// Clock register
-constexpr uint16_t REG_CLOCK_CH3_EN = 1 << 11;
-constexpr uint16_t REG_CLOCK_CH2_EN = 1 << 10;
-constexpr uint16_t REG_CLOCK_CH1_EN = 1 << 9;
-constexpr uint16_t REG_CLOCK_CH0_EN = 1 << 8;
-constexpr uint16_t REG_CLOCK_OSR    = 7 << 2;
-constexpr uint16_t REG_CLOCK_PWR    = 3;
-
-// Gain register
-constexpr uint16_t REG_GAIN_PGAGAIN3 = 7 << 12;
-constexpr uint16_t REG_GAIN_PGAGAIN2 = 7 << 8;
-constexpr uint16_t REG_GAIN_PGAGAIN1 = 7 << 4;
-constexpr uint16_t REG_GAIN_PGAGAIN0 = 7;
-
-// Configuration register
-constexpr uint16_t REG_CFG_GC_DLY   = 0xF << 9;
-constexpr uint16_t REG_CFG_GC_EN    = 1 << 8;
-constexpr uint16_t REG_CFG_CD_ALLCH = 1 << 7;
-constexpr uint16_t REG_CFG_CD_NUM   = 7 << 4;
-constexpr uint16_t REG_CFG_CD_LEN   = 7 << 1;
-constexpr uint16_t REG_CFG_CD_EN    = 1;
-
-// Channel configuration register
-constexpr uint16_t REG_CHx_CFG_PHASE     = 0x3FF << 6;
-constexpr uint16_t REG_CHx_CFG_DCBLK_DIS = 1 << 2;
-constexpr uint16_t REG_CHx_CFG_MUX       = 3;
-
-}  // namespace ADS131M08RegisterBitMasks
+    ADS131M08Defs::Register getChannelGainRegisterMSB(
+        ADS131M08Defs::Channel channel);
+
+    ADS131M08Defs::Register getChannelGainRegisterLSB(
+        ADS131M08Defs::Channel channel);
+
+    bool readSamples(int32_t rawValues[ADS131M08Defs::CHANNELS_NUM]);
+
+    uint16_t readRegister(ADS131M08Defs::Register reg);
+
+    void writeRegister(ADS131M08Defs::Register reg, uint16_t data);
+
+    void changeRegister(ADS131M08Defs::Register reg, uint16_t newValue,
+                        uint16_t mask);
+
+    void sendCommand(SPITransaction& transaction, ADS131M08Defs::Command cmd,
+                     uint8_t data[ADS131M08Defs::FULL_FRAME_SIZE]);
+
+    float getLSBSizeFromGain(ADS131M08Defs::PGA gain);
+
+    SPISlave spiSlave;
+
+    // Current channels configuration
+    ADS131M08Defs::PGA channelsPGAGain[ADS131M08Defs::CHANNELS_NUM];
+    uint32_t channelsOffset[ADS131M08Defs::CHANNELS_NUM];
+    double channelsGain[ADS131M08Defs::CHANNELS_NUM];
+
+    PrintLogger logger = Logging::getLogger("ads131m08");
+};
 
 }  // namespace Boardcore
diff --git a/src/shared/sensors/ADS131M08/ADS131M08Data.h b/src/shared/sensors/ADS131M08/ADS131M08Data.h
index 355bcd4dc944197999a379a4eb0e043babd83033..ac28f168b6dc248e23f5e5ebfa566ced2353c17a 100644
--- a/src/shared/sensors/ADS131M08/ADS131M08Data.h
+++ b/src/shared/sensors/ADS131M08/ADS131M08Data.h
@@ -26,6 +26,8 @@
 
 #include <ostream>
 
+#include "ADS131M08Defs.h"
+
 namespace Boardcore
 {
 
@@ -64,6 +66,12 @@ struct ADS131M08Data
            << voltage[2] << "," << voltage[3] << "," << voltage[4] << ","
            << voltage[5] << "," << voltage[6] << "," << voltage[7] << "\n";
     }
+
+    ADCData getVoltage(ADS131M08Defs::Channel channel)
+    {
+        return {timestamp, static_cast<uint8_t>(channel),
+                voltage[static_cast<uint8_t>(channel)]};
+    }
 };
 
 }  // namespace Boardcore
diff --git a/src/shared/sensors/ADS131M08/ADS131M08Defs.h b/src/shared/sensors/ADS131M08/ADS131M08Defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..8600aaedab7ed1068621da5ebf3aca24419d9084
--- /dev/null
+++ b/src/shared/sensors/ADS131M08/ADS131M08Defs.h
@@ -0,0 +1,241 @@
+/* Copyright (c) 2023 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 <stdint.h>
+
+namespace Boardcore
+{
+
+namespace ADS131M08Defs
+{
+
+static constexpr int CHANNELS_NUM            = 8;
+static constexpr int CALIBRATION_SAMPLES     = 250;
+static constexpr int SELF_TEST_SAMPLES       = 250;
+static constexpr int FULL_FRAME_SIZE         = 30;
+static constexpr uint16_t RESET_CMD_RESPONSE = 0xFF28;
+static constexpr uint16_t WRITE_CMD_RESPONSE = 0x4000;
+
+///< Digit value in mV for each pga configurations
+constexpr float PGA_LSB_SIZE[8] = {143.0511e-9, 71.5256e-9, 35.7628e-9,
+                                   17.8814e-9,  8.9407e-9,  4.4703e-9,
+                                   2.2352e-9,   1.1176e-9};
+
+static constexpr float V_REF              = 1.2;
+static constexpr float TEST_SIGNAL_FACTOR = 2 / 15;
+static constexpr float TEST_SIGNAL_SLACK  = 0.1;  // Not defined in DS
+
+/**
+ * @brief ADC's oversampling ratio configurations.
+ *
+ * The OSR determins the output data rate, depending on the master clock
+ * frequency.
+ *
+ * ODR = f_CLK / 2 / OSR
+ *
+ * On Skyward's boards an 8.192MHz clock is used.
+ */
+enum class OversamplingRatio : uint16_t
+{
+    OSR_128   = 0,         // ODR is 32KHz
+    OSR_256   = 0x1 << 2,  // ODR is 16KHz
+    OSR_512   = 0x2 << 2,  // ODR is 8KHz
+    OSR_1024  = 0x3 << 2,  // ODR is 4KHz
+    OSR_2048  = 0x4 << 2,  // ODR is 2KHz
+    OSR_4096  = 0x5 << 2,  // ODR is 1KHz
+    OSR_8192  = 0x6 << 2,  // ODR is 500Hz
+    OSR_16256 = 0x7 << 2   // ODR is 250Hz
+};
+
+enum class PGA : uint16_t
+{
+    PGA_1   = 0,    ///< Full scale resolution is ±1.2V
+    PGA_2   = 0x1,  ///< Full scale resolution is ±600mV
+    PGA_4   = 0x2,  ///< Full scale resolution is ±300mV
+    PGA_8   = 0x3,  ///< Full scale resolution is ±150mV
+    PGA_16  = 0x4,  ///< Full scale resolution is ±75mV
+    PGA_32  = 0x5,  ///< Full scale resolution is ±37.5mV
+    PGA_64  = 0x6,  ///< Full scale resolution is ±18.75mV
+    PGA_128 = 0x7   ///< Full scale resolution is ±9.375mV
+};
+
+enum class Channel : uint8_t
+{
+    CHANNEL_0 = 0,
+    CHANNEL_1 = 1,
+    CHANNEL_2 = 2,
+    CHANNEL_3 = 3,
+    CHANNEL_4 = 4,
+    CHANNEL_5 = 5,
+    CHANNEL_6 = 6,
+    CHANNEL_7 = 7
+};
+
+enum class Input : uint8_t
+{
+    DEFAULT          = 0,  // AINxP and AINxN (default)
+    SHORTED          = 1,  // ADC inputs shorted
+    POSITIVE_DC_TEST = 2,  // Positive DC test signal
+    NEGATIVE_DC_TEST = 3   // Negative DC test signal
+};
+
+enum class Register : uint16_t
+{
+    // Device settings and indicators
+    REG_ID     = 0,
+    REG_STATUS = 0x1,
+
+    // Global settings across channels
+    REG_MODE        = 0x2,
+    REG_CLOCK       = 0x3,
+    REG_GAIN_1      = 0x4,
+    REG_GAIN_2      = 0x5,
+    REG_CFG         = 0x6,
+    REG_THRSHLD_MSB = 0x7,
+    REG_THRSHLD_LSB = 0x8,
+
+    // Channel specific settings
+    REG_CH0_CFG      = 0x9,
+    REG_CH0_OCAL_MSB = 0xA,
+    REG_CH0_OCAL_LSB = 0xB,
+    REG_CH0_GCAL_MSB = 0xC,
+    REG_CH0_GCAL_LSB = 0xD,
+    REG_CH1_CFG      = 0xE,
+    REG_CH1_OCAL_MSB = 0xF,
+    REG_CH1_OCAL_LSB = 0x10,
+    REG_CH1_GCAL_MSB = 0x11,
+    REG_CH1_GCAL_LSB = 0x12,
+    REG_CH2_CFG      = 0x13,
+    REG_CH2_OCAL_MSB = 0x14,
+    REG_CH2_OCAL_LSB = 0x15,
+    REG_CH2_GCAL_MSB = 0x16,
+    REG_CH2_GCAL_LSB = 0x17,
+    REG_CH3_CFG      = 0x18,
+    REG_CH3_OCAL_MSB = 0x19,
+    REG_CH3_OCAL_LSB = 0x1A,
+    REG_CH3_GCAL_MSB = 0x1B,
+    REG_CH3_GCAL_LSB = 0x1C,
+    REG_CH4_CFG      = 0x1D,
+    REG_CH4_OCAL_MSB = 0x1E,
+    REG_CH4_OCAL_LSB = 0x1F,
+    REG_CH4_GCAL_MSB = 0x20,
+    REG_CH4_GCAL_LSB = 0x21,
+    REG_CH5_CFG      = 0x22,
+    REG_CH5_OCAL_MSB = 0x23,
+    REG_CH5_OCAL_LSB = 0x24,
+    REG_CH5_GCAL_MSB = 0x25,
+    REG_CH5_GCAL_LSB = 0x26,
+    REG_CH6_CFG      = 0x27,
+    REG_CH6_OCAL_MSB = 0x28,
+    REG_CH6_OCAL_LSB = 0x29,
+    REG_CH6_GCAL_MSB = 0x2A,
+    REG_CH6_GCAL_LSB = 0x2B,
+    REG_CH7_CFG      = 0x2C,
+    REG_CH7_OCAL_MSB = 0x2D,
+    REG_CH7_OCAL_LSB = 0x2E,
+    REG_CH7_GCAL_MSB = 0x2F,
+    REG_CH7_GCAL_LSB = 0x30,
+
+    // Register map CRC
+    REG_REGMAP_CRC = 0x3E
+};
+
+enum class Command : uint16_t
+{
+    NULL_CMD = 0x0000,
+    RESET    = 0x0011,
+    STANDBY  = 0x0022,
+    WAKEUP   = 0x0033,
+    LOCK     = 0x0555,
+    UNLOCK   = 0x0655,
+    RREG     = 0xA000,
+    WREG     = 0x6000
+};
+
+namespace RegStatusMasks
+{
+constexpr uint16_t LOCK     = 0x1 << 15;
+constexpr uint16_t F_RESYNC = 0x1 << 14;
+constexpr uint16_t REG_MAP  = 0x1 << 13;
+constexpr uint16_t CRC_ERR  = 0x1 << 12;
+constexpr uint16_t CRC_TYPE = 0x1 << 11;
+constexpr uint16_t RESET    = 0x1 << 10;
+constexpr uint16_t WLENGTH  = 0x3 << 8;
+constexpr uint16_t DRDY3    = 0x1 << 3;
+constexpr uint16_t DRDY2    = 0x1 << 2;
+constexpr uint16_t DRDY1    = 0x1 << 1;
+constexpr uint16_t DRDY0    = 0x1 << 0;
+}  // namespace RegStatusMasks
+
+namespace RegModeMasks
+{
+constexpr uint16_t REG_CRC_EN = 0x1 << 13;
+constexpr uint16_t RX_CRC_EN  = 0x1 << 12;
+constexpr uint16_t CRC_TYPE   = 0x1 << 11;
+constexpr uint16_t RESET      = 0x1 << 10;
+constexpr uint16_t WLENGTH    = 0x3 << 8;
+constexpr uint16_t TIMEOUT    = 0x1 << 4;
+constexpr uint16_t DRDY_SEL   = 0x3 << 2;
+constexpr uint16_t DRDY_HiZ   = 0x1 << 1;
+constexpr uint16_t DRDY_FMT   = 0x1 << 0;
+}  // namespace RegModeMasks
+
+namespace RegClockMasks
+{
+constexpr uint16_t CH3_EN     = 0x1 << 11;
+constexpr uint16_t CH2_EN     = 0x1 << 10;
+constexpr uint16_t CH1_EN     = 0x1 << 9;
+constexpr uint16_t CH0_EN     = 0x1 << 8;
+constexpr uint16_t OSR        = 0x7 << 2;
+constexpr uint16_t POWER_MODE = 0x3 << 0;
+}  // namespace RegClockMasks
+
+namespace RegGainMasks
+{
+constexpr uint16_t PGA_GAIN_3 = 0x7 << 12;
+constexpr uint16_t PGA_GAIN_2 = 0x7 << 8;
+constexpr uint16_t PGA_GAIN_1 = 0x7 << 4;
+constexpr uint16_t PGA_GAIN_0 = 0x7 << 0;
+}  // namespace RegGainMasks
+
+namespace RegConfigurationMasks
+{
+constexpr uint16_t GC_DLY   = 0xF << 9;
+constexpr uint16_t GC_EN    = 0x1 << 8;
+constexpr uint16_t CD_ALLCH = 0x1 << 7;
+constexpr uint16_t CD_NUM   = 0x7 << 4;
+constexpr uint16_t CD_LEN   = 0x7 << 1;
+constexpr uint16_t CD_EN    = 0x1 << 0;
+}  // namespace RegConfigurationMasks
+
+namespace RegChannelMasks
+{
+constexpr uint16_t CFG_PHASE     = 0x3FF << 6;
+constexpr uint16_t CFG_DCBLK_DIS = 0x001 << 2;
+constexpr uint16_t CFG_MUX       = 0x003 << 0;
+}  // namespace RegChannelMasks
+
+}  // namespace ADS131M08Defs
+
+}  // namespace Boardcore
diff --git a/src/tests/sensors/test-ads131m04.cpp b/src/tests/sensors/test-ads131m04.cpp
index 89deaf0d6e8a20d8f89d7f7cabd86ea27dcbc78b..6c001ae8edfd83eb48b4f19c9c53f186ce5d0530 100644
--- a/src/tests/sensors/test-ads131m04.cpp
+++ b/src/tests/sensors/test-ads131m04.cpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2021 Skyward Experimental Rocketry
+/* Copyright (c) 2023 Skyward Experimental Rocketry
  * Author: Alberto Nidasio
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -36,14 +36,14 @@ GpioPin csPin   = GpioPin(GPIOE_BASE, 4);
 void initBoard()
 {
     // Setup gpio pins
-    csPin.mode(Mode::OUTPUT);
-    csPin.high();
     sckPin.mode(Mode::ALTERNATE);
     sckPin.alternateFunction(5);
     misoPin.mode(Mode::ALTERNATE);
     misoPin.alternateFunction(5);
     mosiPin.mode(Mode::ALTERNATE);
     mosiPin.alternateFunction(5);
+    csPin.mode(Mode::OUTPUT);
+    csPin.high();
 }
 
 int main()
@@ -53,20 +53,30 @@ int main()
 
     // SPI configuration setup
     SPIBus spiBus(SPI4);
-    SPIBusConfig spiConfig = {};
-    spiConfig.mode         = SPI::Mode::MODE_1;
-    spiConfig.clockDivider = SPI::ClockDivider::DIV_64;
-    SPISlave spiSlave(spiBus, csPin, spiConfig);
+    SPISlave spiSlave(spiBus, csPin, ADS131M04::getDefaultSPIConfig());
 
     // Device initialization
     ADS131M04 ads131(spiSlave);
 
-    // Initialize the device
-    ads131.init();
+    ads131.reset();
+
     ads131.enableGlobalChopMode();
-    ads131.setOversamplingRatio(ADS131M04::OversamplingRatio::OSR_16256);
+    ads131.setOversamplingRatio(ADS131M04Defs::OversamplingRatio::OSR_16256);
+
+    // WARNING: After changing the OSR the device needs some time to settle
+    delayMs(20);
     ads131.calibrateOffset();
 
+    printf("Now performing self test...\n");
+    if (ads131.selfTest())
+    {
+        printf("Self test failed!\n");
+    }
+    else
+    {
+        printf("Self test succeeded\n");
+    }
+
     while (true)
     {
         ads131.sample();
diff --git a/src/tests/sensors/test-ads131m08.cpp b/src/tests/sensors/test-ads131m08.cpp
index 37728444090c7d7b5b38ca2ee1f7de4da22293af..180ec04b59d8466c731620223352b93eec2c47d0 100644
--- a/src/tests/sensors/test-ads131m08.cpp
+++ b/src/tests/sensors/test-ads131m08.cpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2021 Skyward Experimental Rocketry
+/* Copyright (c) 2023 Skyward Experimental Rocketry
  * Author: Alberto Nidasio
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -58,26 +58,24 @@ int main()
     // Device initialization
     ADS131M08 ads131(spiSlave);
 
-    // Initialize the device
-    if (!ads131.init())
-    {
-        printf("Initialization failed!\n");
-    }
-    else
-    {
-        printf("Initialization done\n");
-    }
-    if (!ads131.selfTest())
+    ads131.reset();
+
+    ads131.enableGlobalChopMode();
+    ads131.setOversamplingRatio(ADS131M08Defs::OversamplingRatio::OSR_16256);
+
+    // WARNING: After changing the OSR the device needs some time to settle
+    delayMs(20);
+    ads131.calibrateOffset();
+
+    printf("Now performing self test...\n");
+    if (ads131.selfTest())
     {
         printf("Self test failed!\n");
     }
     else
     {
-        printf("Self test done\n");
+        printf("Self test succeeded\n");
     }
-    ads131.enableGlobalChopMode();
-    ads131.setOversamplingRatio(ADS131M08::OversamplingRatio::OSR_16256);
-    ads131.calibrateOffset();
 
     while (true)
     {