diff --git a/epicardium/ble/ble_api.h b/epicardium/ble/ble_api.h
index 059b0f6e85c964b6a4906743aed4698ee85427a3..38c60f731a27ada94022ad2dcbd11f467824f0d6 100644
--- a/epicardium/ble/ble_api.h
+++ b/epicardium/ble/ble_api.h
@@ -34,6 +34,7 @@ void ble_epic_att_api_free_att_write_data(struct epic_att_write *w);
 
 void ble_epic_ble_api_trigger_event(enum epic_ble_event_type type, void *data);
 void ble_epic_ble_api_init(void);
+void ble_epic_dm_api_event(dmEvt_t *dm_event);
 
 /**************************************************************************************************
   Data Types
diff --git a/epicardium/ble/ble_main.c b/epicardium/ble/ble_main.c
index 61e43788d7a13569c906ed0024bfb3596b6d800b..c40d3df62c1ce290b7378475a61537e4d7df9e51 100644
--- a/epicardium/ble/ble_main.c
+++ b/epicardium/ble/ble_main.c
@@ -686,7 +686,6 @@ static void scannerScanReport(dmEvt_t *pMsg)
 static void bleProcMsg(bleMsg_t *pMsg)
 {
   hciLeConnCmplEvt_t *connOpen;
-  attEvt_t e;
 
   switch(pMsg->hdr.event)
   {
@@ -733,8 +732,6 @@ static void bleProcMsg(bleMsg_t *pMsg)
       bleESS_ccc_update();
       HidProcMsg(&pMsg->hdr);
       UartProcMsg(pMsg);
-      e.hdr.event = ATT_CONNECTION_OPENED; e.hdr.param = connOpen->hdr.param;
-      ble_epic_att_api_event(&e);
       break;
 
     case DM_CONN_CLOSE_IND:
@@ -770,8 +767,6 @@ static void bleProcMsg(bleMsg_t *pMsg)
        */
       advertising_mode = APP_MODE_NONE;
       AppAdvStop();
-      e.hdr.param = pMsg->dm.connClose.hdr.param; e.hdr.event = ATT_CONNECTION_CLOSED;
-      ble_epic_att_api_event(&e);
       bleClose(pMsg);
       break;
 
@@ -905,6 +900,8 @@ static void BleHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
 
       /* process discovery-related messages */
       AppDiscProcDmMsg((dmEvt_t *) pMsg);
+
+      ble_epic_dm_api_event((dmEvt_t *)pMsg);
     }
     else if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
     {
diff --git a/epicardium/ble/epic_att_api.c b/epicardium/ble/epic_att_api.c
index d35574200e3a54ce6700c57c220c25d8a47cb00a..284c623519e6715d65a2dc48ed7047a0a89c5b89 100644
--- a/epicardium/ble/epic_att_api.c
+++ b/epicardium/ble/epic_att_api.c
@@ -16,18 +16,13 @@
 
 void ble_epic_att_api_event(attEvt_t *att_event)
 {
-	bool enabled;
-	epic_interrupt_is_enabled(EPIC_INT_BLE, &enabled);
+	attEvt_t *e = WsfBufAlloc(sizeof(*e));
 
-	if (enabled) {
-		attEvt_t *e = WsfBufAlloc(sizeof(*e));
-
-		if (e) {
-			memcpy(e, att_event, sizeof(*e));
-			ble_epic_ble_api_trigger_event(BLE_EVENT_ATT_EVENT, e);
-		} else {
-			LOG_WARN("ble", "could not allocate att event");
-		}
+	if (e) {
+		memcpy(e, att_event, sizeof(*e));
+		ble_epic_ble_api_trigger_event(BLE_EVENT_ATT_EVENT, e);
+	} else {
+		LOG_WARN("ble", "could not allocate att event");
 	}
 }
 
diff --git a/epicardium/ble/epic_ble_api.c b/epicardium/ble/epic_ble_api.c
index 4e68170be752fea04025edbdd222720a0d3a41df..910bc8ad7011c8db336816e4a577af111942139a 100644
--- a/epicardium/ble/epic_ble_api.c
+++ b/epicardium/ble/epic_ble_api.c
@@ -10,6 +10,7 @@
 #include "queue.h"
 
 #include <stdint.h>
+#include <string.h>
 
 #define BLE_EVENT_QUEUE_SIZE 10
 
@@ -38,16 +39,24 @@ void ble_epic_ble_api_trigger_event(enum epic_ble_event_type type, void *data)
 
 	struct epic_ble_event e = { .type = type, .data = data };
 
-	if (xQueueSend(ble_event_queue, &e, 0) != pdTRUE) {
-		/* Print a warning if the app is missing events. Missing scan results
-		 * is considered OK though, as they are queued and periodic. */
-		if (type != BLE_EVENT_SCAN_REPORT) {
-			LOG_WARN("ble", "Application missed event %u", type);
+	if (enabled) {
+		if (xQueueSend(ble_event_queue, &e, 0) != pdTRUE) {
+			/* Print a warning if the app is missing events. Missing scan results
+			* is considered OK though, as they are queued and periodic. */
+			if (type != BLE_EVENT_SCAN_REPORT) {
+				LOG_WARN(
+					"ble",
+					"Application missed event %u",
+					type
+				);
+			}
+			epic_ble_free_event(&e);
 		}
+
+		api_interrupt_trigger(EPIC_INT_BLE);
+	} else {
 		epic_ble_free_event(&e);
 	}
-
-	api_interrupt_trigger(EPIC_INT_BLE);
 }
 
 int epic_ble_get_event(struct epic_ble_event *e)
@@ -69,3 +78,15 @@ void ble_epic_ble_api_init(void)
 
 	ble_epic_att_api_init();
 }
+
+void ble_epic_dm_api_event(dmEvt_t *dm_event)
+{
+	dmEvt_t *e = WsfBufAlloc(sizeof(*e));
+
+	if (e) {
+		memcpy(e, dm_event, sizeof(*e));
+		ble_epic_ble_api_trigger_event(BLE_EVENT_DM_EVENT, e);
+	} else {
+		LOG_WARN("ble", "could not allocate dm event");
+	}
+}
diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h
index 6ac9a8d6121ca5e89f750f3e1a7f2e475e0ba9ee..d3d7df16995a0009df837d106ce3950f89925ae1 100644
--- a/epicardium/epicardium.h
+++ b/epicardium/epicardium.h
@@ -2348,7 +2348,7 @@ enum epic_ble_event_type {
 	BLE_EVENT_SCAN_REPORT                             = 4,
 	BLE_EVENT_ATT_EVENT                               = 5,
 	BLE_EVENT_ATT_WRITE                               = 6,
-	BLE_EVENT_DM_EVENT                                = 6,
+	BLE_EVENT_DM_EVENT                                = 7,
 };
 
 struct epic_wsf_header
@@ -2368,6 +2368,45 @@ struct epic_att_event
   uint16_t              mtu;          /*!< \brief Negotiated MTU value */
 };
 
+#define BDA_ADDR_LEN 6
+typedef uint8_t bdAddr_t[BDA_ADDR_LEN];
+
+struct epic_hciLeConnCmpl_event
+{
+  struct epic_wsf_header hdr;        /*!< \brief Event header */
+  uint8_t             status;        /*!< \brief Status. */
+  uint16_t            handle;        /*!< \brief Connection handle. */
+  uint8_t             role;          /*!< \brief Local connection role. */
+  uint8_t             addrType;      /*!< \brief Peer address type. */
+  bdAddr_t            peerAddr;      /*!< \brief Peer address. */
+  uint16_t            connInterval;  /*!< \brief Connection interval */
+  uint16_t            connLatency;   /*!< \brief Connection latency. */
+  uint16_t            supTimeout;    /*!< \brief Supervision timeout. */
+  uint8_t             clockAccuracy; /*!< \brief Clock accuracy. */
+
+  /* \brief enhanced fields */
+  bdAddr_t            localRpa;      /*!< \brief Local RPA. */
+  bdAddr_t            peerRpa;       /*!< \brief Peer RPA. */
+};
+
+/*! \brief Disconnect complete event */
+struct epic_hciDisconnectCmpl_event
+{
+  struct epic_wsf_header hdr;        /*!< \brief Event header */
+  uint8_t             status;        /*!< \brief Disconnect complete status. */
+  uint16_t            handle;        /*!< \brief Connect handle. */
+  uint8_t             reason;        /*!< \brief Reason. */
+};
+
+
+struct epic_dm_event
+{
+	union {
+		struct epic_hciLeConnCmpl_event     leConnCmpl;                  /*!< \brief LE connection complete. */
+		struct epic_hciDisconnectCmpl_event disconnectCmpl;              /*!< \brief Disconnect complete. */
+	};
+};
+
 struct epic_att_write
 {
   struct epic_wsf_header hdr;          /*!< \brief Header structure */
@@ -2378,12 +2417,12 @@ struct epic_att_write
   void *buffer;
 };
 
-
 struct epic_ble_event {
 	enum epic_ble_event_type type;
 	union {
 		void *data;
 		struct epic_att_event *att_event;
+		struct epic_dm_event *dm_event;
 		struct epic_att_write *att_write;
 	};
 };
diff --git a/pycardium/modules/modbluetooth_card10.c b/pycardium/modules/modbluetooth_card10.c
index 36dd81dcb230ad0307e188e2b866dd3bffa2b46d..55df544db7bb61c2d824c95d3fea7bcb01b8e1ae 100644
--- a/pycardium/modules/modbluetooth_card10.c
+++ b/pycardium/modules/modbluetooth_card10.c
@@ -85,9 +85,81 @@ const uint8_t attCliChCfgUuid[] = { UINT16_TO_BYTES(
 #define ATT_RSP_PENDING                                                        \
 	0x79 /*!< \brief Responsed delayed pending higher layer */
 
-// card10 att interface specific events
-#define ATT_CONNECTION_OPENED 0x81
-#define ATT_CONNECTION_CLOSED 0x82
+#define DM_CBACK_START 0x20 /*!< \brief DM callback event starting value */
+
+/*! \brief DM callback events */
+enum { DM_RESET_CMPL_IND = DM_CBACK_START, /*!< \brief Reset complete */
+       DM_ADV_START_IND,                   /*!< \brief Advertising started */
+       DM_ADV_STOP_IND,                    /*!< \brief Advertising stopped */
+       DM_ADV_NEW_ADDR_IND, /*!< \brief New resolvable address has been generated */
+       DM_SCAN_START_IND,    /*!< \brief Scanning started */
+       DM_SCAN_STOP_IND,     /*!< \brief Scanning stopped */
+       DM_SCAN_REPORT_IND,   /*!< \brief Scan data received from peer device */
+       DM_CONN_OPEN_IND,     /*!< \brief Connection opened */
+       DM_CONN_CLOSE_IND,    /*!< \brief Connection closed */
+       DM_CONN_UPDATE_IND,   /*!< \brief Connection update complete */
+       DM_SEC_PAIR_CMPL_IND, /*!< \brief Pairing completed successfully */
+       DM_SEC_PAIR_FAIL_IND, /*!< \brief Pairing failed or other security failure */
+       DM_SEC_ENCRYPT_IND,      /*!< \brief Connection encrypted */
+       DM_SEC_ENCRYPT_FAIL_IND, /*!< \brief Encryption failed */
+       DM_SEC_AUTH_REQ_IND, /*!< \brief PIN or OOB data requested for pairing */
+       DM_SEC_KEY_IND,      /*!< \brief Security key indication */
+       DM_SEC_LTK_REQ_IND,  /*!< \brief LTK requested for encyption */
+       DM_SEC_PAIR_IND,     /*!< \brief Incoming pairing request from master */
+       DM_SEC_SLAVE_REQ_IND, /*!< \brief Incoming security request from slave */
+       DM_SEC_CALC_OOB_IND, /*!< \brief Result of OOB Confirm Calculation Generation */
+       DM_SEC_ECC_KEY_IND, /*!< \brief Result of ECC Key Generation */
+       DM_SEC_COMPARE_IND, /*!< \brief Result of Just Works/Numeric Comparison Compare Value Calculation */
+       DM_SEC_KEYPRESS_IND, /*!< \brief Keypress indication from peer in passkey security */
+       DM_PRIV_RESOLVED_ADDR_IND, /*!< \brief Private address resolved */
+       DM_PRIV_GENERATE_ADDR_IND, /*!< \brief Private resolvable address generated */
+       DM_CONN_READ_RSSI_IND,           /*!< \brief Connection RSSI read */
+       DM_PRIV_ADD_DEV_TO_RES_LIST_IND, /*!< \brief Device added to resolving list */
+       DM_PRIV_REM_DEV_FROM_RES_LIST_IND, /*!< \brief Device removed from resolving list */
+       DM_PRIV_CLEAR_RES_LIST_IND,     /*!< \brief Resolving list cleared */
+       DM_PRIV_READ_PEER_RES_ADDR_IND, /*!< \brief Peer resolving address read */
+       DM_PRIV_READ_LOCAL_RES_ADDR_IND, /*!< \brief Local resolving address read */
+       DM_PRIV_SET_ADDR_RES_ENABLE_IND, /*!< \brief Address resolving enable set */
+       DM_REM_CONN_PARAM_REQ_IND, /*!< \brief Remote connection parameter requested */
+       DM_CONN_DATA_LEN_CHANGE_IND, /*!< \brief Data length changed */
+       DM_CONN_WRITE_AUTH_TO_IND, /*!< \brief Write authenticated payload complete */
+       DM_CONN_AUTH_TO_EXPIRED_IND, /*!< \brief Authenticated payload timeout expired */
+       DM_PHY_READ_IND,             /*!< \brief Read PHY */
+       DM_PHY_SET_DEF_IND,          /*!< \brief Set default PHY */
+       DM_PHY_UPDATE_IND,           /*!< \brief PHY update */
+       DM_ADV_SET_START_IND,  /*!< \brief Advertising set(s) started */
+       DM_ADV_SET_STOP_IND,   /*!< \brief Advertising set(s) stopped */
+       DM_SCAN_REQ_RCVD_IND,  /*!< \brief Scan request received */
+       DM_EXT_SCAN_START_IND, /*!< \brief Extended scanning started */
+       DM_EXT_SCAN_STOP_IND,  /*!< \brief Extended scanning stopped */
+       DM_EXT_SCAN_REPORT_IND, /*!< \brief Extended scan data received from peer device */
+       DM_PER_ADV_SET_START_IND, /*!< \brief Periodic advertising set started */
+       DM_PER_ADV_SET_STOP_IND,  /*!< \brief Periodic advertising set stopped */
+       DM_PER_ADV_SYNC_EST_IND, /*!< \brief Periodic advertising sync established */
+       DM_PER_ADV_SYNC_EST_FAIL_IND, /*!< \brief Periodic advertising sync establishment failed */
+       DM_PER_ADV_SYNC_LOST_IND, /*!< \brief Periodic advertising sync lost */
+       DM_PER_ADV_SYNC_TRSF_EST_IND, /*!< \brief Periodic advertising sync transfer established */
+       DM_PER_ADV_SYNC_TRSF_EST_FAIL_IND, /*!< \brief Periodic advertising sync transfer establishment failed */
+       DM_PER_ADV_SYNC_TRSF_IND, /*!< \brief Periodic advertising sync info transferred */
+       DM_PER_ADV_SET_INFO_TRSF_IND, /*!< \brief Periodic advertising set sync info transferred */
+       DM_PER_ADV_REPORT_IND, /*!< \brief Periodic advertising data received from peer device */
+       DM_REMOTE_FEATURES_IND, /*!< \brief Remote features from peer device */
+       DM_READ_REMOTE_VER_INFO_IND, /*!< \brief Remote LL version information read */
+       DM_CONN_IQ_REPORT_IND, /*!< \brief IQ samples from CTE of received packet from peer device */
+       DM_CTE_REQ_FAIL_IND,             /*!< \brief CTE request failed */
+       DM_CONN_CTE_RX_SAMPLE_START_IND, /*!< \brief Sampling received CTE started */
+       DM_CONN_CTE_RX_SAMPLE_STOP_IND, /*!< \brief Sampling received CTE stopped */
+       DM_CONN_CTE_TX_CFG_IND, /*!< \brief Connection CTE transmit parameters configured */
+       DM_CONN_CTE_REQ_START_IND, /*!< \brief Initiating connection CTE request started */
+       DM_CONN_CTE_REQ_STOP_IND, /*!< \brief Initiating connection CTE request stopped */
+       DM_CONN_CTE_RSP_START_IND, /*!< \brief Responding to connection CTE request started */
+       DM_CONN_CTE_RSP_STOP_IND, /*!< \brief Responding to connection CTE request stopped */
+       DM_READ_ANTENNA_INFO_IND, /*!< \brief Antenna information read */
+       DM_L2C_CMD_REJ_IND,       /*!< \brief L2CAP Command Reject */
+       DM_ERROR_IND,             /*!< \brief General error */
+       DM_HW_ERROR_IND,          /*!< \brief Hardware error */
+       DM_VENDOR_SPEC_IND        /*!< \brief Vendor specific event */
+};
 
 #define ATTS_HANDLE_VALUE_CNF 15
 
@@ -171,21 +243,32 @@ static void handle_att_event(struct epic_att_event *att_event)
 		} else if (att_event->hdr.status == ATT_ERR_OVERFLOW) {
 			e->notification_status = NOTIFICATION_STATUS_OVERFLOW;
 		}
-	} else if (att_event->hdr.event == ATT_CONNECTION_OPENED) {
-		uint8_t event        = MP_BLUETOOTH_IRQ_CENTRAL_CONNECT;
-		uint16_t conn_handle = att_event->hdr.param;
-		uint8_t addr_type    = 0;  // TODO
-		uint8_t addr[8]      = {}; // TODO
+	}
+}
+
+static void handle_dm_event(struct epic_dm_event *dm_event)
+{
+	struct epic_wsf_header *hdr = (struct epic_wsf_header *)dm_event;
+	printf("MP got dm event %d,%d\n", hdr->event, hdr->status);
+
+	if (hdr->event == DM_CONN_OPEN_IND) {
+		struct epic_hciLeConnCmpl_event *e =
+			(struct epic_hciLeConnCmpl_event *)dm_event;
 		mp_bluetooth_gap_on_connected_disconnected(
-			event, conn_handle, addr_type, addr
+			MP_BLUETOOTH_IRQ_CENTRAL_CONNECT,
+			e->hdr.param,
+			e->addrType,
+			e->peerAddr
 		);
-	} else if (att_event->hdr.event == ATT_CONNECTION_CLOSED) {
-		uint8_t event        = MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT;
-		uint16_t conn_handle = att_event->hdr.param;
-		uint8_t addr_type    = 0;  // TODO
-		uint8_t addr[8]      = {}; // TODO
+	} else if (hdr->event == DM_CONN_CLOSE_IND) {
+		struct epic_hciDisconnectCmpl_event *e =
+			(struct epic_hciDisconnectCmpl_event *)dm_event;
+		uint8_t addr[6] = {};
 		mp_bluetooth_gap_on_connected_disconnected(
-			event, conn_handle, addr_type, addr
+			MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT,
+			e->hdr.param,
+			0xFF,
+			addr
 		);
 	}
 }
@@ -228,6 +311,9 @@ static mp_obj_t mp_ble_poll_events(mp_obj_t interrupt_id)
 			if (ble_event.type == BLE_EVENT_ATT_EVENT) {
 				handle_att_event(ble_event.att_event);
 			}
+			if (ble_event.type == BLE_EVENT_DM_EVENT) {
+				handle_dm_event(ble_event.dm_event);
+			}
 			if (ble_event.type == BLE_EVENT_ATT_WRITE) {
 				handle_att_write(ble_event.att_write);
 			}