Waking up from sleep with remote control

I am having trouble getting things to wake up when I use a remote. I can wake up in other ways, such as when I push a button, but for some reason it won't wake up with a remote interrupt.

First, this is on an Atmega4808, so all pins can be interrupt pins. I am using digitalPinToInterrupt just in case, but that seems to just return the same pin number.

From my tests, just triggering an interrupt wakes things up until I send the sleep command again, but I can't get it to work with the remote receiver. I thought that maybe it was something to do with the interrupt being called from a library, so I even abandoned the rc-switch library and just inserted code to read and decode the data directly into my code, so I could have more control over what happens during the interrupt. When that didn't work, I even tried putting a sleep_disable() command at the top of the interrupt. That doesn't work either.

Here is the code I use:

void remoteStart(uint8_t intr)
{
  remoteReady = 0;
  remoteData= 0;
  attachInterrupt(intr, &interrupt, CHANGE);
}

uint8_t remoteDataReady(){
  return remoteReady;
}

uint32_t remoteGetData(){
  if(remoteReady){
    remoteReady = 0;
    return remoteData;
  } else {
    return 0;
  }
}

#define SYNC -1
void interrupt(){
  sleep_disable();
  static int8_t bits = SYNC;
  static unsigned int dataPulseLength;
  static unsigned long lastTime = 0;

  if(remoteReady){ return; } // We've got a value to be consumed, don't clobber it (alternatively, store the most recent, alternatively, store a ringbuffer)
  unsigned long now = micros();
  unsigned int length = now - lastTime;
  lastTime = now;
  if(bits == SYNC) { // synchronizing
    static unsigned int lastLength = 0;
    unsigned int expectedMin = (lastLength - (lastLength >> 2)) * 31;
    unsigned int expectedMax = (lastLength + (lastLength >> 2)) * 31;
    if(length > 5000 && lastLength && length > expectedMin && length < expectedMax){
      // success, continue to read state
      dataPulseLength = length / 31;
      lastLength = 0;
      bits = 0;
    } else {
      lastLength = length;
    }
  } else {
    // read data bits
    static unsigned long int current_data = 0;
    static unsigned int pulse0 = 0;
    if(pulse0 == 0){
      pulse0 = length;
    } else {
      unsigned int minPulse = dataPulseLength - (dataPulseLength >> 2);
      unsigned int maxPulse = dataPulseLength + (dataPulseLength >> 2);
      if(pulse0 > minPulse * 3 && pulse0 < maxPulse * 3 && length > minPulse     && length < maxPulse){
        // read high bit
        current_data = (current_data << 1) | 1;
        ++bits;
      } else if(length > minPulse * 3 && length < maxPulse * 3 && pulse0 > minPulse && pulse0 < maxPulse){
        // read low bit
        current_data = (current_data << 1);
        ++bits;
      } else {
        // failure, return to waiting for sync
        bits = SYNC;
        current_data = 0;
        dataPulseLength = 0;
      }
      pulse0 = 0; // always reset pulse 0
      if(bits == 24){
        // Save data
        remoteData= current_data;
        remoteReady = 1;
        
        // return to sync
        current_data = 0;
        dataPulseLength = 0;
        bits = SYNC;
      }
    }
  }
}

I add remoteStart(PIN_PA7) to the setup, and it will read codes when it isn't asleep, but once I put it into sleep mode, it won't read codes again until I wake it up using a different interrupt.

You’re doing a bit too much inside the interrupt…
Best practice is to set a flag,or increment a variable then return.

In your main loop(), check that value and do whatever needs to be done.

The wake up behaviour of the Mega AVR Series 0 may depend on which pin you use. Pins 2 and 6 on each port should be OK for waking up on an edge triggered interrupt.

I've had a similar experience on an ATtiny1614 TinyAVR Series 1 e.g. ATtiny1614 wake up from interrupt

Okay, I have switched the pin to PIN_PA6, and still no joy. I then changed the interrupt method to be two methods, one which is called in the loop.

void interrupt() {
  remoteIntTriggered = true;
}

void checkCode() {
  static int8_t bits = SYNC;
  static unsigned int dataPulseLength;
  static unsigned long lastTime = 0;

  if(remoteReady){ return; } // We've got a value to be consumed, don't clobber it (alternatively, store the most recent, alternatively, store a ringbuffer)
  unsigned long now = micros();
  unsigned int length = now - lastTime;
  lastTime = now;
  if(bits == SYNC) { // synchronizing
    static unsigned int lastLength = 0;
    unsigned int expectedMin = (lastLength - (lastLength >> 2)) * 31;
    unsigned int expectedMax = (lastLength + (lastLength >> 2)) * 31;
    if(length > 5000 && lastLength && length > expectedMin && length < expectedMax){
      // success, continue to read state
      dataPulseLength = length / 31;
      lastLength = 0;
      bits = 0;
    } else {
      lastLength = length;
    }
  } else {
    // read data bits
    static unsigned long int current_data = 0;
    static unsigned int pulse0 = 0;
    if(pulse0 == 0){
      pulse0 = length;
    } else {
      unsigned int minPulse = dataPulseLength - (dataPulseLength >> 2);
      unsigned int maxPulse = dataPulseLength + (dataPulseLength >> 2);
      if(pulse0 > minPulse * 3 && pulse0 < maxPulse * 3 && length > minPulse     && length < maxPulse){
        // read high bit
        current_data = (current_data << 1) | 1;
        ++bits;
      } else if(length > minPulse * 3 && length < maxPulse * 3 && pulse0 > minPulse && pulse0 < maxPulse){
        // read low bit
        current_data = (current_data << 1);
        ++bits;
      } else {
        // failure, return to waiting for sync
        bits = SYNC;
        current_data = 0;
        dataPulseLength = 0;
      }
      pulse0 = 0; // always reset pulse 0
      if(bits == 24){
        // Save data
        remoteData= current_data;
        remoteReady = 1;
        
        // return to sync
        current_data = 0;
        dataPulseLength = 0;
        bits = SYNC;
      }
    }
  }
}

I call it in the loop like this:

if (remoteIntTriggered) {
    checkCode();
    remoteIntTriggered = false;
  }

This code still works when I don't put things into sleep mode, but doesn't work once it is asleep.

I did figure out some strange behavior though. If I put the sleep_mode() command in the setup, and disable all interrupts, with a Serial.println("loop") in the loop, it will not print loop at all, and the device seems to be completely asleep. I can do that and enable any one of the other interrupts, and it will only turn on when the device sends the interrupt. When I enable the interrupt for the remote sensor though, it immediately wakes up. I connected an LED to the output of the remote receiver, and it flashes erratically, apparently responding to incoherent signals. To me, this means that I shouldn't be able to put it in sleep mode at all if I have an interrupt on the receiver. This isn't the case though. If I put the sleep_mode() command in the loop, it won't detect the code from the remote after it goes to sleep.

What device are you connecting to pin PA6 of your Atmega4808 to trigger the interrupt and what methods are you using to enter sleep mode?

Serial.print() relies on interrupts being enabled so what you have described is normal.

I suggest you start by creating a simple test program which (a) wakes on interrupt from the connected device and sets a timer (b) blinks a led while the loop is running so you can verify the status sleep/awake (c) enters sleep mode after X seconds.
If you have trouble getting that to work, post the entire program here.

I ended up deciding that the RF module is sending sporadic signals almost constantly, and it isn't just the one module. I tried three different types of RF module, and they all had the same problem. I won't be able to sleep with the RF module directly connected to an input. Instead, I put a small sketch on an attiny85 to only send an interrupt and code when it receives a coherent code. This works, albeit at a bit more of a power draw than I was hoping for, but not as much as having the Atmega4808 awake.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.