diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep
index e51c1100adfea31f8f21cf26096603a98e5f2f74..de0135945a06fa92d5511ad1184e8e75dcb1bbfe 100644
--- a/drivers/Makefile.dep
+++ b/drivers/Makefile.dep
@@ -29,6 +29,11 @@ ifneq (,$(filter bmp180,$(USEMODULE)))
     USEMODULE += xtimer
 endif
 
+ifneq (,$(filter bme280,$(USEMODULE)))
+    FEATURES_REQUIRED += periph_i2c
+    USEMODULE += xtimer
+endif
+
 ifneq (,$(filter cc110x,$(USEMODULE)))
   USEMODULE += ieee802154
   USEMODULE += uuid
diff --git a/drivers/Makefile.include b/drivers/Makefile.include
index 1461d12e7853e89aaf132afc7698238287c03d01..da1880178b2628019093c7bb8d0fa50baa81ab77 100644
--- a/drivers/Makefile.include
+++ b/drivers/Makefile.include
@@ -67,6 +67,9 @@ endif
 ifneq (,$(filter jc42,$(USEMODULE)))
     USEMODULE_INCLUDES += $(RIOTBASE)/drivers/jc42/include
 endif
+ifneq (,$(filter bme280,$(USEMODULE)))
+    USEMODULE_INCLUDES += $(RIOTBASE)/drivers/bme280/include
+endif
 ifneq (,$(filter cc2420,$(USEMODULE)))
     USEMODULE_INCLUDES += $(RIOTBASE)/drivers/cc2420/include
 endif
diff --git a/drivers/bme280/Makefile b/drivers/bme280/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..693aef674e6208f60f577ec96b1ad0f117b04a4c
--- /dev/null
+++ b/drivers/bme280/Makefile
@@ -0,0 +1,3 @@
+MODULE = bme280
+
+include $(RIOTBASE)/Makefile.base
diff --git a/drivers/bme280/bme280.c b/drivers/bme280/bme280.c
new file mode 100644
index 0000000000000000000000000000000000000000..c9f5706d8dfc4a6a111778840b83ff2ed014dd15
--- /dev/null
+++ b/drivers/bme280/bme280.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2016 Kees Bakker, SODAQ
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_bme280
+ * @{
+ *
+ * @file
+ * @brief       Device driver implementation for the BME280 temperature,
+ *              pressure and humidity sensor.
+ *
+ * @author      Kees Bakker <kees@sodaq.com>
+ *
+ * @}
+ */
+
+#include <string.h>
+#include <math.h>
+
+#include "log.h"
+#include "bme280.h"
+#include "bme280_internals.h"
+#include "bme280_params.h"
+#include "periph/i2c.h"
+#include "xtimer.h"
+
+#define ENABLE_DEBUG        (0)
+#include "debug.h"
+
+static int read_calibration_data(bme280_t* dev);
+static int do_measurement(bme280_t* dev);
+static uint8_t get_ctrl_meas(bme280_t* dev);
+static uint8_t get_status(bme280_t* dev);
+static uint8_t read_u8_reg(bme280_t* dev, uint8_t reg);
+static void write_u8_reg(bme280_t* dev, uint8_t reg, uint8_t b);
+static uint16_t get_uint16_le(const uint8_t *buffer, size_t offset);
+static int16_t get_int16_le(const uint8_t *buffer, size_t offset);
+
+#if ENABLE_DEBUG
+static void dump_buffer(const char *txt, uint8_t *buffer, size_t size);
+#define DUMP_BUFFER(txt, buffer, size)  dump_buffer(txt, buffer, size)
+#else
+#define DUMP_BUFFER(txt, buffer, size)
+#endif
+
+/**
+ * @brief   Fine resolution temperature value, also needed for pressure and humidity.
+ */
+static int32_t t_fine;
+
+/**
+ * @brief   The measurement registers, including temperature, pressure and humidity
+ *
+ * A temporary buffer for the memory map 0xF7..0xFE
+ * These are read all at once and then used to compute the three sensor values.
+ */
+static uint8_t measurement_regs[8];
+
+/*---------------------------------------------------------------------------*
+ *                          BME280 Core API                                  *
+ *---------------------------------------------------------------------------*/
+
+int bme280_init(bme280_t* dev, const bme280_params_t* params)
+{
+    uint8_t chip_id;
+
+    dev->params = *params;
+
+    /* Initialize I2C interface */
+    if (i2c_init_master(dev->params.i2c_dev, I2C_SPEED_NORMAL)) {
+        DEBUG("[Error] I2C device not enabled\n");
+        return BME280_ERR_I2C;
+    }
+
+    /* Read chip ID */
+    chip_id = read_u8_reg(dev, BME280_CHIP_ID_REG);
+    if (chip_id != BME280_CHIP_ID) {
+        DEBUG("[Error] Did not detect a BME280 at address %02x (%02X != %02X)\n",
+              dev->params.i2c_addr, chip_id, BME280_CHIP_ID);
+        return BME280_ERR_NODEV;
+    }
+
+    /* Read compensation data, 0x88..0x9F, 0xA1, 0xE1..0xE7 */
+    if (read_calibration_data(dev)) {
+        DEBUG("[Error] Could not read calibration data\n");
+        return BME280_ERR_NOCAL;
+    }
+
+    return BME280_OK;
+}
+
+/*
+ * Returns temperature in DegC, resolution is 0.01 DegC.
+ * t_fine carries fine temperature as global value
+ */
+int16_t bme280_read_temperature(bme280_t* dev)
+{
+    if (do_measurement(dev) < 0) {
+        return INT16_MIN;
+    }
+
+    bme280_calibration_t *cal = &dev->calibration;      /* helper variable */
+
+    /* Read the uncompensated temperature */
+    int32_t adc_T = (((uint32_t)measurement_regs[3 + 0]) << 12) |
+        (((uint32_t)measurement_regs[3 + 1]) << 4) |
+        ((((uint32_t)measurement_regs[3 + 2]) >> 4) & 0x0F);
+
+    /*
+     * Compensate the temperature value.
+     * The following is code from Bosch's BME280_driver bme280_compensate_temperature_int32()
+     * The variable names and the many defines have been modified to make the code
+     * more readable.
+     */
+    int32_t var1;
+    int32_t var2;
+
+    var1 = ((((adc_T >> 3) - ((int32_t)cal->dig_T1 << 1))) * ((int32_t)cal->dig_T2)) >> 11;
+    var2 = (((((adc_T >> 4) - ((int32_t)cal->dig_T1)) * ((adc_T >> 4) - ((int32_t)cal->dig_T1))) >> 12) *
+            ((int32_t)cal->dig_T3)) >> 14;
+
+    /* calculate t_fine (used for pressure and humidity too) */
+    t_fine = var1 + var2;
+
+    return (t_fine * 5 + 128) >> 8;
+}
+
+/*
+ * Returns pressure in Pa
+ */
+uint32_t bme280_read_pressure(bme280_t *dev)
+{
+    bme280_calibration_t *cal = &dev->calibration;      /* helper variable */
+
+    /* Read the uncompensated pressure */
+    int32_t adc_P = (((uint32_t)measurement_regs[0 + 0]) << 12) |
+        (((uint32_t)measurement_regs[0 + 1]) << 4) |
+        ((((uint32_t)measurement_regs[0 + 2]) >> 4) & 0x0F);
+
+    int64_t var1;
+    int64_t var2;
+    int64_t p_acc;
+
+    /*
+     * Compensate the pressure value.
+     * The following is code from Bosch's BME280_driver bme280_compensate_pressure_int64()
+     * The variable names and the many defines have been modified to make the code
+     * more readable.
+     */
+    var1 = ((int64_t)t_fine) - 128000;
+    var2 = var1 * var1 * (int64_t)cal->dig_P6;
+    var2 = var2 + ((var1 * (int64_t)cal->dig_P5) << 17);
+    var2 = var2 + (((int64_t)cal->dig_P4) << 35);
+    var1 = ((var1 * var1 * (int64_t)cal->dig_P3) >> 8) + ((var1 * (int64_t)cal->dig_P2) << 12);
+    var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)cal->dig_P1) >> 33;
+    /* Avoid division by zero */
+    if (var1 == 0) {
+        return UINT32_MAX;
+    }
+
+    p_acc = 1048576 - adc_P;
+    p_acc = (((p_acc << 31) - var2) * 3125) / var1;
+    var1 = (((int64_t)cal->dig_P9) * (p_acc >> 13) * (p_acc >> 13)) >> 25;
+    var2 = (((int64_t)cal->dig_P8) * p_acc) >> 19;
+    p_acc = ((p_acc + var1 + var2) >> 8) + (((int64_t)cal->dig_P7) << 4);
+
+    return p_acc >> 8;
+}
+
+uint16_t bme280_read_humidity(bme280_t *dev)
+{
+    bme280_calibration_t *cal = &dev->calibration;      /* helper variable */
+
+    /* Read the uncompensated pressure */
+    int32_t adc_H = (((uint32_t)measurement_regs[6 + 0]) << 8) |
+        (((uint32_t)measurement_regs[6 + 1]));
+
+    /*
+     * Compensate the humidity value.
+     * The following is code from Bosch's BME280_driver bme280_compensate_humidity_int32()
+     * The variable names and the many defines have been modified to make the code
+     * more readable.
+     * The value is first computed as a value in %rH as unsigned 32bit integer
+     * in Q22.10 format(22 integer 10 fractional bits).
+     */
+    int32_t var1;
+
+    /* calculate x1*/
+    var1 = (t_fine - ((int32_t)76800));
+    /* calculate x1*/
+    var1 = (((((adc_H << 14) - (((int32_t)cal->dig_H4) << 20) - (((int32_t)cal->dig_H5) * var1)) +
+              ((int32_t)16384)) >> 15) *
+            (((((((var1 * ((int32_t)cal->dig_H6)) >> 10) *
+                 (((var1 * ((int32_t)cal->dig_H3)) >> 11) + ((int32_t)32768))) >> 10) +
+               ((int32_t)2097152)) * ((int32_t)cal->dig_H2) + 8192) >> 14));
+    var1 = (var1 - (((((var1 >> 15) * (var1 >> 15)) >> 7) * ((int32_t)cal->dig_H1)) >> 4));
+    var1 = (var1 < 0 ? 0 : var1);
+    var1 = (var1 > 419430400 ? 419430400 : var1);
+    /* First multiply to avoid losing the accuracy after the shift by ten */
+    return (100 * ((uint32_t)var1 >> 12)) >> 10;
+}
+
+/**
+ * Read compensation data, 0x88..0x9F, 0xA1, 0xE1..0xE7
+ *
+ * This function reads all calibration bytes at once. These are
+ * the registers DIG_T1_LSB (0x88) upto and including DIG_H6 (0xE7).
+ */
+static int read_calibration_data(bme280_t* dev)
+{
+    uint8_t buffer[128];        /* 128 should be enough to read all calibration bytes */
+    int nr_bytes;
+    int nr_bytes_to_read = (BME280_DIG_H6_REG - BME280_DIG_T1_LSB_REG) + 1;
+    uint8_t offset = 0x88;
+
+    memset(buffer, 0, sizeof(buffer));
+    nr_bytes = i2c_read_regs(dev->params.i2c_dev, dev->params.i2c_addr, offset,
+                             buffer, nr_bytes_to_read);
+    if (nr_bytes != nr_bytes_to_read) {
+        LOG_ERROR("Unable to read calibration data\n");
+        return -1;
+    }
+    DUMP_BUFFER("Raw Calibration Data", buffer, nr_bytes);
+
+    /* All little endian */
+    dev->calibration.dig_T1 = get_uint16_le(buffer, BME280_DIG_T1_LSB_REG - offset);
+    dev->calibration.dig_T2 = get_int16_le(buffer, BME280_DIG_T2_LSB_REG - offset);
+    dev->calibration.dig_T3 = get_int16_le(buffer, BME280_DIG_T3_LSB_REG - offset);
+
+    dev->calibration.dig_P1 = get_uint16_le(buffer, BME280_DIG_P1_LSB_REG - offset);
+    dev->calibration.dig_P2 = get_int16_le(buffer, BME280_DIG_P2_LSB_REG - offset);
+    dev->calibration.dig_P3 = get_int16_le(buffer, BME280_DIG_P3_LSB_REG - offset);
+    dev->calibration.dig_P4 = get_int16_le(buffer, BME280_DIG_P4_LSB_REG - offset);
+    dev->calibration.dig_P5 = get_int16_le(buffer, BME280_DIG_P5_LSB_REG - offset);
+    dev->calibration.dig_P6 = get_int16_le(buffer, BME280_DIG_P6_LSB_REG - offset);
+    dev->calibration.dig_P7 = get_int16_le(buffer, BME280_DIG_P7_LSB_REG - offset);
+    dev->calibration.dig_P8 = get_int16_le(buffer, BME280_DIG_P8_LSB_REG - offset);
+    dev->calibration.dig_P9 = get_int16_le(buffer, BME280_DIG_P9_LSB_REG - offset);
+
+    dev->calibration.dig_H1 = buffer[BME280_DIG_H1_REG - offset];
+    dev->calibration.dig_H2 = get_int16_le(buffer, BME280_DIG_H2_LSB_REG - offset);
+    dev->calibration.dig_H3 = buffer[BME280_DIG_H3_REG - offset];
+    dev->calibration.dig_H4 = (((int16_t)buffer[BME280_DIG_H4_MSB_REG - offset]) << 4) +
+        (buffer[BME280_DIG_H4_H5_REG - offset] & 0x0F);
+    dev->calibration.dig_H5 = (((int16_t)buffer[BME280_DIG_H5_MSB_REG - offset]) << 4) +
+        ((buffer[BME280_DIG_H4_H5_REG - offset] & 0xF0) >> 4);
+    dev->calibration.dig_H6 = buffer[BME280_DIG_H6_REG - offset];
+
+    DEBUG("[INFO] Chip ID = 0x%02X\n", buffer[BME280_CHIP_ID_REG - offset]);
+
+    /* Config is only be writable in sleep mode */
+    (void)i2c_write_reg(dev->params.i2c_dev, dev->params.i2c_addr,
+                        BME280_CTRL_MEAS_REG, 0);
+
+    uint8_t b;
+
+    /* Config Register */
+    /* spi3w_en unused */
+    b = ((dev->params.t_sb & 7) << 5) | ((dev->params.filter & 7) << 2);
+    write_u8_reg(dev, BME280_CONFIG_REG, b);
+
+    /*
+     * Note from the datasheet about ctrl_hum: "Changes to this register only become effective
+     * after a write operation to "ctrl_meas".
+     * So, set ctrl_hum first.
+     */
+    b = dev->params.humid_oversample & 7;
+    write_u8_reg(dev, BME280_CTRL_HUMIDITY_REG, b);
+
+    b = ((dev->params.temp_oversample & 7) << 5) |
+        ((dev->params.press_oversample & 7) << 2) |
+        (dev->params.run_mode & 3);
+    write_u8_reg(dev, BME280_CTRL_MEAS_REG, b);
+
+    return 0;
+}
+
+/**
+ * @brief Start a measurement and read the registers
+ */
+static int do_measurement(bme280_t* dev)
+{
+    /*
+     * If settings has FORCED mode, then the device go to sleep after
+     * it finished the measurement. To read again we have to set the
+     * run_mode back to FORCED.
+     */
+    uint8_t ctrl_meas = get_ctrl_meas(dev);
+    uint8_t run_mode = ctrl_meas & 3;
+    if (run_mode != dev->params.run_mode) {
+        /* Set the run_mode back to what we want. */
+        ctrl_meas &= ~3;
+        ctrl_meas |= dev->params.run_mode;
+        write_u8_reg(dev, BME280_CTRL_MEAS_REG, ctrl_meas);
+
+        /* Wait for measurement ready? */
+        size_t count = 0;
+        while (count < 10 && (get_status(dev) & 0x08) != 0) {
+            ++count;
+        }
+        /* What to do when measuring is still on? */
+    }
+    int nr_bytes;
+    int nr_bytes_to_read = sizeof(measurement_regs);
+    uint8_t offset = BME280_PRESSURE_MSB_REG;
+
+    nr_bytes = i2c_read_regs(dev->params.i2c_dev, dev->params.i2c_addr,
+                             offset, measurement_regs, nr_bytes_to_read);
+    if (nr_bytes != nr_bytes_to_read) {
+        LOG_ERROR("Unable to read temperature data\n");
+        return -1;
+    }
+    DUMP_BUFFER("Raw Sensor Data", measurement_regs, nr_bytes);
+
+    return 0;
+}
+
+static uint8_t get_ctrl_meas(bme280_t* dev)
+{
+    return read_u8_reg(dev, BME280_CTRL_MEAS_REG);
+}
+
+static uint8_t get_status(bme280_t* dev)
+{
+    return read_u8_reg(dev, BME280_STAT_REG);
+}
+
+static uint8_t read_u8_reg(bme280_t* dev, uint8_t reg)
+{
+    uint8_t b;
+    /* Assuming device is correct, it should return 1 (nr bytes) */
+    (void)i2c_read_reg(dev->params.i2c_dev, dev->params.i2c_addr, reg, &b);
+    return b;
+}
+
+static void write_u8_reg(bme280_t* dev, uint8_t reg, uint8_t b)
+{
+    /* Assuming device is correct, it should return 1 (nr bytes) */
+    (void)i2c_write_reg(dev->params.i2c_dev, dev->params.i2c_addr, reg, b);
+}
+
+static uint16_t get_uint16_le(const uint8_t *buffer, size_t offset)
+{
+    return (((uint16_t)buffer[offset + 1]) << 8) + buffer[offset];
+}
+
+static int16_t get_int16_le(const uint8_t *buffer, size_t offset)
+{
+    return (((int16_t)buffer[offset + 1]) << 8) + buffer[offset];
+}
+
+#if ENABLE_DEBUG
+static void dump_buffer(const char *txt, uint8_t *buffer, size_t size)
+{
+    size_t ix;
+    DEBUG("%s\n", txt);
+    for (ix = 0; ix < size; ix++) {
+        DEBUG("%02X", buffer[ix]);
+        if ((ix + 1) == size || (((ix + 1) % 16) == 0)) {
+            DEBUG("\n");
+        }
+    }
+}
+#endif
diff --git a/drivers/bme280/bme280_saul.c b/drivers/bme280/bme280_saul.c
new file mode 100644
index 0000000000000000000000000000000000000000..e37c9ed82f389d719710cbf38a10c8abcd1cee55
--- /dev/null
+++ b/drivers/bme280/bme280_saul.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 Kees Bakker, SODAQ
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     driver_bme280
+ * @{
+ *
+ * @file
+ * @brief       SAUL adoption for BME280 sensor.
+ *
+ * @author      Kees Bakker <kees@sodaq.com>
+ *
+ * @}
+ */
+
+#include "saul.h"
+
+#include "bme280.h"
+
+static int read_temperature(void *dev, phydat_t *res)
+{
+    bme280_t *d = (bme280_t *)dev;
+
+    res->val[0] = bme280_read_temperature(d);
+    res->unit = UNIT_TEMP_C;
+    res->scale = -2;
+
+    return 1;
+}
+
+static int read_relative_humidity(void *dev, phydat_t *res)
+{
+    bme280_t *d = (bme280_t *)dev;
+
+    res->val[0] = bme280_read_humidity(d);
+    res->unit = UNIT_PERCENT;
+    res->scale = -2;
+
+    return 1;
+}
+
+static int read_pressure(void *dev, phydat_t *res)
+{
+    bme280_t *d = (bme280_t *)dev;
+
+    res->val[0] = bme280_read_pressure(d) / 100;
+    res->unit = UNIT_PA;
+    res->scale = 2;
+
+    return 1;
+}
+
+const saul_driver_t bme280_temperature_saul_driver = {
+    .read = read_temperature,
+    .write = saul_notsup,
+    .type = SAUL_SENSE_TEMP,
+};
+
+const saul_driver_t bme280_relative_humidity_saul_driver = {
+    .read = read_relative_humidity,
+    .write = saul_notsup,
+    .type = SAUL_SENSE_HUM,
+};
+
+const saul_driver_t bme280_pressure_saul_driver = {
+    .read = read_pressure,
+    .write = saul_notsup,
+    .type = SAUL_SENSE_PRESS,
+};
diff --git a/drivers/bme280/include/bme280_internals.h b/drivers/bme280/include/bme280_internals.h
new file mode 100644
index 0000000000000000000000000000000000000000..2c28b676e584ae90b612647716e877862a83a116
--- /dev/null
+++ b/drivers/bme280/include/bme280_internals.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 Kees Bakker, SODAQ
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_bme280
+ * @brief       Internal addresses, registers, constants for the BME280 sensor.
+ * @{
+ * @file
+ * @brief       Internal addresses, registers, constants for the BME280 sensor.
+ *
+ * @author      Kees Bakker <kees@sodaq.com>
+ */
+
+#ifndef BME280_INTERNALS_H
+#define BME280_INTERNALS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @name BME280 registers
+ * @{
+ */
+#define BME280_DIG_T1_LSB_REG                   0x88
+#define BME280_DIG_T1_MSB_REG                   0x89
+#define BME280_DIG_T2_LSB_REG                   0x8A
+#define BME280_DIG_T2_MSB_REG                   0x8B
+#define BME280_DIG_T3_LSB_REG                   0x8C
+#define BME280_DIG_T3_MSB_REG                   0x8D
+#define BME280_DIG_P1_LSB_REG                   0x8E
+#define BME280_DIG_P1_MSB_REG                   0x8F
+#define BME280_DIG_P2_LSB_REG                   0x90
+#define BME280_DIG_P2_MSB_REG                   0x91
+#define BME280_DIG_P3_LSB_REG                   0x92
+#define BME280_DIG_P3_MSB_REG                   0x93
+#define BME280_DIG_P4_LSB_REG                   0x94
+#define BME280_DIG_P4_MSB_REG                   0x95
+#define BME280_DIG_P5_LSB_REG                   0x96
+#define BME280_DIG_P5_MSB_REG                   0x97
+#define BME280_DIG_P6_LSB_REG                   0x98
+#define BME280_DIG_P6_MSB_REG                   0x99
+#define BME280_DIG_P7_LSB_REG                   0x9A
+#define BME280_DIG_P7_MSB_REG                   0x9B
+#define BME280_DIG_P8_LSB_REG                   0x9C
+#define BME280_DIG_P8_MSB_REG                   0x9D
+#define BME280_DIG_P9_LSB_REG                   0x9E
+#define BME280_DIG_P9_MSB_REG                   0x9F
+
+#define BME280_DIG_H1_REG                       0xA1
+
+#define BME280_CHIP_ID                          0x60    /* The identifier of the BME280 */
+#define BME280_CHIP_ID_REG                      0xD0
+#define BME280_RST_REG                          0xE0 /* Softreset Reg */
+
+#define BME280_DIG_H2_LSB_REG                   0xE1
+#define BME280_DIG_H2_MSB_REG                   0xE2
+#define BME280_DIG_H3_REG                       0xE3
+#define BME280_DIG_H4_MSB_REG                   0xE4 /* H4[11:4] */
+#define BME280_DIG_H4_H5_REG                    0xE5 /* H5[3:0]  H4[3:0] */
+#define BME280_DIG_H5_MSB_REG                   0xE6 /* H5[11:4] */
+#define BME280_DIG_H6_REG                       0xE7
+
+#define BME280_CTRL_HUMIDITY_REG                0xF2 /* Ctrl Humidity Reg */
+#define BME280_STAT_REG                         0xF3 /* Status Reg */
+#define BME280_CTRL_MEAS_REG                    0xF4 /* Ctrl Measure Reg */
+#define BME280_CONFIG_REG                       0xF5 /* Configuration Reg */
+#define BME280_PRESSURE_MSB_REG                 0xF7 /* Pressure MSB */
+#define BME280_PRESSURE_LSB_REG                 0xF8 /* Pressure LSB */
+#define BME280_PRESSURE_XLSB_REG                0xF9 /* Pressure XLSB */
+#define BME280_TEMPERATURE_MSB_REG              0xFA /* Temperature MSB */
+#define BME280_TEMPERATURE_LSB_REG              0xFB /* Temperature LSB */
+#define BME280_TEMPERATURE_XLSB_REG             0xFC /* Temperature XLSB */
+#define BME280_HUMIDITY_MSB_REG                 0xFD /* Humidity MSB */
+#define BME280_HUMIDITY_LSB_REG                 0xFE /* Humidity LSB */
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BME280_INTERNALS_H */
+/** @} */
diff --git a/drivers/bme280/include/bme280_params.h b/drivers/bme280/include/bme280_params.h
new file mode 100644
index 0000000000000000000000000000000000000000..8f1f7c6180c95f1a9d7ee49e735ed765aff6adff
--- /dev/null
+++ b/drivers/bme280/include/bme280_params.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 Kees Bakker, SODAQ
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_bme280
+ *
+ * @{
+ * @file
+ * @brief       Default configuration for BME280
+ *
+ * @author      Kees Bakker <kees@sodaq.com>
+ */
+
+#ifndef BME280_PARAMS_H
+#define BME280_PARAMS_H
+
+#include "bme280.h"
+#include "saul_reg.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Set default configuration parameters for the BME280
+ * @{
+ */
+#ifndef BME280_PARAM_I2C_DEV
+#define BME280_PARAM_I2C_DEV         I2C_DEV(0)
+#endif
+#ifndef BME280_PARAM_I2C_ADDR
+#define BME280_PARAM_I2C_ADDR        (0x77)
+#endif
+
+/* Defaults for Weather Monitoring */
+#define BME280_PARAMS_DEFAULT              \
+    {                                      \
+        .i2c_dev = BME280_PARAM_I2C_DEV,   \
+        .i2c_addr = BME280_PARAM_I2C_ADDR, \
+        .t_sb = BME280_SB_0_5,             \
+        .filter = BME280_FILTER_OFF,       \
+        .run_mode = BME280_MODE_FORCED,     \
+        .temp_oversample = BME280_OSRS_X1,  \
+        .press_oversample = BME280_OSRS_X1, \
+        .humid_oversample = BME280_OSRS_X1, \
+    }
+/**@}*/
+
+/**
+ * @brief   Configure BME280
+ */
+static const bme280_params_t bme280_params[] =
+{
+#ifdef BME280_PARAMS_BOARD
+    BME280_PARAMS_BOARD,
+#else
+    BME280_PARAMS_DEFAULT,
+#endif
+};
+
+/**
+ * @brief   The number of configured sensors
+ */
+#define BME280_NUMOF    (sizeof(bme280_params) / sizeof(bme280_params[0]))
+
+/**
+ * @brief   Configuration details of SAUL registry entries
+ *
+ * This two dimensional array contains static details of the sensors
+ * for each device. Please be awar that the indexes are used in
+ * auto_init_bme280, so make sure the indexes match.
+ */
+static const saul_reg_info_t bme280_saul_reg_info[BME280_NUMOF][3] =
+{
+    {
+        { .name = "bme280-temp" },
+        { .name = "bme280-humidity" },
+        { .name = "bme280-press" },
+    },
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BME280_PARAMS_H */
+/** @} */
diff --git a/drivers/include/bme280.h b/drivers/include/bme280.h
new file mode 100644
index 0000000000000000000000000000000000000000..3a336414bf28c7c101cad39ee8b222477f8da861
--- /dev/null
+++ b/drivers/include/bme280.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2016 Kees Bakker, SODAQ
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @defgroup    drivers_bme280 BME280
+ * @ingroup     drivers_sensors
+ * @brief       Device driver interface for the BME280 sensor
+ *
+ * @{
+ * @file
+ * @brief       Device driver interface for the BME280 sensor.
+ *
+ * @details     There are three sensor values that can be read: temperature,
+ *              pressure and humidity.  The BME280 device usually measures them
+ *              all at once.  It is possible to skip measuring either of the
+ *              values by changing the oversampling settings.  The driver is
+ *              written in such a way that a measurement is only started from
+ *              the function that reads the temperature.  In other words, you
+ *              always have to call bme280_read_temperature, and then optionally
+ *              you can call the other two.
+ *
+ * @author      Kees Bakker <kees@sodaq.com>
+ */
+
+#ifndef BME280_H
+#define BME280_H
+
+#include "saul.h"
+#include "periph/i2c.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Calibration struct for the BME280 sensor
+ *
+ * This must be read from the device at startup.
+ */
+typedef struct {
+    uint16_t dig_T1;    /**< T1 coefficient */
+    int16_t dig_T2;     /**< T2 coefficient */
+    int16_t dig_T3;     /**< T3 coefficient */
+
+    uint16_t dig_P1;    /**< P1 coefficient */
+    int16_t dig_P2;     /**< P2 coefficient */
+    int16_t dig_P3;     /**< P3 coefficient */
+    int16_t dig_P4;     /**< P4 coefficient */
+    int16_t dig_P5;     /**< P5 coefficient */
+    int16_t dig_P6;     /**< P6 coefficient */
+    int16_t dig_P7;     /**< P7 coefficient */
+    int16_t dig_P8;     /**< P8 coefficient */
+    int16_t dig_P9;     /**< P9 coefficient */
+
+    uint8_t dig_H1;     /**< H1 coefficient */
+    int16_t dig_H2;     /**< H2 coefficient */
+    uint8_t dig_H3;     /**< H3 coefficient */
+    int16_t dig_H4;     /**< H4 coefficient */
+    int16_t dig_H5;     /**< H5 coefficient */
+    int8_t dig_H6;      /**< H6 coefficient */
+} bme280_calibration_t;
+
+/**
+ * @brief Values for t_sb field of the BME280 config register
+ */
+typedef enum {
+    BME280_SB_0_5 = 0,
+    BME280_SB_62_5 = 1,
+    BME280_SB_125 = 2,
+    BME280_SB_250 = 3,
+    BME280_SB_500 = 4,
+    BME280_SB_1000 = 5,
+    BME280_SB_10 = 6,
+    BME280_SB_20 = 7
+} bme280_t_sb_t;
+
+/**
+ * @brief Values for filter field of the BME280 config register
+ */
+typedef enum {
+    BME280_FILTER_OFF = 0,
+    BME280_FILTER_2 = 1,
+    BME280_FILTER_4 = 2,
+    BME280_FILTER_8 = 3,
+    BME280_FILTER_16 = 4,
+} bme280_filter_t;
+
+/**
+ * @brief Values for mode field of the BME280 ctrl_meas register
+ */
+typedef enum {
+    BME280_MODE_SLEEP = 0,
+    BME280_MODE_FORCED = 1,
+    BME280_MODE_FORCED2 = 2,    /* Same as FORCED */
+    BME280_MODE_NORMAL = 3
+} bme280_mode_t;
+
+/**
+ * @brief Values for oversampling settings
+ *
+ * These values are used for:
+ *  - osrs_h field of the BME280 ctrl_hum register
+ *  - osrs_t field of the BME280 ctrl_meas register
+ *  - osrs_p field of the BME280 ctrl_meas register
+ */
+typedef enum {
+    BME280_OSRS_SKIPPED = 0,
+    BME280_OSRS_X1 = 1,
+    BME280_OSRS_X2 = 2,
+    BME280_OSRS_X4 = 3,
+    BME280_OSRS_X8 = 4,
+    BME280_OSRS_X16 = 5,
+} bme280_osrs_t;
+
+/**
+ * @brief Parameters for the BME280 sensor
+ *
+ * These parameters are needed to configure the device at startup.
+ */
+typedef struct {
+    /* I2C details */
+    i2c_t i2c_dev;                      /**< I2C device which is used */
+    uint8_t i2c_addr;                   /**< I2C address */
+
+    /* Config Register */
+    bme280_t_sb_t t_sb;                 /**< standby */
+    bme280_filter_t filter;             /**< filter coefficient */
+    uint8_t spi3w_en;                   /**< Enables 3-wire SPI interface */
+
+    /* ctrl_meas */
+    bme280_mode_t run_mode;             /**< ctrl_meas mode */
+    bme280_osrs_t temp_oversample;      /**< ctrl_meas osrs_t */
+    bme280_osrs_t press_oversample;     /**< ctrl_meas osrs_p */
+
+    /* ctrl_hum */
+    bme280_osrs_t humid_oversample;     /**< ctrl_hum osrs_h */
+} bme280_params_t;
+
+/**
+ * @brief Device descriptor for the BME280 sensor
+ */
+typedef struct {
+    bme280_params_t params;             /**< Device Parameters */
+    bme280_calibration_t calibration;   /**< Calibration Data */
+} bme280_t;
+
+/**
+ * @brief   Status and error return codes
+ */
+enum {
+    BME280_OK           =  0,     /**< everything was fine */
+    BME280_ERR_I2C      = -1,     /**< error initializing the I2C bus */
+    BME280_ERR_NODEV    = -2,     /**< did not detect BME280 */
+    BME280_ERR_NOCAL    = -3,     /**< could not read calibration data */
+};
+
+/**
+ * @brief export SAUL endpoints
+ * @{
+ */
+extern const saul_driver_t bme280_temperature_saul_driver;
+extern const saul_driver_t bme280_relative_humidity_saul_driver;
+extern const saul_driver_t bme280_pressure_saul_driver;
+/** @} */
+
+/**
+ * @brief Initialize the given BME280 device
+ *
+ * @param[out] dev          Initialized device descriptor of BME280 device
+ * @param[in]  params       The parameters for the BME280 device (sampling rate, etc)
+ *
+ * @return                  BME280_OK on success
+ * @return                  BME280_ERR_I2C
+ * @return                  BME280_ERR_NODEV
+ * @return                  BME280_ERR_NOCAL
+ */
+int bme280_init(bme280_t* dev, const bme280_params_t* params);
+
+/**
+ * @brief Read temperature value from the given BME280 device, returned in centi °C
+ *
+ * @param[in] dev           Device descriptor of BME280 device to read from
+ *
+ * @returns                 The temperature in centi Celsius. In case of an error
+ *                          it returns INT16_MIN.
+ */
+int16_t bme280_read_temperature(bme280_t* dev);
+
+/**
+ * @brief Read humidity value from the given BME280 device, returned in centi %RH
+ *
+ * @details This function should only be called after doing bme280_read_temperature
+ *          first.
+ *
+ * @param[in]  dev          Device descriptor of BME280 device to read from
+ *
+ * @returns                 Humidity in centi %RH (i.e. the percentage times 100)
+ */
+uint16_t bme280_read_humidity(bme280_t *dev);
+
+/**
+ * @brief Read air pressure value from the given BME280 device, returned in PA
+ *
+ * @details This function should only be called after doing bme280_read_temperature
+ *          first.
+ *
+ * @param[in]  dev          Device descriptor of BME280 device to read from
+ *
+ * @returns                 The air pressure in Pa
+ */
+uint32_t bme280_read_pressure(bme280_t *dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BME280_H */
+/** @} */
diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c
index 5af0e8f00592db6e0e1d33274da05190b57207fb..9ce5db858fef23cdf4f8fcd216aa8afbe1bb2bdc 100644
--- a/sys/auto_init/auto_init.c
+++ b/sys/auto_init/auto_init.c
@@ -294,6 +294,10 @@ void auto_init(void)
     extern void auto_init_bmp180(void);
     auto_init_bmp180();
 #endif
+#ifdef MODULE_BME280
+    extern void auto_init_bme280(void);
+    auto_init_bme280();
+#endif
 #ifdef MODULE_JC42
     extern void auto_init_jc42(void);
     auto_init_jc42();
diff --git a/sys/auto_init/saul/auto_init_bme280.c b/sys/auto_init/saul/auto_init_bme280.c
new file mode 100644
index 0000000000000000000000000000000000000000..8b8657e2361ba80bdfb3c82b7d497116ead2722b
--- /dev/null
+++ b/sys/auto_init/saul/auto_init_bme280.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 Kees Bakker, SODAQ
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     auto_init_saul
+ * @{
+ *
+ * @file
+ * @brief       Auto initialization of BME280 driver.
+ *
+ * @author      Kees Bakker <kees@sodaq.com>
+ *
+ * @}
+ */
+
+#ifdef MODULE_BME280
+
+#include "log.h"
+#include "saul_reg.h"
+
+#include "bme280_params.h"
+#include "bme280.h"
+
+/**
+ * @brief   Allocation of memory for device descriptors
+ */
+static bme280_t bme280_devs[BME280_NUMOF];
+
+/**
+ * @brief   Memory for the SAUL registry entries
+ */
+static saul_reg_t saul_entries[BME280_NUMOF * 3];
+
+void auto_init_bme280(void)
+{
+    size_t se_ix = 0;
+    for (size_t i = 0; i < BME280_NUMOF; i++) {
+        LOG_DEBUG("[auto_init_saul] initializing BME280 #%u\n", i);
+        int res = bme280_init(&bme280_devs[i], &bme280_params[i]);
+        if (res < 0) {
+            LOG_ERROR("[auto_init_saul] error initializing BME280 #%i\n", i);
+        }
+        else {
+            /* temperature */
+            saul_entries[se_ix].dev = &bme280_devs[i];
+            saul_entries[se_ix].name = bme280_saul_reg_info[i][0].name;
+            saul_entries[se_ix].driver = &bme280_temperature_saul_driver;
+            saul_reg_add(&saul_entries[se_ix]);
+            se_ix++;
+
+            /* relative humidity */
+            saul_entries[se_ix].dev = &bme280_devs[i];
+            saul_entries[se_ix].name = bme280_saul_reg_info[i][1].name;
+            saul_entries[se_ix].driver = &bme280_relative_humidity_saul_driver;
+            saul_reg_add(&saul_entries[se_ix]);
+            se_ix++;
+
+            /* pressure */
+            saul_entries[se_ix].dev = &bme280_devs[i];
+            saul_entries[se_ix].name = bme280_saul_reg_info[i][2].name;
+            saul_entries[se_ix].driver = &bme280_pressure_saul_driver;
+            saul_reg_add(&saul_entries[se_ix]);
+            se_ix++;
+        }
+    }
+}
+
+#else
+typedef int dont_be_pedantic;
+#endif /* MODULE_BME280 */
diff --git a/tests/driver_bme280/Makefile b/tests/driver_bme280/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..17763b6c5caf1fc42978680840dcf35feb70588e
--- /dev/null
+++ b/tests/driver_bme280/Makefile
@@ -0,0 +1,7 @@
+APPLICATION = driver_bme280
+include ../Makefile.tests_common
+
+USEMODULE += bme280
+USEMODULE += xtimer
+
+include $(RIOTBASE)/Makefile.include
diff --git a/tests/driver_bme280/Readme.md b/tests/driver_bme280/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..07d01fe1e52c1100eefa02358d1703289a3307c4
--- /dev/null
+++ b/tests/driver_bme280/Readme.md
@@ -0,0 +1,30 @@
+## About
+This is a test application for the BME280 Pressure, Temperature and
+Humidity sensor.
+
+## Usage
+The application will initialize the BME280 device and display its
+calibration coefficients. More information can be found on the
+[Bosch website][1]
+And in this [BST-BME280_DS001-11 datasheet] [2]
+
+Notice that it is necessary to first read the temperature even if only one
+of the other values (humidity or pressure) is needed. This is described in
+the above mentioned datasheet.
+
+After initialization, every 2 seconds, the application:
+* reads and displays the temperature (d°C);
+* reads and displays the pressure (Pa);
+* reads and displays the humidity (%rH);
+
+## Overruling default parameters
+
+If your device is at a different I2C address than the default (0x77) you
+can build the test as follows:
+
+    export CFLAGS=-DBME280_PARAM_I2C_ADDR=0x76
+    make -C tests/driver_bme280 BOARD=sodaq-autonomo
+
+[1]: http://www.bosch-sensortec.com/en/bst/products/all_products/bme280
+
+[2]: https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280_DS001-11.pdf
diff --git a/tests/driver_bme280/main.c b/tests/driver_bme280/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..ea2dbe70f51d64524c2ec655e95a78fc2adf6cad
--- /dev/null
+++ b/tests/driver_bme280/main.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 Kees Bakker, SODAQ
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup tests
+ * @{
+ *
+ * @file
+ * @brief       Test application for the BME280 temperature, pressure
+ *              and humidity sensor.
+ *
+ * @author      Kees Bakker <kees@sodaq.com>
+ *
+ * @}
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "bme280_params.h"
+#include "bme280.h"
+#include "xtimer.h"
+
+#define MAINLOOP_DELAY  (2 * 1000 * 1000u)      /* 2 seconds delay between printf's */
+
+int main(void)
+{
+    bme280_t dev;
+    int result;
+
+    puts("BME280 test application\n");
+
+    printf("+------------Initializing------------+\n");
+    result = bme280_init(&dev, &bme280_params[0]);
+    if (result == -1) {
+        puts("[Error] The given i2c is not enabled");
+        return 1;
+    }
+
+    if (result == -2) {
+        printf("[Error] The sensor did not answer correctly at address 0x%02X\n", bme280_params[0].i2c_addr);
+        return 1;
+    }
+
+    printf("Initialization successful\n\n");
+
+    printf("+------------Calibration Data------------+\n");
+    printf("dig_T1: %u\n", dev.calibration.dig_T1);
+    printf("dig_T2: %i\n", dev.calibration.dig_T2);
+    printf("dig_T3: %i\n", dev.calibration.dig_T3);
+
+    printf("dig_P1: %u\n", dev.calibration.dig_P1);
+    printf("dig_P2: %i\n", dev.calibration.dig_P2);
+    printf("dig_P3: %i\n", dev.calibration.dig_P3);
+    printf("dig_P4: %i\n", dev.calibration.dig_P4);
+    printf("dig_P5: %i\n", dev.calibration.dig_P5);
+    printf("dig_P6: %i\n", dev.calibration.dig_P6);
+    printf("dig_P7: %i\n", dev.calibration.dig_P7);
+    printf("dig_P8: %i\n", dev.calibration.dig_P8);
+    printf("dig_P9: %i\n", dev.calibration.dig_P9);
+
+    printf("dig_H1: %u\n", dev.calibration.dig_H1);
+    printf("dig_H2: %i\n", dev.calibration.dig_H2);
+    printf("dig_H3: %i\n", dev.calibration.dig_H3);
+    printf("dig_H4: %i\n", dev.calibration.dig_H4);
+    printf("dig_H5: %i\n", dev.calibration.dig_H5);
+    printf("dig_H6: %i\n", dev.calibration.dig_H6);
+
+    printf("\n+--------Starting Measurements--------+\n");
+    while (1) {
+        int16_t temperature;
+        uint32_t pressure;
+        uint16_t humidity;
+
+        /* Get temperature in centi degrees Celsius */
+        temperature = bme280_read_temperature(&dev);
+
+        /* Get pressure in Pa */
+        pressure = bme280_read_pressure(&dev);
+
+        /* Get pressure in %rH */
+        humidity = bme280_read_humidity(&dev);
+
+        printf("Temperature [°C]: %d.%d\n"
+               "Pressure [Pa]: %lu\n"
+               "Humidity [%%rH]: %u.%02u\n"
+               "\n+-------------------------------------+\n",
+               temperature / 100, (temperature % 100) / 10,
+               (unsigned long)pressure,
+               (unsigned int)(humidity / 100), (unsigned int)(humidity % 100));
+
+        xtimer_usleep(MAINLOOP_DELAY);
+    }
+
+    return 0;
+}