So this is the ideea:
I want the ARduino to sleep and wake up with an external interrupt or by incoming serial data.
The way I figured it out would be to do my stuff in the interrupt routine, and then put it to sleep in it. If it gets another interrupt it starts over again. If however it wakes up from serial it ends the interrupt routine and returns to loop, where it does another thing...
In code the interrupt routine it would look like:
void alarm()
{
move(); //do whatever
energy.Idle(); //we continue from here if serial interrupt
}
notice that I use the Enerlib to do the sleeping...
The issue is I am not sure whether it needs to return from the interrupt routine in order to set the interrupt bit again, or something like that.
I dont have much exp with arduino. If you see any isses with the code above just let me know.
i wanna sleep because i only need to do the move once each half an hour or so...
I know that idle doesn't save a lot, but it is the only one that can still use the serial port (as far as i know)..., plus, like in the link I gave, I will use:
Your code sound better, with doing the move in the loop. And since I only do the move when the wake-up was the int (not the serial), in the ISR i would set a flag or something?
The ISR doesn't need to do anything. Its mere existence will wake the chip up. Without it, the chip would reset (I think). Once you get past the "sleep" function call, clearly the processor has woken up. You don't need a flag to tell you that.
I think you could use a deeper sleep mode if you used a pin-change interrupt to tell you if the Rx line had changed, but you might miss the first incoming byte as the processor wakes up.
I believe I can spare a byte, since I will build the pc inteface as well...
Do you have any examples for this, like someone who did it? Sounds like a better solution. And I would connect the Rx pin to both Rx and the remaining interrupt pin on my arduino?
I am trying to get the pin change interrupts to work but...
What I am struggling with is debouncing (i guess).This is why I placed a delay in the isr but for some reason it doesn;t work. Each time I even touch the cable i get like 20 increments in a (and prints on the serial).
Any ideas?
#include <avr/io.h>
#include <avr/interrupt.h>
volatile int a=0;
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
DDRJ = 0b11111110; // set Pj0 to input
PORTJ = 0b00000001; // set Pj0 pullup
PCICR |= _BV(PCIE1); //Enable PCINT1
PCMSK1 |= _BV(PCINT9); //Trigger on change of PCINT9 (PJ0)
sei();
}
void loop() {
}
//rx3 / pcint 9 / pj.0
ISR(PCINT1_vect)
{
a+=1;
Serial.println(a); delay(1000);
}
Yes, well you know what we say about interrupts and serial prints.
You can debounce easily in an ISR by simply comparing "time now" to the last interrupt time. eg.
volatile unsigned long lastKeypress;
ISR(PCINT1_vect)
{
// debounce for 1/10 of a second
if (millis () - lastKeypress < 100)
return; // ignore it
lastKeypress = millis ();
a++;
}
The variable "a" needs to be volatile too. Also you may want to look for a transition (eg. check if the relevant pin is now high or low) because the pin change will capture both high and low changes.
done!
It works better if I simply disable the pin int in the isr, and then activate it when I want to, in the loop...
Then I switched the pin change pin to RX0, so i can wake her up when data is received from my pc.
I works, but i need to send two bytes from the terminal to wake up, and the first is lost... the second reads ok.
It's not very thoroughly implemented, but it's good start. From my searches on the net I saw there might be others that want to do this and dont know how...
Thanks again
#include <avr/io.h>
#include <avr/interrupt.h>
#include <Enerlib.h>
#include <avr/power.h>
Energy energy;
volatile int a=0,b=0;
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
pinMode(13, OUTPUT);
digitalWrite(13, HIGH);
DDRE = 0b11111110; // set PE0 to input
PORTE = 0b00000001; // set PE0 pullup
PCICR |= _BV(PCIE1); //Enable PCINT1
PCMSK1 |= _BV(PCINT8); //Trigger on change of PCINT8 (PE0 = RX0)
sei();
}
void loop() {
if (b) //we woke up
{
b = 0;
digitalWrite(13, HIGH);
Serial.println(a);
Serial.println("woke up");
}
if (Serial.available() > 0)
{
char in = Serial.read(); Serial.println(in);
if (in == 's') sleep();
}
}
//rx0 / pcint 8 / pe.0
ISR(PCINT1_vect)
{
if (bit_is_set(PINE,0))
return;
PCMSK1 &= ~_BV(PCINT8); //Trigger on change of PCINT8 (PE0)
b=1;
a++;
}
void sleep()
{
PCMSK1 |= _BV(PCINT8); //Trigger on change of PCINT8 (PE0)
digitalWrite(13,0);
energy.PowerDown(); //we continue from here if serial interrupt
}
Issue:
If i put the arduino to sleep then wait and then send something it wakes up ok.
If when it is asleep i quit the serial terminal tool, reopen it and send something it does not wake up anymore.
I don't get it. The wake up should be something hardware. The Rx led blinks, but it doesn't wake up whatever. And what is changed between the 2 situations, so that one works and the other doesn't?
Any ideas?
i thought it may involve the bootloader. However I dont know much about how it functions. Is there any place i can find this kind of info?
And then again, like I said, the Rx pin gets the signals, so in my thinking it should trigger the interrupt. I mean the sleeping should be the same right?
What if I Serial.end before sleeping and then start again when awake? (i will try tomorrow, it's late...)
You have the pin-change interrupt now, right? Serial input doesn't appear to wake the processor except from Idle.
I suggest you do a Serial.end before sleeping, set up the pin-change interrupt, sleep, then when awake cancel the pin-change interrupt and do a Serial.begin again.
Also you almost certainly will miss the first character by the time all this gets done.
alkalin:
if you can see in my code i wake it up from power down mode, except from the issue i reported
I shall try tomorrow (it's midnight here)
Well, have a good night. Hopefully something comes to you while you sleep. On the PC side of things, what are you using for your serial terminal? Remember the ArduinoIDE will toggle one of the hardware handshaking lines that (most) Arduinos will interpret as a hardware reset. You may need to use another terminal program and open the serial port without handshaking, and/or make a custom serial cable with only RX, TX, and GND wired (pins 2,3,& 5 on a DE9 connector) to connect directly to the uP (may need level shifting) bypassing the USB interface on your Arduino. Most of the serial communication we use here (on other processors) we only use those 3 lines because we are basically communicating UART over the serial line, and UART doesn't have hardware handshaking (no CTS/RTS or DTR/DSR pins on the uP).
I don't know if you have thought of this, but regarding Nick's concern that:
Since you are writing the PC end as well, have one of the first things the Arduino does when it wakes up send out a "I'm awake" type message so your PC end knows that it is ok to start actual communication.