Jump to content
43oh

[Energia Library] Nordic nRF24L01+ library


Recommended Posts

Hi folks... So I'm a little new to Energia, but since I wrote a library for the nRF24L01+ digital transceivers for native C-based apps I figured it'd be a great idea to port this to Energia.

 

I'm rewriting it from scratch, trying to make it simpler & feel more like a native C++ energia lib.

 

edit: This has been written and the latest version is up on Github.

Github project- https://github.com/spirilis/Enrf24

Latest version + comments- http://forum.43oh.com/topic/3237-energia-library-nordic-nrf24l01-library/?p=48805

 

 

 

 

To that end, I'd like some feedback on the API choices I've made.  I want to know what some of you guys think should go in here.

 

For now, here's an example of my "Nrf24.h" class definition (public's only, I haven't drafted the private's and probably won't until I really start writing the code):

class Nrf24 {
  public:
    Nrf24(uint8_t cePin, uint8_t csnPin, uint8_t irqPin);
    void begin();    // Defaults used
    void begin(uint32_t datarate);  // Specify bitrate
    void begin(uint32_t datarate, uint8_t channel);  // Specify bitrate & channel
    void end();      // Shut it off, clear the library's state

    // I/O
    boolean available();  // Check if incoming data is ready to be read
    boolean write(void *buf, uint8_t len);   /* Send packet, return true/false for autoACK
                                              * (true always if autoACK disabled or 250Kbps rate used)
                                              */
    uint8_t read(void *inbuf);    // Read contents of RX buffer, return length
    uint8_t read(void *inbuf, uint8_t maxlen);  // Read contents of RX buffer up to 'maxlen' bytes, return final length.
    void autoAck(boolean onoff);  // Enable/disable auto-acknowledgements (enabled by default)

    // Power-state related stuff-
    void deepsleep();  // Enter POWERDOWN mode, ~0.9uA power consumption
    void enableRX();   // Enter PRX mode (~14mA)
    void disableRX();  /* Disable PRX mode (PRIM_RX bit in CONFIG register)
                        * Note this won't necessarily push the transceiver into deep sleep, but rather
                        * an idle standby mode where its internal oscillators are ready & running but
                        * the RF transceiver PLL is disabled.  ~26uA power consumption.
                        */
 
    // Custom tweaks to RF parameters, packet parameters
    void setChannel(uint8_t channel);
    void setTXpower(int8_t dBm);  // Only a few values supported by this (0, -6, -12, -18 dBm)
    void setSpeed(uint32_t rfspeed);
    void setCRC(boolean onoff, boolean crc16bit);  /* Enable/disable CRC usage inside nRF24's hardware packet engine,
                                                    * specify 8 or 16-bit CRC.
                                                    */

    // Protocol addressing -- receive, transmit addresses
    void setAddressLength(uint8_t len);  // Valid parameters = 3, 4 or 5.  Defaults to 5.
    void setRXaddress(uint8_t *rxaddr);  // 3-5 byte RX address loaded into pipe#1
    void setTXaddress(uint8_t *txaddr);  // 3-5 byte TX address loaded into TXaddr register

    // Miscellaneous feature
    boolean rfSignalDetected();  /* Read RPD register to determine if transceiver has presently detected an RF signal
                                  * of -64dBm or greater.  Only works in PRX (enableRX()) mode.
                                  */
}

What do you think?  General usage would be to instantiate an Nrf24 object in the global, use begin() to start it and specify some custom params (data rate, or data rate & channel), then the transceiver is just sitting there idle until you either transmit (with setTXaddress() + write()) or enable RX mode with setRXaddress() + enableRX() and poll the available() function periodically.

 

One thing I haven't worked out yet is how to implement the IRQ feature with Energia.  The nRF24 has an IRQ pin that is best hooked up for optimal function.  Can the library just use attachInterrupt() on the irqPin value that the user passes up top?

Link to post
Share on other sites
  • Replies 352
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Popular Posts

Hi folks... So I'm a little new to Energia, but since I wrote a library for the nRF24L01+ digital transceivers for native C-based apps I figured it'd be a great idea to port this to Energia.   I'm r

Ok, so I ported the Enrf library over so that it works with both MSP430 and Stellaris.  Seems to work great.  I am watching the tx rx demo between msp430g2553 (on the RX side), and the StellarPad doin

Okie doke - https://github.com/spirilis/Enrf24   The examples are the same as the ones I posted... I'll write up the API in a wiki page soon.  Alternately, I put documentation in the Enrf24.h file f

Posted Images

Hmm ok, brainstormed with Rick over IRC a bit.

 

I'm going to inherit from the Print class, and make a write interface that should feel somewhat intuitive although with a catch.

 

The user will set the TX address, then start writing into a buffer maintained by the lib, so that every nrf.print() function basically stuffs a buffer but doesn't actually transmit the data OTA yet.

 

Once that buffer hits its limit (32 bytes), it will force a send.  AutoACK results will be available from another function like "nrf.txAckOK()" or something.

Alternately, the user can force the library to transmit what it has using the flush() function.

 

So to send a single 16-bit integer to a remote transceiver, it will be as simple as:

 

nrf.print(intvar);

nrf.flush();  // Transmit occurs here

if (!nrf.txAckOK()) {  // Oh dear, something bad happened or the remote transceiver is no longer present!

 ...

}

 

On the receiver side though, I think the best approach will be to stick with my current idea--a read() function that expects you to provide a buffer up to 32 bytes long (or "maxlen" if you specify it) and tells you how many were read.  Doing a byte-by-byte read is neither useful (IMO) nor optimal since that means the Nrf24 lib would have to maintain yet another 32-byte buffer internally--for storing the read data.  That means the instance would take up 64 bytes + whatever else, just feels fat for an MSP430.

Link to post
Share on other sites

Made a lot of progress-- got two simple demo programs working, one periodically sends "ON" and "OFF" alternating once a second, the other turns on the P1_0 red LED in response to those strings (strcmp(inbuf, str_on) == 0 turns it on, strcmp(inbuf, str_off) == 0 turns it off, str_on/str_off are const's)

 

 

Enrf24_TXdemo:

#include <Enrf24.h>
#include <nRF24L01.h>
#include <string.h>
#include <SPI.h>

Enrf24 radio(P2_0, P2_1, P2_2);  // P2.0=CE, P2.1=CSN, P2.2=IRQ
const uint8_t txaddr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x01 };

const char *str_on = "ON";
const char *str_off = "OFF";

void setup() {
  SPI.begin();
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(1); // MSB-first

  radio.begin();  // Defaults 1Mbps, channel 0, max TX power
  radio.setTXaddress((void*)txaddr);
}

void loop() {
  radio.print(str_on);
  radio.flush();  // Force transmit (don't wait for any more data)
  delay(1000);
  radio.print(str_off);
  radio.flush();  //
  delay(1000);
}

 

 

Enrf24_RXdemo:

#include <Enrf24.h>
#include <nRF24L01.h>
#include <string.h>
#include <SPI.h>

Enrf24 radio(P2_0, P2_1, P2_2);  // P2.0=CE, P2.1=CSN, P2.2=IRQ

const uint8_t rxaddr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x01 };

const char *str_on = "ON";
const char *str_off = "OFF";

void setup() {
  SPI.begin();
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(1); // MSB-first
  
  radio.begin();  // Defaults 1Mbps, channel 0, max TX power
  radio.setRXaddress((void*)rxaddr);
  
  pinMode(P1_0, OUTPUT);
  digitalWrite(P1_0, LOW);
  
  radio.enableRX();  // Start listening
}

void loop() {
  char inbuf[33];
  
  while (!radio.available(true))
    ;
  if (radio.read(inbuf)) {
    if (!strcmp(inbuf, str_on))
      digitalWrite(P1_0, HIGH);
    if (!strcmp(inbuf, str_off))
      digitalWrite(P1_0, LOW);
  }
}
Link to post
Share on other sites

Going to roll this up (with examples in the examples/ subdir, create the IDE hints, etc) and put it up on github shortly.

 

This library is not the most flexible way to manipulate the transceiver, as it makes a few simple assumptions:

 

1. Dynamic Payload Length is always enabled

2. Only 1 receive pipe is supported, the MultiCeiver stuff is not (truth is, it's extensible enough that I could add that in the future)

3. Pipe#0 is always using its default (E7E7E7E7E7) address and any data coming in to that addr gets silently flushed.  I recommend not using the E7E7E7E7E7 address for any of your applications.  (This internally has to do with implementing AutoACK properly)

4. User needs to poll available() to see if incoming data is waiting, there are no ISRs that callback/wake-up the MSP430 in the event that data is available.

 

The goal here is simplicity, in keeping with the spirit of Energia.  I think this will fulfill that and give users a quick & simple way to use their radios.

 

Ideally I'd like to implement ISR/IRQ wakeup so the user can put the chip into deep LPM4 sleep, but it looks like the Energia/Arduino API just doesn't facilitate that.  I'd like to be able to attach an interrupt function to the IRQ pin so if it fires, it wakes the chip up, but I'd need some API in attachInterrupt() that tells it to do this (i.e. perform the __bic_SR_register_on_exit(LPM4_bits) in the main P1/P2 ISR function).  Rick tossed some ideas around with me and I'm just going to hold off on implementing any of that.

Link to post
Share on other sites

Okie doke - https://github.com/spirilis/Enrf24

 

The examples are the same as the ones I posted... I'll write up the API in a wiki page soon.  Alternately, I put documentation in the Enrf24.h file for the various functions.

 

edit: Zip file of the v1.0 release- Enrf24_v1_0.zip

Link to post
Share on other sites

Ok, I installed Fritzing and was going to hunt for a part for the nRF24L01+ but didn't find one at first peek... so I'll describe it in text.

 

Here's the general idea, for the G2553 chip at least:

 

nRF pin / LaunchPad pin

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

Vcc -> Vcc

GND -> GND

 

SCK -> P1.5

MO -> P1.7

MI -> P1.6

(note-- MO and MI should be reversed for the G2xx2 and G2xx1 chips)

CE -> P2.0

CSN -> P2.1

IRQ -> P2.2

 

That should work with the examples out of the box.  Remember the constructor for the Enrf24 class "Enrf24 radio(..., ..., ...);" near the top takes the pins in order of CE, CSN and IRQ.

Link to post
Share on other sites

Thank you, that allow detect issue.

Here is color scheme of how to connect wires to balck nrf module(first phone it 2 side of nrf, second one is msp430)

https://picasaweb.google.com/arheops/Nrf24L01

 

looking forward for more options. Actualy best variant i am looking is: turn on to 16mhz,setup radio, send packet, change to read, read response from main node, go power saving radio, go 32khz,go LM3 mode, wait Xsec, radio wakeup&send packet(that can be used to create solar powered sensor network)

 

But your current code already huge breakout in code complexity decrease.

 

Checking sleep modes.

This code:

 

void loop() {
  radio.begin();
  radio.setTXaddress((void*)txaddr);
  radio.print(str_on);
  radio.flush();  // Force transmit (don't wait for any more data)
  delay(100);
  radio.deepsleep();
  delay(5000);
  radio.begin();
  radio.setTXaddress((void*)txaddr);
  radio.print(str_off);
  radio.flush();  //
  delay(100);
  radio.deepsleep();
  delay(5000);
}

 

 

take less then <10uA )) sorry,not able measure <10uA current.

 

 

update: looks like i measure it wrong. consumption without lpm3 is 0.7-1.3ma depend of voltage.

post-3-0-35261600-1359125484_thumb.jpg

post-3-0-34227200-1359125485_thumb.jpg

Link to post
Share on other sites

After deepsleep, just do another radio.print() and radio.flush() ... during .flush() it will wake it back up, pausing 5ms or so to let it spin up.

 

Also .enableRX() will wake it up and wait 5ms likewise.

 

The low-power stuff I'm not going to do anything with for a while, since IMO Energia doesn't provide enough infrastructure to make it easy for libraries to use/etc.

 

However, if you'd like to implement it yourself, on the TX side there's nothing stopping you from sleeping (going into LPM3, waking up at your leisure and doing more I/O).  On the RX side though, the central idea is to .enableRX(), then set up the port interrupt so when the IRQ line goes low, an ISR is called that does the __bic_SR_register_on_exit(LPM4_bits); ... that's the piece Energia does not sufficiently handle right now.  Keep in mind the transceiver uses ~14mA continuously when in RX mode though, so the power savings you get are just the few mA's that the MSP430 uses when it's in active mode.

Link to post
Share on other sites

There WAS problem in wakeup nrf after lm3 sleep when i tested it last time. looks like spi go off after that. same issue if go to 2mhz mode.

but anyway even current power consumption is low enought, i will play with modes letter.

 

my transiver use 18mA when in RX mode. but i am thinking about just start it to rx for 50ms after send packet. so on server side it do send instruction 20 ms after it get packet. working for 50ms/per every 10 second will give enought low power.

yes, i understand that RX take power. but RX side will be connected to wifi,so it need power anyway. while tx part have allow 2 side communication(at least to turn/off connected sensors and change resolution) and will be powered by solar or battery/capacitor.

Link to post
Share on other sites

ah yeah, my own grill monitor project has a base station USB-attached to a PC so it sits in RX mode all the time.  The TX wireless unit keeps an RX back-window open for about 300ms (although I don't make use of it yet).  I figure 100ms would be a good "minimum" back-window time to give the base station enough time to process the packet I just sent and then turn around to transmit something back...

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...