diff --git a/Makefile.dep b/Makefile.dep
index c0b24c5f550cfb900c9dfa24d92ce695b1249230..0e999f9411b9b587bc81101605e63952eab96310 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -698,6 +698,15 @@ ifneq (,$(filter benchmark,$(USEMODULE)))
   USEMODULE += xtimer
 endif
 
+ifneq (,$(filter skald_%,$(USEMODULE)))
+  USEMODULE += skald
+endif
+
+ifneq (,$(filter skald,$(USEMODULE)))
+  FEATURES_REQUIRED += radio_ble
+  USEMODULE += xtimer
+  USEMODULE += random
+endif
 # always select gpio (until explicit dependencies are sorted out)
 FEATURES_OPTIONAL += periph_gpio
 
diff --git a/boards/common/nrf52xxxdk/Makefile.dep b/boards/common/nrf52xxxdk/Makefile.dep
index 5472bf8b8d8fd463a18815c0f10e5d348f90fe51..09f460dcc6367499130caacccca64e86f39454b8 100644
--- a/boards/common/nrf52xxxdk/Makefile.dep
+++ b/boards/common/nrf52xxxdk/Makefile.dep
@@ -1,3 +1,7 @@
 ifneq (,$(filter saul_default,$(USEMODULE)))
   USEMODULE += saul_gpio
 endif
+
+ifneq (,$(filter skald,$(USEMODULE)))
+  USEMODULE += nrfble
+endif
diff --git a/boards/common/nrf52xxxdk/Makefile.features b/boards/common/nrf52xxxdk/Makefile.features
index 56ed0022c03b13df2b60ce2c12b0963bfefcafc7..1783223faf9024647f0aa517abf18fe9b223021e 100644
--- a/boards/common/nrf52xxxdk/Makefile.features
+++ b/boards/common/nrf52xxxdk/Makefile.features
@@ -7,6 +7,8 @@ FEATURES_PROVIDED += periph_timer
 FEATURES_PROVIDED += periph_uart
 
 # Various other features (if any)
+FEATURES_PROVIDED += radio_ble
+FEATURES_PROVIDED += radio_nrfble
 FEATURES_PROVIDED += radio_nrfmin
 
 # The board MPU family (used for grouping by the CI system)
diff --git a/cpu/nrf5x_common/Makefile b/cpu/nrf5x_common/Makefile
index ef599b387d5510d053d265cd2ddfea8ed0686619..41f24c0a2df0e4e4ecba038c051736f666fae930 100644
--- a/cpu/nrf5x_common/Makefile
+++ b/cpu/nrf5x_common/Makefile
@@ -2,6 +2,9 @@ MODULE = cpu_common
 DIRS = periph
 
 # build one of the radio drivers, if enabled
+ifneq (,$(filter nrfble,$(USEMODULE)))
+  DIRS += radio/nrfble
+endif
 ifneq (,$(filter nrfmin,$(USEMODULE)))
   DIRS += radio/nrfmin
 endif
diff --git a/cpu/nrf5x_common/include/nrfble.h b/cpu/nrf5x_common/include/nrfble.h
new file mode 100644
index 0000000000000000000000000000000000000000..9c41b3ee443de400c9a5e445a7cd3726363bb8fe
--- /dev/null
+++ b/cpu/nrf5x_common/include/nrfble.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017-2018 Freie Universität Berlin
+ *
+ * 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_nrf5x_nrfble NRF BLE radio driver
+ * @ingroup     drivers_netdev
+ * @brief       Radio driver for nRF5x SoCs for using the radio in BLE mode
+ * @{
+ *
+ * @file
+ * @brief       Interface definition for the nrfble radio driver
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef NRFBLE_H
+#define NRFBLE_H
+
+#include "net/netdev.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   nrfble channel configuration
+ * @{
+ */
+#define NRFBLE_CHAN_MIN             (0U)
+#define NRFBLE_CHAN_MAX             (39U)
+/** @} */
+
+/**
+ * @brief   Default transmission power used
+ */
+#define NRFBLE_TXPOWER_DEFAULT      (0)                 /* 0dBm */
+
+/**
+ * @brief   Setup the device driver's data structures
+ *
+ * @return  pointer to the device's netdev struct
+ */
+netdev_t *nrfble_setup(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NRFBLE_H */
+/** @} */
diff --git a/cpu/nrf5x_common/radio/nrfble/Makefile b/cpu/nrf5x_common/radio/nrfble/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..1f6a482e4fe16f6ecbf6ad792cc3224b8541fc24
--- /dev/null
+++ b/cpu/nrf5x_common/radio/nrfble/Makefile
@@ -0,0 +1,3 @@
+MODULE = nrfble
+
+include $(RIOTBASE)/Makefile.base
diff --git a/cpu/nrf5x_common/radio/nrfble/nrfble.c b/cpu/nrf5x_common/radio/nrfble/nrfble.c
new file mode 100644
index 0000000000000000000000000000000000000000..dd500cbde3972e536bb3627bb2ce361821ca61e5
--- /dev/null
+++ b/cpu/nrf5x_common/radio/nrfble/nrfble.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2017-2018 Freie Universität Berlin
+ *
+ * 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_nrf5x_nrfble
+ *
+ * @note        This driver is not thread safe and should only be used by a
+ *              single thread at once!
+ * @{
+ *
+ * @file
+ * @brief       Bluetooth low energy radio driver for nRF5x SoCs
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include <errno.h>
+
+#include "cpu.h"
+#include "assert.h"
+
+#include "nrfble.h"
+#include "net/netdev/ble.h"
+
+#define ENABLE_DEBUG            (0)
+#include "debug.h"
+
+/* driver specific device configuration */
+#define CONF_MODE               RADIO_MODE_MODE_Ble_1Mbit
+#define CONF_LEN                (8U)
+#define CONF_S0                 (1U)
+#define CONF_S1                 (0U)
+#define CONF_STATLEN            (0U)
+#define CONF_BASE_ADDR_LEN      (3U)
+#define CONF_ENDIAN             RADIO_PCNF1_ENDIAN_Little
+#define CONF_WHITENING          RADIO_PCNF1_WHITEEN_Enabled
+#define CONF_TIFS               (150U)
+#define CONF_CRC_LEN            (0X3 | RADIO_CRCCNF_SKIPADDR_Msk)
+#define CONF_CRC_POLY           (0x00065b)
+
+/* used shortcuts */
+#define SHORTS_BASE             (RADIO_SHORTS_READY_START_Msk | \
+                                 RADIO_SHORTS_END_DISABLE_Msk)
+#define SHORTS_RX               (SHORTS_BASE | RADIO_SHORTS_DISABLED_TXEN_Msk)
+#define SHORTS_TX               (SHORTS_BASE | RADIO_SHORTS_DISABLED_RXEN_Msk)
+
+/* interrupt masks */
+#define INT_DIS                 (RADIO_INTENCLR_DISABLED_Msk | \
+                                 RADIO_INTENCLR_ADDRESS_Msk)
+#define INT_EN                  (RADIO_INTENSET_DISABLED_Msk | \
+                                 RADIO_INTENSET_ADDRESS_Msk)
+
+/* driver internal radio states */
+#define STATE_IDLE              (0x00)
+#define STATE_RX                (0x01)
+#define STATE_TX                (0x02)
+#define STATE_BUSY              (0x80)
+
+/* forward declaration of the netdev driver struct */
+static const netdev_driver_t netdev_driver;
+
+/* current radio state */
+static volatile uint8_t _state = STATE_IDLE;
+
+/* active radio context */
+static netdev_ble_ctx_t *_ctx = NULL;
+
+/* allocate the netdev device descriptor */
+netdev_t _nrfble_dev;
+
+/* map logical BLE channel values to actual radio frequencies */
+static const uint8_t _ble_chan_map[40] = {
+    [ 0] = 4,
+    [ 1] = 6,
+    [ 2] = 8,
+    [ 3] = 10,
+    [ 4] = 12,
+    [ 5] = 14,
+    [ 6] = 16,
+    [ 7] = 18,
+    [ 8] = 20,
+    [ 9] = 22,
+    [10] = 24,
+    [11] = 28,
+    [12] = 30,
+    [13] = 32,
+    [14] = 34,
+    [15] = 36,
+    [16] = 38,
+    [17] = 40,
+    [18] = 42,
+    [19] = 44,
+    [20] = 46,
+    [21] = 48,
+    [22] = 50,
+    [23] = 52,
+    [24] = 54,
+    [25] = 56,
+    [26] = 58,
+    [27] = 60,
+    [28] = 62,
+    [29] = 64,
+    [30] = 66,
+    [31] = 68,
+    [32] = 70,
+    [33] = 72,
+    [34] = 74,
+    [35] = 76,
+    [36] = 78,
+    [37] = 2,
+    [38] = 26,
+    [39] = 80,
+};
+
+/**
+ * @brief   Set radio into idle (DISABLED) state
+ */
+static void _go_idle(void)
+{
+    if (!(_state & STATE_BUSY)) {
+        NRF_RADIO->INTENCLR = INT_DIS;
+        NRF_RADIO->SHORTS = 0;
+        NRF_RADIO->EVENTS_DISABLED = 0;
+        NRF_RADIO->TASKS_DISABLE = 1;
+        while (NRF_RADIO->EVENTS_DISABLED == 0) {}
+        _state = STATE_IDLE;
+    }
+}
+
+/**
+ * @brief   Set radio context (channel, CRC, whitening, and access address)
+ */
+static void _set_context(netdev_ble_ctx_t *ctx)
+{
+    if (ctx) {
+        assert(ctx->chan <= NRFBLE_CHAN_MAX);
+
+        _ctx = ctx;
+        NRF_RADIO->FREQUENCY = _ble_chan_map[ctx->chan];
+        NRF_RADIO->PREFIX0 = ctx->aa.raw[3];
+        NRF_RADIO->BASE0   = (uint32_t)((ctx->aa.raw[0] << 8) |
+                                        (ctx->aa.raw[1] << 16) |
+                                        (ctx->aa.raw[2] << 24));
+        NRF_RADIO->DATAWHITEIV = ctx->chan;
+        NRF_RADIO->CRCINIT = (ctx->crc & NETDEV_BLE_CRC_MASK);
+    }
+    else {
+        _go_idle();
+    }
+}
+
+static int16_t _nrfble_get_txpower(void)
+{
+    int8_t p = (int8_t)NRF_RADIO->TXPOWER;
+    if (p < 0) {
+        return (int16_t)(0xff00 | p);
+    }
+    return (int16_t)p;
+}
+
+static void _nrfble_set_txpower(int16_t power)
+{
+    if (power > 2) {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Pos4dBm;
+    }
+    else if (power > -2) {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_0dBm;
+    }
+    else if (power > -6) {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg4dBm;
+    }
+    else if (power > -10) {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg8dBm;
+    }
+    else if (power > -14) {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg12dBm;
+    }
+    else if (power > -18) {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg16dBm;
+    }
+    else if (power > -25) {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg20dBm;
+    }
+    else {
+        NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg30dBm;
+    }
+}
+
+/**
+ * @brief   Radio interrupt routine
+ */
+void isr_radio(void)
+{
+    if (NRF_RADIO->EVENTS_ADDRESS) {
+        NRF_RADIO->EVENTS_ADDRESS = 0;
+        _state |= STATE_BUSY;
+    }
+
+    if (NRF_RADIO->EVENTS_DISABLED) {
+        NRF_RADIO->EVENTS_DISABLED = 0;
+
+        if (_state == (STATE_BUSY | STATE_RX)) {
+            _state = STATE_TX;
+            if (NRF_RADIO->CRCSTATUS) {
+                _ctx->crc |= NETDEV_BLE_CRC_OK;
+            }
+            else {
+                _ctx->crc &= ~(NETDEV_BLE_CRC_OK);
+            }
+            _nrfble_dev.event_callback(&_nrfble_dev, NETDEV_EVENT_RX_COMPLETE);
+        }
+        else {  /* on TX done */
+            _state = STATE_RX;
+            _nrfble_dev.event_callback(&_nrfble_dev, NETDEV_EVENT_TX_COMPLETE);
+        }
+    }
+
+    cortexm_isr_end();
+}
+
+netdev_t *nrfble_setup(void)
+{
+    _nrfble_dev.driver = &netdev_driver;
+    _nrfble_dev.event_callback = NULL;
+    _nrfble_dev.context = NULL;
+#ifdef MODULE_NETSTATS_L2
+    memset(&_nrfble_dev.stats, 0, sizeof(netstats_t));;
+#endif
+    return &_nrfble_dev;
+}
+
+static int _nrfble_init(netdev_t *dev)
+{
+    (void)dev;
+    assert(_nrfble_dev.driver && _nrfble_dev.event_callback);
+
+    /* power on the NRFs radio */
+    NRF_RADIO->POWER = 1;
+    /* configure variable parameters to default values */
+    NRF_RADIO->TXPOWER = NRFBLE_TXPOWER_DEFAULT;
+    /* always send from and listen to logical address 0 */
+    NRF_RADIO->TXADDRESS = 0x00UL;
+    NRF_RADIO->RXADDRESSES = 0x01UL;
+    /* load driver specific configuration */
+    NRF_RADIO->MODE = CONF_MODE;
+    /* configure data fields and packet length whitening and endianess */
+    NRF_RADIO->PCNF0 = ((CONF_S1 << RADIO_PCNF0_S1LEN_Pos) |
+                        (CONF_S0 << RADIO_PCNF0_S0LEN_Pos) |
+                        (CONF_LEN << RADIO_PCNF0_LFLEN_Pos));
+    NRF_RADIO->PCNF1 = ((CONF_WHITENING << RADIO_PCNF1_WHITEEN_Pos) |
+                        (CONF_ENDIAN << RADIO_PCNF1_ENDIAN_Pos) |
+                        (CONF_BASE_ADDR_LEN << RADIO_PCNF1_BALEN_Pos) |
+                        (CONF_STATLEN << RADIO_PCNF1_STATLEN_Pos) |
+                        (NETDEV_BLE_PDU_MAXLEN << RADIO_PCNF1_MAXLEN_Pos));
+    /* set inter frame spacing to 150us */
+    NRF_RADIO->TIFS = CONF_TIFS;
+    /* configure CRC length and polynomial */
+    NRF_RADIO->CRCCNF = CONF_CRC_LEN;
+    NRF_RADIO->CRCPOLY = CONF_CRC_POLY;
+    /* enable global interrupts, but mask all local interrupts for now */
+    NRF_RADIO->INTENCLR = 0xffffffff;
+    NVIC_EnableIRQ(RADIO_IRQn);
+
+    DEBUG("[nrfble] initialization successful\n");
+    return 0;
+}
+
+static int _nrfble_send(netdev_t *dev, const iolist_t *data)
+{
+    (void)dev;
+    assert(data);
+
+    NRF_RADIO->PACKETPTR = (uint32_t)data->iol_base;
+    NRF_RADIO->SHORTS = SHORTS_TX;
+
+    /* in case no trx sequence is active, we start a new one now */
+    if (_state == STATE_IDLE) {
+        _state = STATE_TX;
+        NRF_RADIO->EVENTS_DISABLED = 0;
+        NRF_RADIO->INTENSET = INT_EN;
+        NRF_RADIO->TASKS_TXEN = 1;
+    }
+
+    return 0;
+}
+
+static int _nrfble_recv(netdev_t *dev, void *buf, size_t len, void *info)
+{
+    (void)dev;
+    (void)len;
+    (void)info;
+    assert(buf && (len == sizeof(netdev_ble_pkt_t)));
+
+    NRF_RADIO->PACKETPTR = (uint32_t)buf;
+    NRF_RADIO->SHORTS = SHORTS_RX;
+
+    /* in case no trx sequence is active, we start a new one now */
+    if (_state == STATE_IDLE) {
+        _state = STATE_RX;
+        NRF_RADIO->EVENTS_DISABLED = 0;
+        NRF_RADIO->INTENSET = INT_EN;
+        NRF_RADIO->TASKS_RXEN = 1;
+    }
+
+    return 0;
+}
+
+static int _nrfble_get(netdev_t *dev, netopt_t opt, void *val, size_t max_len)
+{
+    (void)dev;
+    (void)max_len;
+
+    switch (opt) {
+        case NETOPT_TX_POWER:
+            assert(max_len >= sizeof(int16_t));
+            *((int16_t *)val) = _nrfble_get_txpower();
+            return sizeof(int16_t);
+        case NETOPT_DEVICE_TYPE:
+            assert(max_len >= sizeof(uint16_t));
+            *((uint16_t *)val) = NETDEV_TYPE_BLE;
+            return sizeof(uint16_t);
+        default:
+            return -ENOTSUP;
+    }
+}
+
+static int _nrfble_set(netdev_t *dev, netopt_t opt, const void *val, size_t len)
+{
+    (void)dev;
+    (void)len;
+
+    switch (opt) {
+        case NETOPT_BLE_CTX:
+            _set_context((netdev_ble_ctx_t *)val);
+            return sizeof(netdev_ble_ctx_t);
+        case NETOPT_TX_POWER:
+            assert(len == sizeof(int16_t));
+            _nrfble_set_txpower(*((const int16_t *)val));
+            return sizeof(int16_t);
+        default:
+            return -ENOTSUP;
+    }
+}
+
+/* export of the netdev interface */
+static const netdev_driver_t netdev_driver = {
+    .send = _nrfble_send,
+    .recv = _nrfble_recv,
+    .init = _nrfble_init,
+    .isr  = NULL,
+    .get  = _nrfble_get,
+    .set  = _nrfble_set
+};
diff --git a/drivers/include/net/netdev/ble.h b/drivers/include/net/netdev/ble.h
new file mode 100644
index 0000000000000000000000000000000000000000..248c0bdeff7907a14725c8ade472e6cd3fc1af1f
--- /dev/null
+++ b/drivers/include/net/netdev/ble.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2017-2018 Freie Universität Berlin
+ *
+ * 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_netdev_ble netdev BLE mode
+ * @ingroup     drivers_netdev_api
+ * @brief       BLE adaption of netdev
+ * @{
+ *
+ * @warning     This API is experimental and in an early state - expect
+ *              significant changes!
+ *
+ * # About
+ *
+ * BLE defines a very specific environment for the used radio, both in terms of
+ * communication sequences and in terms of timings. BLE communication is
+ * structured in so called events, where each event is a sequence of request and
+ * reply packets send between two peers. A radio context (frequency, CRC
+ * initializer, and access address) is used throughout such an event and
+ * typically changed for the next one. In addition, the timing of the packets
+ * sent in a sequence is fixed to an inter-frame-spacing of exactly 150us.
+ *
+ * To cater with these specific attributes of BLE, this interface tailors the
+ * generic netdev interface to be used for BLE radios.
+ *
+ *
+ * # Interface Adaption / Netdev Interpretation
+ *
+ * ## Transmission Sequence Based Approach
+ *
+ * To be able to handle the exact inter-packet-spacing if 150us seconds, this
+ * interface expects the device driver to stay in a continuous alternating
+ * RX-TX sequence, until it is manually aborted. While in this sequence, the
+ * radio driver needs to take care of switching to RX mode 150us after sending
+ * the last packet, and to send the next packet 150us after the last packet was
+ * received.
+ *
+ * Such a transmission sequence is started by calling either the radio's send
+ * or receive function while the radio is in idle/standby mode.
+ *
+ * Once a transmission sequence is in progress, the next packet to be send, or
+ * the next reception buffer to be used is specified also using the send/recv
+ * functions. They should be called in the `event_callback` right after the
+ * last transmission (RX or TX) was finished.
+ *
+ * The transmission sequence is aborted by calling `netdev_ble_stop(dev)`
+ * (`netdev->set(dev, NETOPT_BLE_CTX, NULL, 0)`). This will put the radio back
+ * into idle/standby mode.
+ *
+ * ## Radio Context
+ *
+ * As BLE uses time sliced channel hopping, the used channel needs to be set
+ * regularly. Additionally, also the used access address and the CRC initializer
+ * need to be set regularly, as they differ for different BLE connections. To
+ * make setting these values more efficient, this interface combines these three
+ * values in to a so called `radio context` and adds a `netopt` option to set
+ * all three values at once using `netdev_ble_set_ctx(dev, ctx)`
+ * (`netdev->set(dev, NETOPT_BLE_CTX, ctx, sizeof(netdev_ble_ctx_t))`).
+ *
+ *
+ * # Implementation Status and Limitations
+ * - This interface works for memory mapped radios only (no support for
+ *   bus-connected devices). This is mainly for timing reasons.
+ * - No support for LE Data Length Extension (bigger packet size), yet
+ *
+ * @file
+ * @brief       BLE specific adaption for the Netdev API
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef NET_NETDEV_BLE_H
+#define NET_NETDEV_BLE_H
+
+#include "net/netdev.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Maximum payload length of a standard BLE packet
+ */
+#define NETDEV_BLE_PDU_MAXLEN       (37U)
+
+/**
+ * @brief   Mask for the actual (3 byte) CRC data in the context's CRC field
+ */
+#define NETDEV_BLE_CRC_MASK         (0x00ffffff)
+
+/**
+ * @brief   Flag for marking a correct CRC on packet reception
+ */
+#define NETDEV_BLE_CRC_OK           (0x80000000)
+
+/**
+ * @brief   BLE packet structure (as defined by the BLE standard)
+ */
+typedef struct __attribute__((packed)) {
+    uint8_t flags;                          /**< header flags */
+    uint8_t len;                            /**< actual length of PDU */
+    uint8_t pdu[NETDEV_BLE_PDU_MAXLEN];     /**< protocol data unit (PDU) */
+} netdev_ble_pkt_t;
+
+/**
+ * @brief   Radio context
+ */
+typedef struct {
+    union {
+        uint8_t raw[4];                     /**< byte-wise access */
+        uint32_t u32;                       /**< compact access */
+    } aa;                                   /**< access address */
+    uint32_t crc;                           /**< CRC: 3 LSB for CRC, most
+                                             *   significant bit for RX state*/
+    uint8_t chan;                           /**< channel to use/used */
+} netdev_ble_ctx_t;
+
+/**
+ * @brief   Send the given packet on the next occasion
+ *
+ * If a transmission sequence is in progress, the given packet will be send
+ * after 150us after receptions of the last packet. If no sequence is currently
+ * active, the packet will be send immediately and a new transmission sequence
+ * is started.
+ *
+ * @note    Call this function only to start a new transmission sequence (radio
+ *          is currently idle), or right after a packet was received. If called
+ *          at any other point in time, the behavior is undefined.
+ *
+ * @param[in] dev   radio to use for sending
+ * @param[in] pkt   data to send
+ *
+ * @return  0 on success
+ * @return `< 0` on error
+ */
+static inline int netdev_ble_send(netdev_t *dev, netdev_ble_pkt_t *pkt)
+{
+    struct iolist data = { NULL, pkt, sizeof(netdev_ble_pkt_t) };
+    return dev->driver->send(dev, &data);
+}
+
+/**
+ * @brief   Start listening for an incoming packet and write it into @p pkt
+ *
+ * If a transmission sequence is in progress, the radio will use the given
+ * buffer for reception when it goes in to RX mode 150us after sending the last
+ * packet. If no sequence is in progress, the radio will go into RX mode
+ * immediately (using the given RX buffer), and a new transmission sequence is
+ * started.
+ *
+ * @note    Call this function only to start a new transmission sequence (radio
+ *          is currently idle), or right after a packet was sent. If called
+ *          at any other point in time, the behavior is undefined.
+ *
+ * @param[in]  dev  radio to use for receiving
+ * @param[out] pkt  buffer to write new packet to
+ *
+ * @return  0 on success
+ * @return `< 0` on error
+ */
+static inline int netdev_ble_recv(netdev_t *dev, netdev_ble_pkt_t *pkt)
+{
+    return dev->driver->recv(dev, pkt, sizeof(netdev_ble_pkt_t), NULL);
+}
+
+/**
+ * @brief   Set the radio context for the given radio device
+ *
+ * @param[in] dev   target radio device
+ * @param[in] ctx   new radio context (CRC, channel, access address)
+ */
+static inline void netdev_ble_set_ctx(netdev_t *dev, netdev_ble_ctx_t *ctx)
+{
+    dev->driver->set(dev, NETOPT_BLE_CTX, ctx, sizeof(netdev_ble_ctx_t));
+}
+
+/**
+ * @brief   Stop the ongoing RX/TX sequence
+ *
+ * @note    This function has not effect if the radio is in the middle of a
+ *          data transfer
+ *
+ * @param[in] dev   target radio device
+ */
+static inline void netdev_ble_stop(netdev_t *dev)
+{
+    dev->driver->set(dev, NETOPT_BLE_CTX, NULL, 0);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_NETDEV_BLE_H */
+/** @} */
diff --git a/examples/skald_eddystone/Makefile b/examples/skald_eddystone/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..a452cae29213a71c5183a59ca8c6cfbe930b999c
--- /dev/null
+++ b/examples/skald_eddystone/Makefile
@@ -0,0 +1,21 @@
+# name of your application
+APPLICATION = skald_eddystone
+
+# If no BOARD is found in the environment, use this default:
+BOARD ?= nrf52dk
+
+# This has to be the absolute path to the RIOT base directory:
+RIOTBASE ?= $(CURDIR)/../..
+
+# include Skald
+USEMODULE += skald_eddystone
+
+# Comment this out to disable code in RIOT that does safety checking
+# which is not needed in a production environment but helps in the
+# development process:
+# CFLAGS += -DDEVELHELP
+
+# Change this to 0 show compiler invocation lines by default:
+QUIET ?= 1
+
+include $(RIOTBASE)/Makefile.include
diff --git a/examples/skald_eddystone/README.md b/examples/skald_eddystone/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..fa0d8f8f6ab0ada117d5d96ee05c06ed3004f47b
--- /dev/null
+++ b/examples/skald_eddystone/README.md
@@ -0,0 +1,8 @@
+# Skald iBeacon Example
+
+This example demonstrates the usage of `Skald` for creating an Google
+`Eddystone` beacon, advertising an`Eddystone-URI` and an `Eddystone-URL`
+concurrently.
+
+Simply compile and flash, and verify your newly created beacon with any type of
+BLE scanner.
diff --git a/examples/skald_eddystone/main.c b/examples/skald_eddystone/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..2892cc85bd4313051a5eed1f134546f35d9aa08e
--- /dev/null
+++ b/examples/skald_eddystone/main.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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     examples
+ * @{
+ *
+ * @file
+ * @brief       BLE Eddystone beacon example using Skald
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include "log.h"
+
+#include "net/skald/eddystone.h"
+
+/* example of an Eddystone URI:
+ * - namespace (ASCII): 'supercool!'
+ * - instance  (ASCII): `_RIOT_` */
+#define URI_NAMESPACE   { 0x73, 0x75, 0x70, 0x65, 0x72, \
+                          0x63, 0x6f, 0x6f, 0x6c, 0x21 }
+#define URI_INSTANCE    { 0x5f, 0x52, 0x49, 0x4f, 0x54, 0x5f }
+
+/* advertise this short URL, points to https://www.riot-os.org */
+#define URL             "bit.ly/2Ep11dm"
+/* calibrated TX power value */
+#define TX_PWR          (0U)
+
+
+/* allocate two advertising contexts, one for Eddystone-URL and one for
+ * Eddystone-URI */
+static skald_ctx_t _ctx_uid;
+static skald_ctx_t _ctx_url;
+
+int main(void)
+{
+    LOG_INFO("Skald and the tail of Eddystone\n");
+
+    /* advertise the defined URI */
+    skald_eddystone_uid_t uid = { URI_NAMESPACE, URI_INSTANCE };
+    skald_eddystone_uid_adv(&_ctx_uid, &uid, TX_PWR);
+
+    /* also advertise the defined short-URL */
+    skald_eddystone_url_adv(&_ctx_url, EDDYSTONE_URL_HTTPS, URL, TX_PWR);
+
+    return 0;
+}
diff --git a/examples/skald_ibeacon/Makefile b/examples/skald_ibeacon/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..da517defcfcf1cf43f70d72b3b9c9e5387fdda37
--- /dev/null
+++ b/examples/skald_ibeacon/Makefile
@@ -0,0 +1,21 @@
+# name of your application
+APPLICATION = skald_ibeacon
+
+# If no BOARD is found in the environment, use this default:
+BOARD ?= nrf52dk
+
+# This has to be the absolute path to the RIOT base directory:
+RIOTBASE ?= $(CURDIR)/../..
+
+# include Skald
+USEMODULE += skald_ibeacon
+
+# Comment this out to disable code in RIOT that does safety checking
+# which is not needed in a production environment but helps in the
+# development process:
+# CFLAGS += -DDEVELHELP
+
+# Change this to 0 show compiler invocation lines by default:
+QUIET ?= 1
+
+include $(RIOTBASE)/Makefile.include
diff --git a/examples/skald_ibeacon/README.md b/examples/skald_ibeacon/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..37911394ddffa22e32114f8cb7c9d266c0c5f988
--- /dev/null
+++ b/examples/skald_ibeacon/README.md
@@ -0,0 +1,6 @@
+# Skald iBeacon Example
+
+This example demonstrates the usage of `Skald` for creating an Apple `iBeacon`.
+
+Simply compile and flash, and verify your newly created beacon with any type of
+BLE scanner.
diff --git a/examples/skald_ibeacon/main.c b/examples/skald_ibeacon/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..fe9ac9d1042da97f900763a97556778e98ca1fe1
--- /dev/null
+++ b/examples/skald_ibeacon/main.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017-2018 Freie Universität Berlin
+ *
+ * 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     examples
+ * @{
+ *
+ * @file
+ * @brief       Setting up an iBeacon using Skald
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include "log.h"
+
+#include "net/skald/ibeacon.h"
+
+/* configure the iBeacon */
+#define UUID            { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, \
+                          0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, }
+#define MAJOR           (0x0023)
+#define MINOR           (0x0017)
+#define TXPOWER         (0U)
+
+/* allocate a single advertising context */
+static skald_ctx_t _ctx;
+
+int main(void)
+{
+    LOG_INFO("Skald and the tail of the iBeacon\n");
+
+    /* this will configure the iBeacon and start advertising it */
+    skald_uuid_t uuid = { UUID };
+    skald_ibeacon_advertise(&_ctx, &uuid, MAJOR, MINOR, TXPOWER);
+
+    return 0;
+}
diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk
index 4fec53670a87cb39bf0bb3b8222ae95fd60f4d30..15b562684e3efafd5f4f6fb89c65ad99b94717a6 100644
--- a/makefiles/pseudomodules.inc.mk
+++ b/makefiles/pseudomodules.inc.mk
@@ -117,3 +117,8 @@ PSEUDOMODULES += stm32_periph_%
 # declare periph submodules as pseudomodules, but exclude periph_common
 PSEUDOMODULES += periph_%
 NO_PSEUDOMODULES += periph_common
+
+# Submodules and auto-init code provided by Skald
+PSEUDOMODULES += auto_init_skald
+PSEUDOMODULES += skald_ibeacon
+PSEUDOMODULES += skald_eddystone
diff --git a/sys/Makefile b/sys/Makefile
index 76701bd74d5398987f56821c50c553722df3e284..cc6d2ab074bcc643ea4ec4a2649237a1c804287d 100644
--- a/sys/Makefile
+++ b/sys/Makefile
@@ -124,6 +124,9 @@ endif
 ifneq (,$(filter nanocoap,$(USEMODULE)))
   DIRS += net/application_layer/nanocoap
 endif
+ifneq (,$(filter skald,$(USEMODULE)))
+  DIRS += net/skald
+endif
 
 DIRS += $(dir $(wildcard $(addsuffix /Makefile, $(USEMODULE))))
 
diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c
index 098b9cd554c49d2ed08f726a9ee9fc5a3f0ec1a5..fcff85bec8a39388994da000c78c9f322f83bdba 100644
--- a/sys/auto_init/auto_init.c
+++ b/sys/auto_init/auto_init.c
@@ -80,6 +80,10 @@
 #include "net/gnrc/ipv6/nib.h"
 #endif
 
+#ifdef MODULE_SKALD
+#include "net/skald.h"
+#endif
+
 
 #define ENABLE_DEBUG (0)
 #include "debug.h"
@@ -151,6 +155,10 @@ void auto_init(void)
     DEBUG("Auto init gnrc_ipv6_nib module.\n");
     gnrc_ipv6_nib_init();
 #endif
+#ifdef MODULE_SKALD
+    DEBUG("Auto init Skald\n");
+    skald_init();
+#endif
 
 /* initialize network devices */
 #ifdef MODULE_AUTO_INIT_GNRC_NETIF
diff --git a/sys/include/net/ble.h b/sys/include/net/ble.h
new file mode 100644
index 0000000000000000000000000000000000000000..ba92caafe5088e7b4c5697caabfa565414b672ec
--- /dev/null
+++ b/sys/include/net/ble.h
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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    net_ble BLE defines
+ * @ingroup     net
+ * @brief       General values defined by the BT standard
+ * @{
+ *
+ * @file
+ * @brief       General BLE values as defined by the BT standard
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef NET_BLE_H
+#define NET_BLE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @name    BT version constants
+ * @{
+ */
+#define BLE_VERSION_40                      (0x06)
+#define BLE_VERSION_41                      (0x07)
+#define BLE_VERSION_42                      (0x08)
+#define BLE_VERSION_50                      (0x09)
+/** @} */
+
+/**
+ * @name    Collections of general BLE constants
+ * @{
+ */
+#define BLE_AA_LEN              (4U)    /**< access address length */
+#define BLE_ADDR_LEN            (6U)    /**< link layer address length */
+#define BLE_CRC_LEN             (3U)    /**< CRC length */
+#define BLE_CHANMAP_LEN         (5U)    /**< channel map length */
+#define BLE_CHAN_NUMOF          (40U)   /**< number of available channels */
+#define BLE_CHAN_ADV_NUMOF      (3U)    /**< number of advertising channels */
+#define BLE_CHAN_DAT_NUMOF      (37U)   /**< number of data channels */
+/** @} */
+
+/**
+ * @name    GATT Declaration UUIDs
+ *
+ * @see https://www.bluetooth.com/specifications/gatt/declarations
+ * @{
+ */
+#define BLE_DECL_PRI_SERVICE                (0x2800)
+#define BLE_DECL_SEC_SERVICE                (0x2801)
+#define BLE_DECL_INCLUDE                    (0x2802)
+#define BLE_DECL_CHAR                       (0x2803)
+/** @} */
+
+/**
+ * @name    GATT descriptor UUIDs
+ *
+ * @see https://www.bluetooth.com/specifications/gatt/descriptors
+ * @{
+ */
+#define BLE_DESC_AGGR_FMT                   (0x2905)
+#define BLE_DESC_EXT_PROP                   (0x2900)
+#define BLE_DESC_PRES_FMT                   (0x2904)
+#define BLE_DESC_USER_DESC                  (0x2901)
+#define BLE_DESC_CLIENT_CONFIG              (0x2902)
+#define BLE_DESC_ENV_CONFIG                 (0x290b)
+#define BLE_DESC_ENV_MEASUREMENT            (0x290c)
+#define BLE_DESC_ENV_TRIGGER_SETTING        (0x290d)
+#define BLE_DESC_EXT_REPORT_REF             (0x2907)
+#define BLE_DESC_NUMOF_DIGITS               (0x2909)
+#define BLE_DESC_REPORT_REF                 (0x2908)
+#define BLE_DESC_SERVER_CONFIG              (0x2903)
+#define BLE_DESC_TIME_TRIGGER_SETTING       (0x290e)
+#define BLE_DESC_VALID_RANGE                (0x2906)
+#define BLE_DESC_VALUE_TRIGGER_SETTING      (0x290a)
+/** @} */
+
+/**
+ * @name    Characteristic format types (8-bit)
+ *
+ * @see https://www.bluetooth.com/specifications/assigned-numbers/format-types
+ * @{
+ */
+#define BLE_UNIT_BLE_FMT_BOOL               (0x01)
+#define BLE_UNIT_BLE_FMT_2bit               (0x02)
+#define BLE_UNIT_BLE_FMT_NIBBLE             (0x03)
+#define BLE_UNIT_BLE_FMT_UINT8              (0x04)
+#define BLE_UNIT_BLE_FMT_UINT12             (0x05)
+#define BLE_UNIT_BLE_FMT_UINT16             (0x06)
+#define BLE_UNIT_BLE_FMT_UINT24             (0x07)
+#define BLE_UNIT_BLE_FMT_UINT32             (0x08)
+#define BLE_UNIT_BLE_FMT_UINT48             (0x09)
+#define BLE_UNIT_BLE_FMT_UINT64             (0x0A)
+#define BLE_UNIT_BLE_FMT_UINT128            (0x0B)
+#define BLE_UNIT_BLE_FMT_SINT8              (0x0C)
+#define BLE_UNIT_BLE_FMT_SINT12             (0x0D)
+#define BLE_UNIT_BLE_FMT_SINT16             (0x0E)
+#define BLE_UNIT_BLE_FMT_SINT24             (0x0F)
+#define BLE_UNIT_BLE_FMT_SINT32             (0x10)
+#define BLE_UNIT_BLE_FMT_SINT48             (0x11)
+#define BLE_UNIT_BLE_FMT_SINT64             (0x12)
+#define BLE_UNIT_BLE_FMT_SINT128            (0x13)
+#define BLE_UNIT_BLE_FMT_FLOAT32            (0x14)
+#define BLE_UNIT_BLE_FMT_FLOAT64            (0x15)
+#define BLE_UNIT_BLE_FMT_SFLOAT             (0x16)
+#define BLE_UNIT_BLE_FMT_FLOAT              (0x17)
+#define BLE_UNIT_BLE_FMT_DUINT16            (0x18)
+#define BLE_UNIT_BLE_FMT_UTF8               (0x19)
+#define BLE_UNIT_BLE_FMT_UTF16              (0x1A)
+#define BLE_UNIT_BLE_FMT_STRUCT             (0x1B)
+/** @} */
+
+/**
+ * @name    Units (16-bit)
+ *
+ * @see https://www.bluetooth.com/specifications/assigned-numbers/units
+ * @{
+ */
+#define BLE_UNIT_NONE                       (0x2700)    /**< no unit */
+#define BLE_UNIT_BLE_UNIT_METRE             (0x2701)    /**< length [metre] */
+#define BLE_UNIT_KILOGRAM                   (0x2702)    /**< mass [kilogram] */
+#define BLE_UNIT_SECOND                     (0x2703)    /**< time [second] */
+#define BLE_UNIT_AMPERE                     (0x2704)    /**< electric_current [ampere] */
+#define BLE_UNIT_KELVIN                     (0x2705)    /**< thermodynamic_temperature [kelvin] */
+#define BLE_UNIT_MOLE                       (0x2706)    /**< amount_of_substance [mole] */
+#define BLE_UNIT_CANDELA                    (0x2707)    /**< luminous_intensity [candela] */
+#define BLE_UNIT_SQUARE_METRES              (0x2710)    /**< area [square_metres] */
+#define BLE_UNIT_CUBIC_METRES               (0x2711)    /**< volume [cubic_metres] */
+#define BLE_UNIT_METRES_PER_SECOND          (0x2712)    /**< velocity [metres_per_second] */
+#define BLE_UNIT_METRES_PER_SECOND_SQUARED  (0x2713)    /**< acceleration [metres_per_second_squared] */
+#define BLE_UNIT_RECIPROCAL_METRE           (0x2714)    /**< wavenumber [reciprocal_metre] */
+#define BLE_UNIT_KG_PER_CUBIC_METRE         (0x2715)    /**< density [kilogram_per_cubic_metre] */
+#define BLE_UNIT_KG_PER_SQUARE_METRE        (0x2716)    /**< surface_density [kilogram_per_square_metre] */
+#define BLE_UNIT_CUBIC_METRE_PER_KILOGRAM   (0x2717)    /**< specific_volume [cubic_metre_per_kilogram] */
+#define BLE_UNIT_AMPERE_PER_SQUARE_METRE    (0x2718)    /**< current_density [ampere_per_square_metre] */
+#define BLE_UNIT_AMPERE_PER_METRE           (0x2719)    /**< magnetic_field_strength [ampere_per_metre] */
+#define BLE_UNIT_MOLE_PER_CUBIC_METRE       (0x271a)    /**< amount_concentration [mole_per_cubic_metre] */
+#define BLE_UNIT_KILOGRAM_PER_CUBIC_METRE   (0x271b)    /**< mass_concentration [kilogram_per_cubic_metre] */
+#define BLE_UNIT_CANDELA_PER_SQUARE_METRE   (0x271c)    /**< luminance [candela_per_square_metre] */
+#define BLE_UNIT_REFRACTIVE_INDEX           (0x271d)    /**< refractive index */
+#define BLE_UNIT_RELATIVE_PERMEABILITY      (0x271e)    /**< relative permeability */
+#define BLE_UNIT_RADIAN                     (0x2720)    /**< plane_angle [radian] */
+#define BLE_UNIT_STERADIAN                  (0x2721)    /**< solid_angle [steradian] */
+#define BLE_UNIT_HERTZ                      (0x2722)    /**< frequency [hertz] */
+#define BLE_UNIT_NEWTON                     (0x2723)    /**< force [newton] */
+#define BLE_UNIT_PASCAL                     (0x2724)    /**< pressure [pascal] */
+#define BLE_UNIT_JOULE                      (0x2725)    /**< energy [joule] */
+#define BLE_UNIT_WATT                       (0x2726)    /**< power [watt] */
+#define BLE_UNIT_COULOMB                    (0x2727)    /**< electric_charge [coulomb] */
+#define BLE_UNIT_VOLT                       (0x2728)    /**< electric_potential_difference [volt] */
+#define BLE_UNIT_FARAD                      (0x2729)    /**< capacitance [farad] */
+#define BLE_UNIT_OHM                        (0x272a)    /**< electric_resistance [ohm] */
+#define BLE_UNIT_SIEMENS                    (0x272b)    /**< electric_conductance [siemens] */
+#define BLE_UNIT_WEBER                      (0x272c)    /**< magnetic_flux [weber] */
+#define BLE_UNIT_TESLA                      (0x272d)    /**< magnetic_flux_density [tesla] */
+#define BLE_UNIT_HENRY                      (0x272e)    /**< inductance [henry] */
+#define BLE_UNIT_DEGREE_CELSIUS             (0x272f)    /**< thermodynamic_temperature [degree_celsius] */
+#define BLE_UNIT_LUMEN                      (0x2730)    /**< luminous_flux [lumen] */
+#define BLE_UNIT_LUX                        (0x2731)    /**< illuminance [lux] */
+#define BLE_UNIT_BECQUEREL                  (0x2732)    /**< activity_referred_to_a_radionuclide [becquerel] */
+#define BLE_UNIT_GRAY                       (0x2733)    /**< absorbed_dose [gray] */
+#define BLE_UNIT_SIEVERT                    (0x2734)    /**< dose_equivalent [sievert] */
+#define BLE_UNIT_KATAL                      (0x2735)    /**< catalytic_activity [katal] */
+#define BLE_UNIT_PASCAL_SECOND              (0x2740)    /**< dynamic_viscosity [pascal_second] */
+#define BLE_UNIT_NEWTON_METRE               (0x2741)    /**< moment_of_force [newton_metre] */
+#define BLE_UNIT_NEWTON_PER_METRE           (0x2742)    /**< surface_tension [newton_per_metre] */
+#define BLE_UNIT_RADIAN_PER_SECOND          (0x2743)    /**< angular_velocity [radian_per_second] */
+#define BLE_UNIT_RADIAN_PER_SECOND_SQUARED  (0x2744)    /**< angular_acceleration [radian_per_second_squared] */
+#define BLE_UNIT_HEAT_FLUX_WATT_PER_M2      (0x2745)    /**< heat_flux_density [watt_per_square_metre] */
+#define BLE_UNIT_JOULE_PER_KELVIN           (0x2746)    /**< heat_capacity [joule_per_kelvin] */
+#define BLE_UNIT_JOULE_PER_KG_KELVIN        (0x2747)    /**< specific_heat_capacity [joule_per_kilogram_kelvin] */
+#define BLE_UNIT_JOULE_PER_KG               (0x2748)    /**< specific_energy [joule_per_kilogram] */
+#define BLE_UNIT_WATT_PER_METRE_KELVIN      (0x2749)    /**< thermal_conductivity [watt_per_metre_kelvin] */
+#define BLE_UNIT_JOULE_PER_CUBIC_METRE      (0x274a)    /**< energy_density [joule_per_cubic_metre] */
+#define BLE_UNIT_VOLT_PER_METRE             (0x274b)    /**< electric_field_strength [volt_per_metre] */
+#define BLE_UNIT_COULOMB_PER_CUBIC_METRE    (0x274c)    /**< electric_charge_density [coulomb_per_cubic_metre] */
+#define BLE_UNIT_COULOMB_PER_M2             (0x274d)    /**< surface_charge_density [coulomb_per_square_metre] */
+#define BLE_UNIT_FLUX_COULOMB_PER_M2        (0x274e)    /**< electric_flux_density [coulomb_per_square_metre] */
+#define BLE_UNIT_FARAD_PER_METRE            (0x274f)    /**< permittivity [farad_per_metre] */
+#define BLE_UNIT_HENRY_PER_METRE            (0x2750)    /**< permeability [henry_per_metre] */
+#define BLE_UNIT_JOULE_PER_MOLE             (0x2751)    /**< molar_energy [joule_per_mole] */
+#define BLE_UNIT_JOULE_PER_MOLE_KELVIN      (0x2752)    /**< molar_entropy [joule_per_mole_kelvin] */
+#define BLE_UNIT_COULOMB_PER_KG             (0x2753)    /**< exposure [coulomb_per_kilogram] */
+#define BLE_UNIT_GRAY_PER_SECOND            (0x2754)    /**< absorbed_dose_rate [gray_per_second] */
+#define BLE_UNIT_WATT_PER_STERADIAN         (0x2755)    /**< radiant_intensity [watt_per_steradian] */
+#define BLE_UNIT_WATT_PER_M2_STERADIAN      (0x2756)    /**< radiance [watt_per_square_metre_steradian] */
+#define BLE_UNIT_KATAL_PER_CUBIC_METRE      (0x2757)    /**< catalytic_activity_concentration [katal_per_cubic_metre] */
+#define BLE_UNIT_MINUTE                     (0x2760)    /**< time [minute] */
+#define BLE_UNIT_HOUR                       (0x2761)    /**< time [hour] */
+#define BLE_UNIT_DAY                        (0x2762)    /**< time [day] */
+#define BLE_UNIT_ANGLE_DEGREE               (0x2763)    /**< plane_angle [degree] */
+#define BLE_UNIT_ANGLE_MINUTE               (0x2764)    /**< plane_angle [minute] */
+#define BLE_UNIT_ANGLE_SECOND               (0x2765)    /**< plane_angle [second] */
+#define BLE_UNIT_HECTARE                    (0x2766)    /**< area [hectare] */
+#define BLE_UNIT_LITRE                      (0x2767)    /**< volume [litre] */
+#define BLE_UNIT_TONNE                      (0x2768)    /**< mass [tonne] */
+#define BLE_UNIT_BAR                        (0x2780)    /**< pressure [bar] */
+#define BLE_UNIT_MILLIMETRE_OF_MERCURY      (0x2781)    /**< pressure [millimetre_of_mercury] */
+#define BLE_UNIT_NGSTRM                     (0x2782)    /**< length [ngstrm] */
+#define BLE_UNIT_NAUTICAL_MILE              (0x2783)    /**< length [nautical_mile] */
+#define BLE_UNIT_BARN                       (0x2784)    /**< area [barn] */
+#define BLE_UNIT_KNOT                       (0x2785)    /**< velocity [knot] */
+#define BLE_UNIT_NEPER                      (0x2786)    /**< logarithmic_radio_quantity [neper] */
+#define BLE_UNIT_BEL                        (0x2787)    /**< logarithmic_radio_quantity [bel] */
+#define BLE_UNIT_YARD                       (0x27a0)    /**< length [yard] */
+#define BLE_UNIT_PARSEC                     (0x27a1)    /**< length [parsec] */
+#define BLE_UNIT_INCH                       (0x27a2)    /**< length [inch] */
+#define BLE_UNIT_FOOT                       (0x27a3)    /**< length [foot] */
+#define BLE_UNIT_MILE                       (0x27a4)    /**< length [mile] */
+#define BLE_UNIT_POUND_FORCE_PER_SQU_INCH   (0x27a5)    /**< pressure [pound_force_per_square_inch] */
+#define BLE_UNIT_KILOMETRE_PER_HOUR         (0x27a6)    /**< velocity [kilometre_per_hour] */
+#define BLE_UNIT_MILE_PER_HOUR              (0x27a7)    /**< velocity [mile_per_hour] */
+#define BLE_UNIT_REVOLUTION_PER_MINUTE      (0x27a8)    /**< angular_velocity [revolution_per_minute] */
+#define BLE_UNIT_GRAM_CALORIE               (0x27a9)    /**< energy [gram_calorie] */
+#define BLE_UNIT_KG_CALORIE                 (0x27aa)    /**< energy [kilogram_calorie] */
+#define BLE_UNIT_KILOWATT_HOUR              (0x27ab)    /**< energy [kilowatt_hour] */
+#define BLE_UNIT_DEGREE_FAHRENHEIT          (0x27ac)    /**< thermodynamic_temperature [degree_fahrenheit] */
+#define BLE_UNIT_PERCENTAGE                 (0x27ad)    /**< percentage */
+#define BLE_UNIT_PER_MILLE                  (0x27ae)    /**< per mille */
+#define BLE_UNIT_BEATS_PER_MINUTE           (0x27af)    /**< period [beats_per_minute] */
+#define BLE_UNIT_AMPERE_HOURS               (0x27b0)    /**< electric_charge [ampere_hours] */
+#define BLE_UNIT_MILLIGRAM_PER_DECILITRE    (0x27b1)    /**< mass_density [milligram_per_decilitre] */
+#define BLE_UNIT_MILLIMOLE_PER_LITRE        (0x27b2)    /**< mass_density [millimole_per_litre] */
+#define BLE_UNIT_YEAR                       (0x27b3)    /**< time [year] */
+#define BLE_UNIT_MONTH                      (0x27b4)    /**< time [month] */
+#define BLE_UNIT_COUNT_PER_CUBIC_METRE      (0x27b5)    /**< concentration [count_per_cubic_metre] */
+#define BLE_UNIT_WATT_PER_SQUARE_METRE      (0x27b6)    /**< irradiance [watt_per_square_metre] */
+#define BLE_UNIT_MLIT_PER_KG_PER_MINUTE     (0x27b7)    /**< transfer_rate [milliliter_per_kilogram_per_minute] */
+#define BLE_UNIT_POUND                      (0x27b8)    /**< mass [pound] */
+#define BLE_UNIT_METABOLIC_EQU              (0x27b9)    /**< metabolic equivalent */
+#define BLE_UNIT_STEP_PER_MINUTE            (0x27ba)    /**< steps per minute */
+#define BLE_UNIT_STROKE_PER_MINUTE          (0x27bc)    /**< strokes per minute */
+#define BLE_UNIT_KILOMETER_PER_MINUTE       (0x27bd)    /**< velocity [kilometer_per_minute] */
+#define BLE_UNIT_LUMEN_PER_WATT             (0x27be)    /**< luminous_efficacy [lumen_per_watt] */
+#define BLE_UNIT_LUMEN_HOUR                 (0x27bf)    /**< luminous_energy [lumen_hour] */
+#define BLE_UNIT_LUX_HOUR                   (0x27c0)    /**< luminous_exposure [lux_hour] */
+#define BLE_UNIT_GRAM_PER_SECOND            (0x27c1)    /**< mass_flow [gram_per_second] */
+#define BLE_UNIT_LITRE_PER_SECOND           (0x27c2)    /**< volume_flow [litre_per_second] */
+/** @} */
+
+/**
+ * @name    ATT protocol opcodes
+ * @{
+ */
+#define BLE_ATT_ERROR_RESP                  (0x01)
+#define BLE_ATT_MTU_REQ                     (0x02)
+#define BLE_ATT_MTU_RESP                    (0x03)
+#define BLE_ATT_FIND_INFO_REQ               (0x04)
+#define BLE_ATT_FIND_INFO_RESP              (0x05)
+#define BLE_ATT_FIND_BY_VAL_REQ             (0x06)
+#define BLE_ATT_FIND_BY_VAL_RESP            (0x07)
+#define BLE_ATT_READ_BY_TYPE_REQ            (0x08)
+#define BLE_ATT_READ_BY_TYPE_RESP           (0x09)
+#define BLE_ATT_READ_REQ                    (0x0a)
+#define BLE_ATT_READ_RESP                   (0x0b)
+#define BLE_ATT_READ_BLOB_REQ               (0x0c)
+#define BLE_ATT_READ_BLOB_RESP              (0x0d)
+#define BLE_ATT_READ_MUL_REQ                (0x0e)
+#define BLE_ATT_READ_MUL_RESP               (0x0f)
+#define BLE_ATT_READ_BY_GROUP_TYPE_REQ      (0x10)
+#define BLE_ATT_READ_BY_GROUP_TYPE_RESP     (0x11)
+#define BLE_ATT_WRITE_REQ                   (0x12)
+#define BLE_ATT_WRITE_RESP                  (0x13)
+#define BLE_ATT_WRITE_COMMAND               (0x52)
+#define BLE_ATT_PREP_WRITE_REQ              (0x16)
+#define BLE_ATT_PREP_WRITE_RESP             (0x17)
+#define BLE_ATT_EXEC_WRITE_REQ              (0x18)
+#define BLE_ATT_EXEC_WRITE_RESP             (0x19)
+#define BLE_ATT_VAL_NOTIFICATION            (0x1b)
+#define BLE_ATT_VAL_INDICATION              (0x1d)
+#define BLE_ATT_VAL_CONFIRMATION            (0x1e)
+#define BLE_ATT_SIGNED_WRITE_CMD            (0xd2)
+/** @} */
+
+/**
+ * @name    ATT protocol error codes
+ * @{
+ */
+#define BLE_ATT_INVALID_HANDLE              (0x01)
+#define BLE_ATT_READ_NOT_PERMITTED          (0x02)
+#define BLE_ATT_WRITE_NOT_PERMITTED         (0x03)
+#define BLE_ATT_INVALID_PDU                 (0x04)
+#define BLE_ATT_INSUFFICIENT_AUTHEN         (0x05)
+#define BLE_ATT_REQUEST_NOT_SUP             (0x06)
+#define BLE_ATT_INVALID_OFFSET              (0x07)
+#define BLE_ATT_INSUFFICIENT_AUTHOR         (0x08)
+#define BLE_ATT_PREPARE_QUEUE_FULL          (0x09)
+#define BLE_ATT_ATTRIBUTE_NOT_FOUND         (0x0a)
+#define BLE_ATT_ATTRIBUTE_NOT_LONG          (0x0b)
+#define BLE_ATT_INSUFFICENT_KEY_SIZE        (0x0c)
+#define BLE_ATT_INVALID_ATTR_VAL_LEN        (0x0d)
+#define BLE_ATT_ULIKELY_ERROR               (0x0e)
+#define BLE_ATT_INSUFFICIENT_ENCRYPTION     (0x0f)
+#define BLE_ATT_UNSUPPORTED_GROUP_TYPE      (0x10)
+#define BLE_ATT_INSUFFICIENT_RESSOURCES     (0x11)
+/** @} */
+
+/**
+ * @name    ATT property flags
+ * @{
+ */
+#define BLE_ATT_BROADCAST                   (0x01)
+#define BLE_ATT_READ                        (0x02)
+#define BLE_ATT_WRITE_WO_RESP               (0x04)
+#define BLE_ATT_WRITE                       (0x08)
+#define BLE_ATT_NOTIFY                      (0x10)
+#define BLE_ATT_INDICATE                    (0x20)
+#define BLE_ATT_AUTH_SIGNED_WRITES          (0x40)
+#define BLE_ATT_EXT_PROPERTIES              (0x80)
+/** @} */
+
+/**
+ * @name    Flags used in certain types of ATT PDUs
+ * @{
+ */
+#define BLE_ATT_FORMAT_U16      (0x01)  /**< used in FIND_INFO_RESP */
+#define BLE_ATT_FORMAT_U128     (0x02)  /**< used in FIND_INFO_RESP */
+/** @} */
+
+/**
+ * @name    Flags used in GAP advertisement packets
+ * @{
+ */
+#define BLE_GAP_DISCOVER_LIM                (0x01)
+#define BLE_GAP_DISCOVERABLE                (0x02)
+#define BLE_GAP_FLAG_BREDR_NOTSUP           (0x04)
+/** @} */
+
+/**
+ * @name    BLE advertising packet types
+ * @{
+ */
+#define BLE_PDU_MASK                        (0x0f)
+#define BLE_ADV_IND                         (0x00)
+#define BLE_DIRECT_IND                      (0x01)
+#define BLE_ADV_NONCON_IND                  (0x02)
+#define BLE_SCAN_REQ                        (0x03)
+#define BLE_AUX_SCAN_REQ                    (0x03)
+#define BLE_SCAN_RESP                       (0x04)
+#define BLE_CONNECT_IND                     (0x05)
+#define BLE_AUX_CONNECT_REQ                 (0x05)
+#define BLE_ADV_SCAN_IND                    (0x06)
+#define BLE_ADV_EXT_IND                     (0x07)
+#define BLE_AUX_ADV_IND                     (0x07)
+#define BLE_AUX_SCAN_RSP                    (0x07)
+#define BLE_AUX_SYNC_IND                    (0x07)
+#define BLE_AUX_CHAIN_IND                   (0x07)
+#define BLE_CONNECT_RESP                    (0x08)
+/** @} */
+
+/**
+ * @name    Advertising packet flags
+ * @{
+ */
+#define BLE_LL_FLAG_CHSEL                   (0x20)
+#define BLE_LL_FLAG_TXADD                   (0x40)
+#define BLE_LL_FLAG_RXADD                   (0x80)
+/** @} */
+
+/**
+ * @name    Link layer control message opcodes
+ * @{
+ */
+#define BLE_LL_CONN_UPDATE_IND              (0x00)
+#define BLE_LL_CHANNEL_MAP_IND              (0x01)
+#define BLE_LL_TERMINATE_IND                (0x02)
+#define BLE_LL_ENC_REQ                      (0x03)
+#define BLE_LL_ENC_RSP                      (0x04)
+#define BLE_LL_START_ENC_REQ                (0x05)
+#define BLE_LL_START_ENC_RSP                (0x06)
+#define BLE_LL_UNKNOWN_RSP                  (0x07)
+#define BLE_LL_FEATURE_REQ                  (0x08)
+#define BLE_LL_FEATURE_RSP                  (0x09)
+#define BLE_LL_PAUSE_ENC_REQ                (0x0a)
+#define BLE_LL_PAUSE_ENC_RSP                (0x0b)
+#define BLE_LL_VERSION_IND                  (0x0c)
+#define BLE_LL_REJECT_IND                   (0x0d)
+#define BLE_LL_SLAVE_FEATURE_REQ            (0x0e)
+#define BLE_LL_CONN_PARAM_REQ               (0x0f)
+#define BLE_LL_CONN_PARAM_RSP               (0x10)
+#define BLE_LL_REJECT_EXT_IND               (0x11)
+#define BLE_LL_PING_REQ                     (0x12)
+#define BLE_LL_PING_RSP                     (0x13)
+#define BLE_LL_LENGTH_REQ                   (0x14)
+#define BLE_LL_LENGTH_RSP                   (0x15)
+#define BLE_LL_PHY_REQ                      (0x16)
+#define BLE_LL_PHY_RSP                      (0x17)
+#define BLE_LL_PHY_UPDATE_IND               (0x18)
+#define BLE_LL_MIN_USED_CHAN_IND            (0x19)
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_BLE_H */
+/** @} */
diff --git a/sys/include/net/eddystone.h b/sys/include/net/eddystone.h
new file mode 100644
index 0000000000000000000000000000000000000000..31c8fd280f2200a360432556b36aa68baac15ede
--- /dev/null
+++ b/sys/include/net/eddystone.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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    net_eddystone Eddystone
+ * @ingroup     net
+ * @brief       General values defined by the BLE Eddystone beacon format
+ *
+ * @see https://github.com/google/eddystone/blob/master/protocol-specification.md
+ * @{
+ *
+ * @file
+ * @brief       Constants defined by the Eddystone specification
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef NET_EDDYSTONE_H
+#define NET_EDDYSTONE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @name    Collection of general Eddystone constants
+ * @{
+ */
+#define EDDYSTONE_NAMESPACE_LEN     (10U)
+#define EDDYSTONE_INSTANCE_LEN      (6U)
+/** @} */
+
+/**
+ * @name    URL scheme prefix values
+ * @{
+ */
+#define EDDYSTONE_URL_HTTP_WWW      (0x00)      /**< `http://www.URL` */
+#define EDDYSTONE_URL_HTTPS_WWWW    (0x01)      /**< `https://www.URL` */
+#define EDDYSTONE_URL_HTTP          (0x02)      /**< `http://URL` */
+#define EDDYSTONE_URL_HTTPS         (0x03)      /**< `https://URL` */
+/** @} */
+
+/**
+ * @name    Eddystone frame types
+ * @{
+ */
+#define EDDYSTONE_UID               (0x00)
+#define EDDYSTONE_URL               (0x10)
+#define EDDYSTONE_TLM               (0x20)
+#define EDDYSTONE_EID               (0x30)
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_EDDYSTONE_H */
+/** @} */
diff --git a/sys/include/net/netopt.h b/sys/include/net/netopt.h
index d921aa72ed3e2d34cf8448505749ca7ca2fcaf7d..61e1965e2ad3c5dfee4708197685bd6f602b8755 100644
--- a/sys/include/net/netopt.h
+++ b/sys/include/net/netopt.h
@@ -532,6 +532,8 @@ typedef enum {
      */
     NETOPT_TX_RETRIES_NEEDED,
 
+    NETOPT_BLE_CTX,             /**< set radio context (channel, CRC, AA) */
+
     /* add more options if needed */
 
     /**
diff --git a/sys/include/net/skald.h b/sys/include/net/skald.h
new file mode 100644
index 0000000000000000000000000000000000000000..4583341c53cb02f295272d02990933e0a5c15b17
--- /dev/null
+++ b/sys/include/net/skald.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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    net_skald Skald, who advertises to the world
+ * @ingroup     net
+ * @brief       Skald, a minimalistic BLE advertising stack
+ *
+ * # About
+ *
+ * Skald is a very minimalistic BLE implementation, implementing the
+ * `broadcaster` role only. With this focus, the stack allows for setting up
+ * different kind of beacons using an extremely low memory footprint.
+ *
+ * # Design Decisions and Limitations
+ * - support for local addresses only (using `luid` to generate them)
+ * - advertising interval is configured during compile time, override by setting
+ *   `CFLAGS+=-DSKALD_INTERVAL=xxx`
+ * - advertising channels are configured during compile time, override by
+ *   setting `CFLAGS+=-DSKALD_ADV_CHAN={37,39}`
+ *
+ * # Implementation state
+ * Supported:
+ * - advertising of custom GAP payloads
+ * - iBeacon (full support)
+ * - Eddystone (partly supported)
+ *
+ * Limitations:
+ * - currently Skald supports random static addresses only (generated using
+ *   the `luid` module)
+ *
+ * @{
+ * @file
+ * @brief       Skald's basic interface
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef NET_SKALD_H
+#define NET_SKALD_H
+
+#include <stdint.h>
+
+#include "xtimer.h"
+#include "net/ble.h"
+#include "net/netdev/ble.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Static advertising interval
+ */
+#ifndef SKALD_INTERVAL
+#define SKALD_INTERVAL          (1 * US_PER_SEC)
+#endif
+
+/**
+ * @brief   Static list of used advertising channels
+ */
+#ifndef SKALD_ADV_CHAN
+#define SKALD_ADV_CHAN          { 37, 38, 39 }
+#endif
+
+/**
+ * @brief   UUID representation format used by Skald
+ */
+typedef struct {
+    uint8_t u8[16];         /**< UUID with byte-wise access */
+} skald_uuid_t;
+
+/**
+ * @brief   Advertising context holding the advertising data and state
+ */
+typedef struct {
+    netdev_ble_pkt_t pkt;   /**< packet holding the advertisement (GAP) data */
+    xtimer_t timer;         /**< timer for scheduling advertising events */
+    uint32_t last;          /**< last timer trigger (for offset compensation) */
+    uint8_t cur_chan;       /**< keep track of advertising channels */
+} skald_ctx_t;
+
+/**
+ * @brief   Initialize Skald and the underlying radio
+ */
+void skald_init(void);
+
+/**
+ * @brief   Start advertising the given packet
+ *
+ * The packet will be send out each advertising interval (see SKALD_INTERVAL) on
+ * each of the defined advertising channels (see SKALD_ADV_CHAN).
+ *
+ * @param[in,out] ctx   start advertising this context
+ */
+void skald_adv_start(skald_ctx_t *ctx);
+
+/**
+ * @brief   Stop the ongoing advertisement
+ *
+ * @param[in,out] ctx   stop advertising this context
+ */
+void skald_adv_stop(skald_ctx_t *ctx);
+
+/**
+ * @brief   Generate a random public address
+ *
+ * @note    @p buf must be able to hold BLE_ADDR_LEN (6) bytes
+ *
+ * @param[out] buf      the generated address is written to this buffer
+ */
+void skald_generate_random_addr(uint8_t *buf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_SKALD_H */
+/** @} */
diff --git a/sys/include/net/skald/eddystone.h b/sys/include/net/skald/eddystone.h
new file mode 100644
index 0000000000000000000000000000000000000000..9c8ddd63651fa54b9e5d9056ea87818c4fb4d432
--- /dev/null
+++ b/sys/include/net/skald/eddystone.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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    net_skald_eddystone Skald meets Eddy
+ * @ingroup     net_skald
+ * @brief       Skald's Eddystone implementation
+ *
+ * # About
+ * This module allows for creation and advertisement of Eddystone beacons (see
+ * https://github.com/google/eddystone).
+ *
+ *
+ * # Implementation state
+ * supported:
+ * - Eddystone-UID
+ * - Eddystone-URL
+ *
+ * not (yet) supported:
+ * - Eddystone-TLM
+ * - Eddystone-EID
+ *
+ * @{
+ * @file
+ * @brief       Skald's basic interface
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef NET_SKALD_EDDYSTONE_H
+#define NET_SKALD_EDDYSTONE_H
+
+#include "net/eddystone.h"
+#include "net/skald.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Unique and opaque 16-byte beacon id format used by Eddystone
+ */
+typedef struct __attribute__((packed)) {
+    uint8_t namespace[EDDYSTONE_NAMESPACE_LEN]; /**< 10-byte namespace */
+    uint8_t instance[EDDYSTONE_INSTANCE_LEN];   /**< 6-byte instance */
+} skald_eddystone_uid_t;
+
+/**
+ * @brief   Advertise Eddystone-UID data
+ *
+ * @see https://github.com/google/eddystone/tree/master/eddystone-uid
+ *
+ * @param[out] ctx      advertising context
+ * @param[in] uid       UID to advertise
+ * @param[in] tx_pwr    calibrated TX power to be advertised by the beacon
+ */
+void skald_eddystone_uid_adv(skald_ctx_t *ctx,
+                             const skald_eddystone_uid_t *uid, uint8_t tx_pwr);
+
+/**
+ * @brief   Advertise Eddystone-URL data
+ *
+ * @see https://github.com/google/eddystone/tree/master/eddystone-url
+ *
+ * @param[out] ctx      advertising context
+ * @param[in] scheme    encoded URL scheme prefix
+ * @param[in] url       (short) url as \0 terminated string
+ * @param[in] tx_pwr    calibrated TX power to be advertised by the beacon
+ */
+void skald_eddystone_url_adv(skald_ctx_t *ctx,
+                             uint8_t scheme, const char *url, uint8_t tx_pwr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_SKALD_EDDYSTONE_H */
+/** @} */
diff --git a/sys/include/net/skald/ibeacon.h b/sys/include/net/skald/ibeacon.h
new file mode 100644
index 0000000000000000000000000000000000000000..0f8743006815fe33eba7f273df46c6be9c098968
--- /dev/null
+++ b/sys/include/net/skald/ibeacon.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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    net_skald_ibeacon Skald about iBeacon
+ * @ingroup     net_skald
+ * @brief       Skald's simple iBeacon abstraction
+ *
+ * # About
+ * This Skald module supports the creation and advertisement of BLE iBeacons as
+ * defined by Apple (see https://developer.apple.com/ibeacon/).
+ *
+ * # Implementation state
+ * - all known iBeacon properties are supported
+ *
+ * @{
+ * @file
+ * @brief       Skald's basic interface
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef NET_SKALD_IBEACON_H
+#define NET_SKALD_IBEACON_H
+
+#include "net/skald.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Configure the IBeacon payload and start advertising
+ *
+ * @param[out] ctx      advertising context
+ * @param[in] uuid      UUID advertised by the iBeacon
+ * @param[in] major     the iBeacon's major number
+ * @param[in] minor     the iBeacon's minor number
+ * @param[in] txpower   calibrated TX power to be advertised by the beacon
+ */
+void skald_ibeacon_advertise(skald_ctx_t *ctx, const skald_uuid_t *uuid,
+                             uint16_t major, uint16_t minor, uint8_t txpower);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_SKALD_IBEACON_H */
+/** @} */
diff --git a/sys/net/crosslayer/netopt/netopt.c b/sys/net/crosslayer/netopt/netopt.c
index 951d2f0a90e1f215d834275809fc6107d1aa2759..e9ba46d5a436104bbf5f6e21c5330df85d34f0de 100644
--- a/sys/net/crosslayer/netopt/netopt.c
+++ b/sys/net/crosslayer/netopt/netopt.c
@@ -89,6 +89,7 @@ static const char *_netopt_strmap[] = {
     [NETOPT_IQ_INVERT]             = "NETOPT_IQ_INVERT",
     [NETOPT_TX_RETRIES_NEEDED]     = "NETOPT_TX_RETRIES_NEEDED",
     [NETOPT_6LO_IPHC]              = "NETOPT_6LO_IPHC",
+    [NETOPT_BLE_CTX]               = "NETOPT_BLE_CTX",
     [NETOPT_NUMOF]                 = "NETOPT_NUMOF",
 };
 
diff --git a/sys/net/skald/Makefile b/sys/net/skald/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..c835b4d8ddc51de4dbc3d9c1c0611cbed34bd3b1
--- /dev/null
+++ b/sys/net/skald/Makefile
@@ -0,0 +1,11 @@
+SRC = skald.c
+
+ifneq (,$(filter skald_ibeacon,$(USEMODULE)))
+  SRC += skald_ibeacon.c
+endif
+
+ifneq (,$(filter skald_eddystone,$(USEMODULE)))
+  SRC += skald_eddystone.c
+endif
+
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/net/skald/skald.c b/sys/net/skald/skald.c
new file mode 100644
index 0000000000000000000000000000000000000000..858f8b03a5816a42394ae5e38d6dd4346feae305
--- /dev/null
+++ b/sys/net/skald/skald.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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     net_skald
+ * @{
+ *
+ * @file
+ * @brief       Skald's link layer implementation
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include <stdint.h>
+
+#include "assert.h"
+#include "random.h"
+#include "luid.h"
+
+#include "net/netdev/ble.h"
+#include "net/skald.h"
+
+/* include fitting radio driver */
+#if defined(MODULE_NRFBLE)
+#include "nrfble.h"
+/* add other BLE radio drivers once implemented - and potentially move to
+ * auto-init at some point */
+#else
+#error "[skald] error: unable to find any netdev-ble capable radio"
+#endif
+
+#define ENABLE_DEBUG            (0)
+#include "debug.h"
+
+#define JITTER_MIN              (0U)            /* 0ms */
+#define JITTER_MAX              (10000U)        /* 10ms */
+
+#define ADV_CHAN_NUMOF          sizeof(_adv_chan)
+#define ADV_AA                  (0x8e89bed6)    /* access address */
+#define ADV_CRC                 (0x00555555)    /* CRC initializer */
+
+static const uint8_t _adv_chan[] = SKALD_ADV_CHAN;
+
+static netdev_ble_ctx_t _ble_ctx = {
+    .aa.u32 = ADV_AA,
+    .crc = ADV_CRC,
+};
+
+static netdev_t *_radio;
+
+static void _stop_radio(void)
+{
+    netdev_ble_stop(_radio);
+    _radio->context = NULL;
+}
+
+static void _sched_next(skald_ctx_t *ctx)
+{
+    ctx->last += SKALD_INTERVAL;
+    /* schedule next advertising event, adding a random jitter between
+     * 0ms and 10ms (see spec v5.0-vol6-b-4.4.2.2.1) */
+    ctx->last += random_uint32_range(JITTER_MIN, JITTER_MAX);
+    /* compensate the time passed since the timer triggered last by using the
+     * current value of the timer */
+    xtimer_set(&ctx->timer, (ctx->last - xtimer_now_usec()));
+}
+
+static void _on_adv_evt(void *arg)
+{
+    skald_ctx_t *ctx = (skald_ctx_t *)arg;
+
+    /* advertise on the next adv channel - or skip this event if the radio is
+     * busy */
+    if ((ctx->cur_chan < ADV_CHAN_NUMOF) && (_radio->context == NULL)) {
+        _radio->context = ctx;
+        _ble_ctx.chan = _adv_chan[ctx->cur_chan];
+        netdev_ble_set_ctx(_radio, &_ble_ctx);
+        netdev_ble_send(_radio, &ctx->pkt);
+        ++ctx->cur_chan;
+    }
+    else {
+        ctx->cur_chan = 0;
+        _sched_next(ctx);
+    }
+}
+
+static void _on_radio_evt(netdev_t *netdev, netdev_event_t event)
+{
+    (void)netdev;
+
+    if (event == NETDEV_EVENT_TX_COMPLETE) {
+        skald_ctx_t *ctx = _radio->context;
+        _stop_radio();
+        xtimer_set(&ctx->timer, 150);
+    }
+}
+
+void skald_init(void)
+{
+    assert(dev);
+
+    /* setup and a fitting radio driver - potentially move to auto-init at some
+     * point */
+#if defined(MODULE_NRFBLE)
+    _radio = nrfble_setup();
+#endif
+
+    _radio->event_callback = _on_radio_evt;
+    _radio->driver->init(_radio);
+}
+
+void skald_adv_start(skald_ctx_t *ctx)
+{
+    assert(ctx);
+
+    /* make sure the given context is not advertising at the moment */
+    skald_adv_stop(ctx);
+
+    /* initialize advertising context */
+    ctx->timer.callback = _on_adv_evt;
+    ctx->timer.arg = ctx;
+    ctx->last = xtimer_now_usec();
+    ctx->cur_chan = 0;
+    ctx->pkt.flags = (BLE_ADV_NONCON_IND | BLE_LL_FLAG_TXADD);
+
+    /* start advertising */
+    _sched_next(ctx);
+}
+
+void skald_adv_stop(skald_ctx_t *ctx)
+{
+    assert(ctx);
+
+    xtimer_remove(&ctx->timer);
+    if (_radio->context == (void *)ctx) {
+        _stop_radio();
+    }
+}
+
+void skald_generate_random_addr(uint8_t *buf)
+{
+    assert(buf);
+
+    luid_get(buf, BLE_ADDR_LEN);
+    /* swap byte 0 and 5, so that the unique byte given by luid does not clash
+     * with universal/local and individual/group bits of address */
+    uint8_t tmp = buf[5];
+    buf[5] = buf[0];
+    /* make address individual and local */
+    buf[0] = ((tmp & 0xfc) | 0x02);
+}
diff --git a/sys/net/skald/skald_eddystone.c b/sys/net/skald/skald_eddystone.c
new file mode 100644
index 0000000000000000000000000000000000000000..847d6f09d807821d2cb62bf1189ef22ea52dc6f4
--- /dev/null
+++ b/sys/net/skald/skald_eddystone.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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     net_skald_eddystone
+ * @{
+ *
+ * @file
+ * @brief       Skald's Eddystone implementation
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include <string.h>
+
+#include "assert.h"
+#include "net/skald/eddystone.h"
+
+#define PREAMBLE_LEN            (11U)
+#define PA_LEN                  (7U)
+#define PB_LEN                  (3U)
+
+#define URL_HDR_LEN             (6U)
+
+#define UID_LEN                 (23U)
+
+typedef struct __attribute__((packed)) {
+    uint8_t txadd[BLE_ADDR_LEN];
+    uint8_t pa[PA_LEN];
+    uint8_t service_data_len;
+    uint8_t pb[PB_LEN];
+    uint8_t type;
+} pre_t;
+
+typedef struct __attribute__((packed)) {
+    pre_t pre;
+    uint8_t tx_pwr;
+    uint8_t namespace[EDDYSTONE_NAMESPACE_LEN];
+    uint8_t instance[EDDYSTONE_INSTANCE_LEN];
+    uint8_t reserved[2];
+} eddy_uid_t;
+
+typedef struct __attribute__((packed)) {
+    pre_t pre;
+    uint8_t tx_pwr;
+    uint8_t scheme;
+    uint8_t url[];
+} eddy_url_t;
+
+/* ćonstant GAP data preamble parts, containing the following GAP fields:
+ * - flags:  BR/EDR not support set
+ * - complete list of 16-bit UUIDs: holding the Eddystone UUID only (0xfeaa)
+ * - service data of type 0xfeaa (Eddystone) */
+static const uint8_t _pa[PA_LEN] = { 0x02, 0x01, 0x04, 0x03, 0x03, 0xaa, 0xfe };
+static const uint8_t _pb[PB_LEN] = { 0x16, 0xaa, 0xfe };
+
+static void _init_pre(pre_t *data, uint8_t type, uint8_t len)
+{
+    skald_generate_random_addr(data->txadd);
+    memcpy(data->pa, _pa, PA_LEN);
+    memcpy(data->pb, _pb, PB_LEN);
+    data->service_data_len = len;
+    data->type = type;
+}
+
+void skald_eddystone_uid_adv(skald_ctx_t *ctx,
+                             const skald_eddystone_uid_t *uid, uint8_t tx_pwr)
+{
+    assert(ctx && uid);
+
+    eddy_uid_t *pdu = (eddy_uid_t *)ctx->pkt.pdu;
+    _init_pre(&pdu->pre, EDDYSTONE_UID, UID_LEN);
+
+    pdu->tx_pwr = tx_pwr;
+    memcpy(pdu->namespace, uid->namespace, EDDYSTONE_NAMESPACE_LEN);
+    memcpy(pdu->instance, uid->instance, EDDYSTONE_INSTANCE_LEN);
+    memset(pdu->reserved, 0, 2);
+
+    /* start advertising */
+    ctx->pkt.len = sizeof(eddy_uid_t);
+    skald_adv_start(ctx);
+}
+
+void skald_eddystone_url_adv(skald_ctx_t *ctx,
+                             uint8_t scheme, const char *url, uint8_t tx_pwr)
+{
+    assert(url && ctx);
+    size_t len = strlen(url);
+    assert(len <= (NETDEV_BLE_PDU_MAXLEN - (URL_HDR_LEN + PREAMBLE_LEN)));
+
+    eddy_url_t *pdu = (eddy_url_t *)ctx->pkt.pdu;
+    _init_pre(&pdu->pre, EDDYSTONE_URL, (URL_HDR_LEN + len));
+
+    /* set remaining service data fields */
+    pdu->tx_pwr = tx_pwr;
+    pdu->scheme = scheme;
+    memcpy(pdu->url, url, len);
+
+    /* start advertising */
+    ctx->pkt.len = (sizeof(pre_t) + 2 + len);
+    skald_adv_start(ctx);
+}
diff --git a/sys/net/skald/skald_ibeacon.c b/sys/net/skald/skald_ibeacon.c
new file mode 100644
index 0000000000000000000000000000000000000000..7d67eb7da22069b20f39208904a5122c3c5515b1
--- /dev/null
+++ b/sys/net/skald/skald_ibeacon.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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     net_skald_ibeacon
+ * @{
+ *
+ * @file
+ * @brief       Skald's iBeacon implementation
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include <string.h>
+
+#include "byteorder.h"
+
+#include "net/skald/ibeacon.h"
+
+#define PREFIX_LEN          (9U)
+
+/**
+ * @brief   PDU format for iBeacon packets
+ */
+typedef struct __attribute__((packed)) {
+    uint8_t txadd[BLE_ADDR_LEN];
+    uint8_t prefix[PREFIX_LEN];
+    skald_uuid_t uuid;
+    be_uint16_t major;
+    be_uint16_t minor;
+    uint8_t txpower;
+} ibeacon_t;
+
+/* constant GAP type value fields, fixed for the iBeacon format */
+static const uint8_t prefix[PREFIX_LEN] = { 0x02, 0x01, 0x06, 0x1a, 0xff,
+                                            0x4c, 0x00, 0x02, 0x15 };
+
+void skald_ibeacon_advertise(skald_ctx_t *ctx, const skald_uuid_t *uuid,
+                             uint16_t major, uint16_t minor, uint8_t txpower)
+{
+    /* configure the iBeacon PDU */
+    ibeacon_t *pdu = (ibeacon_t *)ctx->pkt.pdu;
+
+    ctx->pkt.len = (uint8_t)sizeof(ibeacon_t);
+
+    skald_generate_random_addr(pdu->txadd);
+    memcpy(pdu->prefix, prefix, PREFIX_LEN);
+    memcpy(&pdu->uuid, uuid, sizeof(skald_uuid_t));
+    be_uint16_t tmp = byteorder_htons(major);
+    memcpy(&pdu->major, &tmp, sizeof(uint16_t));
+    tmp = byteorder_htons(minor);
+    memcpy(&pdu->minor, &tmp, sizeof(uint16_t));
+    pdu->txpower = txpower;
+
+    skald_adv_start(ctx);
+}