spirilis 1,265 Posted January 18, 2013 Share Posted January 18, 2013 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? xv4y, 43two, izdane and 2 others 5 Quote Link to post Share on other sites
spirilis 1,265 Posted January 18, 2013 Author Share Posted January 18, 2013 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. Quote Link to post Share on other sites
spirilis 1,265 Posted January 24, 2013 Author Share Posted January 24, 2013 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); } } Quote Link to post Share on other sites
spirilis 1,265 Posted January 24, 2013 Author Share Posted January 24, 2013 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. 43two 1 Quote Link to post Share on other sites
spirilis 1,265 Posted January 24, 2013 Author Share Posted January 24, 2013 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 calinp, Rickta59 and bluehash 3 Quote Link to post Share on other sites
spirilis 1,265 Posted January 24, 2013 Author Share Posted January 24, 2013 As a side note, I suppose this means the TI Stellaris LaunchPad now supports the nRF24 modules too! bluehash 1 Quote Link to post Share on other sites
arheops 0 Posted January 25, 2013 Share Posted January 25, 2013 Hi Can you please send scheme or picture of how to connect nrf24l01+ device to msp430? Or add that on github. Also will be nice to get some feedback to serial port in examples. It is hard determine if I have connected it ok or not, so i not success use your lib(( Quote Link to post Share on other sites
spirilis 1,265 Posted January 25, 2013 Author Share Posted January 25, 2013 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. Quote Link to post Share on other sites
arheops 0 Posted January 25, 2013 Share Posted January 25, 2013 yes, i setup like that except i put CE,CSN,IRQ to 1_4, 2_0,2_1 and not got any result. so i see no way how ot determine if my nrf module broken or ur code not work for me, thats why i am asking for some feedback from your Begin() code. i am using black module, it have gnd&vcc on top http://arduino-info.wikispaces.com/Nrf24L01-2.4GHz-HowTo Quote Link to post Share on other sites
arheops 0 Posted January 25, 2013 Share Posted January 25, 2013 i just have tried with 2 brand new nrf modules &new launchpads. set all wires like you say,not changed code. it not work( swapped mi/mo,same result Quote Link to post Share on other sites
spirilis 1,265 Posted January 25, 2013 Author Share Posted January 25, 2013 Add a Serial.begin(9600); before radio.begin(), then after radio.begin(), add a Serial.println(radio.radioState()); Tell me what it spits out... Sent from my C3PO via Tapatalk Quote Link to post Share on other sites
arheops 0 Posted January 25, 2013 Share Posted January 25, 2013 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. Quote Link to post Share on other sites
spirilis 1,265 Posted January 25, 2013 Author Share Posted January 25, 2013 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. Quote Link to post Share on other sites
arheops 0 Posted January 25, 2013 Share Posted January 25, 2013 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. Quote Link to post Share on other sites
spirilis 1,265 Posted January 25, 2013 Author Share Posted January 25, 2013 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... Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.