nRF8001 and Snooze library with Teensy 3.1

Status
Not open for further replies.

SorX14

Member
I'm trying to make a device which has Bluetooth LE connectivity that will be battery powered.

So far I've used the Adafruit nRF8001 library which has had some attention from Paul in the past correcting the AVR specificity. I've managed to get the nRF8001 module working with the basic echo demo, and it works perfectly.

However, whenever I try to sleep using Duffs Snooze library (https://github.com/duff2013/Snooze) the module stops working; i.e. doesn't process the next event.

I've tried emulating sleep, by using a `while` loop waiting for a LOW signal on the RDY line from the nRF8001 and that works fine. But when I replace this with the `Snooze.sleep(config)` line, everything falls apart.

I believe I've narrowed it down to the `toggle_eimsk` function inside `hal_aci_tl.cpp`. This seems to disable the interrupts for the Snooze library as well, preventing them from waking the Teensy from sleep.

Thinking that the interrupts might be clashing with each other, I paired pin 0 with pin 2 (nRF8001 RDY) so that I could attach a separate interrupt handler to pin 0. When using my `while` block, the pin levels toggle correctly and everything is good. When I use the Snooze library, the RDY line stays LOW. If I remove the pairing between pin 0 and 2, pin 0 will no longer wake the Teensy 3 from sleep even if I manually ground it. If I attach another wakeup pin, and call `pollACI()`, it also stops working.

I changed all calls to `Serial` to `Serial3` so that I could see what was going on when the Teensy was asleep (Duffs library severs the USB connection).

My C knowledge is pretty limited, so I normally end up grabbing libraries and trying to use them together, but this has gotten the better of me :(

Is there any way to get these 2 libraries to work together?

EDIT: Should've added source code.

I modified the `pollACI` function to return true if there is nothing to do and false otherwise

Code:
#include <SPI.h>
#include "Adafruit_BLE_UART.h"
#include <Snooze.h>

// nRF8001 pins
#define NRF_RQN 23
#define NRF_RDY 2
#define NRF_RST 6
#define NRF_ACT 3

Adafruit_BLE_UART BTLEserial = Adafruit_BLE_UART(NRF_RQN, NRF_RDY, NRF_RST);

SnoozeBlock config;
aci_evt_opcode_t status;

#define SNOOZE_ENABLED

bool canSleep = false;

#ifndef SNOOZE_ENABLED
  volatile bool canContinue = false;
#endif

void setup(void) { 
  Serial3.begin(115200);
  delay(2000);
  Serial3.println("STARTING");
  
    // Make sure we start with the radio fully configured
  BTLEserial.begin();
  
  while (status != ACI_EVT_DEVICE_STARTED) {
    status = BTLEserial.getState();
    BTLEserial.pollACI();
  }
    
  #ifdef SNOOZE_ENABLED
    config.pinMode(14, INPUT, FALLING);
    config.pinMode(0, INPUT_PULLUP, FALLING);
  #else
    attachInterrupt(0, paired_interrupt, LOW);
  #endif
  
  // LED
  pinMode(16, OUTPUT);
}

#ifndef SNOOZE_ENABLED
  void paired_interrupt() {
    canContinue = true;
    detachInterrupt(0);
  }
#endif

void loop() {
  canSleep = BTLEserial.pollACI();
  //status = BTLEserial.getState();

  // Only sleep if the RQN line is HIGH
//  if (digitalRead(0) == HIGH){// && status == ACI_EVT_DEVICE_STARTED) {  
  if (canSleep) {
    Serial3.print("SLEEP\t");
    Serial3.println(millis());
    Serial3.flush();

  #ifdef SNOOZE_ENABLED   
      Snooze.sleep(config);
  #else
    while (!canContinue) {}
    canContinue = false;
    attachInterrupt(0, paired_interrupt, LOW);
  #endif  
    canSleep = false;
    Serial3.println("WAKE");
  }

  delay(10);
}
 
Last edited:
can u see what version of the Snooze library you have? Its in Snooze.h at the top in the header, and where can I down load the version of nRF8001 library you have?
 
can u see what version of the Snooze library you have? Its in Snooze.h at the top in the header, and where can I down load the version of nRF8001 library you have?

Hey duff, first, thanks for the library, it makes a lot more sense than the cryptic registers and sleep modes in the manual :)

I'm using version 5.1 of your library. I've noticed a couple of strange things with it, which may be based on my wrong assumptions. For example, when setting a pin as a wake up source with `config.pinMode(...)` I'm unable to read that pin in a standard loop, unless I add `pinMode(x, INPUT)` in front of the `config.pinMode(...)` line. I was under the impression that setting a pin as a wake up source will also set the pin to work as an input?

For the nRF8001 library, I've tried the 'official' Adafruit_nRF8001, a modified library made by tato123 and currently Paul's update to the library. With all three of these libraries, I've tried multiple setups; enabling/disabling nRF interrupts, polling etc, but can never get it to sleep in between requests. I didn't do it in a particularly scientific method, so I may have missed the correct combination :eek:
 
I'm using version 5.1 of your library. I've noticed a couple of strange things with it, which may be based on my wrong assumptions. For example, when setting a pin as a wake up source with `config.pinMode(...)` I'm unable to read that pin in a standard loop, unless I add `pinMode(x, INPUT)` in front of the `config.pinMode(...)` line. I was under the impression that setting a pin as a wake up source will also set the pin to work as an input?
it actually sets the pin when you call the sleep function, this was so you can use mulitple configurations. You can have different SnoozeBlocks that get supplied to whatever sleep function you choose.

For the nRF8001 library, I've tried the 'official' Adafruit_nRF8001, a modified library made by tato123 and currently Paul's update to the library. With all three of these libraries, I've tried multiple setups; enabling/disabling nRF interrupts, polling etc, but can never get it to sleep in between requests. I didn't do it in a particularly scientific method, so I may have missed the correct combination :eek:
Since i don't have one of these I'm using the nRF8001 library installed by Teensyduino to test but not sure if this will work? Do i need radio to use the library?
 
it actually sets the pin when you call the sleep function, this was so you can use mulitple configurations. You can have different SnoozeBlocks that get supplied to whatever sleep function you choose.

Ah ha, that makes a lot more sense. I'm going to have to take another look at everything now that I know that's the case. With that in mind, after the Teensy is slept and woken up, are the pins set back to their original state? For example:

Code:
SnoozeBlock config;

void setup() {
  pinMode(1, OUTPUT);
  config.pinMode(1, INPUT_PULLUP, FALLING);
}

void loop() {
  Snooze.sleep(config);
  // Is pin 1 INPUT_PULLUP or OUTPUT?
}

Since i don't have one of these I'm using the nRF8001 library installed by Teensyduino to test but not sure if this will work? Do i need radio to use the library?

The issue I'm seeing is that the RDY pin appears to remain low after wakeup (regardless of how the Teensy is woken back up). If you're interested in helping me figure this out, I wouldn't mind sending you a nRF8001 breakout board. I'm hopefully going to have another look this evening, and I'll give you my findings in a more scientific approach.

Looks like this function in `utilities\hal_aci_tl.cpp` of the `Adafruit_nRF8001` library could be part of the problem?:

Code:
static void toggle_eimsk(bool state)
{
  //Serial3.print("toggle eimsk");
  //Serial3.println(state);
  /* ToDo: This will currently only work with the UNO/ATMega48/88/128/328 */
  /*       due to EIMSK. Abstract this away to something MCU nuetral! */
  if (HAL_IO_RADIO_IRQ != 0xFF)
  {
    if (state) {
      EIMSK |= (1 << HAL_IO_RADIO_IRQ);
    } else {
      EIMSK &= ~(1 << HAL_IO_RADIO_IRQ);
    }
    //Serial.println(EIMSK, BIN);
  } else {
    /* RDY isn't a valid HW INT pin! */
    Serial3.println("RDY isn't a valid HW INT pin!");
    while(1);
  }         
}

Paul has commented before (http://forums.adafruit.com/viewtopic.php?f=19&p=265695 second post) that the interrupt isn't just detached and reattached as that may clear a pending interrupt?

My end goal is to have the Teensy woken by either the nRF8001, an ADXL345, a couple of switches and potentially the RTC too. Unfortunately I've fallen at the first hurdle of getting the nRF8001 working :(
 
With that in mind, after the Teensy is slept and woken up, are the pins set back to their original state? For example:
Code:
SnoozeBlock config;

void setup() {
  pinMode(1, OUTPUT);
  config.pinMode(1, INPUT_PULLUP, FALLING);
}

void loop() {
  Snooze.sleep(config);

  // Is pin 1 INPUT_PULLUP or OUTPUT?
}
The pin mode would be OUTPUT the first time before sleep and INPUT_PULLUP for there after in this example. I don't set back to whatever it was before the call to sleep. Maybe I should save the state of the pin before sleep and configure it back after sleep? This will add extra latency to the sleep functions though.

So if you want the pin mode to be OUTPUT after and before sleep you would have to explicatively call pinMode(1, OUTPUT) after Snooze.sleep(config).
Code:
SnoozeBlock config;

void setup() {
  pinMode(1, OUTPUT);
  config.pinMode(1, INPUT_PULLUP, FALLING);
}

void loop() {
  Snooze.sleep(config);
[COLOR=#ff0000]  // pin 1 will be INPUT_PULLUP now
  // if you want another mode you have to set it
  [/COLOR][COLOR=#0000cd]pinMode(1, OUTPUT);[/COLOR]
}

That being said I was able to use the nRF8001 library without the module i think and was able to get sleep to work but still not sure if the module works though. If want to send me one i can PM my address?
 
Maybe I should save the state of the pin before sleep and configure it back after sleep? This will add extra latency to the sleep functions though.

I'd say no, if its as easy as adding the pinMode block after, then people who need it can. I was just unclear on how everything would be set after sleeping.

I think that you'll need the board itself to prove that it does in fact carry on working after sleep. I'll definitely be working on this tonight, so I'll have more findings tomorrow.

PM'd about a breakout.
 
Okay, I've tried the following which is using the packaged nRF8001 library with Teensyduino and the latest version of the Snooze library.

Code:
#include <SPI.h>
#include "Adafruit_BLE_UART.h"
#include <Snooze.h>

// nRF8001 pins
#define NRF_RQN 23
#define NRF_RDY 2
#define NRF_RST 6
#define NRF_ACT 3

Adafruit_BLE_UART BTLEserial = Adafruit_BLE_UART(NRF_RQN, NRF_RDY, NRF_RST);
aci_evt_opcode_t status;
SnoozeBlock config;

void setup(void)
{ 
  Serial3.begin(115200);
  delay(1000);
  Serial3.println(F("Adafruit Bluefruit Low Energy nRF8001 Print echo demo"));
  
  pinMode(NRF_RDY, INPUT_PULLUP);
  config.pinMode(NRF_RDY, INPUT_PULLUP, LOW);

  BTLEserial.begin();
  
  // Get the radio advertising before we sleep
  while (status != ACI_EVT_DEVICE_STARTED) {
    status = BTLEserial.getState();
    BTLEserial.pollACI();
  }
}

aci_evt_opcode_t laststatus = ACI_EVT_DISCONNECTED;

void loop()
{
  if (digitalRead(NRF_RDY) == HIGH) {
    Serial3.println("SLEEP");
    Serial3.flush();
    Snooze.sleep(config);
    Serial3.println("WAKE");
  }
  // Tell the nRF8001 to do whatever it should be working on.
  BTLEserial.pollACI();

  // Ask what is our current status
  status = BTLEserial.getState();
  // If the status changed....
  if (status != laststatus) {
    // print it out!
    if (status == ACI_EVT_DEVICE_STARTED) {
        Serial3.println(F("* Advertising started"));
    }
    if (status == ACI_EVT_CONNECTED) {
        Serial3.println(F("* Connected!"));
    }
    if (status == ACI_EVT_DISCONNECTED) {
        Serial3.println(F("* Disconnected or advertising timed out"));
    }
    // OK set the last status change to this one
    laststatus = status;
  }

  if (status == ACI_EVT_CONNECTED) {
    // Lets see if there's any data for us!
    if (BTLEserial.available()) {
      Serial3.print("* "); Serial3.print(BTLEserial.available()); Serial3.println(F(" bytes available from BTLE"));
    }
    // OK while we still have something to read, get a character and print it out
    while (BTLEserial.available()) {
      char c = BTLEserial.read();
      Serial3.print(c);
    }

    // Next up, see if we have any data to get from the Serial console

    if (Serial3.available()) {
      // Read a line from Serial
      Serial3.setTimeout(100); // 100 millisecond timeout
      String s = Serial3.readString();

      // We need to convert the line to bytes, no more than 20 at this time
      uint8_t sendbuffer[20];
      s.getBytes(sendbuffer, 20);
      char sendbuffersize = min(20, s.length());

      Serial3.print(F("\n* Sending -> \"")); Serial3.print((char *)sendbuffer); Serial3.println("\"");

      // write the data
      BTLEserial.write(sendbuffer, sendbuffersize);
    }
  }
}

On my serial monitor, I get:

Code:
[03][10][00][00][00][00]€p?€[00][02]–[06]W[01][03][00]Adafruit Bluefruit Low Energy nRF8001 Print echo demo
* Advertising started
SLEEP

There seems to be garbage coming from the serial port when the reset button is pushed. That doesn't matter to me, but I thought I would include it if it matters.
 
Hi,

I'm doing a similar thing and I was wondering if you guys figured out why the Teensy doesn't wake up after being put to sleep. I tried with the code posted above and the Teensy never wakes up. Thanks
 
Hi,

I'm doing a similar thing and I was wondering if you guys figured out why the Teensy doesn't wake up after being put to sleep. I tried with the code posted above and the Teensy never wakes up. Thanks
The problem is that library sets an interrupt on the rdy pin in the begin function and will not allow you change its function and there is no way to reconfigure it back after sleep, calling "begin" after does not work. Just pass a blank SnoozeBlock since sleep will wake with any interrupt not just the ones defined in the SnoozBlock. Also you can set your RTC or Timer SnoozeBlock to make sure it wakes as a test.
 
Status
Not open for further replies.
Back
Top