cancel
Showing results for 
Search instead for 
Did you mean: 

[USB Host] Keyboard Output Report - How to control the 3 LEDs? (NumLock, Caps Lock and Scroll Lock status)

Not applicable

Could anyone tell how to add this functionality in the USB Host library for STM32CubeIDE?

I found this:

u8 c=3;
 
USBH_StatusTypeDef res = USBH_BUSY;
 
do {
res=USBH_Set_Report(&USB_OTG_Host_Hnd,&USB_Host,0x02,0x00,0x01,&c);
} while(res != USBH_OK);

Source: https://community.st.com/s/question/0D50X00009XkZ11/caps-lock-led-turn-onoff

I didn't find this function (USBH_Set_Report) in the library, I found only USBH_HID_SetReport, but I couldn't implement that.

Is there any ST document that talks about this?

I found a document written by Microchip:

A  standard  keyboard  has  three  LEDs  to  display  NumLock, Caps Lock and Scroll Lock status. The LEDs are absolute  output  items;  the  state  of  each  LED  must  be included in the output reports (0 = OFF, 1 = ON)

Source: http://ww1.microchip.com/downloads/en/AppNotes/01212a.pdf

1 ACCEPTED SOLUTION

Accepted Solutions
Not applicable

Okay, that worked for me, maybe there's a better way to do it, but I have other things going on:

usb_host.c:

uint32_t keyboardMain_debounce = 0;
HID_KEYBD_Info_TypeDef *keybd_info1;
 
uint8_t kbd_LED_status[1] = { 0B001 }; // kbd_num_lock_state = 1
uint8_t kbd_LED_status_old = 0;
 
uint8_t kbd_num_lock_state = 1;
uint8_t caps_lock_state = 0;
uint8_t kbd_scroll_lock_state = 0;
 
void USBH_HID_EventCallback(USBH_HandleTypeDef *phost) {
	HID_HandleTypeDef *HID_Handle =
			(HID_HandleTypeDef*) phost->pActiveClass->pData;
	if (HID_Handle->Init == USBH_HID_KeybdInit) {
		keybd_info1 = USBH_HID_GetKeybdInfo(phost);
 
		USBH_DbgLog("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x",
				keybd_info1->keys[0], keybd_info1->keys[1],
				keybd_info1->keys[2], keybd_info1->keys[3],
				keybd_info1->keys[4], keybd_info1->keys[5]); // USBH_DbgLog
 
		if (HAL_GetTick() > (keyboardMain_debounce + 100)) {
			keyboardMain_debounce = HAL_GetTick();
 
			uint8_t key = keybd_info1->keys[0];
 
			if (key == KEY_INSERT) {
				keyboard_insert = !keyboard_insert;
			} else if (key == KEY_KEYPAD_NUM_LOCK_AND_CLEAR) {
				if ((kbd_LED_status[0] & 1) == 0) {
					kbd_LED_status[0] |= 0B001;
				} else {
					kbd_LED_status[0] &= 0B110;
				}
			} else if (key == KEY_CAPS_LOCK) {
				if (((kbd_LED_status[0] >> 1) & 1) == 0) {
					kbd_LED_status[0] |= 0B010;
				} else {
					kbd_LED_status[0] &= 0B101;
				}
			} else if (key == KEY_SCROLL_LOCK) {
				if (((kbd_LED_status[0] >> 2) & 1) == 0) {
					kbd_LED_status[0] |= 0B100;
				} else {
					kbd_LED_status[0] &= 0B011;
				}
			}
		}
 
	} else if (HID_Handle->Init == USBH_HID_MouseInit) {
		USBH_HID_GetMouseInfo(phost);
 
		USBH_DbgLog(
				"Mouse action: x=  0x%x, y=  0x%x, button1 = 0x%x, button2 = 0x%x, button3 = 0x%x \n",
				mouse_info.x, mouse_info.y, mouse_info.buttons[0],
				mouse_info.buttons[1], mouse_info.buttons[2]);
	}
}

usb_host.c:

/* USER CODE BEGIN 1 */
USBH_StatusTypeDef USB_Set_Keyboard_LED_Status(uint8_t *LED_status) {
	if (kbd_LED_status_old == kbd_LED_status[0]) {
		return HAL_OK;
	}
 
	kbd_LED_status_old = LED_status[0];
 
	uint8_t kbd_led[2] = { 0x00, 0x00 };
 
	USBH_StatusTypeDef ret = HAL_BUSY;
	USBH_StatusTypeDef ret2 = HAL_OK;
 
	kbd_led[0] = LED_status[0] & 7;
 
	for (uint8_t timeout = 0; timeout < 15; timeout++) {
		ret = USBH_HID_SetReport(&hUsbHostFS, 0x02U, 0U, kbd_led,
				sizeof(kbd_led));
 
		UsrLog("USBH_HID_SetReport return: %d", ret);
		IWDG_delay_ms(1);
 
		if (ret == ret2) {
			break;
		}
	}
 
//	ret = HAL_BUSY;
//	ret2 = HAL_OK;
 
	for (uint8_t timeout = 0; timeout < 15; timeout++) {
		ret = USBH_HID_GetReport(&hUsbHostFS, 0x02U, 0U, kbd_led,
				sizeof(kbd_led));
 
		UsrLog("USBH_HID_GetReport return: %d; kbd_led: %d", ret, kbd_led[0]);
		IWDG_delay_ms(1);
 
		if (ret == ret2) {
			break;
		}
	}
 
	if (kbd_led[0] == (LED_status[0] & 7)) {
		ret = HAL_OK;
	} else {
		ret = HAL_ERROR;
	}
 
	LED_status[0] = kbd_led[0];
 
	if ((kbd_LED_status[0] & 1) == 1) {
		kbd_num_lock_state = 1;
	} else {
		kbd_num_lock_state = 0;
	}
 
	if (((kbd_LED_status[0] >> 1) & 1) == 1) {
		caps_lock_state = 1;
	} else {
		caps_lock_state = 0;
	}
 
	if (((kbd_LED_status[0] >> 2) & 1) == 1) {
		kbd_scroll_lock_state = 1;
	} else {
		kbd_scroll_lock_state = 0;
	}
 
	return ret;
}

usb_host.c:

static void USBH_UserProcess2(USBH_HandleTypeDef *phost, uint8_t id) {
	/* USER CODE BEGIN CALL_BACK_21 */
	switch (id) {
	case HOST_USER_SELECT_CONFIGURATION:
		break;
 
	case HOST_USER_DISCONNECTION:
		Appli_state = APPLICATION_DISCONNECT;
 
		Appli_state_FS = Appli_state;
 
		kbd_LED_status_old = 99; // <--------- to update keyboard if reconnected

 usbh_core.c:

uint32_t keyboard_led_status_tick = 0;
 
/**
 * @brief  USBH_Process
 *         Background process of the USB Core.
 * @param  phost: Host Handle
 * @retval USBH Status
 */
USBH_StatusTypeDef USBH_Process(USBH_HandleTypeDef *phost) {
[...]
	case HOST_CLASS:
		/* process class state machine */
		if (phost->pActiveClass != NULL) {
			/* Update keyboard LED status */
			if (HAL_GetTick() > (keyboard_led_status_tick + 10)) {
				keyboard_led_status_tick = HAL_GetTick();
				HID_HandleTypeDef *HID_Handle =
						(HID_HandleTypeDef*) phost->pActiveClass->pData;
				if (HID_Handle->Init == USBH_HID_KeybdInit) {
					USB_Set_Keyboard_LED_Status();
				}
			}
 
			phost->pActiveClass->BgndProcess(phost);
		}
		break;
 
	case HOST_DEV_DISCONNECTED:
[...]

View solution in original post

12 REPLIES 12
Not applicable
Not applicable

I found another reference

"In the following example the function is used to turn on the NUM LOCK, CAPS LOCK and SCROLL LOCK LEDs on a keyboard. The HID report is sent over the control endpoint. The _OnCompletion callback function is called at the end of data transfer."

/*********************************************************************
*
* _TurnOnKeyboardLEDs
*
* Function description
* Turns on NUM LOCK, CAPS LOCK and SCROLL LOCK LEDs on a keyboard.
*
* Parameters
* hInterface Handle to a HID device
*/
static void _TurnOnKeyboardLEDs(USBH_INTERFACE_HANDLE hInterface) {
 USBH_URB Urb;
 U8 LedState;
 LedState = 0x07;
 Urb.Header.pContext = NULL;
 Urb.Header.Function = USBH_FUNCTION_CONTROL_REQUEST;
 Urb.Header.pfOnCompletion = _OnCompletion;
 Urb.Request.ControlRequest.Setup.Type = 0x21;
 Urb.Request.ControlRequest.Setup.Request = 0x09;
 Urb.Request.ControlRequest.Setup.Value = 0x0200;
 Urb.Request.ControlRequest.Setup.Index = 0;
 Urb.Request.ControlRequest.Setup.Length = 1;
 Urb.Request.ControlRequest.pBuffer = &LedState;
 Urb.Request.ControlRequest.Length = 1;
 USBH_SubmitUrb(hInterface, &Urb);
}

http://www.farnell.com/datasheets/1912494.pdf

Not applicable

I found another reference:

"you should be able to send 8 bytes to the keyboard using the "USBHost_Send".

Note that sends data to endpoint, It is non blocking function. After calling it, user should poll transaction status on the endpoint with USBHost_GetTransactionStatus.

Also, on the page.59 you should find how the Report should look:

(Not found) http://www.usb.org/developers/hidpage/HID1_11.pdf"

Source: https://forum.mikroe.com/viewtopic.php?t=68414

(New link) https://www.usb.org/sites/default/files/documents/hid1_11.pdf

Not applicable

I found another reference:

"Hi anyone looking at this thread, the USBH_HID_SetReport can be used to control the LED status of a keyboard from a host device. Unfortunately there must have been some issues with the compatibility between STM Cube and IAR as when I have tried the same code I was trying with IAR on True Studio the function works. I have asked ST if this is a bug."

https://community.st.com/s/question/0D50X0000B0yHZj/i-am-struggling-with-how-and-where-to-implement-the-led-controls-caps-lock-etc-of-a-keyboard-as-far-as-i-understand-it-this-is-accomplished-through-a-usbhhidsetreportphost-0x02-0x00-rep-0x01-please-help

Not applicable

I managed to make it work, but I don't know why it's working this way, I understood that I should send data in another way, based on this document, on page 79 (this information doesn't seem to have much explanation on the Internet):

https://www.usb.org/sites/default/files/documents/hid1_11.pdf

I saw a lot of people suggesting things but it seems that nobody had been able to do with STM32 and USB Host using STM32CubeIDE,

I hope it's revised and incorporated in a sample code, it's unfortunate that a company like ST can read the keys and can't give an example of how to light an LED on the keyboard.

usb_host.c:

// 0B001:  NumLock
// 0B010:  Caps Lock
// 0B100:  Scroll Lock
// 0B111:  NumLock, Caps Lock and Scroll Lock
uint8_t LED_status = 7; // 7 = 0B111
uint8_t LED_status_old = 0;
 
USBH_StatusTypeDef USB_Set_Keyboard_LED_Status(void) {
	if (LED_status_old == LED_status) {
		return HAL_OK;
	}
 
	LED_status_old = LED_status;
 
	uint8_t kbd_led[2] = { 0x00, 0x00 };
 
	USBH_StatusTypeDef ret = HAL_BUSY;
	USBH_StatusTypeDef ret2 = HAL_OK;
 
	kbd_led[0] = LED_status & 7;
 
	for(uint8_t timeout = 0; timeout < 100; timeout++) {
		ret = USBH_HID_SetReport(&hUsbHostFS, 0x02U, 0U, kbd_led, sizeof(kbd_led));
 
		// 7x ret=1 ???
		USBH_DbgLog("USBH_HID_SetReport return: %d", ret);
 
		if(ret == ret2){
			break;
		}
	}
 
	/* This part reads the memory that is on the keyboard. */
	for (uint8_t timeout = 0; timeout < 15; timeout++) {
		ret = USBH_HID_GetReport(&hUsbHostFS, 0x02U, 0U, kbd_led,
				sizeof(kbd_led));
 
		UsrLog("USBH_HID_GetReport return: %d; kbd_led: %d", ret, kbd_led[0]);
		HAL_Delay(1);
 
		if (ret == ret2) {
			break;
		}
	}
 
	if (kbd_led[0] == (LED_status & 7)) {
		return HAL_OK;
	} else {
		return HAL_ERROR;
	}
}

usbh_core.c:

uint32_t keyboard_led_status_tick = 0;
 
/**
 * @brief  USBH_Process
 *         Background process of the USB Core.
 * @param  phost: Host Handle
 * @retval USBH Status
 */
USBH_StatusTypeDef USBH_Process(USBH_HandleTypeDef *phost) {
[...]
	case HOST_CLASS:
		/* process class state machine */
		if (phost->pActiveClass != NULL) {
			/* Update keyboard LED status */
			if (HAL_GetTick() > (keyboard_led_status_tick + 10)) {
				keyboard_led_status_tick = HAL_GetTick();
				HID_HandleTypeDef *HID_Handle =
						(HID_HandleTypeDef*) phost->pActiveClass->pData;
				if (HID_Handle->Init == USBH_HID_KeybdInit) {
					USB_Set_Keyboard_LED_Status();
				}
			}
 
			phost->pActiveClass->BgndProcess(phost);
		}
		break;
 
	case HOST_DEV_DISCONNECTED:
[...]

This video is not about USB Host, but it helped me understand a little better about Report Descriptor:

[Custom HID keyboard device | VIDEO 33]:

https://www.youtube.com/watch?v=ZT1tLuo9saw

Not applicable

Best result:

case HOST_CLASS:
		/* process class state machine */
		if (phost->pActiveClass != NULL) {
			/* Update keyboard LED status */
			if (HAL_GetTick() > (keyboard_led_status_tick + 10)) {
				keyboard_led_status_tick = HAL_GetTick();
				HID_HandleTypeDef *HID_Handle =
						(HID_HandleTypeDef*) phost->pActiveClass->pData;
				if (HID_Handle->Init == USBH_HID_KeybdInit) {
					USB_Set_Keyboard_LED_Status();
				}
			}
 
			phost->pActiveClass->BgndProcess(phost);
		}
		break;
 
	case HOST_DEV_DISCONNECTED:

Not applicable

This part reads the memory status that is on the keyboard:

for (uint8_t timeout = 0; timeout < 15; timeout++) {
		ret = USBH_HID_GetReport(&hUsbHostFS, 0x02U, 0U, kbd_led,
				sizeof(kbd_led));
 
		USBH_DbgLog("USBH_HID_GetReport return: %d; kbd_led: %d", ret, kbd_led[0]);
		HAL_Delay(1);
 
		if (ret == ret2) {
			break;
		}
	}

I tried this after seeing someone say it wasn't possible. Again a misinterpretation of the confusing USB protocol manual:

https://community.st.com/s/global-search/%5Busb%20host%5D%20keyboard%20num%20lock

there is no way to read out the state of LEDs from the keyboard (it even may not implement the LEDs at all). This is even explicitly stated in the standard (HID1.11) in Appendix C:

Synchronization between LED states and CAPS LOCK , NUM L OCK , SCROLL L OCK ,

COMPOSE , and KANA events is maintained by the host and NOT the keyboard

Not applicable

I think the best way to control the status of these functions, indicated via the keyboard's LED, is like this:

  • Press key (eg NumLock key)
  • Update LED status
  • If the LED was updated successfully, then the system updates the control variable (eg NumLock_status = ON)
Not applicable

Okay, that worked for me, maybe there's a better way to do it, but I have other things going on:

usb_host.c:

uint32_t keyboardMain_debounce = 0;
HID_KEYBD_Info_TypeDef *keybd_info1;
 
uint8_t kbd_LED_status[1] = { 0B001 }; // kbd_num_lock_state = 1
uint8_t kbd_LED_status_old = 0;
 
uint8_t kbd_num_lock_state = 1;
uint8_t caps_lock_state = 0;
uint8_t kbd_scroll_lock_state = 0;
 
void USBH_HID_EventCallback(USBH_HandleTypeDef *phost) {
	HID_HandleTypeDef *HID_Handle =
			(HID_HandleTypeDef*) phost->pActiveClass->pData;
	if (HID_Handle->Init == USBH_HID_KeybdInit) {
		keybd_info1 = USBH_HID_GetKeybdInfo(phost);
 
		USBH_DbgLog("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x",
				keybd_info1->keys[0], keybd_info1->keys[1],
				keybd_info1->keys[2], keybd_info1->keys[3],
				keybd_info1->keys[4], keybd_info1->keys[5]); // USBH_DbgLog
 
		if (HAL_GetTick() > (keyboardMain_debounce + 100)) {
			keyboardMain_debounce = HAL_GetTick();
 
			uint8_t key = keybd_info1->keys[0];
 
			if (key == KEY_INSERT) {
				keyboard_insert = !keyboard_insert;
			} else if (key == KEY_KEYPAD_NUM_LOCK_AND_CLEAR) {
				if ((kbd_LED_status[0] & 1) == 0) {
					kbd_LED_status[0] |= 0B001;
				} else {
					kbd_LED_status[0] &= 0B110;
				}
			} else if (key == KEY_CAPS_LOCK) {
				if (((kbd_LED_status[0] >> 1) & 1) == 0) {
					kbd_LED_status[0] |= 0B010;
				} else {
					kbd_LED_status[0] &= 0B101;
				}
			} else if (key == KEY_SCROLL_LOCK) {
				if (((kbd_LED_status[0] >> 2) & 1) == 0) {
					kbd_LED_status[0] |= 0B100;
				} else {
					kbd_LED_status[0] &= 0B011;
				}
			}
		}
 
	} else if (HID_Handle->Init == USBH_HID_MouseInit) {
		USBH_HID_GetMouseInfo(phost);
 
		USBH_DbgLog(
				"Mouse action: x=  0x%x, y=  0x%x, button1 = 0x%x, button2 = 0x%x, button3 = 0x%x \n",
				mouse_info.x, mouse_info.y, mouse_info.buttons[0],
				mouse_info.buttons[1], mouse_info.buttons[2]);
	}
}

usb_host.c:

/* USER CODE BEGIN 1 */
USBH_StatusTypeDef USB_Set_Keyboard_LED_Status(uint8_t *LED_status) {
	if (kbd_LED_status_old == kbd_LED_status[0]) {
		return HAL_OK;
	}
 
	kbd_LED_status_old = LED_status[0];
 
	uint8_t kbd_led[2] = { 0x00, 0x00 };
 
	USBH_StatusTypeDef ret = HAL_BUSY;
	USBH_StatusTypeDef ret2 = HAL_OK;
 
	kbd_led[0] = LED_status[0] & 7;
 
	for (uint8_t timeout = 0; timeout < 15; timeout++) {
		ret = USBH_HID_SetReport(&hUsbHostFS, 0x02U, 0U, kbd_led,
				sizeof(kbd_led));
 
		UsrLog("USBH_HID_SetReport return: %d", ret);
		IWDG_delay_ms(1);
 
		if (ret == ret2) {
			break;
		}
	}
 
//	ret = HAL_BUSY;
//	ret2 = HAL_OK;
 
	for (uint8_t timeout = 0; timeout < 15; timeout++) {
		ret = USBH_HID_GetReport(&hUsbHostFS, 0x02U, 0U, kbd_led,
				sizeof(kbd_led));
 
		UsrLog("USBH_HID_GetReport return: %d; kbd_led: %d", ret, kbd_led[0]);
		IWDG_delay_ms(1);
 
		if (ret == ret2) {
			break;
		}
	}
 
	if (kbd_led[0] == (LED_status[0] & 7)) {
		ret = HAL_OK;
	} else {
		ret = HAL_ERROR;
	}
 
	LED_status[0] = kbd_led[0];
 
	if ((kbd_LED_status[0] & 1) == 1) {
		kbd_num_lock_state = 1;
	} else {
		kbd_num_lock_state = 0;
	}
 
	if (((kbd_LED_status[0] >> 1) & 1) == 1) {
		caps_lock_state = 1;
	} else {
		caps_lock_state = 0;
	}
 
	if (((kbd_LED_status[0] >> 2) & 1) == 1) {
		kbd_scroll_lock_state = 1;
	} else {
		kbd_scroll_lock_state = 0;
	}
 
	return ret;
}

usb_host.c:

static void USBH_UserProcess2(USBH_HandleTypeDef *phost, uint8_t id) {
	/* USER CODE BEGIN CALL_BACK_21 */
	switch (id) {
	case HOST_USER_SELECT_CONFIGURATION:
		break;
 
	case HOST_USER_DISCONNECTION:
		Appli_state = APPLICATION_DISCONNECT;
 
		Appli_state_FS = Appli_state;
 
		kbd_LED_status_old = 99; // <--------- to update keyboard if reconnected

 usbh_core.c:

uint32_t keyboard_led_status_tick = 0;
 
/**
 * @brief  USBH_Process
 *         Background process of the USB Core.
 * @param  phost: Host Handle
 * @retval USBH Status
 */
USBH_StatusTypeDef USBH_Process(USBH_HandleTypeDef *phost) {
[...]
	case HOST_CLASS:
		/* process class state machine */
		if (phost->pActiveClass != NULL) {
			/* Update keyboard LED status */
			if (HAL_GetTick() > (keyboard_led_status_tick + 10)) {
				keyboard_led_status_tick = HAL_GetTick();
				HID_HandleTypeDef *HID_Handle =
						(HID_HandleTypeDef*) phost->pActiveClass->pData;
				if (HID_Handle->Init == USBH_HID_KeybdInit) {
					USB_Set_Keyboard_LED_Status();
				}
			}
 
			phost->pActiveClass->BgndProcess(phost);
		}
		break;
 
	case HOST_DEV_DISCONNECTED:
[...]