Questions about Moteino mcu sleep/wake on RFM95 transmission received

Started by rr_rr, November 09, 2021, 05:02:59 PM

rr_rr

Hi, I'm working with an RFM95 module on a Moteino, trying to have the mcu sleep and then wake on a received transmission.  I'm using the RadioHead library and hacked something together with the ReliableDatagram client/server example, and I have a server node periodically sending a simple transmission which I've verified I can receive on the client with other code.

I'm not sure if I understand everything conceptually, or if the RFM69 libraries are just easier to use/more robust than RadioHead.  My understanding is that there is some firmware in the RFM95 module which will receive an incoming transmission, put it in its own buffer, emit an ACK message to the server, and raise logic level on DIO0 (i believe), which is on pin2/interrupt 0 on the mcu.  But, I've tried all sorts of things to trigger an interrupt event on the mcu, and the moteino just refuses to cooperate.  Sometimes I see one interrupt on the client moteino, then it goes back to not responding.  Also I can't seem to read the transmission once the mcu wakes up this way, so I feel like I must not understand something correctly.

Here's a wall of rough code trying to make this work.  I have tried a number of variations on this and have just hacked it together so far.  Any clarification of stuff will help, I'm trying to read through the forums and documentation but not always sure what I'm reading.  For instance in another post there was mention of having to set the RFM95 to RX mode to receive transmissions like this, so I tried that with 'rf95_driver.setModeRx();'.  But when/how often should I be setting that?  Another question is if there is maybe some bit to clear a transmission flag in the RFM95 when retrieving the buffer (and how that would be done).  Also, is there information about the firmware in the RFM95? -the datasheet talks about the DIO pins being 'software configurable', so I'm not sure what they are configured to do/how to access them in the Moteino versions.

Thank you!


// LoRa module stuff
#include <RH_RF95.h>
#include <RHReliableDatagram.h>

#include <LowPower.h>  // Nice library for sleep
#include <avr/sleep.h> // sleep
#include <avr/power.h> // power control

// LoRa Packet handling
#define SERVER_ADDRESS (uint8_t)1
#define CLIENT_ADDRESS (uint8_t)2

// Singleton instance of the radio driver
RH_RF95 rf95_driver; // Instantiate a LoRa driver
RHReliableDatagram manager(rf95_driver, CLIENT_ADDRESS);
#define LORA_FREQ (float)915.0 // Change the frequency here.

// RX buffer (Dont put this on the stack)
uint8_t buf[RH_RF95_MAX_MESSAGE_LEN]; // Buffer for incoming data

volatile bool was_interrupted = false;

// The LED
int led_pin = 9;

void mcu_sleep()
{
  // Full power down mode
  // Only external interrupts or watchdog timer can wake mcu
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  // Turn off the ADC while asleep.
  power_adc_disable();
  // Enable sleep and enter sleep mode.
  sleep_mode();
  // SLEEPING
  // ...
  // Woke up, disable sleep mode
  sleep_disable();
  // put everything on again
  power_all_enable();
}

void ISR_radio()
{
  detachInterrupt(digitalPinToInterrupt(2));
  was_interrupted = true;
}

void blink_led()
{
  PORTB |= (1 << PORTB1); //make pin 9 high and power on the led
  delay(200);
  PORTB &= ~(1 << PORTB1); //make pin 9 low and power off the led
}

//////////////////////////// SETUP and LOOP ////////////////////////////////////
void setup()
{
  Serial.begin(115200);

  while (!Serial)
    ; // Wait for serial port to be available
  Serial.println(F("Start Moteino Client"));

  if (!manager.init())
    Serial.println(F("Init failed"));
  rf95_driver.setFrequency(LORA_FREQ);
  rf95_driver.setTxPower(13);
  // Define the LED pin as Output
  pinMode(led_pin, OUTPUT);
  pinMode(digitalPinToInterrupt(2), INPUT);

  rf95_driver.setModeRx();
  attachInterrupt(digitalPinToInterrupt(2), ISR_radio, HIGH);
}

void loop()
{
  // Serial.println("WAKE");
  if (was_interrupted)
  {
    was_interrupted = false;

    rf95_driver.setModeRx();  // Do you set this again?
    Serial.println("Got interrupt");

    uint8_t len = sizeof(buf);
    uint8_t from;
    if (manager.available())
    {
      if (manager.recvfromAck(buf, &len, &from))
      {
        Serial.print(F("RX from: 0x"));
        Serial.print(from, HEX);
        Serial.print(F(": "));
        Serial.println((char *)buf);

        // Send a reply back to the originator client
        // if (!manager.sendtoWait(data, sizeof(data), from))
        //   Serial.println(F("sendtoWait failed"));
      }
    }
  }

  if (!was_interrupted)
  {
    Serial.println(F("SLEEPING"));
    attachInterrupt(digitalPinToInterrupt(2), ISR_radio, HIGH);

    Serial.flush();
    // rf95_driver.sleep();
    // mcu_sleep();
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); // sleep for 8s
    // LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); // sleep for 8s
  }
}





rr_rr

I'm looking at the Moteino pin 2 with an oscilloscope.  When starting life with "pinMode(digitalPinToInterrupt(2), INPUT);", it initializes to LOW state.  When I get my first RF transmission, the pin goes to HIGH state.  Then it stays HIGH forever.  The server node is also not receiving an ACK response from the client, either.

I'm on version=1.119 of the RadioHead library.
...What could I be missing?    ???

Felix

1) This statement is erroneous, why do you call digitalPinToInterrupt?

pinMode(digitalPinToInterrupt(2), INPUT);

2) Why do you need to mess with the pin at all? The library handles that.

3) Why not start with known working examples in the library and make your changes from there, then you know when you have done something that's breaking functionality.

rr_rr

Sorry for the delay.  I've made a lot of progress in other areas and finally returned to this.  I'm new to certain aspects of this but not to others, so it's a matter of filling in the blanks.

Answering Felix:
1. Oops, just a dumb mistake.  I know had it correct at some point, and it still wasn't waking after an initial interrupt.  Should have reviewed it more before posting, sorry.
2. Ah, I wasn't aware of that!  Maybe I read about it and it didn't click.  I took out my own interrupt handling.  First attempt and it still failed to work correctly.  Then I re-uploaded and...it works?!  I'll take it.
3. But..that's what I did.  This was stitched together a piece at a time from examples.  Couple of changed variables is all.

But anyway, this was enough to clarify things, thanks for the help!  Here's some more cleaned up client code for anyone else trying to do this.   Again, the server just periodically sends a message. 

// LoRa module stuff
#include <RH_RF95.h>
#include <RHReliableDatagram.h>

#include <LowPower.h>  // Nice library for sleep

// LoRa Packet handling
#define SERVER_ADDRESS (uint8_t)1
#define CLIENT_ADDRESS (uint8_t)2

// Singleton instance of the radio driver
RH_RF95 rf95_driver; // Instantiate a LoRa driver
RHReliableDatagram manager(rf95_driver, CLIENT_ADDRESS);
#define LORA_FREQ (float)915.0 // Change the frequency here.

// RX buffer (Dont put this on the stack)
uint8_t buf[RH_RF95_MAX_MESSAGE_LEN]; // Buffer for incoming data

void blink_led()
{ // Twiddle the port - Moteino Uno
  PORTB |= (1 << PORTB1); // pin 9 high
  delay(20);
  PORTB &= ~(1 << PORTB1); // pin 9 low
}

//////////////////////////// SETUP and LOOP ////////////////////////////////////
void setup()
{
  Serial.begin(115200);

  while (!Serial)
    ; // Wait for serial port to be available
  Serial.println(F("Start Moteino Client"));

  if (!manager.init())
    Serial.println(F("Init failed"));
  rf95_driver.setFrequency(LORA_FREQ);
  rf95_driver.setTxPower(13);
  // Define the LED pin as Output
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(2, INPUT);

  rf95_driver.setModeRx();
}
uint8_t data[] = "I'm a reply";
void loop()
{
  Serial.println("WAKE");
  uint8_t len = sizeof(buf);
  uint8_t from;
  if (manager.available())
  {
    blink_led();
    if (manager.recvfromAck(buf, &len, &from))
    {
      Serial.print(F("RX from: 0x"));
      Serial.print(from, HEX);
      Serial.print(F(": "));
      Serial.println((char *)buf);

      //Send a reply back to the originator client
      if (!manager.sendtoWait(data, sizeof(data), from))
        Serial.println(F("sendtoWait failed"));
    }
  }
  //  rf95_driver.setModeRx();  // Doesn't seem to be necessary
  Serial.println(F("SLEEPING"));
  Serial.flush();
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); // sleep indefinitely; radio still on
}