diff --git a/epicardium/main.c b/epicardium/main.c
index c90d008248e0562c184701099be9ff59d7639b60..66d3ad598e922fa30a8edc66fc32cdd7683cd631 100644
--- a/epicardium/main.c
+++ b/epicardium/main.c
@@ -24,6 +24,9 @@
 #include "task.h"
 
 TaskHandle_t dispatcher_task_id;
+TaskHandle_t ble_task_id;
+
+void vBleTask(void*pvParameters);
 
 /*
  * API dispatcher task.  This task will sleep until an API call is issued and
@@ -103,6 +106,18 @@ int main(void)
 		abort();
 	}
 
+	if (xTaskCreate(
+		vBleTask,
+		(const char*)"BLE",
+		configMINIMAL_STACK_SIZE,
+		NULL,
+		tskIDLE_PRIORITY  + 3,
+		&ble_task_id
+	) != pdPASS) {
+		printf("Failed to create BLE task!\n");
+		abort();
+	}
+
 	LOG_INFO("startup", "Initializing dispatcher ...");
 	api_dispatcher_init();
 
diff --git a/epicardium/meson.build b/epicardium/meson.build
index c5dc701d6164c79246e9a6aa8bc761c01626b32e..bf727f0715673fdf9e430ddd8685a777ac1c3b10 100644
--- a/epicardium/meson.build
+++ b/epicardium/meson.build
@@ -75,7 +75,7 @@ elf = executable(
   'support.c',
   module_sources,
   l0der_sources,
-  dependencies: [libcard10, max32665_startup_core0, maxusb, libff13],
+  dependencies: [libcard10, max32665_startup_core0, maxusb, libff13, ble],
   link_with: [api_dispatcher_lib, freertos],
   link_whole: [max32665_startup_core0_lib, board_card10_lib, newlib_heap_lib],
   include_directories: [freertos_includes],
diff --git a/epicardium/modules/ble.c b/epicardium/modules/ble.c
new file mode 100644
index 0000000000000000000000000000000000000000..230b51a97781118b54db32d78f9a3236b5a8d52b
--- /dev/null
+++ b/epicardium/modules/ble.c
@@ -0,0 +1,134 @@
+#include "wsf_types.h"
+#include "wsf_os.h"
+#include "wsf_buf.h"
+#include "wsf_timer.h"
+#include "wsf_trace.h"
+#include "app_ui.h"
+#include "ll_api.h"
+#include "sch_api.h"
+#include "fit/fit_api.h"
+#include "mxc_config.h"
+#include "gcr_regs.h"
+#include "mcr_regs.h"
+#include "hci_core.h"
+
+#include <stdio.h>
+#include <string.h>
+
+/* Number of WSF buffer pools */
+#define WSF_BUF_POOLS              6
+
+/*! Free memory for pool buffers (use word elements for word alignment). */
+static uint32_t mainBufMem[3584/sizeof(uint32_t)+96];
+
+/*! Default pool descriptor. */
+static wsfBufPoolDesc_t mainPoolDesc[WSF_BUF_POOLS] =
+{
+  {  16,  8 },
+  {  32,  4 },
+  {  64,  4 },
+  { 128,  4 },
+  { 256,  4 },
+  { 384,  4 }
+};
+
+
+static void PlatformInit(void)
+{
+    /* Change the pullup on the RST pin to 25K */
+    /* TODO: Is this really needed? */
+    MXC_MCR->ctrl = 0x202;
+
+    /* Set VREGO_D to 1.3V */
+    *((volatile uint32_t*)0x40004410) = 0x50;
+
+    /* Set TX LDO to 1.1V and enable LDO. Set RX LDO to 0.9V and enable LDO */
+    MXC_GCR->btleldocn = 0xD9; // TX 1.1V RX 0.9V
+
+    /* Power up the 32MHz XO */
+    MXC_GCR->clkcn |= MXC_F_GCR_CLKCN_X32M_EN;
+
+    /* Enable peripheral clocks */
+    /* TODO: Is this really needed? */
+    MXC_GCR->perckcn0 &= ~(MXC_F_GCR_PERCKCN0_GPIO0D | MXC_F_GCR_PERCKCN0_GPIO1D);  // Clear GPIO0 and GPIO1 Disable
+    MXC_GCR->perckcn1 &= ~(MXC_F_GCR_PERCKCN1_BTLED | MXC_F_GCR_PERCKCN1_TRNGD );  // Clear BTLE and ICACHE0 disable
+}
+
+static void myTrace(const char *pStr, va_list args)
+{
+    extern uint8_t wsfCsNesting;
+
+    if (wsfCsNesting == 0)
+    {
+        vprintf(pStr, args);
+        printf("\r\n");
+    }
+}
+
+static void WsfInit(void)
+{
+    WsfTimerInit();
+    WsfBufInit(sizeof(mainBufMem), (uint8_t*)mainBufMem, WSF_BUF_POOLS, mainPoolDesc);
+    WsfTraceRegister(myTrace);
+}
+
+void MacInit(void)
+{
+    wsfHandlerId_t handlerId;
+
+    /* Initialize link layer. */
+    BbInit();
+    handlerId = WsfOsSetNextHandler(SchHandler);
+    SchInit(handlerId);
+    LlAdvSlaveInit();
+    LlConnSlaveInit();
+    handlerId = WsfOsSetNextHandler(LlHandler);
+    LlHandlerInit(handlerId);
+}
+
+
+/* TODO: WTF? */
+/*
+ * In two-chip solutions, setting the address must wait until the HCI interface is initialized.
+ * This handler can also catch other Application events, but none are currently implemented.
+ * See ble-profiles/sources/apps/app/common/app_ui.c for further details.
+ *
+ */
+void SetAddress(uint8_t event)
+{
+    uint8_t bdAddr[6] = {0x02, 0x02, 0x44, 0x8B, 0x05, 0x00};
+
+    switch (event) {
+    case APP_UI_RESET_CMPL:
+        printf("Setting address -- MAC %02X:%02X:%02X:%02X:%02X:%02X\n", bdAddr[5], bdAddr[4], bdAddr[3], bdAddr[2], bdAddr[1], bdAddr[0]);
+        LlSetBdAddr((uint8_t*)&bdAddr);
+        LlGetBdAddr(hciCoreCb.bdAddr);
+        break;
+    default:
+        break;
+    }
+}
+
+
+
+static void ble_init(void)
+{
+    PlatformInit();
+    WsfInit();
+    MacInit();
+    //StackInitFit();
+    //FitStart();
+
+    /* Register a handler for Application events */
+    AppUiActionRegister(SetAddress);
+}
+
+
+void vBleTask(void*pvParameters)
+{
+    ble_init();
+    while (1){
+        // TODO: this need some timing and sleeping
+        wsfOsDispatcher();
+    }
+}
diff --git a/epicardium/modules/meson.build b/epicardium/modules/meson.build
index 113e3cb8ecaebc932a65ce9e6124cc2ff3be60ce..677d3f1e1c633bce74b28dda37a4e434d670d7a0 100644
--- a/epicardium/modules/meson.build
+++ b/epicardium/modules/meson.build
@@ -9,5 +9,6 @@ module_sources = files(
   'stream.c',
   'vibra.c',
   'light_sensor.c',
-  'rtc.c'
+  'rtc.c',
+  'ble.c'
 )