diff --git a/CMakeLists.txt b/CMakeLists.txt
index cdfe8fc38ffb106eeb966de8c630f26b9a0dc5fd..6798867219d444c49cf6c84becb4e1c970dd684c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -338,11 +338,17 @@ sbs_target(test-calibration-benchmark stm32f407vg_stm32f4discovery)
 add_executable(test-calibration-stats src/tests/sensors/calibration/test-calibration-stats.cpp)
 sbs_target(test-calibration-stats stm32f407vg_stm32f4discovery)
 
-add_executable(test-bme280 src/tests/sensors/test-bme280.cpp)
-sbs_target(test-bme280 stm32f429zi_stm32f4discovery)
+add_executable(test-bme280-spi src/tests/sensors/test-bme280-spi.cpp)
+sbs_target(test-bme280-spi stm32f429zi_stm32f4discovery)
 
-add_executable(test-bmp280 src/tests/sensors/test-bmp280.cpp)
-sbs_target(test-bmp280 stm32f429zi_stm32f4discovery)
+add_executable(test-bme280-i2c src/tests/sensors/test-bme280-i2c.cpp)
+sbs_target(test-bme280-i2c stm32f429zi_stm32f4discovery)
+
+add_executable(test-bmp280-spi src/tests/sensors/test-bmp280-spi.cpp)
+sbs_target(test-bmp280-spi stm32f429zi_stm32f4discovery)
+
+add_executable(test-bmp280-i2c src/tests/sensors/test-bmp280-i2c.cpp)
+sbs_target(test-bmp280-i2c stm32f429zi_stm32f4discovery)
 
 add_executable(test-bmx160 src/tests/sensors/test-bmx160.cpp)
 sbs_target(test-bmx160 stm32f429zi_skyward_death_stack_x)
diff --git a/cmake/boardcore.cmake b/cmake/boardcore.cmake
index 777fad422c136021d0b1f3d5255bc3867c72ab0a..2036b43467026eca06f315b8d689f7f81484e08c 100644
--- a/cmake/boardcore.cmake
+++ b/cmake/boardcore.cmake
@@ -82,7 +82,9 @@ foreach(OPT_BOARD ${BOARDS})
         ${SBS_BASE}/src/shared/sensors/ADS1118/ADS1118.cpp
         ${SBS_BASE}/src/shared/sensors/ADS131M04/ADS131M04.cpp
         ${SBS_BASE}/src/shared/sensors/BME280/BME280.cpp
+        ${SBS_BASE}/src/shared/sensors/BME280/BME280I2C.cpp
         ${SBS_BASE}/src/shared/sensors/BMP280/BMP280.cpp
+        ${SBS_BASE}/src/shared/sensors/BMP280/BMP280I2C.cpp
         ${SBS_BASE}/src/shared/sensors/BMX160/BMX160.cpp
         ${SBS_BASE}/src/shared/sensors/BMX160/BMX160WithCorrection.cpp
         ${SBS_BASE}/src/shared/sensors/HX711/HX711.cpp
diff --git a/src/shared/sensors/BME280/BME280.cpp b/src/shared/sensors/BME280/BME280.cpp
index 80ab597f187c771a6e69c4febbef423d0f2b1b66..1ece0a1019bc4fbb657031832dcd04e480ad6040 100644
--- a/src/shared/sensors/BME280/BME280.cpp
+++ b/src/shared/sensors/BME280/BME280.cpp
@@ -54,64 +54,39 @@ BME280::BME280(SPISlave spiSlave, BME280Config config)
 
 bool BME280::init()
 {
-    // Check WHO AM I
     if (!checkWhoAmI())
     {
         LOG_ERR(logger, "Invalid WHO AM I");
 
         lastError = SensorErrors::INVALID_WHOAMI;
-
         return false;
     }
 
-    loadCompensationParameters();
+    reset();
+    miosix::Thread::sleep(3);
 
-    // Set the configuration 10 times to be sure
-    for (int i = 0; i < 10; i++)
-    {
-        // Read once the temperature to compute fineTemperature
-        setConfiguration(BME280_CONFIG_TEMP_SINGLE);
-        miosix::Thread::sleep(
-            calculateMaxMeasurementTime(BME280_CONFIG_TEMP_SINGLE));
-        readTemperature();
-
-        setConfiguration();
-    }
+    loadCompensationParameters();
 
-    // Set a sleep time to allow the sensor to change internally the data
-    miosix::Thread::sleep(100);
+    // Read once the temperature to compute fineTemperature
+    setConfiguration(BME280_CONFIG_TEMP_SINGLE);
+    miosix::Thread::sleep(
+        calculateMaxMeasurementTime(BME280_CONFIG_TEMP_SINGLE));
+    readTemperature();
 
-    // I create the config state which represents the logic or of all the
-    // 5 readConfiguration controls (We perform 5 checks to avoid that the
-    // sensor is busy implicating in wrong responses)
-    bool readConfigResult = false;
-    BME280Config readBackConfig;
+    // Set the target configuration
+    setConfiguration();
 
-    for (int i = 0; i < 10; i++)
-    {
-        readBackConfig = readConfiguration();
-        // Check if the configration on the device matches ours
-        if (config.bytes.ctrlHumidity == readBackConfig.bytes.ctrlHumidity &&
-            config.bytes.ctrlPressureAndTemperature ==
-                readBackConfig.bytes.ctrlPressureAndTemperature &&
-            config.bytes.config == readBackConfig.bytes.config)
-        {
-            readConfigResult = true;
-            break;
-        }
-
-        // After the check i sleep 100 milliseconds
-        miosix::Thread::sleep(20);
-    }
+    BME280Config readBackConfig = readConfiguration();
 
-    //   If after the 5 iterations the sensor didn't report the configuration
-    //   set I can report the init error
-    if (!readConfigResult)
+    // Check if the configuration on the device matches ours
+    if (config.bytes.ctrlHumidity != readBackConfig.bytes.ctrlHumidity ||
+        config.bytes.ctrlPressureAndTemperature !=
+            readBackConfig.bytes.ctrlPressureAndTemperature ||
+        config.bytes.config != readBackConfig.bytes.config)
     {
-        LOG_ERR(logger, "Device configuration incorrect, setup failed");
+        LOG_ERR(logger, "Device configuration incorrect, setup failed.");
 
         lastError = SensorErrors::NOT_INIT;
-
         return false;
     }
 
@@ -163,77 +138,72 @@ void BME280::setStandbyTime(StandbyTime standbyTime)
 HumidityData BME280::readHumidity()
 {
     uint8_t buffer[2];
-    int32_t adc_H = 0;
-
     {
         SPITransaction transaction(spiSlave);
 
         transaction.readRegisters(REG_HUM_MSB, buffer, 2);
     }
 
-    adc_H |= ((uint32_t)buffer[0] << 8);
+    int32_t adc_H = ((uint32_t)buffer[0] << 8);
     adc_H |= buffer[1];
 
-    // Compensate humidity
-    lastSample.humidityTimestamp = TimestampTimer::getTimestamp();
-    lastSample.humidity =
-        (float)compensateHumidity(adc_H) / 1024;  // Converto to %RH
+    HumidityData data;
+    data.humidityTimestamp = TimestampTimer::getTimestamp();
+    data.humidity          = compensateHumidity(adc_H);
+    data.humidity /= 1024;  // Convert to to %RH
 
-    return lastSample;
+    return data;
 }
 
 PressureData BME280::readPressure()
 {
     uint8_t buffer[3];
-    int32_t adc_P = 0;
-
     {
         SPITransaction transaction(spiSlave);
 
         transaction.readRegisters(REG_PRESS_MSB, buffer, 3);
     }
 
-    adc_P |= ((uint32_t)buffer[0]) << 12;
+    int32_t adc_P = ((uint32_t)buffer[0]) << 12;
     adc_P |= ((uint32_t)buffer[1]) << 4;
     adc_P |= (buffer[2] >> 4) & 0x0F;
 
-    // Compensate pressure
-    lastSample.pressureTimestamp = TimestampTimer::getTimestamp();
-    lastSample.pressure =
-        (float)compensatePressure(adc_P) / 256;  // Convert to Pa
+    PressureData data;
+    data.pressureTimestamp = TimestampTimer::getTimestamp();
+    data.pressure          = compensatePressure(adc_P);
+    data.pressure /= 256;  // Convert to Pa
 
-    return lastSample;
+    return data;
 }
 
 TemperatureData BME280::readTemperature()
 {
     uint8_t buffer[3];
-    int32_t adcTemperature = 0;
-
     {
         SPITransaction transaction(spiSlave);
 
         transaction.readRegisters(REG_TEMP_MSB, buffer, 3);
     }
 
-    adcTemperature |= ((uint32_t)buffer[0]) << 12;
+    int32_t adcTemperature = ((uint32_t)buffer[0]) << 12;
     adcTemperature |= ((uint32_t)buffer[1]) << 4;
     adcTemperature |= (buffer[2] >> 4) & 0x0F;
 
-    // Compensate temperature
-    fineTemperature                 = computeFineTemperature(adcTemperature);
-    lastSample.temperatureTimestamp = TimestampTimer::getTimestamp();
-    lastSample.temperature = (float)compensateTemperature(fineTemperature) /
-                             100;  // Converto to DegC
+    fineTemperature = computeFineTemperature(adcTemperature);
+
+    TemperatureData data;
+    data.temperatureTimestamp = TimestampTimer::getTimestamp();
+    data.temperature          = compensateTemperature(fineTemperature);
+    data.temperature /= 100;  // Convert to to DegC
 
-    return lastSample;
+    return data;
 }
 
-unsigned int BME280::calculateMaxMeasurementTime(BME280Config config_)
+unsigned int BME280::calculateMaxMeasurementTime(BME280Config config)
 {
-    return ceil(1.25 + (2.3 * config_.bits.oversamplingTemperature) +
-                (2.3 * config_.bits.oversamplingPressure + 0.575) +
-                (2.3 * config_.bits.oversamplingHumidity + 0.575));
+    return ceil(1.25 + (2.3 * config.bits.oversamplingTemperature) +
+                (2.3 * config.bits.oversamplingPressure + 0.575) +
+                (2.3 * config.bits.oversamplingHumidity + 0.575));
 }
 
 unsigned int BME280::getMaxMeasurementTime()
@@ -245,49 +215,54 @@ bool BME280::selfTest() { return checkWhoAmI(); }
 
 BME280Data BME280::sampleImpl()
 {
-    uint8_t buffer[8];
-    int32_t adcTemperature = 0;
-    int32_t adc_P          = 0;
-    int32_t adc_H          = 0;
-    BME280Data data;
-
     // TODO: implement selective read!
 
-    // Burst read pressure, temperature and humidity
+    uint8_t buffer[8];
     {
         SPITransaction transaction(spiSlave);
 
         transaction.readRegisters(REG_PRESS_MSB, buffer, 8);
     }
 
-    adcTemperature |= ((uint32_t)buffer[3]) << 12;
+    BME280Data data;
+
+    int32_t adcTemperature = ((uint32_t)buffer[3]) << 12;
     adcTemperature |= ((uint32_t)buffer[4]) << 4;
     adcTemperature |= (buffer[5] >> 4) & 0x0F;
 
-    adc_P |= ((uint32_t)buffer[0]) << 12;
+    int32_t adc_P = ((uint32_t)buffer[0]) << 12;
     adc_P |= ((uint32_t)buffer[1]) << 4;
     adc_P |= (buffer[2] >> 4) & 0x0F;
 
-    adc_H |= ((uint32_t)buffer[6] << 8);
+    int32_t adc_H = ((uint32_t)buffer[6] << 8);
     adc_H |= buffer[7];
 
     // Compensate temperature
     fineTemperature           = computeFineTemperature(adcTemperature);
     data.temperatureTimestamp = TimestampTimer::getTimestamp();
-    data.temperature          = (float)compensateTemperature(fineTemperature) /
-                       100;  // Converto to DegC
+    data.temperature          = compensateTemperature(fineTemperature);
+    data.temperature /= 100;  // Convert to to DegC
 
     // Compensate pressure
     data.pressureTimestamp = TimestampTimer::getTimestamp();
-    data.pressure = (float)compensatePressure(adc_P) / 256;  // Convert to Pa
+    data.pressure          = compensatePressure(adc_P);
+    data.pressure /= 256;  // Convert to Pa
 
     // Compensate humidity
     data.humidityTimestamp = TimestampTimer::getTimestamp();
-    data.humidity = (float)compensateHumidity(adc_H) / 1024;  // Converto to %RH
+    data.humidity          = compensateHumidity(adc_H);
+    data.humidity /= 1024;  // Convert to to %RH
 
     return data;
 }
 
+void BME280::reset()
+{
+    SPITransaction transaction(spiSlave);
+
+    transaction.writeRegister(REG_RESET, 0xB6);
+}
+
 bool BME280::checkWhoAmI()
 {
     SPITransaction transaction(spiSlave);
@@ -299,14 +274,14 @@ bool BME280::checkWhoAmI()
 
 void BME280::setConfiguration() { setConfiguration(config); }
 
-void BME280::setConfiguration(BME280Config config_)
+void BME280::setConfiguration(BME280Config config)
 {
     SPITransaction transaction(spiSlave);
 
-    transaction.writeRegister(REG_CONFIG & 0x7F, config_.bytes.config);
-    transaction.writeRegister(REG_CTRL_HUM & 0x7F, config_.bytes.ctrlHumidity);
-    transaction.writeRegister(REG_CTRL_MEAS & 0x7F,
-                              config_.bytes.ctrlPressureAndTemperature);
+    transaction.writeRegister(REG_CONFIG, config.bytes.config);
+    transaction.writeRegister(REG_CTRL_HUM, config.bytes.ctrlHumidity);
+    transaction.writeRegister(REG_CTRL_MEAS,
+                              config.bytes.ctrlPressureAndTemperature);
 }
 
 BME280::BME280Config BME280::readConfiguration()
@@ -328,7 +303,7 @@ void BME280::loadCompensationParameters()
         transaction.readRegisters(REG_CALIB_0, (uint8_t *)&compParams, 25);
     }
 
-    // Reat second batch of compensation parameters
+    // Read second batch of compensation parameters
     {
         SPITransaction transaction(spiSlave);
 
diff --git a/src/shared/sensors/BME280/BME280.h b/src/shared/sensors/BME280/BME280.h
index 7da38ced045595836d85fcd9b59925f25f9d579d..1e7e9f1b764dee8da541f2e0294d4ee626eeb551 100644
--- a/src/shared/sensors/BME280/BME280.h
+++ b/src/shared/sensors/BME280/BME280.h
@@ -148,12 +148,14 @@ public:
 
     static constexpr uint8_t REG_ID_VAL = 0x60;  ///< Who am I value
 
-    static const BME280Config
-        BME280_DEFAULT_CONFIG;  ///< Default register values
-    static const BME280Config
-        BME280_CONFIG_ALL_ENABLED;  ///< Datasheet values for indoor navigation
-    static const BME280Config
-        BME280_CONFIG_TEMP_SINGLE;  ///< Temperature enabled in forced mode
+    ///< Default register values
+    static const BME280Config BME280_DEFAULT_CONFIG;
+
+    ///< Datasheet values for indoor navigation
+    static const BME280Config BME280_CONFIG_ALL_ENABLED;
+
+    ///< Temperature enabled in forced mode
+    static const BME280Config BME280_CONFIG_TEMP_SINGLE;
 
     explicit BME280(SPISlave spiSlave,
                     BME280Config config = BME280_CONFIG_ALL_ENABLED);
@@ -225,7 +227,7 @@ public:
      *
      * @return Time in milliseconds
      */
-    static unsigned int calculateMaxMeasurementTime(BME280Config config_);
+    static unsigned int calculateMaxMeasurementTime(BME280Config config);
 
     unsigned int getMaxMeasurementTime();
 
@@ -239,9 +241,11 @@ public:
 private:
     BME280Data sampleImpl() override;
 
+    void reset();
+
     void setConfiguration();
 
-    void setConfiguration(BME280Config config_);
+    void setConfiguration(BME280Config config);
 
     BME280Config readConfiguration();
 
@@ -269,25 +273,25 @@ private:
         REG_CALIB_0 = 0x88,
         // Calibration register 1-25
 
-        REG_ID    = 0xD0,
-        REG_RESET = 0xE0,
+        REG_ID    = 0x50,
+        REG_RESET = 0x60,
 
         REG_CALIB_26 = 0xE1,
         // Calibration register 27-41
 
-        REG_CTRL_HUM  = 0xF2,
-        REG_STATUS    = 0xF3,
-        REG_CTRL_MEAS = 0xF4,
-        REG_CONFIG    = 0xF5,
-
-        REG_PRESS_MSB  = 0xF7,
-        REG_PRESS_LSB  = 0xF8,
-        REG_PRESS_XLSB = 0xF9,
-        REG_TEMP_MSB   = 0xFA,
-        REG_TEMP_LSB   = 0xFB,
-        REG_TEMP_XLSB  = 0xFC,
-        REG_HUM_MSB    = 0xFD,
-        REG_HUM_LSB    = 0xFE,
+        REG_CTRL_HUM  = 0x72,
+        REG_STATUS    = 0x73,
+        REG_CTRL_MEAS = 0x74,
+        REG_CONFIG    = 0x75,
+
+        REG_PRESS_MSB  = 0x77,
+        REG_PRESS_LSB  = 0x78,
+        REG_PRESS_XLSB = 0x79,
+        REG_TEMP_MSB   = 0x7A,
+        REG_TEMP_LSB   = 0x7B,
+        REG_TEMP_XLSB  = 0x7C,
+        REG_HUM_MSB    = 0x7D,
+        REG_HUM_LSB    = 0x7E,
     };
 
     const SPISlave spiSlave;
diff --git a/src/shared/sensors/BME280/BME280I2C.cpp b/src/shared/sensors/BME280/BME280I2C.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4d0e67c566dc816000f55f74e0efc71d42795519
--- /dev/null
+++ b/src/shared/sensors/BME280/BME280I2C.cpp
@@ -0,0 +1,431 @@
+/* Copyright (c) 2021 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.
+ */
+
+#include "BME280I2C.h"
+
+#include <drivers/timer/TimestampTimer.h>
+#include <math.h>
+
+using namespace std;
+
+namespace Boardcore
+{
+
+const BME280I2C::BME280Config BME280I2C::BME280_DEFAULT_CONFIG = {
+    SKIPPED, 0, 0, SLEEP_MODE, SKIPPED, SKIPPED, 0, FILTER_OFF, STB_TIME_0_5};
+
+const BME280I2C::BME280Config BME280I2C::BME280_CONFIG_ALL_ENABLED = {
+    OVERSAMPLING_1,
+    0,
+    0,
+    NORMAL_MODE,
+    OVERSAMPLING_16,
+    OVERSAMPLING_2,
+    0,
+    FILTER_COEFF_16,
+    STB_TIME_0_5};
+
+const BME280I2C::BME280Config BME280I2C::BME280_CONFIG_TEMP_SINGLE = {
+    SKIPPED,        0, 0,          FORCED_MODE, SKIPPED,
+    OVERSAMPLING_1, 0, FILTER_OFF, STB_TIME_0_5};
+
+BME280I2C::BME280I2C(I2C &bus, BME280Config config) : bus(bus), config(config)
+{
+}
+
+bool BME280I2C::init()
+{
+    if (!checkWhoAmI())
+    {
+        LOG_ERR(logger, "Invalid WHO AM I");
+
+        lastError = SensorErrors::INVALID_WHOAMI;
+        return false;
+    }
+
+    if (!reset())
+    {
+        return false;
+    }
+    miosix::Thread::sleep(3);
+
+    loadCompensationParameters();
+
+    // Read once the temperature to compute fineTemperature
+    setConfiguration(BME280_CONFIG_TEMP_SINGLE);
+    miosix::Thread::sleep(
+        calculateMaxMeasurementTime(BME280_CONFIG_TEMP_SINGLE));
+    readTemperature();
+
+    // Set the target configuration
+    setConfiguration();
+
+    BME280Config readBackConfig = readConfiguration();
+
+    // Check if the configuration on the device matches ours
+    if (config.bytes.ctrlHumidity != readBackConfig.bytes.ctrlHumidity ||
+        config.bytes.ctrlPressureAndTemperature !=
+            readBackConfig.bytes.ctrlPressureAndTemperature ||
+        config.bytes.config != readBackConfig.bytes.config)
+    {
+        LOG_ERR(logger, "Device configuration incorrect, setup failed.");
+
+        lastError = SensorErrors::NOT_INIT;
+        return false;
+    }
+
+    return true;
+}
+
+void BME280I2C::setSensorMode(Mode mode)
+{
+    config.bits.mode = mode;
+
+    setConfiguration();
+}
+
+void BME280I2C::setHumidityOversampling(Oversampling oversampling)
+{
+    config.bits.oversamplingHumidity = oversampling;
+
+    setConfiguration();
+}
+
+void BME280I2C::setPressureOversampling(Oversampling oversampling)
+{
+    config.bits.oversamplingPressure = oversampling;
+
+    setConfiguration();
+}
+
+void BME280I2C::setTemperatureOversampling(Oversampling oversampling)
+{
+    config.bits.oversamplingTemperature = oversampling;
+
+    setConfiguration();
+}
+
+void BME280I2C::setFilterCoeff(FilterCoeff filterCoeff)
+{
+    config.bits.filter = filterCoeff;
+
+    setConfiguration();
+}
+
+void BME280I2C::setStandbyTime(StandbyTime standbyTime)
+{
+    config.bits.standbyTime = standbyTime;
+
+    setConfiguration();
+}
+
+HumidityData BME280I2C::readHumidity()
+{
+    uint8_t buffer[2];
+    if (bus.readFromRegister(slaveConfig, REG_HUM_MSB, buffer, 2))
+    {
+
+        int32_t adc_H = ((uint32_t)buffer[0] << 8);
+        adc_H |= buffer[1];
+
+        HumidityData data;
+        data.humidityTimestamp = TimestampTimer::getTimestamp();
+        data.humidity          = compensateHumidity(adc_H);
+        data.humidity /= 1024;  // Convert to to %RH
+
+        return data;
+    }
+    else
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return lastSample;
+    }
+}
+
+PressureData BME280I2C::readPressure()
+{
+    uint8_t buffer[3];
+    if (bus.readFromRegister(slaveConfig, REG_PRESS_MSB, buffer, 3))
+    {
+
+        int32_t adc_P = ((uint32_t)buffer[0]) << 12;
+        adc_P |= ((uint32_t)buffer[1]) << 4;
+        adc_P |= (buffer[2] >> 4) & 0x0F;
+
+        PressureData data;
+        data.pressureTimestamp = TimestampTimer::getTimestamp();
+        data.pressure          = compensatePressure(adc_P);
+        data.pressure /= 256;  // Convert to Pa
+
+        return data;
+    }
+    else
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return lastSample;
+    }
+}
+
+TemperatureData BME280I2C::readTemperature()
+{
+    uint8_t buffer[3];
+    if (bus.readFromRegister(slaveConfig, REG_TEMP_MSB, buffer, 3))
+    {
+        int32_t adcTemperature = ((uint32_t)buffer[0]) << 12;
+        adcTemperature |= ((uint32_t)buffer[1]) << 4;
+        adcTemperature |= (buffer[2] >> 4) & 0x0F;
+
+        fineTemperature = computeFineTemperature(adcTemperature);
+
+        TemperatureData data;
+        data.temperatureTimestamp = TimestampTimer::getTimestamp();
+        data.temperature          = compensateTemperature(fineTemperature);
+        data.temperature /= 100;  // Convert to to DegC
+
+        return data;
+    }
+    else
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return lastSample;
+    }
+}
+
+unsigned int BME280I2C::calculateMaxMeasurementTime(BME280Config config)
+{
+    return ceil(1.25 + (2.3 * config.bits.oversamplingTemperature) +
+                (2.3 * config.bits.oversamplingPressure + 0.575) +
+                (2.3 * config.bits.oversamplingHumidity + 0.575));
+}
+
+unsigned int BME280I2C::getMaxMeasurementTime()
+{
+    return calculateMaxMeasurementTime(config);
+}
+
+bool BME280I2C::selfTest() { return checkWhoAmI(); }
+
+BME280Data BME280I2C::sampleImpl()
+{
+    // TODO: implement selective read!
+
+    uint8_t buffer[8];
+    if (bus.readFromRegister(slaveConfig, REG_PRESS_MSB, buffer, 8))
+    {
+        BME280Data data;
+
+        int32_t adcTemperature = ((uint32_t)buffer[3]) << 12;
+        adcTemperature |= ((uint32_t)buffer[4]) << 4;
+        adcTemperature |= (buffer[5] >> 4) & 0x0F;
+
+        int32_t adc_P = ((uint32_t)buffer[0]) << 12;
+        adc_P |= ((uint32_t)buffer[1]) << 4;
+        adc_P |= (buffer[2] >> 4) & 0x0F;
+
+        int32_t adc_H = ((uint32_t)buffer[6] << 8);
+        adc_H |= buffer[7];
+
+        // Compensate temperature
+        fineTemperature           = computeFineTemperature(adcTemperature);
+        data.temperatureTimestamp = TimestampTimer::getTimestamp();
+        data.temperature          = compensateTemperature(fineTemperature);
+        data.temperature /= 100;  // Convert to to DegC
+
+        // Compensate pressure
+        data.pressureTimestamp = TimestampTimer::getTimestamp();
+        data.pressure          = compensatePressure(adc_P);
+        data.pressure /= 256;  // Convert to Pa
+
+        // Compensate humidity
+        data.humidityTimestamp = TimestampTimer::getTimestamp();
+        data.humidity          = compensateHumidity(adc_H);
+        data.humidity /= 1024;  // Convert to to %RH
+
+        return data;
+    }
+    else
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return lastSample;
+    }
+}
+
+bool BME280I2C::reset()
+{
+    if (!bus.writeRegister(slaveConfig, REG_RESET, 0xB6))
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return false;
+    }
+
+    return true;
+}
+
+bool BME280I2C::checkWhoAmI()
+{
+    uint8_t whoAmIValue;
+
+    if (bus.readRegister(slaveConfig, REG_ID, whoAmIValue))
+    {
+
+        return whoAmIValue == REG_ID_VAL;
+    }
+    else
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return false;
+    }
+}
+
+void BME280I2C::setConfiguration() { setConfiguration(config); }
+
+void BME280I2C::setConfiguration(BME280Config config)
+{
+    if (!bus.writeRegister(slaveConfig, REG_CONFIG, config.bytes.config))
+    {
+        LOG_ERR(logger, "Error while writing to register REG_CONFIG");
+        return;
+    }
+
+    if (!bus.writeRegister(slaveConfig, REG_CTRL_HUM,
+                           config.bytes.ctrlHumidity))
+    {
+        LOG_ERR(logger, "Error while writing to register REG_CTRL_HUM");
+        return;
+    }
+
+    if (!bus.writeRegister(slaveConfig, REG_CTRL_MEAS,
+                           config.bytes.ctrlPressureAndTemperature))
+    {
+        LOG_ERR(logger, "Error while writing to register REG_CTRL_MEAS");
+        return;
+    }
+}
+
+BME280I2C::BME280Config BME280I2C::readConfiguration()
+{
+    BME280Config tmp;
+
+    if (bus.readFromRegister(slaveConfig, REG_CTRL_HUM, (uint8_t *)&tmp, 4))
+    {
+        return tmp;
+    }
+    else
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return BME280_DEFAULT_CONFIG;
+    }
+}
+
+void BME280I2C::loadCompensationParameters()
+{
+    // Read first batch of compensation parameters
+    if (!bus.readFromRegister(slaveConfig, REG_CALIB_0, (uint8_t *)&compParams,
+                              25))
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return;
+    }
+
+    // Read second batch of compensation parameters
+    if (!bus.readFromRegister(slaveConfig, REG_CALIB_26,
+                              (uint8_t *)&compParams.bits.dig_H2, 7))
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return;
+    }
+
+    // Adjust unaligned data
+    compParams.bytesArray[29] =
+        (compParams.bytesArray[29] << 4) | (compParams.bytesArray[29] >> 4);
+    compParams.bits.dig_H4 =
+        (compParams.bits.dig_H4 << 4) | (compParams.bits.dig_H4 >> 8);
+}
+
+int32_t BME280I2C::computeFineTemperature(int32_t adcTemperature)
+{
+    int32_t var1, var2;
+    var1 = ((((adcTemperature >> 3) - ((int32_t)compParams.bits.dig_T1 << 1))) *
+            ((int32_t)compParams.bits.dig_T2)) >>
+           11;
+    var2 = (((((adcTemperature >> 4) - ((int32_t)compParams.bits.dig_T1)) *
+              ((adcTemperature >> 4) - ((int32_t)compParams.bits.dig_T1))) >>
+             12) *
+            ((int32_t)compParams.bits.dig_T3)) >>
+           14;
+    return var1 + var2;
+}
+
+int32_t BME280I2C::compensateTemperature(int32_t fineTemperature)
+{
+    return (fineTemperature * 5 + 128) >> 8;
+}
+
+uint32_t BME280I2C::compensatePressure(int32_t adc_P)
+{
+    int64_t var1, var2, p;
+    var1 = ((int64_t)fineTemperature) - 128000;
+    var2 = var1 * var1 * (int64_t)compParams.bits.dig_P6;
+    var2 = var2 + ((var1 * (int64_t)compParams.bits.dig_P5) << 17);
+    var2 = var2 + (((int64_t)compParams.bits.dig_P4) << 35);
+    var1 = ((var1 * var1 * (int64_t)compParams.bits.dig_P3) >> 8) +
+           ((var1 * ((int64_t)compParams.bits.dig_P2) << 12));
+    var1 =
+        ((((int64_t)1) << 47) + var1) * ((int64_t)compParams.bits.dig_P1) >> 33;
+    if (var1 == 0)
+    {
+        return 0;  // avoid exception caused by division by zero
+    }
+    p    = 1048576 - adc_P;
+    p    = (((p << 31) - var2) * 3125) / var1;
+    var1 = (((int64_t)compParams.bits.dig_P9) * (p >> 13) * (p >> 13)) >> 25;
+    var2 = (((int64_t)compParams.bits.dig_P8) * p) >> 19;
+    p    = ((p + var1 + var2) >> 8) + (((int64_t)compParams.bits.dig_P7) << 4);
+    return (uint32_t)p;
+}
+
+uint32_t BME280I2C::compensateHumidity(int32_t adc_H)
+{
+    int32_t v_x1_u32r;
+
+    v_x1_u32r = (fineTemperature - ((int32_t)768000));
+    v_x1_u32r = (((((adc_H << 14) - (((int32_t)compParams.bits.dig_H4) << 20) -
+                    (((int32_t)compParams.bits.dig_H5) * v_x1_u32r)) +
+                   ((int32_t)16384)) >>
+                  15) *
+                 (((((((v_x1_u32r * ((int32_t)compParams.bits.dig_H6)) >> 10) *
+                      (((v_x1_u32r * ((int32_t)compParams.bits.dig_H3)) >> 11) +
+                       ((int32_t)32768))) >>
+                     10) +
+                    ((int32_t)2097152)) *
+                       ((int32_t)compParams.bits.dig_H2) +
+                   8192) >>
+                  14));
+    v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) *
+                               ((int32_t)compParams.bits.dig_H1)) >>
+                              4));
+    v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
+    v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
+    return (uint32_t)(v_x1_u32r >> 12);
+}
+
+}  // namespace Boardcore
diff --git a/src/shared/sensors/BME280/BME280I2C.h b/src/shared/sensors/BME280/BME280I2C.h
new file mode 100644
index 0000000000000000000000000000000000000000..fcc512bdb16ae7210753fdb2f454273f09f4df6f
--- /dev/null
+++ b/src/shared/sensors/BME280/BME280I2C.h
@@ -0,0 +1,308 @@
+/* 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 <diagnostic/PrintLogger.h>
+#include <drivers/i2c/I2C.h>
+#include <sensors/Sensor.h>
+
+#include "BME280Data.h"
+
+namespace Boardcore
+{
+
+class BME280I2C : public Sensor<BME280Data>
+{
+public:
+    enum Oversampling
+    {
+        SKIPPED         = 0x0,  ///< Skipped (output set to 0x8000)
+        OVERSAMPLING_1  = 0x1,  ///< Oversampling x1
+        OVERSAMPLING_2  = 0x2,  ///< Oversampling x2
+        OVERSAMPLING_4  = 0x3,  ///< Oversampling x4
+        OVERSAMPLING_8  = 0x4,  ///< Oversampling x8
+        OVERSAMPLING_16 = 0x5,  ///< Oversampling x16
+    };
+
+    enum Mode
+    {
+        SLEEP_MODE  = 0x0,  ///< Sleep mode
+        FORCED_MODE = 0x1,  ///< Forced mode
+        NORMAL_MODE = 0x3   ///< Normal mode
+    };
+
+    enum StandbyTime
+    {
+        STB_TIME_0_5  = 0x0,  ///< 0.5 ms
+        STB_TIME_62_5 = 0x1,  ///< 62.5 ms
+        STB_TIME_125  = 0x2,  ///< 125 ms
+        STB_TIME_250  = 0x3,  ///< 250 ms
+        STB_TIME_500  = 0x4,  ///< 500 ms
+        STB_TIME_1000 = 0x5,  ///< 1000 ms
+        STB_TIME_10   = 0x6,  ///< 10 ms
+        STB_TIME_20   = 0x7   ///< 20 ms
+    };
+
+    enum FilterCoeff
+    {
+        FILTER_OFF      = 0x0,  ///< Filter off
+        FILTER_COEFF_2  = 0x1,  ///< Filter coefficient = 2
+        FILTER_COEFF_4  = 0x2,  ///< Filter coefficient = 4
+        FILTER_COEFF_8  = 0x3,  ///< Filter coefficient = 8
+        FILTER_COEFF_16 = 0x4   ///< Filter coefficient = 16
+    };
+
+    union BME280Config
+    {
+        struct __attribute__((packed)) BME280ConfigBits
+        {
+            Oversampling
+                oversamplingHumidity : 3;  ///< Oversampling of humidity
+            uint8_t : 5;
+
+            // status
+            /**
+             * '1' when the NVM data are being copied to image registers, '0'
+             * when the copying is done
+             */
+            uint8_t imUpdate : 1;
+            uint8_t : 2;
+            /**
+             * '1' whenever a conversion is running, '0' when the result have
+             * been transferred to the data registers
+             */
+            uint8_t measuring : 1;
+            uint8_t : 4;
+
+            Mode mode : 2;  ///< Device modes
+            Oversampling
+                oversamplingPressure : 3;  ///< Oversampling of pressure
+            Oversampling
+                oversamplingTemperature : 3;  ///< Oversampling of temperature
+
+            // config
+            uint8_t spi3wEn : 1;  ///< Enables 3-wire SPI interface
+            uint8_t : 1;
+            FilterCoeff filter : 3;       ///< Time constant of the IIR filter
+            StandbyTime standbyTime : 3;  ///< Inactive duration in normal mode
+        } bits;
+
+        struct
+        {
+            uint8_t ctrlHumidity;                ///< Humidity options
+            uint8_t status;                      ///< Device status
+            uint8_t ctrlPressureAndTemperature;  ///< Pressure and temperature
+                                                 ///< options
+            uint8_t config;  ///< Rate, filter and interface options
+        } bytes;
+
+        uint8_t bytesArray[4];
+    };
+
+    union BME280Comp
+    {
+        struct __attribute__((packed))
+        {
+            uint16_t dig_T1;
+            int16_t dig_T2;
+            int16_t dig_T3;
+            uint16_t dig_P1;
+            int16_t dig_P2;
+            int16_t dig_P3;
+            int16_t dig_P4;
+            int16_t dig_P5;
+            int16_t dig_P6;
+            int16_t dig_P7;
+            int16_t dig_P8;
+            int16_t dig_P9;
+            uint8_t dig_H1;
+            int16_t dig_H2;
+            uint8_t dig_H3;
+            int16_t dig_H4 : 12;
+            int16_t dig_H5 : 12;
+            int8_t dig_H6;
+        } bits;
+
+        uint8_t bytesArray[32];
+    };
+
+    static constexpr uint8_t REG_ID_VAL = 0x60;  ///< Who am I value
+
+    ///< Default register values
+    static const BME280Config BME280_DEFAULT_CONFIG;
+
+    ///< Datasheet values for indoor navigation
+    static const BME280Config BME280_CONFIG_ALL_ENABLED;
+
+    ///< Temperature enabled in forced mode
+    static const BME280Config BME280_CONFIG_TEMP_SINGLE;
+
+    explicit BME280I2C(I2C& bus,
+                       BME280Config config = BME280_CONFIG_ALL_ENABLED);
+
+    /**
+     * @brief Initialize the device with the specified configuration
+     */
+    bool init() override;
+
+    /**
+     * @brief Sets the sensor mode
+     *
+     * Values:
+     * - SLEEP_MODE: No measurements are performed
+     * - FORCED_MODE: A single measurement is performed when this function
+     * writes the configuration, after the measurement the sensor return to
+     * sleep mode
+     * - NORMAL_MODE: Automated cycling between measurements, standby time can
+     * be set using setStandbyTime()
+     */
+    void setSensorMode(Mode mode);
+
+    /**
+     * @brief Sets the oversampling for humidity readings, use SKIPPED to
+     * disable humidity sampling
+     */
+    void setHumidityOversampling(Oversampling oversampling);
+
+    /**
+     * @brief Sets the oversampling for pressure readings, use SKIPPED to
+     * disable pressure sampling
+     */
+    void setPressureOversampling(Oversampling oversampling);
+
+    /**
+     * @brief Sets the oversampling for temperature readings, use SKIPPED to
+     * disable temperature sampling
+     */
+    void setTemperatureOversampling(Oversampling oversampling);
+
+    /**
+     * @brief Sets the coefficient for the IIR filter (applied to temperature
+     * and pressure)
+     */
+    void setFilterCoeff(FilterCoeff filterCoeff);
+
+    /**
+     * @brief Sets the standby time between readings in normal mode
+     */
+    void setStandbyTime(StandbyTime standbyTime);
+
+    /**
+     * @brief Reads only the humidity, does not set the configuration
+     */
+    HumidityData readHumidity();
+
+    /**
+     * @brief Reads only the pressure, does not set the configuration
+     */
+    PressureData readPressure();
+
+    /**
+     * @brief Reads only the temperature, does not set the configuration
+     */
+    TemperatureData readTemperature();
+
+    /**
+     * @brief Maximum measurement time formula from datasheet page 51
+     *
+     * @return Time in milliseconds
+     */
+    static unsigned int calculateMaxMeasurementTime(BME280Config config);
+
+    unsigned int getMaxMeasurementTime();
+
+    /**
+     * @brief Reads the WHO AM I register
+     *
+     * @return True if everything ok
+     */
+    bool selfTest() override;
+
+private:
+    BME280Data sampleImpl() override;
+
+    bool reset();
+
+    void setConfiguration();
+
+    void setConfiguration(BME280Config config);
+
+    BME280Config readConfiguration();
+
+    void loadCompensationParameters();
+
+    // Compensation algorithm rev.1.1 from Bosh datasheet
+
+    int32_t computeFineTemperature(int32_t adcTemperature);
+
+    int32_t compensateTemperature(int32_t fineTemperature);
+
+    uint32_t compensatePressure(int32_t adcPressure);
+
+    uint32_t compensateHumidity(int32_t adcHumidity);
+
+    /**
+     * @brief Check the WHO AM I code from the device.
+     *
+     * @return true if the device is recognized
+     */
+    bool checkWhoAmI();
+
+    enum Registers : uint8_t
+    {
+        REG_CALIB_0 = 0x88,
+        // Calibration register 1-25
+
+        REG_ID    = 0xD0,
+        REG_RESET = 0xE0,
+
+        REG_CALIB_26 = 0xE1,
+        // Calibration register 27-41
+
+        REG_CTRL_HUM  = 0xF2,
+        REG_STATUS    = 0xF3,
+        REG_CTRL_MEAS = 0xF4,
+        REG_CONFIG    = 0xF5,
+
+        REG_PRESS_MSB  = 0xF7,
+        REG_PRESS_LSB  = 0xF8,
+        REG_PRESS_XLSB = 0xF9,
+        REG_TEMP_MSB   = 0xFA,
+        REG_TEMP_LSB   = 0xFB,
+        REG_TEMP_XLSB  = 0xFC,
+        REG_HUM_MSB    = 0xFD,
+        REG_HUM_LSB    = 0xFE,
+    };
+
+    I2C& bus;
+    I2CDriver::I2CSlaveConfig slaveConfig{0x76, I2CDriver::Addressing::BIT7,
+                                          I2CDriver::Speed::STANDARD};
+
+    BME280Config config;
+    BME280Comp compParams;
+    int32_t fineTemperature;  // Used in compensation algorithm
+
+    PrintLogger logger = Logging::getLogger("bme280");
+};
+
+}  // namespace Boardcore
diff --git a/src/shared/sensors/BMP280/BMP280.cpp b/src/shared/sensors/BMP280/BMP280.cpp
index 45dcc1db2e050fb297e486fcc09e0ea95487e153..325ca0982f745e3d69618258c5eaed0c28387736 100644
--- a/src/shared/sensors/BMP280/BMP280.cpp
+++ b/src/shared/sensors/BMP280/BMP280.cpp
@@ -52,16 +52,17 @@ BMP280::BMP280(SPISlave spiSlave, BMP280Config config)
 
 bool BMP280::init()
 {
-    // Check WHO AM I
     if (!checkWhoAmI())
     {
         LOG_ERR(logger, "Invalid WHO AM I");
 
         lastError = SensorErrors::INVALID_WHOAMI;
-
         return false;
     }
 
+    reset();
+    miosix::Thread::sleep(3);
+
     loadCompensationParameters();
 
     // Read once the temperature to compute fineTemperature
@@ -70,11 +71,12 @@ bool BMP280::init()
         calculateMaxMeasurementTime(BMP280_CONFIG_TEMP_SINGLE));
     readTemperature();
 
+    // Set the target configuration
     setConfiguration();
 
     BMP280Config readBackConfig = readConfiguration();
 
-    // Check if the configration on the device matches ours
+    // Check if the configuration on the device matches ours
     if (config.bytes.ctrlPressureAndTemperature !=
             readBackConfig.bytes.ctrlPressureAndTemperature ||
         config.bytes.config != readBackConfig.bytes.config)
@@ -82,7 +84,6 @@ bool BMP280::init()
         LOG_ERR(logger, "Device configuration incorrect, setup failed");
 
         lastError = SensorErrors::NOT_INIT;
-
         return false;
     }
 
@@ -127,7 +128,6 @@ void BMP280::setStandbyTime(StandbyTime standbyTime)
 PressureData BMP280::readPressure()
 {
     uint8_t buffer[3];
-    int32_t adc_P = 0;
 
     {
         SPITransaction transaction(spiSlave);
@@ -135,22 +135,21 @@ PressureData BMP280::readPressure()
         transaction.readRegisters(REG_PRESS_MSB, buffer, 3);
     }
 
-    adc_P |= ((uint32_t)buffer[0]) << 12;
+    int32_t adc_P = ((uint32_t)buffer[0]) << 12;
     adc_P |= ((uint32_t)buffer[1]) << 4;
     adc_P |= (buffer[2] >> 4) & 0x0F;
 
-    // Compensate pressure
-    lastSample.pressureTimestamp = TimestampTimer::getTimestamp();
-    lastSample.pressure =
-        (float)compensatePressure(adc_P) / 256;  // Convert to Pa
+    PressureData data;
+    data.pressureTimestamp = TimestampTimer::getTimestamp();
+    data.pressure          = compensatePressure(adc_P);
+    data.pressure /= 256;  // Convert to Pa
 
-    return lastSample;
+    return data;
 }
 
 TemperatureData BMP280::readTemperature()
 {
     uint8_t buffer[3];
-    int32_t adcTemperature = 0;
 
     {
         SPITransaction transaction(spiSlave);
@@ -158,25 +157,26 @@ TemperatureData BMP280::readTemperature()
         transaction.readRegisters(REG_TEMP_MSB, buffer, 3);
     }
 
-    adcTemperature |= ((uint32_t)buffer[0]) << 12;
+    int32_t adcTemperature = ((uint32_t)buffer[0]) << 12;
     adcTemperature |= ((uint32_t)buffer[1]) << 4;
     adcTemperature |= (buffer[2] >> 4) & 0x0F;
 
-    // Compensate temperature
-    fineTemperature                 = computeFineTemperature(adcTemperature);
-    lastSample.temperatureTimestamp = TimestampTimer::getTimestamp();
-    lastSample.temperature = (float)compensateTemperature(fineTemperature) /
-                             100;  // Converto to DegC
+    fineTemperature = computeFineTemperature(adcTemperature);
+
+    TemperatureData data;
+    data.temperatureTimestamp = TimestampTimer::getTimestamp();
+    data.temperature          = compensateTemperature(fineTemperature);
+    data.temperature /= 100;  // Convert to to DegC
 
-    return lastSample;
+    return data;
 }
 
-unsigned int BMP280::calculateMaxMeasurementTime(BMP280Config config_)
+unsigned int BMP280::calculateMaxMeasurementTime(BMP280Config config)
 {
-    // TODO: This folrmula is not present in the BMP280's datasheet, it should
+    // TODO: This formula is not present in the BMP280's datasheet, it should
     // be checked
-    return ceil(1.25 + (2.3 * config_.bits.oversamplingTemperature) +
-                (2.3 * config_.bits.oversamplingPressure + 0.575));
+    return ceil(1.25 + (2.3 * config.bits.oversamplingTemperature) +
+                (2.3 * config.bits.oversamplingPressure + 0.575));
 }
 
 unsigned int BMP280::getMaxMeasurementTime()
@@ -188,41 +188,46 @@ bool BMP280::selfTest() { return checkWhoAmI(); }
 
 BMP280Data BMP280::sampleImpl()
 {
-    uint8_t buffer[8];
-    int32_t adcTemperature = 0;
-    int32_t adc_P          = 0;
-    BMP280Data data;
-
     // TODO: implement selective read!
 
-    // Burst read pressure, temperature and humidity
+    uint8_t buffer[8];
     {
         SPITransaction transaction(spiSlave);
 
         transaction.readRegisters(REG_PRESS_MSB, buffer, 8);
     }
 
-    adcTemperature |= ((uint32_t)buffer[3]) << 12;
+    BMP280Data data;
+
+    int32_t adcTemperature = ((uint32_t)buffer[3]) << 12;
     adcTemperature |= ((uint32_t)buffer[4]) << 4;
     adcTemperature |= (buffer[5] >> 4) & 0x0F;
 
-    adc_P |= ((uint32_t)buffer[0]) << 12;
+    int32_t adc_P = ((uint32_t)buffer[0]) << 12;
     adc_P |= ((uint32_t)buffer[1]) << 4;
     adc_P |= (buffer[2] >> 4) & 0x0F;
 
     // Compensate temperature
     fineTemperature           = computeFineTemperature(adcTemperature);
     data.temperatureTimestamp = TimestampTimer::getTimestamp();
-    data.temperature          = (float)compensateTemperature(fineTemperature) /
-                       100;  // Converto to DegC
+    data.temperature          = compensateTemperature(fineTemperature);
+    data.temperature /= 100;  // Convert to to DegC
 
     // Compensate pressure
     data.pressureTimestamp = TimestampTimer::getTimestamp();
-    data.pressure = (float)compensatePressure(adc_P) / 256;  // Convert to Pa
+    data.pressure          = compensatePressure(adc_P);
+    data.pressure /= 256;  // Convert to Pa
 
     return data;
 }
 
+void BMP280::reset()
+{
+    SPITransaction transaction(spiSlave);
+
+    transaction.writeRegister(REG_RESET, 0xB6);
+}
+
 bool BMP280::checkWhoAmI()
 {
     SPITransaction transaction(spiSlave);
@@ -234,13 +239,13 @@ bool BMP280::checkWhoAmI()
 
 void BMP280::setConfiguration() { setConfiguration(config); }
 
-void BMP280::setConfiguration(BMP280Config config_)
+void BMP280::setConfiguration(BMP280Config config)
 {
     SPITransaction transaction(spiSlave);
 
-    transaction.writeRegister(REG_CONFIG & 0x7F, config_.bytes.config);
-    transaction.writeRegister(REG_CTRL_MEAS & 0x7F,
-                              config_.bytes.ctrlPressureAndTemperature);
+    transaction.writeRegister(REG_CONFIG, config.bytes.config);
+    transaction.writeRegister(REG_CTRL_MEAS,
+                              config.bytes.ctrlPressureAndTemperature);
 }
 
 BMP280::BMP280Config BMP280::readConfiguration()
diff --git a/src/shared/sensors/BMP280/BMP280.h b/src/shared/sensors/BMP280/BMP280.h
index 5786a0c4854d033077e8f91249c6fec5b740e0d0..8ba79d6f49f2ba7c2faac1fd91d95ea7dbf3d415 100644
--- a/src/shared/sensors/BMP280/BMP280.h
+++ b/src/shared/sensors/BMP280/BMP280.h
@@ -137,15 +137,14 @@ public:
 
     static constexpr uint8_t REG_ID_VAL = 0x58;  ///< Who am I value
 
-    static const BMP280Config BMP280_DEFAULT_CONFIG;      ///< Default register
-                                                          ///< values
-    static const BMP280Config BMP280_CONFIG_ALL_ENABLED;  ///< Datasheet
-                                                          ///< values for
-                                                          ///< indoor
-                                                          ///< navigation
-    static const BMP280Config BMP280_CONFIG_TEMP_SINGLE;  ///< Temperature
-                                                          ///< enabled in
-                                                          ///< forced mode
+    ///< Default register values
+    static const BMP280Config BMP280_DEFAULT_CONFIG;
+
+    ///< Datasheet values for indoor navigation
+    static const BMP280Config BMP280_CONFIG_ALL_ENABLED;
+
+    ///< Temperature enabled in forced mode
+    static const BMP280Config BMP280_CONFIG_TEMP_SINGLE;
 
     explicit BMP280(SPISlave spiSlave,
                     BMP280Config config = BMP280_CONFIG_ALL_ENABLED);
@@ -206,7 +205,7 @@ public:
      *
      * @return Time in milliseconds
      */
-    static unsigned int calculateMaxMeasurementTime(BMP280Config config_);
+    static unsigned int calculateMaxMeasurementTime(BMP280Config config);
 
     unsigned int getMaxMeasurementTime();
 
@@ -220,9 +219,11 @@ public:
 private:
     BMP280Data sampleImpl() override;
 
+    void reset();
+
     void setConfiguration();
 
-    void setConfiguration(BMP280Config config_);
+    void setConfiguration(BMP280Config config);
 
     BMP280Config readConfiguration();
 
@@ -244,22 +245,22 @@ private:
 
     enum Registers : uint8_t
     {
-        REG_CALIB_0 = 0x88,
+        REG_CALIB_0 = 0x08,
         // Calibration register 1-25
 
-        REG_ID    = 0xD0,
-        REG_RESET = 0xE0,
+        REG_ID    = 0x50,
+        REG_RESET = 0x60,
 
-        REG_STATUS    = 0xF3,
-        REG_CTRL_MEAS = 0xF4,
-        REG_CONFIG    = 0xF5,
+        REG_STATUS    = 0x73,
+        REG_CTRL_MEAS = 0x74,
+        REG_CONFIG    = 0x75,
 
-        REG_PRESS_MSB  = 0xF7,
-        REG_PRESS_LSB  = 0xF8,
-        REG_PRESS_XLSB = 0xF9,
-        REG_TEMP_MSB   = 0xFA,
-        REG_TEMP_LSB   = 0xFB,
-        REG_TEMP_XLSB  = 0xFC
+        REG_PRESS_MSB  = 0x77,
+        REG_PRESS_LSB  = 0x78,
+        REG_PRESS_XLSB = 0x79,
+        REG_TEMP_MSB   = 0x7A,
+        REG_TEMP_LSB   = 0x7B,
+        REG_TEMP_XLSB  = 0x7C
     };
 
     const SPISlave spiSlave;
diff --git a/src/shared/sensors/BMP280/BMP280I2C.cpp b/src/shared/sensors/BMP280/BMP280I2C.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..66c1a1212ca0dc0c228e2245cb4748f00ed1fd58
--- /dev/null
+++ b/src/shared/sensors/BMP280/BMP280I2C.cpp
@@ -0,0 +1,345 @@
+/* Copyright (c) 2021 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.
+ */
+
+#include "BMP280I2C.h"
+
+#include <drivers/timer/TimestampTimer.h>
+#include <math.h>
+
+using namespace std;
+
+namespace Boardcore
+{
+
+const BMP280I2C::BMP280Config BMP280I2C::BMP280_DEFAULT_CONFIG = {
+    0, 0, SLEEP_MODE, SKIPPED, SKIPPED, 0, FILTER_OFF, STB_TIME_0_5};
+
+const BMP280I2C::BMP280Config BMP280I2C::BMP280_CONFIG_ALL_ENABLED = {
+    0,
+    0,
+    NORMAL_MODE,
+    OVERSAMPLING_16,
+    OVERSAMPLING_2,
+    0,
+    FILTER_COEFF_16,
+    STB_TIME_0_5};
+
+const BMP280I2C::BMP280Config BMP280I2C::BMP280_CONFIG_TEMP_SINGLE = {
+    0, 0, FORCED_MODE, SKIPPED, OVERSAMPLING_1, 0, FILTER_OFF, STB_TIME_0_5};
+
+BMP280I2C::BMP280I2C(I2C &bus, BMP280Config config) : bus(bus), config(config)
+{
+}
+
+bool BMP280I2C::init()
+{
+    if (!checkWhoAmI())
+    {
+        LOG_ERR(logger, "Invalid WHO AM I");
+
+        lastError = SensorErrors::INVALID_WHOAMI;
+        return false;
+    }
+
+    if (!reset())
+    {
+        return false;
+    }
+    miosix::Thread::sleep(3);
+
+    loadCompensationParameters();
+
+    // Read once the temperature to compute fineTemperature
+    setConfiguration(BMP280_CONFIG_TEMP_SINGLE);
+    miosix::Thread::sleep(
+        calculateMaxMeasurementTime(BMP280_CONFIG_TEMP_SINGLE));
+    readTemperature();
+
+    // Set the target configuration
+    setConfiguration();
+
+    BMP280Config readBackConfig = readConfiguration();
+
+    // Check if the configuration on the device matches ours
+    if (config.bytes.ctrlPressureAndTemperature !=
+            readBackConfig.bytes.ctrlPressureAndTemperature ||
+        config.bytes.config != readBackConfig.bytes.config)
+    {
+        LOG_ERR(logger, "Device configuration incorrect, setup failed");
+
+        lastError = SensorErrors::NOT_INIT;
+        return false;
+    }
+
+    return true;
+}
+
+void BMP280I2C::setSensorMode(Mode mode)
+{
+    config.bits.mode = mode;
+
+    setConfiguration();
+}
+
+void BMP280I2C::setPressureOversampling(Oversampling oversampling)
+{
+    config.bits.oversamplingPressure = oversampling;
+
+    setConfiguration();
+}
+
+void BMP280I2C::setTemperatureOversampling(Oversampling oversampling)
+{
+    config.bits.oversamplingTemperature = oversampling;
+
+    setConfiguration();
+}
+
+void BMP280I2C::setFilterCoeff(FilterCoeff filterCoeff)
+{
+    config.bits.filter = filterCoeff;
+
+    setConfiguration();
+}
+
+void BMP280I2C::setStandbyTime(StandbyTime standbyTime)
+{
+    config.bits.standbyTime = standbyTime;
+
+    setConfiguration();
+}
+
+PressureData BMP280I2C::readPressure()
+{
+    uint8_t buffer[3];
+    if (bus.readFromRegister(slaveConfig, REG_PRESS_MSB, buffer, 3))
+    {
+
+        int32_t adc_P = ((uint32_t)buffer[0]) << 12;
+        adc_P |= ((uint32_t)buffer[1]) << 4;
+        adc_P |= (buffer[2] >> 4) & 0x0F;
+
+        PressureData data;
+        data.pressureTimestamp = TimestampTimer::getTimestamp();
+        data.pressure          = compensatePressure(adc_P);
+        data.pressure /= 256;  // Convert to Pa
+
+        return data;
+    }
+    else
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return lastSample;
+    }
+}
+
+TemperatureData BMP280I2C::readTemperature()
+{
+    uint8_t buffer[3];
+    if (bus.readFromRegister(slaveConfig, REG_TEMP_MSB, buffer, 3))
+    {
+        int32_t adcTemperature = ((uint32_t)buffer[0]) << 12;
+        adcTemperature |= ((uint32_t)buffer[1]) << 4;
+        adcTemperature |= (buffer[2] >> 4) & 0x0F;
+
+        fineTemperature = computeFineTemperature(adcTemperature);
+
+        TemperatureData data;
+        data.temperatureTimestamp = TimestampTimer::getTimestamp();
+        data.temperature          = compensateTemperature(fineTemperature);
+        data.temperature /= 100;  // Convert to to DegC
+
+        return data;
+    }
+    else
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return lastSample;
+    }
+}
+
+unsigned int BMP280I2C::calculateMaxMeasurementTime(BMP280Config config)
+{
+    // TODO: This formula is not present in the BMP280's datasheet, it should
+    // be checked
+    return ceil(1.25 + (2.3 * config.bits.oversamplingTemperature) +
+                (2.3 * config.bits.oversamplingPressure + 0.575));
+}
+
+unsigned int BMP280I2C::getMaxMeasurementTime()
+{
+    return calculateMaxMeasurementTime(config);
+}
+
+bool BMP280I2C::selfTest() { return checkWhoAmI(); }
+
+BMP280Data BMP280I2C::sampleImpl()
+{
+    // TODO: implement selective read!
+
+    uint8_t buffer[6];
+    if (bus.readFromRegister(slaveConfig, REG_PRESS_MSB, buffer, 6))
+    {
+        BMP280Data data;
+
+        int32_t adcTemperature = ((uint32_t)buffer[3]) << 12;
+        adcTemperature |= ((uint32_t)buffer[4]) << 4;
+        adcTemperature |= (buffer[5] >> 4) & 0x0F;
+
+        int32_t adc_P = ((uint32_t)buffer[0]) << 12;
+        adc_P |= ((uint32_t)buffer[1]) << 4;
+        adc_P |= (buffer[2] >> 4) & 0x0F;
+
+        // Compensate temperature
+        fineTemperature           = computeFineTemperature(adcTemperature);
+        data.temperatureTimestamp = TimestampTimer::getTimestamp();
+        data.temperature          = compensateTemperature(fineTemperature);
+        data.temperature /= 100;  // Convert to to DegC
+
+        // Compensate pressure
+        data.pressureTimestamp = TimestampTimer::getTimestamp();
+        data.pressure          = compensatePressure(adc_P);
+        data.pressure /= 256;  // Convert to Pa
+
+        return data;
+    }
+    else
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return lastSample;
+    }
+}
+
+bool BMP280I2C::reset()
+{
+    if (!bus.writeRegister(slaveConfig, REG_RESET, 0xB6))
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return false;
+    }
+
+    return true;
+}
+
+bool BMP280I2C::checkWhoAmI()
+{
+    uint8_t whoAmIValue;
+
+    if (bus.readRegister(slaveConfig, REG_ID, whoAmIValue))
+    {
+
+        return whoAmIValue == REG_ID_VAL;
+    }
+    else
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return false;
+    }
+}
+
+void BMP280I2C::setConfiguration() { setConfiguration(config); }
+
+void BMP280I2C::setConfiguration(BMP280Config config)
+{
+    if (!bus.writeRegister(slaveConfig, REG_CONFIG, config.bytes.config))
+    {
+        LOG_ERR(logger, "Error while writing to register REG_CONFIG");
+        return;
+    }
+
+    if (!bus.writeRegister(slaveConfig, REG_CTRL_MEAS,
+                           config.bytes.ctrlPressureAndTemperature))
+    {
+        LOG_ERR(logger, "Error while writing to register REG_CTRL_MEAS");
+        return;
+    }
+}
+
+BMP280I2C::BMP280Config BMP280I2C::readConfiguration()
+{
+    BMP280Config tmp;
+
+    if (bus.readFromRegister(slaveConfig, REG_STATUS, (uint8_t *)&tmp, 3))
+    {
+        return tmp;
+    }
+    else
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return BMP280_DEFAULT_CONFIG;
+    }
+}
+
+void BMP280I2C::loadCompensationParameters()
+{
+    // Read first batch of compensation parameters
+    if (!bus.readFromRegister(slaveConfig, REG_CALIB_0, (uint8_t *)&compParams,
+                              25))
+    {
+        lastError = SensorErrors::BUS_FAULT;
+        return;
+    }
+}
+
+int32_t BMP280I2C::computeFineTemperature(int32_t adcTemperature)
+{
+    int32_t var1, var2;
+    var1 = ((((adcTemperature >> 3) - ((int32_t)compParams.bits.dig_T1 << 1))) *
+            ((int32_t)compParams.bits.dig_T2)) >>
+           11;
+    var2 = (((((adcTemperature >> 4) - ((int32_t)compParams.bits.dig_T1)) *
+              ((adcTemperature >> 4) - ((int32_t)compParams.bits.dig_T1))) >>
+             12) *
+            ((int32_t)compParams.bits.dig_T3)) >>
+           14;
+    return var1 + var2;
+}
+
+int32_t BMP280I2C::compensateTemperature(int32_t fineTemperature)
+{
+    return (fineTemperature * 5 + 128) >> 8;
+}
+
+uint32_t BMP280I2C::compensatePressure(int32_t adc_P)
+{
+    int64_t var1, var2, p;
+    var1 = ((int64_t)fineTemperature) - 128000;
+    var2 = var1 * var1 * (int64_t)compParams.bits.dig_P6;
+    var2 = var2 + ((var1 * (int64_t)compParams.bits.dig_P5) << 17);
+    var2 = var2 + (((int64_t)compParams.bits.dig_P4) << 35);
+    var1 = ((var1 * var1 * (int64_t)compParams.bits.dig_P3) >> 8) +
+           ((var1 * ((int64_t)compParams.bits.dig_P2) << 12));
+    var1 =
+        ((((int64_t)1) << 47) + var1) * ((int64_t)compParams.bits.dig_P1) >> 33;
+    if (var1 == 0)
+    {
+        return 0;  // avoid exception caused by division by zero
+    }
+    p    = 1048576 - adc_P;
+    p    = (((p << 31) - var2) * 3125) / var1;
+    var1 = (((int64_t)compParams.bits.dig_P9) * (p >> 13) * (p >> 13)) >> 25;
+    var2 = (((int64_t)compParams.bits.dig_P8) * p) >> 19;
+    p    = ((p + var1 + var2) >> 8) + (((int64_t)compParams.bits.dig_P7) << 4);
+    return (uint32_t)p;
+}
+
+}  // namespace Boardcore
diff --git a/src/shared/sensors/BMP280/BMP280I2C.h b/src/shared/sensors/BMP280/BMP280I2C.h
new file mode 100644
index 0000000000000000000000000000000000000000..1c682f6a290f7fb821ab965a36ed36724db77c9f
--- /dev/null
+++ b/src/shared/sensors/BMP280/BMP280I2C.h
@@ -0,0 +1,277 @@
+/* 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 <diagnostic/PrintLogger.h>
+#include <drivers/i2c/I2C.h>
+#include <sensors/Sensor.h>
+
+#include "BMP280Data.h"
+
+namespace Boardcore
+{
+
+class BMP280I2C : public Sensor<BMP280Data>
+{
+public:
+    enum Oversampling
+    {
+        SKIPPED         = 0x0,  ///< Skipped (output set to 0x8000)
+        OVERSAMPLING_1  = 0x1,  ///< Oversampling x1
+        OVERSAMPLING_2  = 0x2,  ///< Oversampling x2
+        OVERSAMPLING_4  = 0x3,  ///< Oversampling x4
+        OVERSAMPLING_8  = 0x4,  ///< Oversampling x8
+        OVERSAMPLING_16 = 0x5,  ///< Oversampling x16
+    };
+
+    enum Mode
+    {
+        SLEEP_MODE  = 0x0,  ///< Sleep mode
+        FORCED_MODE = 0x1,  ///< Forced mode
+        NORMAL_MODE = 0x3   ///< Normal mode
+    };
+
+    enum StandbyTime
+    {
+        STB_TIME_0_5  = 0x0,  ///< 0.5 ms
+        STB_TIME_62_5 = 0x1,  ///< 62.5 ms
+        STB_TIME_125  = 0x2,  ///< 125 ms
+        STB_TIME_250  = 0x3,  ///< 250 ms
+        STB_TIME_500  = 0x4,  ///< 500 ms
+        STB_TIME_1000 = 0x5,  ///< 1000 ms
+        STB_TIME_20   = 0x6,  ///< 20 ms
+        STB_TIME_40   = 0x7   ///< 40 ms
+    };
+
+    enum FilterCoeff
+    {
+        FILTER_OFF      = 0x0,  ///< Filter off
+        FILTER_COEFF_2  = 0x1,  ///< Filter coefficient = 2
+        FILTER_COEFF_4  = 0x2,  ///< Filter coefficient = 4
+        FILTER_COEFF_8  = 0x3,  ///< Filter coefficient = 8
+        FILTER_COEFF_16 = 0x4   ///< Filter coefficient = 16
+    };
+
+    union BMP280Config
+    {
+        struct __attribute__((packed)) BMP280ConfigBits
+        {
+            // status
+            /**
+             * '1' when the NVM data are being copied to image registers, '0'
+             * when the copying is done
+             */
+            uint8_t imUpdate : 1;
+            uint8_t : 2;
+            /**
+             * '1' whenever a conversion is running, '0' when the result have
+             * been transferred to the data registers
+             */
+            uint8_t measuring : 1;
+            uint8_t : 4;
+
+            Mode mode : 2;  ///< Device modes
+            Oversampling
+                oversamplingPressure : 3;  ///< Oversampling of pressure
+            Oversampling
+                oversamplingTemperature : 3;  ///< Oversampling of temperature
+
+            // config
+            uint8_t spi3wEn : 1;  ///< Enables 3-wire SPI interface
+            uint8_t : 1;
+            FilterCoeff filter : 3;       ///< Time constant of the IIR filter
+            StandbyTime standbyTime : 3;  ///< Inactive duration in normal mode
+        } bits;
+
+        struct
+        {
+            uint8_t status;                      ///< Device status
+            uint8_t ctrlPressureAndTemperature;  ///< Pressure and temperature
+                                                 ///< options
+            uint8_t config;  ///< Rate, filter and interface options
+        } bytes;
+
+        uint8_t bytesArray[3];
+    };
+
+    union BMP280Comp
+    {
+        struct __attribute__((packed))
+        {
+            uint16_t dig_T1;
+            int16_t dig_T2;
+            int16_t dig_T3;
+            uint16_t dig_P1;
+            int16_t dig_P2;
+            int16_t dig_P3;
+            int16_t dig_P4;
+            int16_t dig_P5;
+            int16_t dig_P6;
+            int16_t dig_P7;
+            int16_t dig_P8;
+            int16_t dig_P9;
+        } bits;
+
+        uint8_t bytesArray[24];
+    };
+
+    static constexpr uint8_t REG_ID_VAL = 0x58;  ///< Who am I value
+
+    ///< Default register values
+    static const BMP280Config BMP280_DEFAULT_CONFIG;
+
+    ///< Datasheet values for indoor navigation
+    static const BMP280Config BMP280_CONFIG_ALL_ENABLED;
+
+    ///< Temperature enabled in forced mode
+    static const BMP280Config BMP280_CONFIG_TEMP_SINGLE;
+
+    explicit BMP280I2C(I2C& bus,
+                       BMP280Config config = BMP280_CONFIG_ALL_ENABLED);
+
+    /**
+     * @brief Initialize the device with the specified configuration
+     */
+    bool init() override;
+
+    /**
+     * @brief Sets the sensor mode
+     *
+     * Values:
+     * - SLEEP_MODE: No measurements are performed
+     * - FORCED_MODE: A single measurement is performed when this function
+     * writes the configuration, after the measurement the sensor return to
+     * sleep mode
+     * - NORMAL_MODE: Automated cycling between measurements, standby time can
+     * be set using setStandbyTime()
+     */
+    void setSensorMode(Mode mode);
+
+    /**
+     * @brief Sets the oversampling for pressure readings, use SKIPPED to
+     * disable pressure sampling
+     */
+    void setPressureOversampling(Oversampling oversampling);
+
+    /**
+     * @brief Sets the oversampling for temperature readings, use SKIPPED to
+     * disable temperature sampling
+     */
+    void setTemperatureOversampling(Oversampling oversampling);
+
+    /**
+     * @brief Sets the coefficient for the IIR filter (applied to temperature
+     * and pressure)
+     */
+    void setFilterCoeff(FilterCoeff filterCoeff);
+
+    /**
+     * @brief Sets the standby time between readings in normal mode
+     */
+    void setStandbyTime(StandbyTime standbyTime);
+
+    /**
+     * @brief Reads only the pressure, does not set the configuration
+     */
+    PressureData readPressure();
+
+    /**
+     * @brief Reads only the temperature, does not set the configuration
+     */
+    TemperatureData readTemperature();
+
+    /**
+     * @brief Maximum measurement time formula from datasheet page 51
+     *
+     * @return Time in milliseconds
+     */
+    static unsigned int calculateMaxMeasurementTime(BMP280Config config);
+
+    unsigned int getMaxMeasurementTime();
+
+    /**
+     * @brief Reads the WHO AM I register
+     *
+     * @return True if everything ok
+     */
+    bool selfTest() override;
+
+private:
+    BMP280Data sampleImpl() override;
+
+    bool reset();
+
+    void setConfiguration();
+
+    void setConfiguration(BMP280Config config);
+
+    BMP280Config readConfiguration();
+
+    void loadCompensationParameters();
+
+    // Compensation algorithm rev.1.1 from Bosh datasheet
+
+    int32_t computeFineTemperature(int32_t adcTemperature);
+
+    int32_t compensateTemperature(int32_t fineTemperature);
+
+    uint32_t compensatePressure(int32_t adcPressure);
+    /**
+     * @brief Check the WHO AM I code from the device.
+     *
+     * @return true if the device is recognized
+     */
+    bool checkWhoAmI();
+
+    enum Registers : uint8_t
+    {
+        REG_CALIB_0 = 0x88,
+        // Calibration register 1-25
+
+        REG_ID    = 0xD0,
+        REG_RESET = 0xE0,
+
+        REG_STATUS    = 0xF3,
+        REG_CTRL_MEAS = 0xF4,
+        REG_CONFIG    = 0xF5,
+
+        REG_PRESS_MSB  = 0xF7,
+        REG_PRESS_LSB  = 0xF8,
+        REG_PRESS_XLSB = 0xF9,
+        REG_TEMP_MSB   = 0xFA,
+        REG_TEMP_LSB   = 0xFB,
+        REG_TEMP_XLSB  = 0xFC
+    };
+
+    I2C& bus;
+    I2CDriver::I2CSlaveConfig slaveConfig{0x76, I2CDriver::Addressing::BIT7,
+                                          I2CDriver::Speed::STANDARD};
+
+    BMP280Config config;
+    BMP280Comp compParams;
+    int32_t fineTemperature;  // Used in compensation algorithm
+
+    PrintLogger logger = Logging::getLogger("bmp280");
+};
+
+}  // namespace Boardcore
diff --git a/src/tests/sensors/test-bme280-i2c.cpp b/src/tests/sensors/test-bme280-i2c.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b7345a1f3d8136781e527396123ff789cd7475f6
--- /dev/null
+++ b/src/tests/sensors/test-bme280-i2c.cpp
@@ -0,0 +1,71 @@
+/* Copyright (c) 2021 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.
+ */
+
+#include <miosix.h>
+#include <sensors/BME280/BME280I2C.h>
+
+using namespace miosix;
+using namespace Boardcore;
+
+GpioPin scl(GPIOA_BASE, 8);
+GpioPin sda(GPIOC_BASE, 9);
+
+int main()
+{
+    I2C bus(I2C3, scl, sda);
+    BME280I2C bme280(bus);
+    bme280.init();
+
+    if (!bme280.selfTest())
+        printf("Self test failed!\n");
+
+    printf("Forced mode\n");
+    for (int i = 0; i < 10; i++)
+    {
+        bme280.setSensorMode(BME280I2C::FORCED_MODE);
+
+        Thread::sleep(bme280.getMaxMeasurementTime());
+
+        bme280.sample();
+
+        printf("temp: %.2f DegC\tpress: %.2f hPa\thumid: %.2f %%RH\n",
+               bme280.getLastSample().temperature,
+               bme280.getLastSample().pressure,
+               bme280.getLastSample().humidity);
+
+        Thread::sleep(1000);
+    }
+
+    printf("Normal mode\n");
+    bme280.setSensorMode(BME280I2C::NORMAL_MODE);
+    while (true)
+    {
+        bme280.sample();
+
+        printf("temp: %.2f DegC\tpress: %.2f Pa\thumid: %.2f %%RH\n",
+               bme280.getLastSample().temperature,
+               bme280.getLastSample().pressure,
+               bme280.getLastSample().humidity);
+
+        Thread::sleep(50);  // 25Hz
+    }
+}
diff --git a/src/tests/sensors/test-bme280.cpp b/src/tests/sensors/test-bme280-spi.cpp
similarity index 100%
rename from src/tests/sensors/test-bme280.cpp
rename to src/tests/sensors/test-bme280-spi.cpp
diff --git a/src/tests/sensors/test-bmp280-i2c.cpp b/src/tests/sensors/test-bmp280-i2c.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..58edd892e2c6d8235bccc7ef5c19988a4adcc466
--- /dev/null
+++ b/src/tests/sensors/test-bmp280-i2c.cpp
@@ -0,0 +1,69 @@
+/* Copyright (c) 2021 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.
+ */
+
+#include <miosix.h>
+#include <sensors/BMP280/BMP280I2C.h>
+
+using namespace miosix;
+using namespace Boardcore;
+
+GpioPin scl(GPIOA_BASE, 8);
+GpioPin sda(GPIOC_BASE, 9);
+
+int main()
+{
+    I2C bus(I2C3, scl, sda);
+    BMP280I2C bmp280(bus);
+    bmp280.init();
+
+    if (!bmp280.selfTest())
+        printf("Self test failed!\n");
+
+    printf("Forced mode\n");
+    for (int i = 0; i < 10; i++)
+    {
+        bmp280.setSensorMode(BMP280I2C::FORCED_MODE);
+
+        Thread::sleep(bmp280.getMaxMeasurementTime());
+
+        bmp280.sample();
+
+        printf("temp: %.2f DegC\tpress: %.2f hPa\n",
+               bmp280.getLastSample().temperature,
+               bmp280.getLastSample().pressure);
+
+        Thread::sleep(1000);
+    }
+
+    printf("Normal mode\n");
+    bmp280.setSensorMode(BMP280I2C::NORMAL_MODE);
+    while (true)
+    {
+        bmp280.sample();
+
+        printf("temp: %.2f DegC\tpress: %.2f Pa\n",
+               bmp280.getLastSample().temperature,
+               bmp280.getLastSample().pressure);
+
+        Thread::sleep(50);  // 25Hz
+    }
+}
diff --git a/src/tests/sensors/test-bmp280.cpp b/src/tests/sensors/test-bmp280-spi.cpp
similarity index 100%
rename from src/tests/sensors/test-bmp280.cpp
rename to src/tests/sensors/test-bmp280-spi.cpp