Using a Serial Interrupt on RX as Interrupt

Hello,

I have got a problem with my project and I have been breaking my head for the past 2 days and I cannot figure out a way of achieving what I am after.
I am using a Beetle BLE(Atmega 328). It has integrated BLE which communicates via Serial.

What I want to do, is be able to turn a Buzzer on an off from my phone via BLE. Since I want to have it battery powered the following would not be ideal:

void loop{
    if (Serial.available() > 0){
          // turn on or off depending on Serial.read()
    }
}

So I thought instead of polling I might use Interrupts to have the arduino sleeping, and woken up by incoming Serial Data.
However, I cannot use:

ISR(USART_RX_vect)
{
   ...
}

(Because HardwareSerial.cpp already has that vector in use?)

So firstly is there any way i can use this Interrupt?

And if not, secondly I have read about a library called NeoHWSerial in this forum. It basically lets you execute custom functions during the Serial ISR which is used internally by the Serial algorithm.
So I have tried the following:

volatile const uint8_t ping = 255;
volatile const uint8_t mute = 0;


static void checkMatch(uint8_t c){
  if (c == ping) digitalWrite(BUZZER,HIGH);
  else if (c == mute) digitalWrite(BUZZER,LOW);
}

void loop() {}

void setup(){
   NeoSerial.attachInterrupt( checkMatch );
   NeoSerial.begin( 9600 ); // Instead of 'Serial1'
   ...
}

However it does not seem to recognise the desired inputs. Actually it does not seem to recognise any Input, I have tried the following function attached as an Interrupt aswell, which is supossed to let an LED blink whenever it receives just any character, but the LED never lit up.

static void blink(uint8_t c){
  digitalWrite(BUZZER,HIGH);
  delay(3000);
  digitalWrite(BUZZER,LOW);    
}

void setup() {
   cli();                           // disable interrupts during setup
   NeoSerial.attachInterrupt( blink );
   NeoSerial.begin( 9600 ); // Instead of 'Serial1'
   ...
}

I am using the App LightBlue on my App in which I can write all kinds of different values with, however none of them triggered the function and made the LED blink.


I am looking forward to your replies. If you have got any other idea on how I can achieve my goal of my Beetle BLE sleeping all the time (the BLE is always on) and only ever getting waked up when receiving a Signal from my phone, processing that Signal and going back to sleep again.

Another thing I have tried: Use the WDT as an Interrupt source every 4 seconds.
During the ISR a flag would get set, that in the Serial should be checked in the loop. After that the Arduino should go back to sleep. However the code works perfectly, I can turn the Buzzer on and off every 8 seconds but when I try to put it back to sleep after checking the Serial it does not work anymore. Serial.available() is never true.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/wdt.h>



#define BUZZER 4
#define SWITCH 2
#define SLEEPTIME 3375


int sleepCounter = 0; // while sleepCounter != 0 => no checking Serial
const int ping = 0xA1;
const int mute = 0x1A;
volatile bool check = true;


void setup() {
  Serial.begin(115200);
  cli(); // disable interrupts during setup
  pinMode(BUZZER, OUTPUT);
  pinMode(SWITCH, INPUT_PULLUP);
  pinMode(3, OUTPUT);
  EIMSK |= (1 << INT0); // Enable external interrupt port zero (switch)
  CLKPR = (1 << CLKPCE); // enable internal clock scale down
  //CLKPR = 0x01; // internal clock/2

  MCUSR &= ~(1 << WDRF);           // delete wdt reset flag
  WDTCSR |= (1 << WDCE) | (1 << WDE); // enable writing
  WDTCSR =  1 << WDP3  ;    // 4 sec timeout
  WDTCSR |= 1 << WDIE;             // wdt interrupts enable
  set_sleep_mode(SLEEP_MODE_PWR_SAVE);
  // power_all_disable();    /* Analog-Eingaenge abschalten */
  sei();
  sleep_mode();
}

void loop() {
  if (check)checkSerial();
  if (!check) {
   sleep_mode();
  }
}


void checkSerial() {
  if (Serial.available()) {
    byte b = Serial.read();
    if (b == ping) {
      digitalWrite(3, HIGH);
    }
    if (b == mute) {
      digitalWrite(3, LOW);
    }
  }
  check = false;
}

ISR(WDT_vect) { // called after 4 seconds
  check = true; // else check
}

Neither this version, having sleep_mode() executed in the loop, whenever it is not time to check works nor does it work when I try to just turn on the sleepMode at the very end of checkSerial();

void checkSerial() {
  if (Serial.available()) {
    byte b = Serial.read();
    if (b == ping) {
      digitalWrite(3, HIGH);
    }
    if (b == mute) {
      digitalWrite(3, LOW);
    }
  }
  check = false;
  sleep_mode(); // HERE
}

I just cannot wrap my head around it... Why doesnt it work?
Thank you guys

You could probably put a jumper wire from the Rx pin to pin 2 and use the regular external interrupt

...R

(Because HardwareSerial.cpp already has that vector in use?)

Correct.

You could probably put a jumper wire from the Rx pin to pin 2 and use the regular external interrupt

This should work pretty well, especially since the interrupt pins are "magic" with respect to power-down modes. There are some chips with a low-power UART that will let you shut everything down till a character is received, but the m328p isn't one of them/
(Actually, I don't know whether the 328 will wake up fast enough from those very-low-power modes to have the uart receive the very first character.)

1. Please, post a picture of your Arduino BLE. Is it like this:

2. Does your BLE contain a built-in Bluetooth Module? If so, please post the schematic of your BLE showing Bluetooth device.

3. Are you using external Bluetooth Module to communicate with your smart phone? If so, please show the connection.

GolamMostafa:
1. Please, post a picture of your Arduino BLE. Is it like this:

2. Does your BLE contain a built-in Bluetooth Module? If so, please post the schematic of your BLE showing Bluetooth device.

3. Are you using external Bluetooth Module to communicate with your smart phone? If so, please show the connection.

  1. My Beetle looks like this

2&3. I do not use an external Bluetooth Module. It is integrated. I have uploaded the schematic in the attachments.

DFR0339-Bluno beetle V1.0.pdf (117 KB)

westfw:
Correct.This should work pretty well, especially since the interrupt pins are "magic" with respect to power-down modes. There are some chips with a low-power UART that will let you shut everything down till a character is received, but the m328p isn't one of them/
(Actually, I don't know whether the 328 will wake up fast enough from those very-low-power modes to have the uart receive the very first character.)

Thanks guys, I will try that later so according to this schematic(PDF in my previous post):


The two external interrupt pins I can use are D2(INT0) and D3(INT1). SO I would solder the jumper wire like this?:

And my code would look like this:

void setup(){
    ...
    EIMSK |= (1 << INT0); // Enable external interrupt port zero
}
ISR(INT0_vect) {
    check = true;
}

What is the best way to return to sleep after the ISR? Should I not be able to just put sleep_mode(); as the last line of code in my function: checkSerial()?

Do you have a working sketch that exchanges data (say: receives A and sends B) with your smart phone using the built-in Bluetooth of Arduino BLE: If yes, please post it. I would like to convert it (the sketch) into register level codes so that your objectives could be achieved.

This one here works, but only if I comment the lines out in which I want to put it back to sleep:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/wdt.h>



#define BUZZER 4
#define SWITCH 2
#define SLEEPTIME 3375


int sleepCounter = 0; // while sleepCounter != 0 => no checking Serial
const int ping = 0xA1;
const int mute = 0x1A;
volatile bool check = true;


void setup() {
  Serial.begin(115200);
  cli(); // disable interrupts during setup
  pinMode(BUZZER, OUTPUT);
  pinMode(SWITCH, INPUT_PULLUP);
  pinMode(3, OUTPUT);
  EIMSK |= (1 << INT0); // Enable external interrupt port zero (switch)
  CLKPR = (1 << CLKPCE); // enable internal clock scale down
  //CLKPR = 0x01; // internal clock/2

  MCUSR &= ~(1 << WDRF);           // delete wdt reset flag
  WDTCSR |= (1 << WDCE) | (1 << WDE); // enable writing
  WDTCSR =  1 << WDP3  ;    // 4 sec timeout
  WDTCSR |= 1 << WDIE;             // wdt interrupts enable
  set_sleep_mode(SLEEP_MODE_PWR_SAVE);
  // power_all_disable();    /* Analog-Eingaenge abschalten */
  sei();
  sleep_mode();
}

void loop() {
  if (check)checkSerial();
}


void checkSerial() {
  if (Serial.available()) {
    byte b = Serial.read();
    if (b == ping) {
      digitalWrite(3, HIGH);
    }
    if (b == mute) {
      digitalWrite(3, LOW);
    }
  }
  check = false;
  //sleep_mode(); // THIS NEEDS TO BE COMMENTED, WORKS ONLY WITHOUT
}

ISR(WDT_vect) { // called after 4 seconds
  check = true; // else check
}

Because I don't won a BLE, I have done some experiments using MEGA-Serial1, BT and phone. These are my observations/comments.

1. In power-save mode, clkIO is OFF which is needed for the operation of UART1 Port. So, I need to choose either idle or power-down mode.

2. If power-down mode is selected, then the possible agents to wake up MCU from sleep are: external interrupts, twi match, and WDT.

3. I can not figure out how the data byte that has arrived into UDR1 Register of UART1 from phone (during sleep mode) could be linked with one of the wake up agents.

Some one has recommended to short RX-pin with INTX-pin; if done so, the MCU will jump to ISR before the character is extracted from the UART frame.