diff --git a/epicardium/ble/hid_work.c b/epicardium/ble/hid_work.c
index b0158feaa039af9fa2dbe35a6701c47a0ae95058..1d529491efecb0a7a1ef3196bc9d4d343e3449cc 100644
--- a/epicardium/ble/hid_work.c
+++ b/epicardium/ble/hid_work.c
@@ -1,6 +1,7 @@
 #include "hid.h"
 #include "cccd.h"
 
+#include "epicardium.h"
 #include "modules/log.h"
 
 #include "dm_api.h"
@@ -9,339 +10,128 @@
 #include "hid/hid_api.h"
 #include "svc_hid.h"
 
+#include "FreeRTOS.h"
+#include "queue.h"
+
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 
-/* Remote Button Identifier bits */
-/* clang-format off */
-#define REMOTE_USAGE_NONE                 (0<<0)
-#define REMOTE_VOLUME_UP                  (1<<0)
-#define REMOTE_VOLUME_DOWN                (1<<1)
-#define REMOTE_MUTE                       (1<<2)
-#define REMOTE_PLAY                       (1<<3)
-#define REMOTE_PAUSE                      (1<<4)
-#define REMOTE_STOP                       (1<<5)
-#define REMOTE_NEXT                       (1<<6)
-#define REMOTE_PREVIOUS                   (1<<7)
-
-/* Keyboard input record message format */
-#define KEYBOARD_IR_MODIFIER_POS          0
-#define KEYBOARD_IR_RESERVED_POS          1
-#define KEYBOARD_IR_KEY_POS               2
-#define KEYBOARD_IR_MAX_KEYS              6
-
-/* Keyboard LED Identifier bits */
-#define KEYBOARD_LED_NUM_LOCK             (1<<0)
-#define KEYBOARD_LED_CAPS_LOCK            (1<<1)
-#define KEYBOARD_LED_SCROLL_LOCK          (1<<2)
-#define KEYBOARD_LED_COMPOSE              (1<<3)
-#define KEYBOARD_LED_KANA                 (1<<4)
-
-/* Mouse button bit mask */
-#define MOUSE_BUTTON_LEFT                 (1<<0)
-#define MOUSE_BUTTON_RIGHT                (1<<1)
-#define MOUSE_BUTTON_MIDDLE               (1<<2)
-
-/* Mouse input record message format */
-#define MOUSE_BUTTON_POS                  0
-#define MOUSE_X_POS                       1
-#define MOUSE_Y_POS                       2
-
-
 /* HidApp TX path flags */
-#define HIDAPP_TX_FLAGS_READY             0x01
-#define HIDAPP_TX_FLAGS_PENDING           0x02
-/* clang-format on */
+#define HIDAPP_TX_FLAGS_READY 0x01
 
 /*! application control block */
 /* clang-format off */
 struct
 {
-  /* Mouse pending event data */
-  uint8_t buttonMask;                   /* pending button mask */
-  uint8_t xDisplacement;                /* pending X Displacement */
-  uint8_t yDisplacement;                /* pending Y Displacement */
-  uint8_t devSpecific;                  /* pending Device Specific */
-
-  /* Keyboard pending event data */
-  uint8_t modifier;                     /* pending key modifiers */
-  uint8_t keys[KEYBOARD_IR_MAX_KEYS];   /* pending keys */
-
-  /* Remote pending event data */
-  uint8_t btnData;                      /* pending remote button data */
-
-  uint8_t reportId;                     /* pending reportId button data */
   uint8_t txFlags;                      /* transmit flags */
   uint8_t protocolMode;                 /* current protocol mode */
   uint8_t hostSuspended;                /* TRUE if host suspended */
 } hidAppCb;
 /* clang-format on */
 
-/*************************************************************************************************/
-/*!
- *  \brief  Send example data.
- *
- *  \param  connId    Connection identifier.
- *
- *  \return None.
- */
-/*************************************************************************************************/
-static void hidAppMouseSendData(dmConnId_t connId)
-{
-	uint8_t cccHandle = HIDAPP_IN_MOUSE_CCC_HDL;
-	uint8_t protocolMode;
-
-	protocolMode = HidGetProtocolMode();
-
-	if (protocolMode == HID_PROTOCOL_MODE_BOOT) {
-		cccHandle = HIDAPP_MBI_CCC_HDL;
-	}
-
-	if (AttsCccEnabled(connId, cccHandle) || 1) {
-		if (hidAppCb.txFlags & HIDAPP_TX_FLAGS_PENDING) {
-			if (hidAppCb.txFlags & HIDAPP_TX_FLAGS_READY) {
-				uint8_t buffer[HIDAPP_MOUSE_INPUT_REPORT_LEN];
-				uint8_t reportId = HIDAPP_MOUSE_REPORT_ID;
-
-				/* mouse record: button mask, x displacement, y displacement, device specific */
-				buffer[MOUSE_BUTTON_POS] = hidAppCb.buttonMask;
-				buffer[MOUSE_X_POS] = hidAppCb.xDisplacement;
-				buffer[MOUSE_Y_POS] = hidAppCb.yDisplacement;
+struct report {
+	uint8_t reportId;
+	uint8_t data[8];
+	uint8_t len;
+};
 
-				hidAppCb.txFlags &=
-					~(HIDAPP_TX_FLAGS_READY |
-					  HIDAPP_TX_FLAGS_PENDING);
+#define QUEUE_SIZE 10
 
-				if (protocolMode == HID_PROTOCOL_MODE_BOOT) {
-					reportId = HID_MOUSE_BOOT_ID;
-				}
+static QueueHandle_t queue;
+static uint8_t buffer[sizeof(struct report) * QUEUE_SIZE];
+static StaticQueue_t queue_data;
 
-				/* Send the message */
-				HidSendInputReport(
-					connId,
-					reportId,
-					HIDAPP_MOUSE_INPUT_REPORT_LEN,
-					buffer
-				);
-			}
-		}
-	} else {
-		hidAppCb.txFlags &= ~HIDAPP_TX_FLAGS_PENDING;
+static int hid_queue_data(uint8_t reportId, uint8_t *data, uint8_t len)
+{
+	struct report report;
+	report.reportId = reportId;
+	memcpy(report.data, data, len);
+	report.len = len;
+
+	if (xQueueSend(queue, &report, 0) != pdTRUE) {
+		/* Likely full */
+		return -EAGAIN;
 	}
+
+	return 0;
 }
 
-/*************************************************************************************************/
-/*!
- *  \brief  Send example data.
- *
- *  \param  connId    Connection identifier.
- *
- *  \return None.
- */
-/*************************************************************************************************/
-static void hidAppkeyboardSendData(dmConnId_t connId)
+static bool hid_dequeue_data(dmConnId_t connId)
 {
-	uint8_t cccHandle = HIDAPP_IN_KEYBOARD_CCC_HDL;
-	uint8_t protocolMode;
+	uint8_t cccHandle;
+	struct report report;
 
-	protocolMode = HidGetProtocolMode();
+	//lock();
 
-	if (protocolMode == HID_PROTOCOL_MODE_BOOT) {
-		cccHandle = HIDAPP_KBI_CCC_HDL;
+	if (!(hidAppCb.txFlags & HIDAPP_TX_FLAGS_READY)) {
+		//unlock();
+		return false;
 	}
 
-	if (AttsCccEnabled(connId, cccHandle) || 1) {
-		if (hidAppCb.txFlags & HIDAPP_TX_FLAGS_PENDING) {
-			if (hidAppCb.txFlags & HIDAPP_TX_FLAGS_READY) {
-				uint8_t buffer[HIDAPP_KEYBOARD_INPUT_REPORT_LEN];
-				uint8_t reportId = HIDAPP_KEYBOARD_REPORT_ID;
-
-				/* modifier, reserved, keys[6] */
-				buffer[KEYBOARD_IR_MODIFIER_POS] =
-					hidAppCb.modifier;
-				buffer[KEYBOARD_IR_RESERVED_POS] = 0;
-				memcpy(buffer + KEYBOARD_IR_KEY_POS,
-				       hidAppCb.keys,
-				       sizeof(hidAppCb.keys));
-
-				hidAppCb.txFlags &=
-					~(HIDAPP_TX_FLAGS_READY |
-					  HIDAPP_TX_FLAGS_PENDING);
-
-				if (protocolMode == HID_PROTOCOL_MODE_BOOT) {
-					reportId = HID_KEYBOARD_BOOT_ID;
-				}
+	// Loop until a CCC is enabled or the queue is empty
+	while (true) {
+		if (xQueueReceive(queue, &report, 0) != pdTRUE) {
+			break;
+		}
 
-				/* Send the message */
-				HidSendInputReport(
-					connId,
-					reportId,
-					HIDAPP_KEYBOARD_INPUT_REPORT_LEN,
-					buffer
-				);
+		if (HidGetProtocolMode() == HID_PROTOCOL_MODE_BOOT) {
+			if (report.reportId == HIDAPP_KEYBOARD_REPORT_ID) {
+				report.reportId = HID_KEYBOARD_BOOT_ID;
+				cccHandle       = HIDAPP_KBI_CCC_HDL;
+			} else if (report.reportId == HIDAPP_MOUSE_REPORT_ID) {
+				report.reportId = HID_MOUSE_BOOT_ID;
+				cccHandle       = HIDAPP_MBI_CCC_HDL;
+			} else {
+				break;
 			}
+		} else {
+			if (report.reportId == HIDAPP_KEYBOARD_REPORT_ID) {
+				cccHandle = HIDAPP_IN_KEYBOARD_CCC_HDL;
+			} else if (report.reportId == HIDAPP_MOUSE_REPORT_ID) {
+				cccHandle = HIDAPP_IN_MOUSE_CCC_HDL;
+			} else if (report.reportId == HIDAPP_REMOTE_REPORT_ID) {
+				cccHandle = HIDAPP_IN_REMOTE_CCC_HDL;
+			} else {
+				break;
+			};
 		}
-	} else {
-		hidAppCb.txFlags &= ~HIDAPP_TX_FLAGS_PENDING;
-	}
-}
-/*************************************************************************************************/
-/*!
- *  \brief  Send example data.
- *
- *  \param  connId    Connection identifier.
- *
- *  \return None.
- */
-/*************************************************************************************************/
-static void hidAppRemoteSendData(dmConnId_t connId)
-{
-	if (AttsCccEnabled(connId, HIDAPP_IN_REMOTE_CCC_HDL) || 1) {
-		if (hidAppCb.txFlags & HIDAPP_TX_FLAGS_PENDING) {
-			if (hidAppCb.txFlags & HIDAPP_TX_FLAGS_READY) {
-				uint8_t buffer;
 
-				/* bitmask of remote buttons */
-				buffer = hidAppCb.btnData;
-
-				hidAppCb.txFlags &=
-					~(HIDAPP_TX_FLAGS_READY |
-					  HIDAPP_TX_FLAGS_PENDING);
-
-				/* Send the message */
-				HidSendInputReport(
-					connId,
-					hidAppCb.reportId,
-					HIDAPP_REMOTE_INPUT_REPORT_LEN,
-					&buffer
-				);
-			}
+		if (AttsCccEnabled(connId, cccHandle) || 1) {
+			hidAppCb.txFlags &= ~(HIDAPP_TX_FLAGS_READY);
+			/* Send the message */
+			HidSendInputReport(
+				connId,
+				report.reportId,
+				report.len,
+				report.data
+			);
+			break;
 		}
-	} else {
-		hidAppCb.txFlags &= ~HIDAPP_TX_FLAGS_PENDING;
 	}
-}
 
-/*************************************************************************************************/
-/*!
- *  \brief  Send example data.
- *
- *  \param  connId    Connection identifier.
- *
- *  \return None.
- */
-/*************************************************************************************************/
-static void hidAppSendData(dmConnId_t connId)
-{
-	/* Send the report based on report ID */
-	switch (hidAppCb.reportId) {
-	case HIDAPP_REMOTE_REPORT_ID:
-		hidAppRemoteSendData(connId);
-		break;
-	case HIDAPP_KEYBOARD_REPORT_ID:
-		hidAppkeyboardSendData(connId);
-		break;
-	case HIDAPP_MOUSE_REPORT_ID:
-		hidAppMouseSendData(connId);
-		break;
-	default:
-		break;
-	}
+	//unlock();
+	return true;
 }
 
-/*************************************************************************************************/
-/*!
- *  \brief  Send or queue a remote event to the host
- *
- *  \param  button        Remote control depressed button
- *
- *  \return None.
- */
-/*************************************************************************************************/
-void epic_hid_remote_report_event(uint8_t button)
+int epic_hid_send_report(uint8_t reportId, uint8_t *data, uint8_t len)
 {
-	dmConnId_t connId;
-
-	if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE) {
-		LOG_INFO("hid", "send button %d\n", button);
-		/* record key data */
-		hidAppCb.btnData  = button;
-		hidAppCb.reportId = HIDAPP_REMOTE_REPORT_ID;
-
-		/* Indicate new data is pending */
-		hidAppCb.txFlags |= HIDAPP_TX_FLAGS_PENDING;
-
-		/* send the data */
-		hidAppSendData(connId);
+	dmConnId_t connId = AppConnIsOpen();
+	if (connId == DM_CONN_ID_NONE) {
+		return -EIO;
 	}
-}
-
-/*************************************************************************************************/
-/*!
- *  \brief  Send or queue a keyboard event to the host
- *
- *  \param  modifiers        Keyboard modifiers.
- *  \param  keys             Keyboard depressed keys.
- *  \param  numKeys          Size of keys parameters in bytes
- *
- *  \return None.
- */
-/*************************************************************************************************/
-void epic_hid_keyboard_report_event(
-	uint8_t modifiers, uint8_t keys[], uint8_t numKeys
-) {
-	dmConnId_t connId;
-
-	if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE) {
-		/* max 6 keys in report map */
-		if (numKeys > KEYBOARD_IR_MAX_KEYS) {
-			numKeys = KEYBOARD_IR_MAX_KEYS;
-		}
 
-		/* record key data */
-		hidAppCb.modifier = modifiers;
-		memset(hidAppCb.keys, 0, sizeof(hidAppCb.keys));
-		memcpy(hidAppCb.keys, keys, numKeys);
-		hidAppCb.reportId = HIDAPP_KEYBOARD_REPORT_ID;
+	int ret;
+	ret = hid_queue_data(reportId, data, len);
 
-		/* indicate new data is pending */
-		hidAppCb.txFlags |= HIDAPP_TX_FLAGS_PENDING;
-
-		/* send the data */
-		hidAppSendData(connId);
+	if (ret < 0) {
+		return ret;
 	}
-}
-
-/*************************************************************************************************/
-/*!
- *  \brief  Report or queue a mouse event to the host.
- *
- *  \param  buttonMask        Mouse button mask.
- *  \param  xDisplacement     Mouse displacement in the x direction.
- *  \param  yDisplacement     Mouse displacement in the y direction.
- *
- *  \return None.
- */
-/*************************************************************************************************/
-void epic_hid_mouse_report_event(
-	uint8_t buttonMask, int8_t xDisplacement, int8_t yDisplacement
-) {
-	dmConnId_t connId;
-
-	if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE) {
-		/* record mouse data */
-		hidAppCb.buttonMask    = buttonMask;
-		hidAppCb.xDisplacement = xDisplacement;
-		hidAppCb.yDisplacement = yDisplacement;
-		hidAppCb.devSpecific   = 0;
-		hidAppCb.reportId      = HIDAPP_MOUSE_REPORT_ID;
-
-		/* Indicate new data is pending */
-		hidAppCb.txFlags |= HIDAPP_TX_FLAGS_PENDING;
 
-		/* send the data */
-		hidAppSendData(connId);
+	if (hid_dequeue_data(connId)) {
+		return 0;
+	} else {
+		return 1;
 	}
 }
 
@@ -409,15 +199,25 @@ void HidProcMsg(wsfMsgHdr_t *pMsg)
 	if (pMsg->event == ATTS_HANDLE_VALUE_CNF) {
 		if (pMsg->status == ATT_SUCCESS) {
 			hidAppCb.txFlags |= HIDAPP_TX_FLAGS_READY;
-			hidAppSendData((dmConnId_t)pMsg->param);
+			hid_dequeue_data((dmConnId_t)pMsg->param);
 		}
 	}
 	if (pMsg->event == DM_CONN_OPEN_IND) {
 		hidAppCb.txFlags = HIDAPP_TX_FLAGS_READY;
+
+		struct report report;
+		while (xQueueReceive(queue, &report, 0) == pdTRUE)
+			;
+
+		/* Todo: At this point the CCC descriptors are not set up yet
+		 * and things which get sent until then are discarded. */
 	}
 }
 
 void hid_work_init(void)
 {
+	queue = xQueueCreateStatic(
+		QUEUE_SIZE, sizeof(struct report), buffer, &queue_data
+	);
 	hidAppCb.txFlags = HIDAPP_TX_FLAGS_READY;
 }
diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h
index 2ca919c196b87a55ea5b7cffa518220fe1fce925..db5369ecb7b162c49a591442bbe07d6cad1ce92d 100644
--- a/epicardium/epicardium.h
+++ b/epicardium/epicardium.h
@@ -158,10 +158,7 @@ typedef _Bool bool;
 #define API_BLE_GET_LAST_PAIRING_NAME 0x145
 #define API_BLE_GET_PEER_DEVICE_NAME  0x146
 
-
-#define API_HID_REMOTE_REPORT      0x150
-#define API_HID_KEYBOARD_REPORT    0x151
-#define API_HID_MOUSE_REPORT       0x152
+#define API_HID_SEND_REPORT        0x150
 
 /* clang-format on */
 
@@ -2325,17 +2322,28 @@ API(API_BLE_SET_MODE, void epic_ble_set_mode(bool bondable, bool scanner));
  */
 API(API_BLE_GET_SCAN_REPORT, int epic_ble_get_scan_report(struct epic_scan_report *rpt));
 
-API(API_HID_REMOTE_REPORT, void epic_hid_remote_report_event(uint8_t button));
 
-API(API_HID_KEYBOARD_REPORT, void epic_hid_keyboard_report_event(
-	uint8_t modifiers,
-	uint8_t *keys,
-	uint8_t numKeys
-));
-API(API_HID_MOUSE_REPORT, void epic_hid_mouse_report_event(
-	uint8_t buttonMask,
-	int8_t xDisplacement,
-	int8_t yDisplacement
-));
+
+/**
+ * Human Interface Device (HID)
+ * ==========================
+ */
+
+/**
+ * Send an input report to the host.
+ *
+ * :param uint8_t reportId: The id of the report to use. 0: remote, 1: keyboard, 2: mouse
+ * :param uint8_t *data: Data to be reported.
+ * :param uint8_t len: Length in bytes of the data to be reported. Maximum length in 8 bytes.
+ *
+ * :return: `0` on success, `1` if the report is queued or a negative value if an error occured. Possible
+ *    errors:
+ *
+ *    - ``-EIO``: There is no host device connected
+ *    - ``-EAGAIN``: There is no space in the queue available. Try again later.
+ *
+ */
+API(API_HID_SEND_REPORT, int epic_hid_send_report(uint8_t reportId, uint8_t *data, uint8_t len));
+
 #endif /* _EPICARDIUM_H */
 
diff --git a/preload/apps/hid.py b/preload/apps/hid.py
index fdf47a52a0c5d860ac0fe43acb3935d81f37c684..53587f739c25a470e7a322c06cbcb8c9498b5b12 100644
--- a/preload/apps/hid.py
+++ b/preload/apps/hid.py
@@ -24,9 +24,10 @@ while True:
         b_old = b_new
         if b_new == buttons.TOP_RIGHT:
             hid.set_button(BUTTON_VOLUME_UP)
+            hid.set_button(0)
         elif b_new == buttons.BOTTOM_RIGHT:
             hid.set_button(BUTTON_VOLUME_DOWN)
+            hid.set_button(0)
         elif b_new == buttons.BOTTOM_LEFT:
             hid.set_button(BUTTON_PLAY)
-        else:
             hid.set_button(0)
diff --git a/pycardium/modules/hid.c b/pycardium/modules/hid.c
index ba134f6ff19c504504d9277474add8f94a8a634e..25134d5dc9225c2e57d4522a6927e41495be01bf 100644
--- a/pycardium/modules/hid.c
+++ b/pycardium/modules/hid.c
@@ -6,9 +6,10 @@
 
 static mp_obj_t mp_hid_set_button(mp_obj_t button_id)
 {
-	int id = mp_obj_get_int(button_id);
-	epic_hid_remote_report_event(id);
-	return mp_const_none;
+	int id         = mp_obj_get_int(button_id);
+	uint8_t data[] = { id };
+	int ret        = epic_hid_send_report(1, data, sizeof(data));
+	return mp_obj_new_int(ret);
 }
 static MP_DEFINE_CONST_FUN_OBJ_1(hid_set_button_obj, mp_hid_set_button);