Can a Teensy 3.x detect when my computer sleeps and wakes?

Status
Not open for further replies.

daperl

Well-known member
Is there some serial USB event that can notify my sketch when the USB connected computer (a Mac in this case) sleeps and wakes?
 
I'm not sure about a serial USB event, but presumably you'd be powering your Teensy from something other than the USB so it could run while the computer is asleep. If so, why not just sense voltage on the USB VCC pin?
 
I'm not sure about a serial USB event, but presumably you'd be powering your Teensy from something other than the USB so it could run while the computer is asleep. If so, why not just sense voltage on the USB VCC pin?

The Teensy is powered by the computer and the Teensy still runs when the computer is asleep.
 
The Teensy is powered by the computer and the Teensy still runs when the computer is asleep.

Interesting…I thought they all behaved the same.

On my 2014 MB Pro (10.9.4) the USB power does go off when the system sleeps but on my 2013 iMac (10.8.5) the ports stay powered even when sleeping. I apologize for barking up the wrong tree.
 
The Teensy is powered by the computer and the Teensy still runs when the computer is asleep.

That depends on the computer. I would guess that any solution you come up with will work for a given computer, but there is no general case.

I've seen in the BIOS settings that many computers have an option of whether to power USB devices when the computer is asleep (and computers tend to have multiple levels of sleep these days). If the computer cuts the power, the Teensy will be powered off, unless it has an alternate power source. If it does have an alternate power source (you would need to cut the VIN/VUSB trace), the teensy could presumably check the VUSB line for power.

Other thoughts include:
  • Check another USB/eSata line for power via an opto-coupler;
  • If it is a desktop, add an extra power cable to the Sata (or IDE for older computers) power chain, and hook it up to an opto-coupler to see if power exists;
  • You can glue a light sensor to the LED that is on when the computer is running, and attach the sensor wires to an analog port on the Teensy and see if the sensor fires;
  • Run the Teensy as a serial device, and have something on the computer that says the computer is still running;
  • If the teensy is on the network, ping the host every so often;
  • Put a light sensor on the display to see if the computer is displaying something on the screen;
  • I imagine it might be possible to determine if a computer is on by having a special purpose plug that you only plug the computer into, and then you measure the power consumption.
 
Last edited:
Inside usb_dev.c is a variable called usb_configuration. In theory, this variable is supposed to be zero when the USB isn't configured by the PC, and non-zero when it is.

Whether it really goes back to zero when your computer sleeps is a good question. It should, but that hasn't been tested, so who knows? (actually, I'm hoping you'll give it a try and post results here either way)
 
Maybe you can add this line to your program, to gain access to usb_configuration:

Code:
extern "C" { extern volatile uint8_t usb_configuration; }
 
I just tested this:

Code:
extern "C" { extern volatile uint8_t usb_configuration; }

void setup(void) {
  pinMode(13, OUTPUT);
}

void loop(void) {
  if (usb_configuration) {
    digitalWrite(13, HIGH);
  }
 else {
   digitalWrite(13, LOW);
 }
  delay(100);
}

On my iMac - The LED goes on when the program runs but doesn't go off when I tell the iMac to sleep. If I unplug the Teensy, sleep the Mac, and plug it in the light stays off until I wake up the computer.

The light doesn't come on if the Teensy's externally powered (as I would expect).

I did some web searching and it looks like there's many different levels of "sleep" that do different things to USB (stay powered, power max 100 mA, no power) it looks like this is settable in Windows 7+ and many flavors of Linux, but I had a hard time getting non-conflicting specifics on Apple products. This link had the highest hit on several searches

http://apple.stackexchange.com/questions/12409/are-any-of-the-usb-ports-on-a-macbook-pro-always-on

but there's some disagreement even in those responses. It probably varies from model to model and chipset to chipset.
 
I'm going to start here:

https://developer.apple.com/library...taSample/Listings/USBPrivateDataSample_c.html

According to the comments, I should be able to get other notifications that are listed in IOMessage.h. A few of those are:

Code:
#define kIOMessageServiceIsTerminated      iokit_common_msg(0x010)
#define kIOMessageServiceIsSuspended       iokit_common_msg(0x020)
#define kIOMessageServiceIsResumed         iokit_common_msg(0x030)

#define kIOMessageServiceIsRequestingClose iokit_common_msg(0x100)
#define kIOMessageServiceIsAttemptingOpen  iokit_common_msg(0x101)
#define kIOMessageServiceWasClosed         iokit_common_msg(0x110)

#define kIOMessageServiceBusyStateChange   iokit_common_msg(0x120)

#define kIOMessageServicePropertyChange    iokit_common_msg(0x130)

#define kIOMessageCanDevicePowerOff        iokit_common_msg(0x200)
#define kIOMessageDeviceWillPowerOff       iokit_common_msg(0x210)
#define kIOMessageDeviceWillNotPowerOff    iokit_common_msg(0x220)
#define kIOMessageDeviceHasPoweredOn       iokit_common_msg(0x230)
#define kIOMessageCanSystemPowerOff        iokit_common_msg(0x240)
#define kIOMessageSystemWillPowerOff       iokit_common_msg(0x250)
#define kIOMessageSystemWillNotPowerOff    iokit_common_msg(0x260)
#define kIOMessageCanSystemSleep           iokit_common_msg(0x270)
#define kIOMessageSystemWillSleep          iokit_common_msg(0x280)
#define kIOMessageSystemWillNotSleep       iokit_common_msg(0x290)
#define kIOMessageSystemHasPoweredOn       iokit_common_msg(0x300)
#define kIOMessageSystemWillRestart        iokit_common_msg(0x310)
#define kIOMessageSystemWillPowerOn        iokit_common_msg(0x320)
 
Okay, if you have a Mac with the proper development tools, here's some code:

Code:
// compile with gcc -Wall -O2 -framework CoreFoundation -framework IOKit -o power power.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <termios.h>
#include <sys/select.h>

#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/pwr_mgt/IOPMLib.h>

#define PORTTYPE int
#define BAUD B115200

PORTTYPE port = -1;

void
die(const char *format, ...)
{
	va_list args;
	va_start(args, format);
	vfprintf(stderr, format, args);
	exit(1);
}

void
MySleepCallBack (void * refCon, io_service_t service, natural_t messageType,
	void * messageArgument)
{
	struct timeval now;

	gettimeofday(&now, NULL);
#if DEBUG
	fprintf(stderr, "messageType %08lx, arg %08lx %ld\n",
		(long unsigned int)messageType,
		(long unsigned int)messageArgument, now.tv_sec);
#endif
	switch (messageType) {
	case kIOMessageSystemWillSleep:
		if (port >= 0) write(port, "s", 1);
		fprintf(stderr, "sleep @ %ld\n", now.tv_sec);
		break;
	case kIOMessageSystemWillPowerOn:
		if (port >= 0) write(port, "a", 1);
		fprintf(stderr, "awake @ %ld\n", now.tv_sec);
		break;
	default:
		break;
	}
}

PORTTYPE
open_port_and_set_baud_or_die(const char *name, long baud)
{
	PORTTYPE fd;
	struct termios tinfo;
	fd = open(name, O_RDWR | O_NONBLOCK);
	if (fd < 0) die("unable to open port %s\n", name);
	if (tcgetattr(fd, &tinfo) < 0) die("unable to get serial parms\n");
	cfmakeraw(&tinfo);
	if (cfsetspeed(&tinfo, baud) < 0) die("error in cfsetspeed\n");
	tinfo.c_cflag |= CLOCAL;
	if (tcsetattr(fd, TCSANOW, &tinfo) < 0) die("unable to set baud rate\n");
	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);

	return fd;
}

int
main (int argc, const char *argv[])
{
	io_connect_t root_port;
	IONotificationPortRef notifyPortRef;
	io_object_t notifierObject;
	void *refCon=NULL;
 
	if (argc >= 2) {
		port = open_port_and_set_baud_or_die(argv[1], BAUD);
	}
	root_port = IORegisterForSystemPower(refCon, &notifyPortRef,
		MySleepCallBack, &notifierObject);
	if (root_port == 0)
	{
		printf("IORegisterForSystemPower failed\n");
		return 1;
	}
	CFRunLoopAddSource(CFRunLoopGetCurrent(),
		IONotificationPortGetRunLoopSource(notifyPortRef),
		kCFRunLoopCommonModes);
	CFRunLoopRun();
}

And here's a simple sketch:

Code:
int led = 13;

void setup() {                
  Serial.begin(115200);
  pinMode(led, OUTPUT);
  digitalWrite(led, HIGH);  
}

void loop() {
  if (Serial.available()) {
    char inChar = (char) Serial.read();
    switch (inChar) {
    case 's':
      digitalWrite(led, LOW);
      break;
    case 'a':
      digitalWrite(led, HIGH);
      break;
    default:
      break;
    }
  }
}
 
Status
Not open for further replies.
Back
Top