leonardo as keyboard does not wake windows 7 from sleep

hello to the forum, would you please help me solving a stupid problem.

i use a leonardo as a hid keyboard on win 7. buttons work fine using:

Keyboard.press('y'); delay(500); Keyboard.releaseAll();

but when the computer goes to sleep, the buttons from my leonardo do not wake up the computer from sleep.

i figured out, that in the win 7 device manager my standard keyboard (also hid) shows up the option: "allow this device to wake the computer", but my leonardo (hid as well) does not.

i checked this out on mac 10.6.8 and - the same problem. i configured the leonardo as a hid mouse - the same thing.

why could that be? wolfgang

I can reproduce that on my Mac, it (the Leonardo) seems to freeze. Maybe it isn't getting enough power in sleep mode.

Normally the "On" LED is on, and when I press a button to send something (keystrokes) the Tx LED lights up briefly. However when the Mac is asleep the Tx LED lights up and stays lit up (and the Mac doesn't wake up).

i guess it could not be a sleep power issue. i am using buttons with leds powered by the usb 5v and a resistor. the leds keep burning while computer is asleep.

i can reproduce this on win7 as well. after pressing a button, the tx light stays on. after bringing back the computer from sleep the tx light goes off or into normal states.

Below is an excerpt from an Atmel app note about the Remote Wake feature: http://www.atmel.com/Images/doc7604.pdf

The Arduino USB code may not have this feature and may need to be added. It doesn't look like it would be difficult to add.

6.3 How to manage the Remote Wake Up feature The Remote Wake Up is an optional feature specified by the USB to allow the device to wake the host up from a stand by mode (refer to the USB specification for further details). This request is the only request which can be initiated by the device, but it has to be allowed by the host. The host sends a Set Feature request to enable the Remote Wake Up feature just before sending the suspend request. If the host did not send the Set Feature (RemoteWakeUpEnable), the device is not allowed to perform this feature. A USB device reports its ability to support remote wakeup in its configuration descriptor (refer below to see how it is done with Atmel library). If a device supports remote wakeup, it must also be allowed the capability to be enabled and disabled using the standard USB requests. The configuration descriptor is defined in the usb_descriptors.h file as below: // HID Mouse CONFIGURATION

define NB_INTERFACE 1

define CONF_NB 1

define CONF_INDEX 0

define CONF_ATTRIBUTES USB_CONFIG_BUSPOWERED

define MAX_POWER 50 // 100 mA

To setup the Remote Wake Up feature, you have to modify the CONF_ATTRIBUTES as below:

define CONF_ATTRIBUTES (USB_CONFIG_BUSPOWERED|USB_CONFIG_REMOTEWAKEUP)

If the device supports the Remote Wake Up feature, the user has to manage the Set_Feature(DEVICE_REMOTE_WAKEUP) request using the void usb_set_feature(void). Once the Set_Feature(DEVICE_REMOTE_WAKEUP) is well managed, you can use any button (must be used in external interrupt/pin change mode) for example to wake up the host. To do this, you have to take care of the following details: • First, the USB controller must have detected the “suspend” state of the line: the remote • wake-up can only be sent when a SUSPI flag is set. • The firmware has then the ability to set RMWKUP to send the “upstream resume” stream. • This will automatically be done by the controller after 5ms of inactivity on the USB line. • When the controller starts to send the “upstream resume”, the UPRSMI interrupt is triggered • (if enabled). SUSPI is cleared by hardware. • RMWKUP is cleared by hardware at the end of the “upstream resume”. • If the controller detects a good “End Of Resume” signal from the host, an EORSMI interrupt is triggered (if enabled).

thank you for the information that it is possible,
as you told and i read this is about the Atmel library,
but in arduino 1.0.3 there is no usb_descriptors.h file - right?
how could that be implemented in arduino and who would be the person for that?
sincerely
w

You could report it here:

https://github.com/arduino/Arduino/issues

Hi,

in theory the following should be enough to make wakeup work, but I don’t get it running, so probably something is missing:

  1. add the flag USB_CONFIG_REMOTE_WAKEUP in the Arduino header file “hardware/arduino/cores/arduino/USBCore.h”
#define D_CONFIG(_totalLength,_interfaces) \
   { 9, 2, _totalLength,_interfaces, 1, 0, USB_CONFIG_BUS_POWERED | USB_CONFIG_REMOTE_WAKEUP, USB_CONFIG_POWER_MA(500) }
  1. To wakeup the PC use the following code:
  USBDevice.attach(); // switches on the USB interface (PLL and USB clock)
  UDCON |= (1 << RMWKUP);
  while (UDCON & (1 << RMWKUP));

Does anybody see what is missing?

Michael

I now have the wakeup from Arduino Leonado running, but the whole SUSPEND / RESUME handling is not clean. I have to read some more parts of the specs to understand it. The contents of this post are highly experimental and I hope someone with more USB knowledge kicks in.

How to get a first unclean version running (as base for further tests, not for production):

  1. add the flag USB_CONFIG_REMOTE_WAKEUP in the Arduino header file “hardware/arduino/cores/arduino/USBCore.h”
#define D_CONFIG(_totalLength,_interfaces) \
   { 9, 2, _totalLength,_interfaces, 1, 0, USB_CONFIG_BUS_POWERED | USB_CONFIG_REMOTE_WAKEUP, USB_CONFIG_POWER_MA(500) }
  1. the following changes are necessary to “hardware/arduino/cores/arduino/USBCore.cpp”
--- USBCore.cpp.orig	2013-05-17 20:20:18.000000000 +0200
+++ USBCore.cpp	2013-06-29 13:28:25.000000000 +0200
@@ -92,6 +92,8 @@
 //==================================================================
 
 volatile u8 _usbConfiguration = 0;
+volatile u8 _usbCurrentStatus = 0; // meaning of bits see usb_20.pdf, Figure 9-4. Information Returned by a GetStatus() Request to a Device
+volatile u8 _usbSuspendState = 0; // copy of UDINT to check SUSPI and WAKEUPI bits
 
 static inline void WaitIN(void)
 {
@@ -508,6 +510,7 @@
 //	Endpoint 0 interrupt
 ISR(USB_COM_vect)
 {
+    // TODO: check UEINT if the IRQ is for endpoint0
     SetEP(0);
 	if (!ReceivedSetupInt())
 		return;
@@ -527,16 +530,35 @@
 	{
 		//	Standard Requests
 		u8 r = setup.bRequest;
+		u16 wValue = setup.wValueL | (setup.wValueH << 8);
 		if (GET_STATUS == r)
 		{
-			Send8(0);		// TODO
-			Send8(0);
+			if(requestType == (REQUEST_DEVICETOHOST | REQUEST_STANDARD | REQUEST_DEVICE))
+			{
+				Send8(_usbCurrentStatus);
+				Send8(0);
+			}
+			else
+			{
+				Send8(0);
+				Send8(0);
+			}
 		}
 		else if (CLEAR_FEATURE == r)
 		{
+			if((requestType == (REQUEST_HOSTTODEVICE | REQUEST_STANDARD | REQUEST_DEVICE))
+				&& (wValue == DEVICE_REMOTE_WAKEUP))
+			{
+				_usbCurrentStatus &= ~FEATURE_REMOTE_WAKEUP_ENABLED;
+			}
 		}
 		else if (SET_FEATURE == r)
 		{
+			if((requestType == (REQUEST_HOSTTODEVICE | REQUEST_STANDARD | REQUEST_DEVICE))
+				&& (wValue == DEVICE_REMOTE_WAKEUP))
+			{
+				_usbCurrentStatus |= FEATURE_REMOTE_WAKEUP_ENABLED;
+			}
 		}
 		else if (SET_ADDRESS == r)
 		{
@@ -595,8 +617,11 @@
 //	General interrupt
 ISR(USB_GEN_vect)
 {
+        // TODO: check UEINT for the source endpoint of this IRQ
 	u8 udint = UDINT;
-	UDINT = 0;
+	//UDINT = 0; // TODO: this also clears other IRQ flags (especially SUSPI) which is not good
+	//UDINT &= ~((1<<EORSTI) | (1<<SOFI)); // clear the IRQ flags for the IRQs which are handled here
+	UDINT &= ~((1<<EORSTI) | (1<<SOFI) | (1<<WAKEUPI) | (1<<SUSPI)); // clear the IRQ flags for the IRQs which are handled here
 
 	//	End of Reset
 	if (udint & (1<<EORSTI))
@@ -621,6 +646,24 @@
 		if (RxLEDPulse && !(--RxLEDPulse))
 			RXLED0;
 	}
+
+	// the WAKEUPI interrupt is triggered as soon as there are non-idle patterns on the data
+	// lines. Thus, the WAKEUPI interrupt can occur even if the controller is not in the "suspend" mode.
+	// Therefore the we enable it only when USB is suspended
+	if (udint & (1<<WAKEUPI))
+	{
+		//TODO
+		UDIEN &= ~(1<<WAKEUPE);	// Disable interrupts for WAKEUP
+		//USBCON &= ~(1<<FRZCLK);	// unfreeze clock
+		_usbSuspendState |= (1<<WAKEUPI);
+	}
+	else if (udint & (1<<SUSPI)) // in fact only one of the WAKEUPI / SUSPI bits can be active at time
+	{
+		//TODO
+		UDIEN |= (1<<WAKEUPE);	// Enable interrupts for WAKEUP
+		//USBCON |= (1<<FRZCLK);	// freeze clock
+		_usbSuspendState |= (1<<SUSPI);
+	}
 }
 
 //	VBUS or counting frames
@@ -644,6 +687,7 @@
 void USBDevice_::attach()
 {
 	_usbConfiguration = 0;
+	_usbCurrentStatus = 0;
 	UHWCON = 0x01;						// power internal reg
 	USBCON = (1<<USBE)|(1<<FRZCLK);		// clock frozen, usb enabled
 #if F_CPU == 16000000UL
@@ -660,7 +704,9 @@
 	delay(1);
 
 	USBCON = ((1<<USBE)|(1<<OTGPADE));	// start USB clock
-	UDIEN = (1<<EORSTE)|(1<<SOFE);		// Enable interrupts for EOR (End of Reset) and SOF (start of frame)
+	UDIEN = (1<<EORSTE)|(1<<SOFE)|(1<<SUSPE);	// Enable interrupts for EOR (End of Reset), SOF (start of frame) and SUSPEND
+	//UDIEN = (1<<EORSTE)|(1<<SOFE)|(1<<SUSPE)|(1<<WAKEUPE);	// Enable interrupts for EOR (End of Reset), SOF (start of frame) and SUSPEND+WAKEUP
+	//UDIEN = (1<<EORSTE)|(1<<SOFE);	// Enable interrupts for EOR (End of Reset) and SOF (start of frame)
 	UDCON = 0;							// enable attach resistor
 	
 	TX_RX_LED_INIT;

The changes which are really necessary:

  • Handle Get_Stats for the device and return the FEATURE_REMOTE_WAKEUP_ENABLED bit
  • Handle Set_Feature and Clear_Feature for DEVICE_REMOTE_WAKEUP

The other changes are for debugging the SUSPEND / WAKEUP handling and as preparation for power saving in SUSPEND mode.

  1. To wakeup the PC use the following code in your sketch:
  // TODO: this is only an ugly workaround which should be removed.
  if((UDCON & (1 << RMWKUP)))
  {
    UDCON &= ~(1 << RMWKUP); // clear any previous wakeup request
  }
  
  // TODO: normally this should only be called when SUSPI=1, but currently SUSPI is always 0, therefore we have to ignore it
  //if((UDINT & SUSPI) && !(UDCON & (1 << RMWKUP)) && (_usbCurrentStatus & FEATURE_REMOTE_WAKEUP_ENABLED))
  if(!(UDCON & (1 << RMWKUP)) && (_usbCurrentStatus & FEATURE_REMOTE_WAKEUP_ENABLED))
  {
    UDCON |= (1 << RMWKUP); // send the wakeup request
  }

Any comments are welcome.

Michael

here a cleaned up version. To use it, just call

USBDevice.wakeupHost();

in your sketch.

The modified Arduino core files can be found in the attached zip file. You have to replace the original files in your Arduino installation path (make a backup copy of the original files first).

Michael

USBWakeup.zip (9.67 KB)

On my Mac at least, if it is asleep, pressing a key just wakes it up. It doesn't also process that key (eg. if a word processor was open as the top window).

I do not get the USBDevice.wakeupHost(); to work.

I am also trying to wake up a PC with leonardo. If I just send a key the leonardo seems to hang (TX light constant light).

I do not experience this hangup when I use USBDevice.wakeupHost(); However nothing happens with the code. With a normal keyboard the computer wakes immediately.

(I have replaced the files you attached.)

I got it working when I used the code:
USBDevice.wakeupHost();
UDCON |= (1 << RMWKUP);

Do you mean pressing a key on a real keyboard or using Keyboard.print() or Keyboard.write() an Arduino emulated keyboard?

Which message do you see in the Utility App “Console” when sleeping/waking up?
The sequence on the Mac should look like this (only some relevant entries):
01.07.13 21:07:28,000 kernel[0]: hibernate_setup(0) took 0 ms
01.07.13 21:07:46,000 kernel[0]: sleep
01.07.13 21:07:46,000 kernel[0]: Wake reason: EHC1
01.07.13 21:08:48,000 kernel[0]: Previous Sleep Cause: 5
01.07.13 21:08:48,000 kernel[0]: The USB device HubDevice (Port 1 of Hub at 0xfd000000) may have caused a wake by issuing a remote wakeup (2)
01.07.13 21:08:48,000 kernel[0]: The USB device Arduino Leonardo (Port 2 of Hub at 0xfd100000) may have caused a wake by issuing a remote wakeup (3)

As you can see in the log it takes up to 18 seconds before the computer really goes to sleep and puts the USB bus in suspend mode. You should wait this time before you wake it up again. Depending on how much RAM you have and the speed of your HDD, it may even take longer.

When a computer is in a Sleep Mode (S3 state), the USB bus is suspended and the devices are no longer polled by the host. In this state it is not possible to send any data to the usb host (e.g. keyboard reports) except the remote wakeup.

In the Microsoft article KB841858 I found an answer why different behaviour can be observed on different computers:
When a USB device is enabled to wake the computer, the default behavior permits the computer to enter the S1 system power state for standby. Standby is not the S3 system power state. The S1 system power state is a “lighter” system power state than S3. The S1 system power state typically conserves less power than the S3 system power state.

So we are talking about different system standby states and it depends on your operating system, the attached peripherals (on the mainboard and via cable) and some settings which of these states is entered and if the USB bus stays in D0 state or enters D3 state. D0 and D3 can be distinguished by checking UDINT & (1<<SUSPI), but unfortunately SUSPI is cleared in the Arduino USB_GEN ISR.

I can try to handle the Power-State cases differently, but first I have to find a computer which uses S1 state.

Still trying and learning…

Michael

harryharry:
I got it working when I used the code:
USBDevice.wakeupHost();
UDCON |= (1 << RMWKUP);

Did you wait at least 20s after going to standby and before calling USBDevice.wakeupHost()?
Which operating system are you using?
Can you please reboot your computer and plug-out and plug-in your Arduino and try again? I’ve seen that the HID descriptor is cached by the operating system and maybe it didn’t recognize the USB_CONFIG_REMOTE_WAKEUP flag in the configuration.

Michael

Do you mean pressing a key on a real keyboard or using Keyboard.print() or Keyboard.write() an Arduino emulated keyboard?

A real keyboard. I was just testing if the wake-up press got "swallowed" or not.

harryharry:
I got it working when I used the code:
USBDevice.wakeupHost();
UDCON |= (1 << RMWKUP);

I don’t understand, why you need this last line.

I made an updated version which handles the SUSPEND mode (at least it acknowledges SUSPEND). Install it like the previous version by replacing the original files in your Arduino installation path (see attachment USBWakup2.zip).

I also added a diagnostic sketch to show the state changes via a blink code of the internal LED (see attachment USBWakup-130702g.zip):

Meaning of the blink codes:
1x remote wakeup enabled from the PC
2x remote wakeup disabled from the PC
3x USB host signals SUSPEND state
4x USB host signals end of SUSPEND state (wakeup)
9x USB wakeup rejected because
10x USB wakeup accepted

When the Leonardo is starting (after the bootloader blinks), you should see [3x], 1x, 4x blink.
When the PC goes to standby, you should see 3x blink
When the PC wakes up, you should see 10x blink followed by 4x blink

Currently I don’t understand the initial 3x blink which I see on Mac OSX.

What blink codes do you get?

Michael

USBWakeup-130702g.zip (1.19 KB)

USBWakeup2.zip (10.1 KB)

Still looking for testers...

Here it works for

  • Mac OSX (on a MacBook Pro)
  • Windows 7 (on a MacBook Pro)
  • Windows 8 (on a Wetab)

Any comments if it is working or not and what blink codes you get are welcome.

In the pull request #1488 I added also a feature to send your computer to sleep mode or shut it down. When you are interested, you can get the code from there.

You can use it in the following way:

  Keyboard.systemControl(SYSTEM_CONTROL_POWER_DOWN);

or

  Keyboard.systemControl(SYSTEM_CONTROL_SLEEP);

There are some more commands defined in USBAPI.h, but most of the do nothing on my Mac:

#define SYSTEM_CONTROL_POWER_DOWN              1
#define SYSTEM_CONTROL_SLEEP                   2
#define SYSTEM_CONTROL_WAKEUP                  3
#define SYSTEM_CONTROL_COLD_RESTART            4
#define SYSTEM_CONTROL_WARM_RESTART            5
#define SYSTEM_CONTROL_DOCK                    6
#define SYSTEM_CONTROL_UNDOCK                  7
#define SYSTEM_CONTROL_SPEAKER_MUTE            8
#define SYSTEM_CONTROL_HIBERNATE               9
#define SYSTEM_CONTROL_DISPLAY_INVERT         10
#define SYSTEM_CONTROL_DISPLAY_INTERNAL       11
#define SYSTEM_CONTROL_DISPLAY_EXTERNAL       12
#define SYSTEM_CONTROL_DISPLAY_BOTH           13
#define SYSTEM_CONTROL_DISPLAY_DUAL           14
#define SYSTEM_CONTROL_DISPLAY_TOGGLE_INT_EXT 15
#define SYSTEM_CONTROL_DISPLAY_SWAP           16

Michael

Dear Michael “Mike T”,
dear “harryharry”,

thanks for this USB hack that saves us the Day, try to waking an old MBP 15" in a multimedia setup on a museum installation.

The command “USBDevice.wakeupHost();” itself works but the second command is useful for a strange rarely situation:

  • the MBP is in sleep status
  • the Arduino Leonardo is double powered (connected via USB and 12V supply)
  • if the AC wall current fall down for few minutes
  • and then the AC wall current return
  • the Arduino try to wake the MBP with “USBDevice…etc” itself, but FAIL
  • if we add the second command it never fails !

Thanks again

Greetings from Italy

M.

sorry for this very late reply.

exchanged the 4 files in the current arduino 105 ide.

HID.cpp
USBAPI.h
USBCore.cpp
USBCore.h

put the
USBDevice.wakeupHost();
UDCON |= (1 << RMWKUP);
into my code.
works fine on my mac 10.8.
thank you so much.

when will this find its way into the official builds?