diff --git a/stm/Makefile b/stm/Makefile
index dace8cb6cf7ac058170108d5d9b76c0bed564a7f..5ad469d7149b238f472a38b6599f0a4e0e27a0ea 100644
--- a/stm/Makefile
+++ b/stm/Makefile
@@ -75,6 +75,7 @@ SRC_STM = \
 	usbd_usr.c \
 	usbd_desc.c \
 	usbd_pyb_core.c \
+	usbd_pyb_core2.c \
 	usbd_cdc_vcp.c \
 	usbd_msc_bot.c  \
 	usbd_msc_data.c \
diff --git a/stm/lib/usbd_pyb_core.c b/stm/lib/usbd_pyb_core.c
index 9c009a064a3688232f095fe8fed951a18eb6517a..ac51e3ad8e6b6b44d33afafae36745574ec0247f 100644
--- a/stm/lib/usbd_pyb_core.c
+++ b/stm/lib/usbd_pyb_core.c
@@ -73,12 +73,27 @@
 #include "usbd_msc_bot.h"
 #include "usbd_msc_mem.h"
 
-#define USB_PYB_CONFIG_DESC_SIZ (98) // for both CDC VCP and MSC interfaces
+#define USB_PYB_USE_MSC (1)
+
+#if USB_PYB_USE_MSC
 //#define USB_PYB_CONFIG_DESC_SIZ (67) // for only CDC VCP interfaces
+#define USB_PYB_CONFIG_DESC_SIZ (98) // for both CDC VCP and MSC interfaces
+#else // USE_HID
+#define USB_PYB_CONFIG_DESC_SIZ (100) // for both CDC VCP and HID interfaces
+#endif
 
 #define MSC_EPIN_SIZE                MSC_MAX_PACKET
 #define MSC_EPOUT_SIZE               MSC_MAX_PACKET
 
+#define HID_MOUSE_REPORT_DESC_SIZE   (74)
+
+#define HID_DESCRIPTOR_TYPE           0x21
+#define HID_REPORT_DESC               0x22
+
+// HID parameters
+#define HID_IN_EP                   (0x83)
+#define HID_IN_PACKET               (4) /* maximum, and actual, packet size */
+
 /*********************************************
    PYB Device library callbacks
  *********************************************/
@@ -108,8 +123,14 @@ __ALIGN_BEGIN uint8_t APP_Rx_Buffer[APP_RX_DATA_SIZE] __ALIGN_END;
 
 __ALIGN_BEGIN static uint8_t CmdBuff[CDC_CMD_PACKET_SZE] __ALIGN_END;
 
+#if USB_PYB_USE_MSC
 __ALIGN_BEGIN static uint8_t USBD_MSC_MaxLun __ALIGN_END = 0;
 __ALIGN_BEGIN static uint8_t USBD_MSC_AltSet __ALIGN_END = 0;
+#else
+__ALIGN_BEGIN static uint8_t USBD_HID_AltSet __ALIGN_END = 0;
+__ALIGN_BEGIN static uint8_t USBD_HID_Protocol __ALIGN_END = 0;
+__ALIGN_BEGIN static uint8_t USBD_HID_IdleState __ALIGN_END = 0;
+#endif
 
 uint32_t APP_Rx_ptr_in  = 0;
 uint32_t APP_Rx_ptr_out = 0;
@@ -246,6 +267,7 @@ __ALIGN_BEGIN static uint8_t usbd_pyb_CfgDesc[USB_PYB_CONFIG_DESC_SIZ] __ALIGN_E
     HIBYTE(CDC_DATA_MAX_PACKET_SIZE),
     0x00,                               // bInterval: ignore for Bulk transfer
 
+#if USB_PYB_USE_MSC
     //==========================================================================
     // MSC only has 1 interface so doesn't need an IAD
 
@@ -278,8 +300,97 @@ __ALIGN_BEGIN static uint8_t usbd_pyb_CfgDesc[USB_PYB_CONFIG_DESC_SIZ] __ALIGN_E
     LOBYTE(MSC_MAX_PACKET),         // wMaxPacketSize
     HIBYTE(MSC_MAX_PACKET),
     0x00,                           // bInterval: ignore for Bulk transfer
+
+#else
+    //==========================================================================
+    // HID only has 1 interface so doesn't need an IAD
+
+    //--------------------------------------------------------------------------
+    // Interface Descriptor
+    0x09,   // bLength: Interface Descriptor size
+    USB_INTERFACE_DESCRIPTOR_TYPE,      // bDescriptorType: interface descriptor
+    0x02,   // bInterfaceNumber: Number of Interface
+    0x00,   // bAlternateSetting: Alternate setting
+    0x01,   // bNumEndpoints
+    0x03,   // bInterfaceClass: HID Class
+    0x01,   // bInterfaceSubClass: 0=no boot, 1=BOOT
+    0x01,   // nInterfaceProtocol: 0=none, 1=keyboard, 2=mouse
+    0x00,   // iInterface:
+
+    // Descriptor of Joystick Mouse HID
+    0x09,   // bLength: HID Descriptor size
+    HID_DESCRIPTOR_TYPE, // bDescriptorType: HID
+    0x11,   // bcdHID: HID Class Spec release number, low byte
+    0x01,   // bcdHID: high byte
+    0x00,   // bCountryCode: Hardware target country (0=unsupported)
+    0x01,   // bNumDescriptors: Number of HID class descriptors to follow
+    HID_REPORT_DESC,            // bDescriptorType: report
+    HID_MOUSE_REPORT_DESC_SIZE, // wItemLength: Total length of Report descriptor
+    0x00,
+
+    // Endpoint IN descriptor
+    0x07,                           // bLength: Endpoint descriptor length
+    USB_ENDPOINT_DESCRIPTOR_TYPE,   // bDescriptorType: Endpoint descriptor type
+    HID_IN_EP,                      // bEndpointAddress: IN, address of HID
+    0x03,                           // bmAttributes: Interrupt endpoint type
+    LOBYTE(HID_IN_PACKET),          // wMaxPacketSize
+    HIBYTE(HID_IN_PACKET),
+    0x0a,                           // bInterval: polling interval, units of 1ms
+
+#endif
 };
 
+#if 0
+__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END =
+{
+  0x05,   0x01,
+  0x09,   0x02,
+  0xA1,   0x01,
+  0x09,   0x01,
+  
+  0xA1,   0x00,
+  0x05,   0x09,
+  0x19,   0x01,
+  0x29,   0x03,
+  
+  0x15,   0x00,
+  0x25,   0x01,
+  0x95,   0x03,
+  0x75,   0x01,
+  
+  0x81,   0x02,
+  0x95,   0x01,
+  0x75,   0x05,
+  0x81,   0x01,
+  
+  0x05,   0x01,
+  0x09,   0x30,
+  0x09,   0x31,
+  0x09,   0x38,
+  
+  0x15,   0x81,
+  0x25,   0x7F,
+  0x75,   0x08,
+  0x95,   0x03,
+  
+  0x81,   0x06,
+  0xC0,   0x09,
+  0x3c,   0x05,
+  0xff,   0x09,
+  
+  0x01,   0x15,
+  0x00,   0x25,
+  0x01,   0x75,
+  0x01,   0x95,
+  
+  0x02,   0xb1,
+  0x22,   0x75,
+  0x06,   0x95,
+  0x01,   0xb1,
+  
+  0x01,   0xc0
+}; 
+#endif
 
 /** @defgroup usbd_pyb_Private_Functions
   * @{
@@ -333,6 +444,7 @@ static uint8_t usbd_pyb_Init(void *pdev, uint8_t cfgidx) {
                      (uint8_t*)(USB_Rx_Buffer),
                      CDC_DATA_OUT_PACKET_SIZE);
 
+#if USB_PYB_USE_MSC
     //----------------------------------
     // MSC component
 
@@ -351,6 +463,17 @@ static uint8_t usbd_pyb_Init(void *pdev, uint8_t cfgidx) {
     // Init the BOT layer
     MSC_BOT_Init(pdev);
 
+#else
+    //----------------------------------
+    // HID component
+
+    // Open EP IN
+    DCD_EP_Open(pdev,
+                HID_IN_EP,
+                HID_IN_PACKET,
+                USB_OTG_EP_INT);
+#endif
+
     return USBD_OK;
 }
 
@@ -372,6 +495,7 @@ static uint8_t usbd_pyb_DeInit(void *pdev, uint8_t cfgidx) {
     // Restore default state of the Interface physical components
     VCP_fops.pIf_DeInit();
 
+#if USB_PYB_USE_MSC
     //----------------------------------
     // MSC component
 
@@ -382,12 +506,27 @@ static uint8_t usbd_pyb_DeInit(void *pdev, uint8_t cfgidx) {
     // Un Init the BOT layer
     MSC_BOT_DeInit(pdev);
 
+#else
+    //----------------------------------
+    // HID component
+
+    // Close HID EP
+    DCD_EP_Close(pdev, HID_IN_EP);
+#endif
+
     return USBD_OK;
 }
 
 #define BOT_GET_MAX_LUN              0xFE
 #define BOT_RESET                    0xFF
 
+#define HID_REQ_SET_PROTOCOL         (0x0B)
+#define HID_REQ_GET_PROTOCOL         (0x03)
+#define HID_REQ_SET_IDLE             (0x0A)
+#define HID_REQ_GET_IDLE             (0x02)
+#define HID_REQ_SET_REPORT           (0x09) // used?
+#define HID_REQ_GET_REPORT           (0x01) // used?
+
 /**
   * @brief  usbd_pyb_Setup
   *         Handle the CDC specific requests
@@ -411,6 +550,7 @@ static uint8_t usbd_pyb_Setup(void *pdev, USB_SETUP_REQ *req) {
                         len = MIN(USB_CDC_DESC_SIZ, req->wLength); // TODO
                     }
                     return USBD_CtlSendData(pdev, pbuf, len);
+                    // TODO stuff here for HID, using HID_MOUSE_ReportDesc
                 }
             }
             */
@@ -424,7 +564,11 @@ static uint8_t usbd_pyb_Setup(void *pdev, USB_SETUP_REQ *req) {
                     if ((req->wIndex & 0xff) <= 1) {
                         return USBD_CtlSendData(pdev, &usbd_cdc_AltSet, 1);
                     } else {
+#if USB_PYB_USE_MSC
                         return USBD_CtlSendData(pdev, &USBD_MSC_AltSet, 1);
+#else
+                        return USBD_CtlSendData(pdev, &USBD_HID_AltSet, 1);
+#endif
                     }
 
                 case USB_REQ_SET_INTERFACE:
@@ -432,7 +576,11 @@ static uint8_t usbd_pyb_Setup(void *pdev, USB_SETUP_REQ *req) {
                         if ((req->wIndex & 0xff) <= 1) {
                             usbd_cdc_AltSet = req->wValue;
                         } else {
+#if USB_PYB_USE_MSC
                             USBD_MSC_AltSet = req->wValue;
+#else
+                            USBD_HID_AltSet = req->wValue;
+#endif
                         }
                         return USBD_OK;
                     }
@@ -442,6 +590,7 @@ static uint8_t usbd_pyb_Setup(void *pdev, USB_SETUP_REQ *req) {
         // Standard Endpoint Request -------------------------------------------
         case (USB_REQ_TYPE_STANDARD | USB_REQ_RECIPIENT_ENDPOINT):
             // req->wIndex is the endpoint number, including direction
+#if USB_PYB_USE_MSC
             if (req->wIndex == MSC_IN_EP || req->wIndex == MSC_OUT_EP) {
                 // MSC component
                 switch (req->bRequest) {
@@ -464,6 +613,7 @@ static uint8_t usbd_pyb_Setup(void *pdev, USB_SETUP_REQ *req) {
                         return USBD_OK;
                 }
             }
+#endif
             break;
 
         // CDC Class Requests ------------------------------
@@ -502,6 +652,7 @@ static uint8_t usbd_pyb_Setup(void *pdev, USB_SETUP_REQ *req) {
                 }
 
             } else if (req->wIndex == 2) {
+#if USB_PYB_USE_MSC
                 // MSC component
                 switch (req->bRequest) {
                     case BOT_GET_MAX_LUN:
@@ -520,6 +671,24 @@ static uint8_t usbd_pyb_Setup(void *pdev, USB_SETUP_REQ *req) {
                         }
                         break;
                 }
+#else
+                // HID component
+                switch (req->bRequest) {
+                    case HID_REQ_SET_PROTOCOL:
+                        USBD_HID_Protocol = req->wValue;
+                        return USBD_OK;
+
+                    case HID_REQ_GET_PROTOCOL:
+                        return USBD_CtlSendData(pdev, &USBD_HID_Protocol, 1);
+
+                    case HID_REQ_SET_IDLE:
+                        USBD_HID_IdleState = (req->wValue >> 8);
+                        return USBD_OK;
+
+                    case HID_REQ_GET_IDLE:
+                        return USBD_CtlSendData(pdev, &USBD_HID_IdleState, 1);
+                }
+#endif
             }
             break;
     }
@@ -590,9 +759,18 @@ static uint8_t usbd_pyb_DataIn(void *pdev, uint8_t epnum) {
             }
             return USBD_OK;
 
+#if USB_PYB_USE_MSC
         case (MSC_IN_EP & 0x7f): // TODO?
             MSC_BOT_DataIn(pdev, epnum);
             return USBD_OK;
+
+#else
+        case (HID_IN_EP & 0x7f):
+            /* Ensure that the FIFO is empty before a new transfer, this condition could
+            be caused by  a new transfer before the end of the previous transfer */
+            DCD_EP_Flush(pdev, HID_IN_EP);
+            return USBD_OK;
+#endif
     }
 
     printf("DI %x\n", epnum);
@@ -626,9 +804,11 @@ static uint8_t usbd_pyb_DataOut(void *pdev, uint8_t epnum) {
                              CDC_DATA_OUT_PACKET_SIZE);
             return USBD_OK;
 
+#if USB_PYB_USE_MSC
         case (MSC_OUT_EP & 0x7f): // TODO is this correct?
             MSC_BOT_DataOut(pdev, epnum);
             return USBD_OK;
+#endif
     }
 
     printf("DO %x\n", epnum);
@@ -733,3 +913,19 @@ static uint8_t *usbd_pyb_GetCfgDesc(uint8_t speed, uint16_t *length) {
     *length = sizeof(usbd_pyb_CfgDesc);
     return usbd_pyb_CfgDesc;
 }
+
+/**
+  * @brief  USBD_HID_SendReport
+  *         Send HID Report
+  * @param  pdev: device instance
+  * @param  buff: pointer to report (4 bytes: ?, x, y, ?)
+  * @retval status
+  */
+/*
+uint8_t USBD_HID_SendReport(USB_OTG_CORE_HANDLE *pdev, uint8_t *report, uint16_t len) {
+    if (pdev->dev.device_status == USB_OTG_CONFIGURED) {
+        DCD_EP_Tx(pdev, HID_IN_EP, report, len);
+    }
+    return USBD_OK;
+}
+*/
diff --git a/stm/lib/usbd_pyb_core.h b/stm/lib/usbd_pyb_core.h
index e4cdb1fc0a1de95748cb87083272c08f66b50d81..761ca225330953d866c3fc65686fee9e30a7cce0 100644
--- a/stm/lib/usbd_pyb_core.h
+++ b/stm/lib/usbd_pyb_core.h
@@ -1 +1,4 @@
 extern USBD_Class_cb_TypeDef USBD_PYB_cb;
+extern USBD_Class_cb_TypeDef USBD_PYB_HID_cb;
+
+uint8_t USBD_HID_SendReport(USB_OTG_CORE_HANDLE *pdev, uint8_t *report, uint16_t len);
diff --git a/stm/lib/usbd_pyb_core2.c b/stm/lib/usbd_pyb_core2.c
new file mode 100644
index 0000000000000000000000000000000000000000..c1abda3e74475b21d73fa063a0eae09fdb796416
--- /dev/null
+++ b/stm/lib/usbd_pyb_core2.c
@@ -0,0 +1,323 @@
+#include <stdio.h>
+
+#include "usbd_ioreq.h"
+#include "usbd_desc.h"
+#include "usbd_req.h"
+#include "usbd_pyb_core.h"
+
+#define USB_PYB_CONFIG_DESC_SIZ (34)
+
+#define HID_MOUSE_REPORT_DESC_SIZE (74)
+
+#define HID_DESCRIPTOR_TYPE (0x21)
+#define HID_REPORT_DESC     (0x22)
+
+#define HID_IN_EP           (0x81)
+#define HID_IN_PACKET       (4) /* maximum, and actual, packet size */
+
+/*********************************************
+   PYB Device library callbacks
+ *********************************************/
+
+static uint8_t usbd_pyb_Init   (void *pdev, uint8_t cfgidx);
+static uint8_t usbd_pyb_DeInit (void *pdev, uint8_t cfgidx);
+static uint8_t usbd_pyb_Setup  (void *pdev, USB_SETUP_REQ *req);
+static uint8_t usbd_pyb_DataIn (void *pdev, uint8_t epnum);
+
+/*********************************************
+   PYB specific management functions
+ *********************************************/
+
+static uint8_t *usbd_pyb_GetCfgDesc(uint8_t speed, uint16_t *length);
+
+__ALIGN_BEGIN static uint8_t USBD_HID_AltSet __ALIGN_END = 0;
+__ALIGN_BEGIN static uint8_t USBD_HID_Protocol __ALIGN_END = 0;
+__ALIGN_BEGIN static uint8_t USBD_HID_IdleState __ALIGN_END = 0;
+
+/* PYB interface class callbacks structure */
+USBD_Class_cb_TypeDef USBD_PYB_HID_cb =
+{
+    usbd_pyb_Init,
+    usbd_pyb_DeInit,
+    usbd_pyb_Setup,
+    NULL, // EP0_TxSent
+    NULL, // usbd_pyb_EP0_RxReady,
+    usbd_pyb_DataIn,
+    NULL, // usbd_pyb_DataOut,
+    NULL, // usbd_pyb_SOF,
+    NULL, // IsoINIncomplete
+    NULL, // IsoOUTIncomplete
+    usbd_pyb_GetCfgDesc,
+    // for OTG_HS support need to add other cfg desc here
+};
+
+/* USB PYB device Configuration Descriptor */
+__ALIGN_BEGIN static uint8_t usbd_pyb_CfgDesc[USB_PYB_CONFIG_DESC_SIZ] __ALIGN_END =
+{
+    //--------------------------------------------------------------------------
+    // Configuration Descriptor
+    0x09,   // bLength: Configuration Descriptor size
+    USB_CONFIGURATION_DESCRIPTOR_TYPE,      // bDescriptorType: Configuration
+    LOBYTE(USB_PYB_CONFIG_DESC_SIZ),        // wTotalLength: no of returned bytes
+    HIBYTE(USB_PYB_CONFIG_DESC_SIZ),
+    0x01,   // bNumInterfaces: 1 interfaces
+    0x01,   // bConfigurationValue: Configuration value
+    0x04,   // iConfiguration: Index of string descriptor describing the configuration
+    0x80,   // bmAttributes: bus powered; 0xc0 for self powered
+    0xfa,   // bMaxPower: in units of 2mA
+
+    //--------------------------------------------------------------------------
+    // Interface Descriptor
+    0x09,   // bLength: Interface Descriptor size
+    USB_INTERFACE_DESCRIPTOR_TYPE,      // bDescriptorType: interface descriptor
+    0x00,   // bInterfaceNumber: Number of Interface
+    0x00,   // bAlternateSetting: Alternate setting
+    0x01,   // bNumEndpoints
+    0x03,   // bInterfaceClass: HID Class
+    0x01,   // bInterfaceSubClass: 0=no boot, 1=BOOT
+    0x02,   // nInterfaceProtocol: 0=none, 1=keyboard, 2=mouse
+    0x00,   // iInterface:
+
+    // Descriptor of Joystick Mouse HID
+    0x09,   // bLength: HID Descriptor size
+    HID_DESCRIPTOR_TYPE, // bDescriptorType: HID
+    0x11,   // bcdHID: HID Class Spec release number, low byte
+    0x01,   // bcdHID: high byte
+    0x00,   // bCountryCode: Hardware target country (0=unsupported)
+    0x01,   // bNumDescriptors: Number of HID class descriptors to follow
+    HID_REPORT_DESC,            // bDescriptorType: report
+    HID_MOUSE_REPORT_DESC_SIZE, // wItemLength: Total length of Report descriptor
+    0x00,
+
+    // Endpoint IN descriptor
+    0x07,                           // bLength: Endpoint descriptor length
+    USB_ENDPOINT_DESCRIPTOR_TYPE,   // bDescriptorType: Endpoint descriptor type
+    HID_IN_EP,                      // bEndpointAddress: IN, address of HID
+    0x03,                           // bmAttributes: Interrupt endpoint type
+    LOBYTE(HID_IN_PACKET),          // wMaxPacketSize
+    HIBYTE(HID_IN_PACKET),
+    0x0a,                           // bInterval: polling interval, units of 1ms
+};
+
+__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END =
+{
+  0x05,   0x01,
+  0x09,   0x02,
+  0xA1,   0x01,
+  0x09,   0x01,
+  
+  0xA1,   0x00,
+  0x05,   0x09,
+  0x19,   0x01,
+  0x29,   0x03,
+  
+  0x15,   0x00,
+  0x25,   0x01,
+  0x95,   0x03,
+  0x75,   0x01,
+  
+  0x81,   0x02,
+  0x95,   0x01,
+  0x75,   0x05,
+  0x81,   0x01,
+  
+  0x05,   0x01,
+  0x09,   0x30,
+  0x09,   0x31,
+  0x09,   0x38,
+  
+  0x15,   0x81,
+  0x25,   0x7F,
+  0x75,   0x08,
+  0x95,   0x03,
+  
+  0x81,   0x06,
+  0xC0,   0x09,
+  0x3c,   0x05,
+  0xff,   0x09,
+  
+  0x01,   0x15,
+  0x00,   0x25,
+  0x01,   0x75,
+  0x01,   0x95,
+  
+  0x02,   0xb1,
+  0x22,   0x75,
+  0x06,   0x95,
+  0x01,   0xb1,
+  
+  0x01,   0xc0
+}; 
+
+/**
+  * @brief  usbd_pyb_Init
+  *         Initilaize the PYB interface
+  * @param  pdev: device instance
+  * @param  cfgidx: Configuration index
+  * @retval status
+  */
+static uint8_t usbd_pyb_Init(void *pdev, uint8_t cfgidx) {
+    // deinit first to reset
+    usbd_pyb_DeInit(pdev, cfgidx);
+
+    // Open EP IN
+    DCD_EP_Open(pdev,
+                HID_IN_EP,
+                HID_IN_PACKET,
+                USB_OTG_EP_INT);
+
+    return USBD_OK;
+}
+
+/**
+  * @brief  usbd_pyb_Init
+  *         DeInitialize the CDC layer
+  * @param  pdev: device instance
+  * @param  cfgidx: Configuration index
+  * @retval status
+  */
+static uint8_t usbd_pyb_DeInit(void *pdev, uint8_t cfgidx) {
+    // Close HID EP
+    DCD_EP_Close(pdev, HID_IN_EP);
+
+    return USBD_OK;
+}
+
+#define HID_REQ_SET_PROTOCOL         (0x0B)
+#define HID_REQ_GET_PROTOCOL         (0x03)
+#define HID_REQ_SET_IDLE             (0x0A)
+#define HID_REQ_GET_IDLE             (0x02)
+#define HID_REQ_SET_REPORT           (0x09) // used?
+#define HID_REQ_GET_REPORT           (0x01) // used?
+
+/**
+  * @brief  usbd_pyb_Setup
+  *         Handle the CDC specific requests
+  * @param  pdev: instance
+  * @param  req: usb requests
+  * @retval status
+  */
+static uint8_t usbd_pyb_Setup(void *pdev, USB_SETUP_REQ *req) {
+    switch (req->bmRequest & (USB_REQ_TYPE_MASK | USB_REQ_RECIPIENT_MASK)) {
+
+        // Standard Device Request ---------------------------------------------
+        case (USB_REQ_TYPE_STANDARD | USB_REQ_RECIPIENT_DEVICE):
+            break;
+
+        // Standard Interface Request ------------------------------------------
+        case (USB_REQ_TYPE_STANDARD | USB_REQ_RECIPIENT_INTERFACE):
+            switch (req->bRequest) {
+                case USB_REQ_GET_DESCRIPTOR: // needed for HID; SU 0x81 0x06 0x2200 0x00 request
+                    // wIndex & 0xff is the interface
+                    if ((req->wIndex & 0xff) == 0) {
+                          uint16_t len = 0;
+                          uint8_t  *pbuf = NULL;
+                          if( req->wValue >> 8 == HID_REPORT_DESC) {
+                            len = MIN(HID_MOUSE_REPORT_DESC_SIZE , req->wLength);
+                            pbuf = HID_MOUSE_ReportDesc;
+                              return USBD_CtlSendData (pdev, pbuf, len);
+                          } else if( req->wValue >> 8 == HID_DESCRIPTOR_TYPE) {
+                            pbuf = usbd_pyb_CfgDesc + 0x09 + 0x09;
+                            len = MIN(0x09 , req->wLength);
+                              return USBD_CtlSendData (pdev, pbuf, len);
+                          }
+                    }
+
+                case USB_REQ_GET_INTERFACE:
+                    // wIndex & 0xff is the interface
+                    if ((req->wIndex & 0xff) == 0) {
+                        return USBD_CtlSendData(pdev, &USBD_HID_AltSet, 1);
+                    }
+
+                case USB_REQ_SET_INTERFACE:
+                    if ((uint8_t)(req->wValue) < USBD_ITF_MAX_NUM) { // TODO
+                        if ((req->wIndex & 0xff) == 0) {
+                            USBD_HID_AltSet = req->wValue;
+                        }
+                        return USBD_OK;
+                    }
+            }
+            break;
+
+        // Standard Endpoint Request -------------------------------------------
+        case (USB_REQ_TYPE_STANDARD | USB_REQ_RECIPIENT_ENDPOINT):
+            // req->wIndex is the endpoint number, including direction
+            break;
+
+        // CDC Class Requests ------------------------------
+        case (USB_REQ_TYPE_CLASS | USB_REQ_RECIPIENT_INTERFACE):
+            // req->wIndex is the recipient interface number
+            if (req->wIndex == 0) {
+                // HID component
+                switch (req->bRequest) {
+                    case HID_REQ_SET_PROTOCOL:
+                        USBD_HID_Protocol = req->wValue;
+                        return USBD_OK;
+
+                    case HID_REQ_GET_PROTOCOL:
+                        return USBD_CtlSendData(pdev, &USBD_HID_Protocol, 1);
+
+                    case HID_REQ_SET_IDLE:
+                        USBD_HID_IdleState = (req->wValue >> 8);
+                        return USBD_OK;
+
+                    case HID_REQ_GET_IDLE:
+                        return USBD_CtlSendData(pdev, &USBD_HID_IdleState, 1);
+                }
+            }
+            break;
+    }
+
+    printf("SU %x %x %x %x\n", req->bmRequest, req->bRequest, req->wValue, req->wIndex);
+
+    // invalid command
+    USBD_CtlError(pdev, req);
+    return USBD_FAIL;
+}
+
+/**
+  * @brief  usbd_pyb_DataIn
+  *         Data sent on non-control IN endpoint
+  * @param  pdev: device instance
+  * @param  epnum: endpoint number
+  * @retval status
+  */
+static uint8_t usbd_pyb_DataIn(void *pdev, uint8_t epnum) {
+    switch (epnum) {
+        case (HID_IN_EP & 0x7f):
+            /* Ensure that the FIFO is empty before a new transfer, this condition could
+            be caused by  a new transfer before the end of the previous transfer */
+            DCD_EP_Flush(pdev, HID_IN_EP);
+            return USBD_OK;
+    }
+
+    printf("DI %x\n", epnum);
+
+    return USBD_OK;
+}
+
+/**
+  * @brief  usbd_pyb_GetCfgDesc 
+  *         Return configuration descriptor
+  * @param  speed : current device speed
+  * @param  length : pointer data length
+  * @retval pointer to descriptor buffer
+  */
+static uint8_t *usbd_pyb_GetCfgDesc(uint8_t speed, uint16_t *length) {
+    *length = sizeof(usbd_pyb_CfgDesc);
+    return usbd_pyb_CfgDesc;
+}
+
+/**
+  * @brief  USBD_HID_SendReport
+  *         Send HID Report
+  * @param  pdev: device instance
+  * @param  buff: pointer to report (4 bytes: ?, x, y, ?)
+  * @retval status
+  */
+uint8_t USBD_HID_SendReport(USB_OTG_CORE_HANDLE *pdev, uint8_t *report, uint16_t len) {
+    if (pdev->dev.device_status == USB_OTG_CONFIGURED) {
+        DCD_EP_Tx(pdev, HID_IN_EP, report, len);
+    }
+    return USBD_OK;
+}
diff --git a/stm/main.c b/stm/main.c
index 5dc03bc69d008d0f24ed111a3fe9d8a0948c3234..ffefbb64c742872f10232384f2476b4a0a817bfa 100644
--- a/stm/main.c
+++ b/stm/main.c
@@ -451,6 +451,17 @@ py_obj_t pyb_mma_read() {
     return rt_build_tuple(4, data); // items in reverse order in data
 }
 
+py_obj_t pyb_hid_send_report(py_obj_t arg) {
+    py_obj_t *items = py_get_array_fixed_n(arg, 4);
+    uint8_t data[4];
+    data[0] = py_get_int(items[0]);
+    data[1] = py_get_int(items[1]);
+    data[2] = py_get_int(items[2]);
+    data[3] = py_get_int(items[3]);
+    usb_hid_send_report(data);
+    return py_const_none;
+}
+
 int main(void) {
     // TODO disable JTAG
 
@@ -514,6 +525,7 @@ soft_reset:
         rt_store_attr(m, qstr_from_str_static("sw"), rt_make_function_0(pyb_sw));
         rt_store_attr(m, qstr_from_str_static("servo"), rt_make_function_1(pyb_servo_set));
         rt_store_attr(m, qstr_from_str_static("mma"), rt_make_function_0(pyb_mma_read));
+        rt_store_attr(m, qstr_from_str_static("hid"), rt_make_function_1(pyb_hid_send_report));
         rt_store_name(qstr_from_str_static("pyb"), m);
     }
 
@@ -814,6 +826,35 @@ soft_reset:
         }
     }
 
+    // HID example
+    if (0) {
+        uint8_t data[4];
+        data[0] = 0;
+        data[1] = 1;
+        data[2] = -2;
+        data[3] = 0;
+        for (;;) {
+            if (sw_get()) {
+                data[0] = 0x01; // 0x04 is middle, 0x02 is right
+            } else {
+                data[0] = 0x00;
+            }
+            mma_start(MMA_ADDR, 1);
+            mma_send_byte(0);
+            mma_restart(MMA_ADDR, 0);
+            for (int i = 0; i <= 1; i++) {
+                int v = mma_read_ack() & 0x3f;
+                if (v & 0x20) {
+                    v |= ~0x1f;
+                }
+                data[1 + i] = v;
+            }
+            mma_read_nack();
+            usb_hid_send_report(data);
+            sys_tick_delay_ms(15);
+        }
+    }
+
     do_repl();
 
     // benchmark C version of impl02.py
diff --git a/stm/printf.c b/stm/printf.c
index 3ccdd7084ba813694e8cd284063162fe244ddb41..4c178b94becd0634ed1d5d8bc8bce6cd8b882adb 100644
--- a/stm/printf.c
+++ b/stm/printf.c
@@ -1,3 +1,4 @@
+#include <stdint.h>
 #include <stdarg.h>
 #include "std.h"
 #include "misc.h"
diff --git a/stm/usb.c b/stm/usb.c
index 97e96c35e8e3adcfad4bbf578ca54f4bcae576bb..b0fbfa19415a0ad3f73b78501226823358f15cf7 100644
--- a/stm/usb.c
+++ b/stm/usb.c
@@ -23,6 +23,7 @@ void usb_init(void) {
     if (!is_enabled) {
         // only init USB once in the device's power-lifetime
         USBD_Init(&USB_OTG_dev, USB_OTG_FS_CORE_ID, &USR_desc, &USBD_PYB_cb, &USR_cb);
+        //USBD_Init(&USB_OTG_dev, USB_OTG_FS_CORE_ID, &USR_desc, &USBD_PYB_HID_cb, &USR_cb);
     }
     rx_buf_in = 0;
     rx_buf_out = 0;
@@ -100,3 +101,7 @@ void usb_vcp_send_strn_cooked(const char *str, int len) {
         APP_Rx_ptr_in = (APP_Rx_ptr_in + 1) & (APP_RX_DATA_SIZE - 1);
     }
 }
+
+void usb_hid_send_report(uint8_t *buf) {
+    USBD_HID_SendReport(&USB_OTG_dev, buf, 4);
+}
diff --git a/stm/usb.h b/stm/usb.h
index 74041043e67bdd469daaf1688019b8010dfd61e2..c4b3b151fb540f600e89a63de6e661af4521b480 100644
--- a/stm/usb.h
+++ b/stm/usb.h
@@ -5,3 +5,4 @@ char usb_vcp_rx_get(void);
 void usb_vcp_send_str(const char* str);
 void usb_vcp_send_strn(const char* str, int len);
 void usb_vcp_send_strn_cooked(const char *str, int len);
+void usb_hid_send_report(uint8_t *buf); // 4 bytes for mouse: ?, x, y, ?