diff --git a/epicardium/ble/ble.c b/epicardium/ble/ble.c
index 973b6326c2e19aa5bae603ebde7ca53349f6ce25..f35dfce7b016017aa80514f38ce6d67fbf8a461b 100644
--- a/epicardium/ble/ble.c
+++ b/epicardium/ble/ble.c
@@ -175,144 +175,6 @@ static void scheduleTimer(void)
     }
 }
 
-#define UART_START_HDL                    0x800                /*!< \brief Service start handle. */
-#define UART_END_HDL                      (BATT_MAX_HDL - 1)  /*!< \brief Service end handle. */
-
-/**************************************************************************************************
- Handles
-**************************************************************************************************/
-
-/*! \brief UART Service Handles */
-enum
-{
-  UART_SVC_HDL = UART_START_HDL,        /*!< \brief UART service declaration */
-  UART_RX_CH_HDL,                      /*!< \brief UART rx characteristic */
-  UART_RX_HDL,                         /*!< \brief UART rx value */
-  UART_TX_CH_HDL,                      /*!< \brief UART tx characteristic */
-  UART_TX_HDL,                         /*!< \brief UART tx value */
-  UART_TX_CH_CCC_HDL,                  /*!< \brief UART tx CCCD */
-  BATT_MAX_HDL                          /*!< \brief Maximum handle. */
-};
-/**@}*/
-
-static const uint8_t UARTSvc[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x01,0x00,0x40,0x6E};
-
-static const uint8_t uartRxCh[] = {ATT_PROP_WRITE, UINT16_TO_BYTES(UART_RX_HDL), 0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E};
-const uint8_t attUartRxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E};
-
-static const uint8_t uartTxCh[] = {ATT_PROP_READ | ATT_PROP_NOTIFY, UINT16_TO_BYTES(UART_TX_HDL), 0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E};
-const uint8_t attUartTxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E};
-/* Battery level client characteristic configuration */
-
-
-
-void *SvcUARTAddGroupDyn(void)
-{
-  void *pSHdl;
-  uint8_t initCcc[] = {UINT16_TO_BYTES(0x0000)};
-  uint8_t initUARTVal[] = {0x20};
-
-  /* Create the service */
-  pSHdl = AttsDynCreateGroup(UART_START_HDL, UART_END_HDL);
-
-  if (pSHdl != NULL)
-  {
-    /* Primary service */
-    AttsDynAddAttrConst(pSHdl, attPrimSvcUuid, UARTSvc, sizeof(UARTSvc), 0, ATTS_PERMIT_READ);
-
-
-    /* UART rx characteristic */
-    AttsDynAddAttrConst(pSHdl, attChUuid, uartRxCh, sizeof(uartRxCh), 0, ATTS_PERMIT_READ);
-    // XXX: attUartRxChUuid is 16 bytes but nothing says so....
-    /* UART rx value */
-    // XXX: not sure if max value of 128 is fine...
-    AttsDynAddAttr(pSHdl, attUartRxChUuid, NULL, 0, 128,
-                   ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN, ATTS_PERMIT_WRITE);
-
-
-    /* UART tx characteristic */
-    AttsDynAddAttrConst(pSHdl, attChUuid, uartTxCh, sizeof(uartTxCh), 0, ATTS_PERMIT_READ);
-    /* UART tx value */
-    /* TODO: do we need ATTS_SET_READ_CBACK ? */
-    AttsDynAddAttr(pSHdl, attUartTxChUuid, initUARTVal, sizeof(uint8_t), sizeof(uint8_t),
-                   ATTS_SET_READ_CBACK, ATTS_PERMIT_READ);
-    /* UART tx CCC descriptor */
-    AttsDynAddAttr(pSHdl, attCliChCfgUuid, initCcc, sizeof(uint16_t), sizeof(uint16_t),
-                   ATTS_SET_CCC, ATTS_PERMIT_READ | ATTS_PERMIT_WRITE);
-  }
-
-  return pSHdl;
-}
-
-dmConnId_t active_connection = 0;
-
-uint8_t UARTReadCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
-                     uint16_t offset, attsAttr_t *pAttr)
-{
-  //AppHwBattRead(pAttr->pValue);
-  printf("read callback\n");
-  return ATT_SUCCESS;
-}
-
-uint8_t UARTWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
-                          uint16_t offset, uint16_t len, uint8_t *pValue,
-                          attsAttr_t *pAttr)
-{
-  active_connection = connId;
-
-  //printf("UARTWriteCback %d: ", len);
-  int i;
-  for(i=0; i<len; i++) {
-    //printf("%c", pValue[i]);
-    serial_enqueue_char(pValue[i]);
-  }
-  serial_enqueue_char('\r');
-  //printf("\n");
-
-#if 0
-  AttsSetAttr(UART_TX_HDL, len, pValue);
-  AttsHandleValueNtf(connId, UART_TX_HDL, len, pValue);
-#endif
-
-  return ATT_SUCCESS;
-}
-
-uint8_t ble_uart_tx_buf[129];
-uint8_t ble_uart_buf_tx_fill;
-int ble_uart_lasttick = 0;
-
-void ble_uart_write(uint8_t *pValue, uint8_t len)
-{
-  int i;
-  for(i=0; i<len; i++) {
-    if(pValue[i] >= 0x20 && pValue[i] < 0x7f) {
-        ble_uart_tx_buf[ble_uart_buf_tx_fill] = pValue[i];
-        ble_uart_buf_tx_fill++;
-    } else if(pValue[i] == '\r' || pValue[i] == '\n') {
-        if(ble_uart_buf_tx_fill > 0) {
-            AttsSetAttr(UART_TX_HDL, ble_uart_buf_tx_fill, ble_uart_tx_buf);
-            if(active_connection) {
-                int x = xTaskGetTickCount() - ble_uart_lasttick;
-                if(x < 100) {
-                    // Ugly hack if we already send something recently.
-                    // TODO: figure out how these notifications are acknowledged
-                    vTaskDelay(100 - x);
-                }
-                //printf("notify: ");
-                //int j;
-                //for(j=0;j<ble_uart_buf_tx_fill;j++) {
-                //    printf("%02x ", ble_uart_tx_buf[j]);
-                //}
-                //printf("\n");
-                AttsHandleValueNtf(active_connection, UART_TX_HDL, ble_uart_buf_tx_fill, ble_uart_tx_buf);
-                ble_uart_lasttick = xTaskGetTickCount();
-            }
-            ble_uart_buf_tx_fill = 0;
-        }
-    }
-  }
-}
-
 
 static void ble_init(void)
 {
@@ -325,13 +187,9 @@ static void ble_init(void)
     NVIC_SetPriority(BTLE_RX_RCVD_IRQn, 2);
     AppInit();
     BleStart();
-
-    /* Add the UART service dynamically */
     AttsDynInit();
-    void *pSHdl;
-    pSHdl = SvcUARTAddGroupDyn();
-    AttsDynRegister(pSHdl, UARTReadCback, UARTWriteCback);
-    //AttsDynRegister(pSHdl, NULL, UARTWriteCback);
+
+    bleuart_init();
 
     lasttick = xTaskGetTickCount();
 
diff --git a/epicardium/ble/meson.build b/epicardium/ble/meson.build
index 9e00b125574d16f6c5c2cd62a2c492cca7ee3b45..02f2eeb58e44eb2840f3bb8bc0a1cf979822bad1 100644
--- a/epicardium/ble/meson.build
+++ b/epicardium/ble/meson.build
@@ -6,5 +6,6 @@ ble_sources = files(
   'svc_core.c',
   'app/app_main.c',
   'app/common/app_db.c',
-  'app/common/app_ui.c'
+  'app/common/app_ui.c',
+  'uart.c'
 )
diff --git a/epicardium/ble/uart.c b/epicardium/ble/uart.c
new file mode 100644
index 0000000000000000000000000000000000000000..add1e1f10511943a6b8de9391dac23b6d013d42f
--- /dev/null
+++ b/epicardium/ble/uart.c
@@ -0,0 +1,169 @@
+#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 "ble_api.h"
+#include "hci_vs.h"
+
+#include "att_defs.h"
+#include "util/bstream.h"
+#include "att_api.h"
+
+#include "modules/modules.h"
+#include "FreeRTOS.h"
+#include "timers.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+
+#define UART_START_HDL                    0x800                /*!< \brief Service start handle. */
+#define UART_END_HDL                      (BATT_MAX_HDL - 1)  /*!< \brief Service end handle. */
+
+/**************************************************************************************************
+ Handles
+**************************************************************************************************/
+
+/*! \brief UART Service Handles */
+enum
+{
+  UART_SVC_HDL = UART_START_HDL,        /*!< \brief UART service declaration */
+  UART_RX_CH_HDL,                      /*!< \brief UART rx characteristic */
+  UART_RX_HDL,                         /*!< \brief UART rx value */
+  UART_TX_CH_HDL,                      /*!< \brief UART tx characteristic */
+  UART_TX_HDL,                         /*!< \brief UART tx value */
+  UART_TX_CH_CCC_HDL,                  /*!< \brief UART tx CCCD */
+  BATT_MAX_HDL                          /*!< \brief Maximum handle. */
+};
+/**@}*/
+
+static const uint8_t UARTSvc[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x01,0x00,0x40,0x6E};
+
+static const uint8_t uartRxCh[] = {ATT_PROP_WRITE, UINT16_TO_BYTES(UART_RX_HDL), 0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E};
+const uint8_t attUartRxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E};
+
+static const uint8_t uartTxCh[] = {ATT_PROP_READ | ATT_PROP_NOTIFY, UINT16_TO_BYTES(UART_TX_HDL), 0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E};
+const uint8_t attUartTxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E};
+/* Battery level client characteristic configuration */
+
+
+
+static void *SvcUARTAddGroupDyn(void)
+{
+  void *pSHdl;
+  uint8_t initCcc[] = {UINT16_TO_BYTES(0x0000)};
+  uint8_t initUARTVal[] = {0x20};
+
+  /* Create the service */
+  pSHdl = AttsDynCreateGroup(UART_START_HDL, UART_END_HDL);
+
+  if (pSHdl != NULL)
+  {
+    /* Primary service */
+    AttsDynAddAttrConst(pSHdl, attPrimSvcUuid, UARTSvc, sizeof(UARTSvc), 0, ATTS_PERMIT_READ);
+
+
+    /* UART rx characteristic */
+    AttsDynAddAttrConst(pSHdl, attChUuid, uartRxCh, sizeof(uartRxCh), 0, ATTS_PERMIT_READ);
+    // XXX: attUartRxChUuid is 16 bytes but nothing says so....
+    /* UART rx value */
+    // XXX: not sure if max value of 128 is fine...
+    AttsDynAddAttr(pSHdl, attUartRxChUuid, NULL, 0, 128,
+                   ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN, ATTS_PERMIT_WRITE);
+
+
+    /* UART tx characteristic */
+    AttsDynAddAttrConst(pSHdl, attChUuid, uartTxCh, sizeof(uartTxCh), 0, ATTS_PERMIT_READ);
+    /* UART tx value */
+    /* TODO: do we need ATTS_SET_READ_CBACK ? */
+    AttsDynAddAttr(pSHdl, attUartTxChUuid, initUARTVal, sizeof(uint8_t), sizeof(uint8_t),
+                   ATTS_SET_READ_CBACK, ATTS_PERMIT_READ);
+    /* UART tx CCC descriptor */
+    AttsDynAddAttr(pSHdl, attCliChCfgUuid, initCcc, sizeof(uint16_t), sizeof(uint16_t),
+                   ATTS_SET_CCC, ATTS_PERMIT_READ | ATTS_PERMIT_WRITE);
+  }
+
+  return pSHdl;
+}
+
+dmConnId_t active_connection = 0;
+
+static uint8_t UARTReadCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
+                     uint16_t offset, attsAttr_t *pAttr)
+{
+  //AppHwBattRead(pAttr->pValue);
+  printf("read callback\n");
+  return ATT_SUCCESS;
+}
+
+static uint8_t UARTWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
+                          uint16_t offset, uint16_t len, uint8_t *pValue,
+                          attsAttr_t *pAttr)
+{
+  active_connection = connId;
+
+  //printf("UARTWriteCback %d: ", len);
+  int i;
+  for(i=0; i<len; i++) {
+    //printf("%c", pValue[i]);
+    serial_enqueue_char(pValue[i]);
+  }
+  serial_enqueue_char('\r');
+  //printf("\n");
+
+#if 0
+  AttsSetAttr(UART_TX_HDL, len, pValue);
+  AttsHandleValueNtf(connId, UART_TX_HDL, len, pValue);
+#endif
+
+  return ATT_SUCCESS;
+}
+
+uint8_t ble_uart_tx_buf[129];
+uint8_t ble_uart_buf_tx_fill;
+int ble_uart_lasttick = 0;
+
+void ble_uart_write(uint8_t *pValue, uint8_t len)
+{
+  int i;
+  for(i=0; i<len; i++) {
+    if(pValue[i] >= 0x20 && pValue[i] < 0x7f) {
+        ble_uart_tx_buf[ble_uart_buf_tx_fill] = pValue[i];
+        ble_uart_buf_tx_fill++;
+    } else if(pValue[i] == '\r' || pValue[i] == '\n') {
+        if(ble_uart_buf_tx_fill > 0) {
+            AttsSetAttr(UART_TX_HDL, ble_uart_buf_tx_fill, ble_uart_tx_buf);
+            if(active_connection) {
+                int x = xTaskGetTickCount() - ble_uart_lasttick;
+                if(x < 100) {
+                    // Ugly hack if we already send something recently.
+                    // TODO: figure out how fast we can send or use indications
+                    vTaskDelay(100 - x);
+                }
+                //printf("notify: ");
+                //int j;
+                //for(j=0;j<ble_uart_buf_tx_fill;j++) {
+                //    printf("%02x ", ble_uart_tx_buf[j]);
+                //}
+                //printf("\n");
+                AttsHandleValueNtf(active_connection, UART_TX_HDL, ble_uart_buf_tx_fill, ble_uart_tx_buf);
+                ble_uart_lasttick = xTaskGetTickCount();
+            }
+            ble_uart_buf_tx_fill = 0;
+        }
+    }
+  }
+}
+
+void bleuart_init(void)
+{
+    /* Add the UART service dynamically */
+    void *pSHdl;
+    pSHdl = SvcUARTAddGroupDyn();
+    AttsDynRegister(pSHdl, UARTReadCback, UARTWriteCback);
+    //AttsDynRegister(pSHdl, NULL, UARTWriteCback);
+}
+