SoftwareSerial + Interrupt

I want to wake up my arduino uno via a serial message sent from another arduino over software serial (pins 11, 12) . I want to use the pin change interrupt. I have enclosed the codes for both the sender and the receiver . However , things are not working as expected.Plz, help me out.

Connections: pin 11 of one arduino connected to pin 12 of other arduino and vice versa.

//Sleeping arduino

#include <avr/sleep.h>
#include <SoftwareSerial.h>

#define RXpin 11

SoftwareSerial mySerial(11,12);

volatile boolean triggered=false;

void sleepNow(){
set_sleep_mode(SLEEP_MODE_PWR_DOWN);

sleep_mode();
}

ISR (PCINT3_vect){
triggered=true;
}

void setup() {
// put your setup code here, to run once:
mySerial.begin(28800);
Serial.begin(9600);
digitalWrite(RXpin,LOW);

//pin change interrupt
PCMSK0 |=bit(PCINT3);
PCIFR |=bit(PCIF0);
PCICR |=bit(PCIE0);
}

void loop() {
// put your main code here, to run repeatedly:
if(triggered==true){
triggered=false;
Serial.println(“Woken!”);
delay(500);

if(mySerial.available()){
int data=mySerial.parseInt();
Serial.println(data);

}

else{

Serial.println(“Sleeping Now”);
delay(500);
sleepNow();
}
}


//Code for sender

#include <SoftwareSerial.h>

SoftwareSerial mySerial(11,12);
int otp=1;
void setup() {
// put your setup code here, to run once:
mySerial.begin(28800);
}

void loop() {
// put your main code here, to run repeatedly:
if(otp==1){
otp=0;
char otwp=‘W’;
mySerial.write(otwp);
delay(90);
int data=42;
mySerial.write(data);
}
}

To quote Mr. Spock, "totally illogical".

Paul

I would have appreciated a little more help!!

Is the SoftwareSerial library also using pin-change interrupts? Then you need to re-connect the software serial after waking up.

I was told by a member on the forum that the softwareserial uses PCI. Earlier I had attached an interrupt routine to digital pin 2 and connected pins 2 and 11. I thought , as soon as a serial message was sent , it would trigger an interrupt.

MorganS ,thank you for your reply , but I could not get your question , I would also be thankful ,if you could suggest mistakes and necessary corrections in my code.

Softwareserual does use interruptOnChange. SO I think you do not need to do its initialisaion

Secondly since it you are waking up the arduino u cannot be sure that is it fully ready to receive data. So I would suggest that you create some kind of handshake to confirm it is ready to receive your data
please see below the changes I’ve made to your code (Please note that I have not tested this!)

//Sleeping arduino

#include <avr/sleep.h>
#include <SoftwareSerial.h>

#define RXpin 11

SoftwareSerial mySerial(11, 12);

boolean triggered = false;

void sleepNow() {
triggered = false;
set_sleep_mode(SLEEP_MODE_PWR_DOWN);

sleep_mode();
}

void setup() {
// put your setup code here, to run once:
mySerial.begin(28800);
Serial.begin(9600);

}

void loop() {
// put your main code here, to run repeatedly:

if (mySerial.available()) {
if (triggered == false) {
Serial.println(“Woken!”);
triggered = true;
}
char c = mySerial.read();
if (c == ‘W’) mySerial.print(‘w’); //acknowledge wake request
else Serial.println(data, DEC); //print data as decimal in serial monitor

}

else {
Serial.println(“Sleeping Now”);
delay(500);
sleepNow();
}
}


//Code for sender

#include <SoftwareSerial.h>

SoftwareSerial mySerial(11, 12);
int otp = 1;
void setup() {
// put your setup code here, to run once:
mySerial.begin(28800);
}

void loop() {
char otwp;
// put your main code here, to run repeatedly:
if (otp == 1) {

if (mySerial.available()) {
otwp = mySerial.read();
}
else {
otwp = ‘W’;
mySerial.write(otwp);
}

if (c == ‘w’) {
otp = 0;
int data = 42;
mySerial.write(data);
}

delay(90);
}
}

I tried it out , sherzaad , sorry , doesn't work. Seems to be stuck at sleeping now!

I learnt that the software serial uses PCI . What does that mean ? Does this mean that I do not have to attach pin change interrupts separately onto the software serial pins? Please explain.

Also , I have a complex problem at hand . I have 3 arduinos . One is awake. Other two are asleep. I want the waking arduino to wake the other two by using serial communication . Will I have to use a relay mechanism or can both be woken by the same signal . Can I accomplish this over the standard Rx, Tx pins in my arduino uno , or do I have to use softwareserial? Please help. An attached piece of code could be very helpful.

I prepared this code ,but it did not seem to work!!

//Sleeping arduino

#include <avr/sleep.h>
#include <SoftwareSerial.h>

#define RXpin 11

SoftwareSerial mySerial(11,12);

volatile boolean triggered=false;

void sleepNow(){
set_sleep_mode(SLEEP_MODE_PWR_DOWN);

sleep_mode();
}

ISR (PCINT3_vect){
triggered=true;
}

void setup() {
// put your setup code here, to run once:
mySerial.begin(28800);
Serial.begin(9600);
digitalWrite(RXpin,LOW);

//pin change interrupt
PCMSK0 |=bit(PCINT3);
PCIFR |=bit(PCIF0);
PCICR |=bit(PCIE0);
}

void loop() {
// put your main code here, to run repeatedly:
if(triggered==true){
triggered=false;
Serial.println(“Woken!”);
delay(500);

if(mySerial.available()){
int data=mySerial.parseInt();
Serial.println(data);

}

else{

Serial.println(“Sleeping Now”);
delay(500);
sleepNow();
}
}


//Code for sender

#include <SoftwareSerial.h>

SoftwareSerial mySerial(11,12);
int otp=1;
void setup() {
// put your setup code here, to run once:
mySerial.begin(28800);
}

void loop() {
// put your main code here, to run repeatedly:
if(otp==1){
otp=0;
char otwp=‘W’;
mySerial.write(otwp);
delay(90);
int data=42;
mySerial.write(data);
}
}

ok. if your orginal code did work (ie woke and slept as intended), use that but as I suggested have the sleep arduino reply to the 'W' command to make sure it is ready to correctly receive data.

Please don't double post. I have suggested to the Moderator to merge this with your other Thread on the same subject

...R

Sorry , but there seems to be no response to the other thread .

@sen_diptangshu, you are wrong. There was a response today.

@sen_diptangshu, stop cross-posting.

Threads merged.

sherzaad: ok. if your orginal code did work (ie woke and slept as intended), use that but as I suggested have the sleep arduino reply to the 'W' command to make sure it is ready to correctly receive data.

Sherzaad , my original code did not work , It had the same problem . it was always showing " Sleeping Now", no matter what!!I cannot understand where the problem is! If softwareserial uses PCI , does this mean that I do not have to do the standard pin change interrupt procedure? Please help.

I'm not familiar with the workings of Software Serial but a quick look at the code suggests that it turns the PCINT off and on and that leads me to wonder if you can rely on it being on when you put the device to sleep.

Maybe it would be a good idea to turn the appropriate PCINT on just before going to sleep. Also it would probably be a good idea to make sure that there was no data being received at that instant.

if it was my problem I think I would write a short program that uses PCINT without any SoftwareSerial and see if that works to wake from sleep.

...R

Looking at the sleep example here, you could modify it at follows to suit your purpose (I’m assuming that u dont actually need PCInt but external interrupt will also do for your project):

//Sleeping arduino

#include <avr/sleep.h>
#include <SoftwareSerial.h>

#define RXPIN 2

SoftwareSerial mySerial(RXPIN, 4);

boolean triggered = false;

void wakeUpNow()        // here the interrupt is handled after wakeup
{
  sleep_disable();         // first thing after waking from sleep:
  // disable sleep...
  detachInterrupt(digitalPinToInterrupt(RXPIN));      // disables interrupt 0 on pin 2 so the

  triggered = true;

  mySerial.begin(28800);

  mySerial.print("w"); //tx 'w' so indicate device in awake and ready to receive
}

void sleepNow() {
  mySerial.end();

  triggered = false;

  sleep_enable();

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);

  /* Trigger mode of the interrupt pin. can be:
                   LOW        a low level triggers
                   CHANGE     a change in level triggers
                   RISING     a rising edge of a level triggers
                   FALLING    a falling edge of a level triggers

       In all but the IDLE sleep modes only LOW can be used.
  */

  attachInterrupt(digitalPinToInterrupt(RXPIN), wakeUpNow, LOW); // wakeUpNow when pin 2 gets LOW

  sleep_mode();
}

void setup() {
  Serial.begin(9600);
  pinMode(RXPIN, INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  if (triggered == true) {
    Serial.println("Woken!");
    triggered = false;
  }
  if (mySerial.available()) {
    char c = mySerial.read();
    Serial.println(c); //print RX data in serial monitor (as ASCII)

  }

  else {
    Serial.println("Sleeping Now");
    delay(500);
    sleepNow();
  }
}

------------------------ -

//Code for sender

#include <SoftwareSerial.h>

SoftwareSerial mySerial(11, 12);

int otp = 1;

void setup() {
  // put your setup code here, to run once:
  mySerial.begin(28800);
}

void loop() {
  char otwp;
  char data;
  // put your main code here, to run repeatedly:
  if (otp == 1) {

    if (mySerial.available()) {
      otwp = mySerial.read();
    }
    else {
      data = 0;
      mySerial.write(data); //to send a LOW to wake up sleeping arduino
    }

    if (otwp == 'w') {
      otp = 0;
      data = 42;
      mySerial.write(data);
    }

    delay(90);
  }
}

Hope that works for you

Sorry, sherzaad , it does n't. I think I know why! The arduino wakes up from pwr doen mode by a low level interrupt . This means that , the input at the interrupt pin should be high before it suddenly goes low and stays low for some time.

In any communication system , the Rx pin is high by default, it turns low only when it is transmitting.But , in this case , the input remains high throughout , so no interrupt is generated.

So,initially , I had tried initializing software serial on pins 10,11 for instance. I also connected pins 10 and 2 and digitalwrote pin 2 to low. However I put the sleep mode to idle and disabled a lot of features to save power.

That also didn't work.

Connections: pin2 on arduino one - pin4 on arduino two pin4 on arduino two-pin2 on arduino one ground on arduino one-ground on arduino two

Hi,

I finally got a second arduino to try out this problem and I think I have managed to come up with a solution. :slight_smile:

It seems that to wake up the sleeping arduino you need the LOW to last for at least 2ms which can be consider as a serial break command.

My code I used to test this

//Sleeping arduino

#include <avr/sleep.h>
#include <SoftwareSerial.h>

#define RXPIN 2
#define TIMEOUT 1000UL

SoftwareSerial mySerial(RXPIN, 4); //pin2 RX, pin4 TX

unsigned long oldtime = 0;

void wakeUpNow()        // here the interrupt is handled after wakeup
{
  sleep_disable();         // first thing after waking from sleep:
  // disable sleep...
  detachInterrupt(digitalPinToInterrupt(RXPIN));      // disables interrupt 0 on pin 2 so the

  Serial.println("Woken!");

  sei();

  mySerial.begin(28800);
  mySerial.print("w"); //tx 'w' so indicate device in awake and ready to receive
  oldtime = millis();

  while (millis() - oldtime < 100) { //wait for 100ms for sender to reply
    if (mySerial.available()) {
      char c = mySerial.read();
      if (c == 'W') {
        Serial.println("WakeUp Acknowledged!");
        break;
      }
    }
  }
}

void sleepNow() {

  sleep_enable();

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);

  /* Trigger mode of the interrupt pin. can be:
                   LOW        a low level triggers
                   CHANGE     a change in level triggers
                   RISING     a rising edge of a level triggers
                   FALLING    a falling edge of a level triggers

       In all but the IDLE sleep modes only LOW can be used.
  */

  Serial.println("Sleeping Now");
  delay(500);
  mySerial.end();

  attachInterrupt(digitalPinToInterrupt(RXPIN), wakeUpNow, LOW); // wakeUpNow when pin 2 gets LOW

  sleep_mode();
}

void setup() {
  Serial.begin(9600);
  mySerial.begin(28800);
  pinMode(RXPIN, INPUT_PULLUP);
}

void loop() {
  // put your main code here, to run repeatedly:

  if (millis() - oldtime < TIMEOUT) { //go to sleep if no data received by TIMEOUT
    if (mySerial.available()) {
      oldtime = millis();
      char c = mySerial.read();
      Serial.println(c); //print RX data in serial monitor (as ASCII)
    }
  }
  else {
    mySerial.print("s"); //tells sender it is going to sleep
    sleepNow();
  }
}
//Code for sender

#include <SoftwareSerial.h>

SoftwareSerial mySerial(11, 12);

unsigned long oldtime = 0;

void sendData() {
  char data[9] = "abcdefgh";
  for (char i = 0; i < 8; ++i) {
    mySerial.print(data[i]);
    delay(50);
  }
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  mySerial.begin(28800);
  Serial.println("READY");
  //mySerial.print(NULL);
}

void loop() {
  char otwp;
  // put your main code here, to run repeatedly:
  if (mySerial.available()) {
    otwp = mySerial.read();
    Serial.println(otwp);
  }

  if (otwp == 'w') {
    mySerial.print("W");
    sendData();
  }



  if (millis() - oldtime > 5000) { //send wakeup command every 5s
    digitalWrite(12, LOW);
    delay(2);
    digitalWrite(12, HIGH);
    oldtime = millis();
  }

}

Hope that helps!