I am unable to get my Pro Micro to wake up from sleep mode with an external interrupt. In an effort to zero in on the problem, I have stripped the wiring and code down to the following:
A Pro Micro 5V on a breadboard with nothing attached, except the serial port to my laptop. When I run this code, the serial monitor displays "Serial monitor begin" and "putting cpu to sleep" as expected. Then I ground pin 2 which should trigger the interrupt, but nothing happens ("cpu now awake" doesn't print.) I believe the cpu does go to sleep, because I have to disconnect the serial port from the computer to get it to start over. One observation that might be a clue is that one of the onboard leds flashes momentarily when I ground pin 2 - unclear why that would happen. Any help greatly appreciated - thanks in advance.
#include <avr/sleep.h>
#include <avr/interrupt.h>
const byte bucketPin = 2; // the interrupt pin
void setup() {
pinMode(bucketPin, INPUT_PULLUP);
Serial.begin(9600);
delay(1000);
Serial.println("\nSerial monitor begin");
sleep(); //puts CPU to sleep
Serial.println("cpu now awake"); //should print upon wakeup
}
void sleep(){
Serial.println("putting cpu to sleep");
delay(1000);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
noInterrupts();
sleep_enable();
attachInterrupt(digitalPinToInterrupt(bucketPin), wakeUp, LOW);
interrupts();
sleep_cpu();// puts Pro Micro to sleep
}
void wakeUp() {
sleep_disable(); // disable sleep mode
detachInterrupt(digitalPinToInterrupt(bucketPin));
}
void loop()
{}
Could be the Tx LED flashing when "cpu now awake" is sent. But maybe your PC hasn't recognised that the Micro has woken up and has not set up the COM port quickly enough.
Try adding a delay(2000) immediately after sleep_cpu().
Sleep mode is problematic for Arduinos with native USB serial, as the PC will drop the connection. Avoid doing that as there is no really clean solution.
If you must use serial, the best approach is to use the hardware Serial1 port on the Pro Micro to talk to the PC, via a UART to USB serial interface (powered by the PC and so not put to sleep).
However, there isn't much point in using sleep modes if the Arduino is going to be connected to the PC at all times.
Of course, I only have it connected to USB for troubleshooting purposes. In my project it will be battery powered. So maybe it will work once I disconnect USB?
You are OK as long as you don't have something like the popular "wait for USB serial connection", as in the following common construction: while (!Serial) delay(1);
It is very difficult to use the native USB serial connection for debugging sleep modes. I use a Serial1 connection, with a separate USB to serial adapter for that.
Try the following sketch. Tested on ATmega328P of UNO R3.
#include <avr/sleep.h>
#include <avr/interrupt.h>
const byte bucketPin = 2; // the interrupt pin
void setup()
{
pinMode(bucketPin, INPUT_PULLUP);
Serial.begin(9600);
delay(1000);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
Serial.println("\nSerial monitor begin");
attachInterrupt(digitalPinToInterrupt(bucketPin), wakeUp, CHANGE);
interrupts();
}
void loop()
{
Serial.println("putting cpu to sleep");
delay(1000);
sleep_enable();
sleep_cpu();// puts Pro Micro to sleep
sleep_disable();
Serial.println("cpu now awake"); //should print upon wakeup
delay(5000);
}
void wakeUp() {
}
Output:
21:57:11.648 -> Serial monitor begin
21:57:11.648 -> putting cpu to sleep
21:57:23.116 -> cpu now awake
21:57:28.120 -> putting cpu to sleep
21:57:30.530 -> cpu now awake
21:57:35.563 -> putting cpu to sleep
@anon94801460 I tried all kinds of ways to get Serial to work after cpu sleep, without success. Should have just done what you suggested and bought a USB-serial converter. Lessons learned the hard way are better, right?
In the meantime, I have debugged using an led write as a low-tech print statement. My sketch seems to work. For future debugging I will put a USB-serial converter on Serial1, and put my project's communication on Serial. Seems like that should work.
While I heartily agree with @anon94801460 that sleep mode is problematic with native USB and that using Serial1 is the best approach, I tinkered with a Pro Micro this morning to see if I could get it working (again - I vaguely remember doing this years back and deciding it wasn't worth it) and I did.
No claims that it's bulletproof but it did work. Mostly. Repeated use eventually confused the !#%^$!%^#$ out of the USB hub the Pro Micro was hooked up to and it (the hub) had to be disconnected and reconnected before things would work again.
And for reasons I didn't dig into, without the delay(1000); after the Serial detection code, the first output would fail to show fairly consistently.
#include <avr/sleep.h>
#include <avr/interrupt.h>
const byte bucketPin = 2; // the interrupt pin
const byte ledPin = 9;
bool haveSerial = false;
unsigned long startTime;
const unsigned long waitForSerialTime = 2000;
void setup() {
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
pinMode(bucketPin, INPUT_PULLUP);
startTime = millis();
Serial.begin(115200);
while( !Serial && millis() - startTime < waitForSerialTime ) {
delay(10);
}
haveSerial = Serial == true;
delay(1000);
if( haveSerial ) {
Serial.println("\nSerial monitor begin");
}
sleep(); //puts CPU to sleep
wakeupUSB();
if( haveSerial ) {
Serial.println("cpu now awake"); //should print upon wakeup
}
digitalWrite(ledPin, HIGH);
}
void sleep() {
Serial.println("putting cpu to sleep");
delay(1000);
// freeze the USB controller clock before sleeping
USBCON |= (1 << FRZCLK); // freeze USB clock
PLLCSR &= ~(1 << PLLE); // disable USB PLL
USBCON &= ~(1 << USBE); // disable USB Controller altogether
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
noInterrupts();
sleep_enable();
attachInterrupt(digitalPinToInterrupt(bucketPin), wakeUp, LOW);
interrupts();
sleep_cpu(); // puts Pro Micro to sleep
// continue after a LOW on pin 2 wakes us up
sleep_disable(); // disable sleep mode
}
void wakeUp() {
detachInterrupt(digitalPinToInterrupt(bucketPin));
}
void wakeupUSB() {
// re-enable the USB controller and PLL clock
USBCON |= (1 << USBE); // enable USB Controller
PLLCSR |= (1 << PLLE); // enable USB PLL
// wait for the hardware PLL lock to stabilize
while( !(PLLCSR & (1 << PLOCK)) ) {
}
USBCON &= ~(1 << FRZCLK); // unfreeze USB clock
// force the USB device to re-attach to the host
USBDevice.attach(); // re-initialize core Arduino USB stack
// restart the Serial interface
Serial.begin(115200); // baud rate doesn't matter for native USB, but initializes CDC
// wait for Serial or timeout
startTime = millis();
while( !Serial && millis() - startTime < waitForSerialTime ) {
delay(10);
}
haveSerial = Serial == true;
delay(1000);
}
void loop() {
}
I did something along those lines a few years ago, and recall running into the same problem you report. That is, after some number of native USB serial disconnects/reconnects, the Pro Micro would consistently fail to reconnect. That was when I gave up trying.
This is not limited to the Pro Micro (ATmega32U4). I have a few Adafruit Feather boards with different MCUs (e.g. SAMD21, ESP32) and similar sorts of difficulties turn up. Adafruit forum staff openly admit that sleep modes and native USB serial don't work well together, and they don't recommend trying.
I've got the serial transmission sorted out using Serial1, and the sleep mode is working with the interrupt. I think I just need to remove the power led resistor from the Pro Micro and I'm ready to put the board into my rain gauge. Thanks for your help.
Do you happen to know if the led resistor is the one that's marked 102, adjacent to the led?
It is trivial to check: use your multimeter to detect a direct, zero Ohm connection between one terminal of the "102" resistor and one terminal of the LED.
Yay! Please post the code that you arrived and works.
I tried with some Pro Micro and was unsuccessful, I can report similar frustration.
@GolamMostafa 's sketch worked on the UNO. But it is fragile.
I never like gratuitous delays of one or five seconds, life is too short. But turn the delays down or eliminate them and the serial machinery won't work.
You may have seen
Serial.befin(9600);
whike (!Serial);
which waits for the Serail to exist. That is how the problem should be handled, not with delay. On boards that need it. UNO does not, there it is superstitious or dogmatic - it is harmless and there if you change boards.
But the mechanism can be exzctly what you don't want to do when the serial monitor is not open; there perhaps a period of waiting should be coded. Give up after 2000 ms, whatever.
Other delays in the UNO demo mask the fact that if you put a machine to sleep whike it is cranking through a buffer of characters, the buffer will not get to the serial monitor. Here one can
Serail.prontln("Critical message: I am out of here!");
Serail.flush(); // buffer empties (blocks until!)
Both are immune to changes in baud rate and message lengths. Speeding up the serial comms can help, but it's still fragile.
Here is the code I used on the Pro Micro to test if the interrupt was working. It avoids the problem of using a Serial.print command to see if the wakeup interrupt works. I wired an led with resistor to pin 5 and gnd. When I run the sketch, the led lights up for 5 sec when pin 2 is grounded momentarily.
In case anyone is interested, here is the code I used to test serial communication through HC-12 modules from Pro Micro to a Mega. For the test, I have both boards connected to my laptop. When I upload to the Pro Micro, for some reason I always have to do the reset double-tap. The sketch produces a series of numbers on the serial monitor of the Pro Micro, and the same series comes up on the Mega serial monitor.
/*Sketch to test serial transmission thru HC-12 module on Pro Micro board
HC-12 wired to Pro Micro board as follows:
VCC to VCC
GND to GND
RX to pin TX0
TX to pin RX1
SET to pin 10
*/
const byte ATModePin = 10; //wired to SET pin of HC12
int test=1;
void setup()
{
pinMode(ATModePin, OUTPUT);
Serial.begin(9600);
while(!Serial);
Serial.println("\n\nSerial monitor begin");
delay(1000);
Serial1.begin(9600);
while(!Serial1);
Serial.println("Serial1 begin");
HC12_Channel_005();
}
void HC12_Channel_005() // Set the HC12 to channel 05
{
digitalWrite(ATModePin,LOW); //puts the HC12 into AT Command Mode
delay(500);
Serial.println("HC-12 in command mode");
delay(500);
Serial1.write("AT+DEFAULT"); // puts HC-12 in default condition
delay(500);
while(Serial1.available())
{Serial.write(Serial1.read());} //prints response from HC-12
Serial1.write("AT+C005");
delay(500);
while(Serial1.available())
{Serial.write(Serial1.read());} //prints response from HC-12
digitalWrite(ATModePin,HIGH); //puts HC12 back into transmit/receive mode
delay(500);
Serial.println("HC-12 back in regular mode");
delay(1000);
}
void loop()
{
delay(5000);
Serial1.write(test);
Serial.print(test);
Serial.print(" ");
test++;
}