This is a work in progress for an NRF2401P

Dependents:   NRF_receiver sender locker4 Weather_Station_Ofiicial ... more

About

This is a simple library to drive the nRF24l01+.

Hardware

This uses the commonly available breakout. The connections are shown below /media/uploads/epgmdm/nrf24l01pinout.png

Software

Use case: For a simple transmitter

tx code snipet

#include "NRF2401P.h"
int main() {
*
*  long long addr1=0xAB00CD; // setup address - any 5 byte number - same as RX
*  int channel =0x12;  // [0-126] setup channel, must be same as RX
*  bool txOK;
*  char msg[32];
*  char ackData[32];
*  char len;
*
*  // Setup 
*  NRF2401P nrf1(PTD6,PTD7, PTD5,PTD4, PTC12); //mosi, miso, sclk, csn, ce)
*  nrf1.quickTxSetup(channel, addr1); // sets nrf24l01+ as transmitter
*
*  // transmit
*  strcpy (msg, "Hello"); 
*  txOK= nrf1.transmitData(msg,strlen(msg));
*
*  // read ack data if available
*  if (nrf1.isAckData()) { 
*      len= nrf1.getRxData(ackData); // len is number of bytes in ackData
*   }
*}

Use case: For a simple receiver

rx code snipet

#include "NRF2401P.h"
*int main(){
*        
*  long long addr1=0xAB00CD; // setup address - any 5 byte number - same as TX
*  int channel =0x12;  // [0-126] setup channel, must be same as TX
*  bool txOK;
*  char msg[32];
*  char ackData[32];
*  char len;
*
*  // Setup 
*  NRF2401P nrf1(PTD6,PTD7, PTD5,PTD4, PTC12); //mosi, miso, sclk, csn, ce)
*  nrf1.quickRxSetup(channel, addr1); // sets nrf24l01+ as  receiver, using pipe 1
*
*  // set ack data
*  sprintf(ackData,"Ack data");
*  nrf1.acknowledgeData(ackData, strlen(ackData),1); // ack for pipe 1
*    
*  // receive
*  while (! nrf1.isRxData()); // note this blocks until RX data
*  len= nrf1.getRxData(msg); // gets the message, len is length of msg
*
*}

NRF2401P.cpp

Committer:
epgmdm
Date:
2015-06-12
Revision:
4:e55807cf840b
Parent:
3:afe8d307b5c3
Child:
6:77ead8abdd1c
Child:
15:9998698cb041

File content as of revision 4:e55807cf840b:

/**
 *@section DESCRIPTION
 * mbed NRF2401+  Library
 *@section LICENSE
 * Copyright (c) 2015, Malcolm McCulloch
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * @file "NRF2401P.cpp"
 */
#include "mbed.h"
#include "NRF2401P.h"
#include "nRF24l01.h"


NRF2401P::NRF2401P ( PinName mosi, PinName miso, PinName sclk, PinName _csn, PinName _ce ) :
    csn( DigitalOut( _csn ) ), ce( DigitalOut( _ce ) )
{
    addressWidth = 5;
    pc = new Serial( USBTX, USBRX ); // tx, rx
    sprintf(logMsg, "Initialise " );
    log(logMsg);
    spi = new SPI( mosi, miso, sclk, NC );  //SPI (PinName mosi, PinName miso, PinName sclk, PinName _unused=NC)
    spi->frequency( 10000000 ); // 1MHZ max 10 MHz
    spi->format( 8, 0 ); // 0: 0e 08; 1: 0e 00; 2:0e 00 ;3:1c 00
    csn = 1;
    ce = 0;
    dynamic = false;
    debug=false;

};

/**
* writes to pc and waits
*/
void NRF2401P::log (char *msg)
{
    if(debug) {
        printf("\t <%s \t %s>\n\r",statusString(), msg);
        wait(0.01);
    };
}

void NRF2401P::scratch()
{
    int status = 0;
    int register1 = 0;
    ce = 0;
    for ( char i = 0; i < 24; i++ ) {
        csn = 0;
        //wait_us(100);
        status = spi->write( i );
        register1 = spi->write( 0x00 );
        csn = 1;
        sprintf(logMsg, "  register %02x (%02x) = %02x", i, status, register1 );
        log(logMsg);
    }

}
/**
* start here to configure the basics of the NRF
*/

void NRF2401P::start()
{
    writeReg( CONFIG, 0x0c ); // set 16 bit crc
    setTxRetry(0x01,0x0f); // 500 uS, 15 retries
    setRadio(0,0x03); // 1MB/S  0dB
    setDynamicPayload();
    setChannel(76);  // should be clear?
    setAddressWidth(5);
    flushRx();
    flushTx();
    setPwrUp();
    setTxMode(); // just make sure no spurious reads....

}
/**
* Sets up a reciever using shockburst and dynamic payload. Uses pipe 1
* defaults to 5 bytes
*/
void NRF2401P::quickRxSetup(int channel,long long addrRx)
{
    start();
    setChannel(channel);
    setRxAddress(addrRx,1);
    setRxMode();
    ce=1;
    wait (0.001f);
}
/**
* Sets up for receive of a message to address 0XA0A0A0
*/

char NRF2401P::testReceive()
{
    char message[64];
    char width;
    int channel = 0x12;
    long long addr=0xA0B0C0;
    debug = true;
    quickRxSetup(channel, addr);

    while (1) {
        while (!isRxData()) {
            //wait(0.5);
        };
        width=getRxData(message);
        message[width]='\0';
        sprintf(logMsg,"Received= [%s]",message);
        log(logMsg);
    };
}

/**
* Sets the number and the timimg of TX retries
*/

void NRF2401P::setTxRetry(char delay, char numTries)
{
    char val  = (delay&0xf)<<4 | (numTries&0xf);
    writeReg (SETUP_RETR, val);
}


/**
* Sets up a transmitter using shockburst and dynamic payload. Uses pipe 1
* defaults to 5 bytes
*/
void NRF2401P::quickTxSetup(int channel,long long addr)
{
    start();
    setChannel(channel);
    setTxAddress(addr);
    setTxMode();
    ce=1;
    wait (0.0016f); // wait for pll to settle
}

/**
* Sets up for transmit of a message to address 0XA0A0A0
*/

char NRF2401P::testTransmit()
{
    long long addr=0xA0B0C0;
    int channel = 0x12;
    char data[32] ;
    int i=0;
    quickRxSetup(channel, addr);
    while (1) {
        sprintf(data," packet %03d", i++ |100);
        transmitData(data,18);
        wait (1.0);
    }

}
/**
*Speed :
‘0x00’ – 1Mbps
‘0x01’ – 2Mbps
‘0x02’ – 250kbps
‘0x03’ – Reserved
Power:
'0x00' – -18dBm
'0x01' – -12dBm
'0x02' – -6dBm
'0x03' – 0dBm
*/
char NRF2401P::setRadio(char speed,char power)
{
    char val=0;
    sprintf(logMsg, "Set radio");
    log(logMsg);
    if (speed & 0x02) {
        val |= (1<<5);
    }
    val |= (speed & 0x01)<<3;

    val |= ((power &0x03)<<1);
    writeReg (0x06,val);

}
/**
Set RF_CH = chan;

F0= 2400 + chan [MHz]
*/
char NRF2401P::setChannel(char chan)
{
    sprintf(logMsg, "Set channel");
    log(logMsg);
    writeReg (0x05,(chan&0x7f));
}
/**
* Transmits width bytes of data. width <32
* returns 1 if succeful
*/
bool NRF2401P::transmitData( char *data, char width )
{
    if (width>32) return 0;

    //clearStatus();
    //ce = 1;
    csn = 0;
    char address = 0XA0;
    int i;
    // set up for writing
    status = spi->write( address );
    for ( i = 0; i <width; i++ ) {
        spi->write( data[ i ] );
    }
    csn = 1;
    wait(0.001);
    checkStatus();
    if ((status>>4)&1) { // Max retries - flush tx
        flushTx();
    }
    if (debug) {
        sprintf(logMsg, " Transmit data %d bytes to %02x (%02x) = %10s", width, address, status, *data );
        log(logMsg);
    }
    return status;

}

/**
* sets acknowledge data width bytes of data. width <32
*/
char NRF2401P::acknowledgeData( char *data, char width, char pipe )
{
    ce = 1;
    csn = 0;
    //writeReg(0x1d,0x06); // enable payload with ack
    char address = W_ACK_PAYLOAD | (pipe&0x07);
    int i;
    // set up for writing
    csn = 0;
    status = spi->write( address );
    for ( i = 0; i <width; i++ ) {
        spi->write( data[ i ] );
    }
    csn = 1;
    if (debug) {
        sprintf(logMsg, "  acknowledge data %d bytes to %02x (%02x) = %c", width, address, status, *data );
        log(logMsg);
    }
    return status;

}
/**
* Writes 1 byte data to a register
**/
char NRF2401P::writeReg( char address, char data )
{
    char status = 0;
    char reg;
    csn = 0;
    address &= 0x1F;
    reg = address | 0x20;
    status = spi->write( reg );
    spi->write( data );
    csn = 1;
    sprintf(logMsg, "  register write %02x (%02x) = %02x", address, status, data );
    log(logMsg);
    return status;
}
/**
* Writes width bytes data to a register, ls byte to ms byte /for adressess
**/
char NRF2401P::writeReg( char address, char *data, char width )
{
    char reg;
    csn = 0;
    int i;
    // set up for writing
    address &= 0x1F;
    reg = address| 0x20;
    status = spi->write( reg );
    for ( i = width - 1; i >= 0; i-- ) {
        spi->write( data[ i ] );
    }
    csn = 1;
    if (debug) {
        sprintf(logMsg, "  register write %d bytes to %02x (%02x) = %02x %02x %02x", width, address, status, data[0], data[1], data[2] );
        log(logMsg);
    }
    return status;
}
/**
* Reads 1 byte from a register
**/
char NRF2401P::readReg( char address, char *data )
{
    csn = 0;
    address &= 0x1F;
    status = spi->write( address );
    *data = spi->write( 0x00 );
    csn = 1;
    // sprintf(logMsg, "  register read %02x (%02x) = %02x", address, status, *data );
    // log(logMsg);
    return status;
}
/**
* Clears the status flags RX_DR, TX_DS, MAX_RT
*/
bool NRF2401P::clearStatus()
{
    status = writeReg(STATUS,0x70);
    if (debug) {
        sprintf(logMsg, "Clear status (%02x)", status );
        log(logMsg);
    }
}
/**
* flushes TX FIFO and resets status flags
*/
bool NRF2401P::flushTx()
{
    csn = 0;
    status = spi->write( FLUSH_TX );
    csn = 1;
    clearStatus();
    if (debug) {
        sprintf(logMsg, "Flush TX FIFO (%02x)", status );
        log(logMsg);
    }
    return;
}

/**
* flushes RX FIFO and resets status flags
*/
bool NRF2401P::flushRx()
{
    csn = 0;
    status = spi->write( FLUSH_RX );
    csn = 1;
    clearStatus();
    if (debug) {
        sprintf(logMsg, "Flush RX FIFO (%02x)", status );
        log(logMsg);
    }
}
/**
* Sets PRIM_RX = 0;
*/
bool NRF2401P::setTxMode()
{
    char data;
    char bit;
    if (debug) {
        sprintf(logMsg, "Set Tx Mode");
        log(logMsg);
    }
    readReg( CONFIG, &data );
    data &= ~( 1 << 0 );
    flushTx();
    flushRx();
    writeReg( CONFIG, data );
    writeReg( RX_ADDR_P0, txAdd, addressWidth ); // reset p0
    writeReg(EN_RXADDR,0x01); // enable pipe 0 for reading
    // check
    readReg( 0x00, &data );
    bit = ( data >> 0 ) & 1;

    ce=1;
    wait(0.003);
    return ( bit == 0 );
}

/**
* Sets the number of bytes of the address width = 3,4,5
*/
bool NRF2401P::setAddressWidth( char width )
{
    addressWidth = width;
    if ( ( width > 5 ) || ( width < 3 ) )
        return false;
    width -= 2;
    return writeReg( 0x03, width );

}
/**
* Sets the address, uses addess width set (either 3,4 or 5)
*/
char NRF2401P::setTxAddress( char *address )
{
    memcpy (txAdd,address, addressWidth);
    writeReg( 0x0A, address, addressWidth ); //Write to RX_ADDR_P0
    return writeReg( 0x10, address, addressWidth );  //Write to TX_ADDR

}

/**
* Sets the address, uses addess width set (either 3,4 or 5)
*/
char NRF2401P::setTxAddress( long long address )
{

    char buff[ 5 ];
    buff[ 0 ] = address & 0xff;
    buff[ 1 ] = ( address >> 8 ) & 0xFF;
    buff[ 2 ] = ( address >> 16 ) & 0xFF;
    buff[ 3 ] = ( address >> 24 ) & 0xFF;
    buff[ 4 ] = ( address >> 32 ) & 0xFF;
    return setTxAddress( buff );

}

/**
* Sets the address, uses addess width set (either 3,4 or 5)
* Enables pipe for receiving;
*/
char NRF2401P::setRxAddress( char *address, char pipe )
{
    if(debug) {
        log ("Set Rx Address");
    }
    if (pipe>5) return 0xff;
    if (pipe ==0) {
        memcpy (pipe0Add,address, addressWidth);
    }

    char reg = 0x0A + pipe;
    switch ( pipe ) {
        case ( 0 ) :
        case ( 1 ) : {
            status = writeReg( reg, address, addressWidth ); //Write to RX_ADDR_P0 or _P1
            break;
        }
        case ( 2 ) :
        case ( 3 ) :
        case ( 4 ) :
        case ( 5 ) : {
            status = writeReg( reg, address, 1 ); //Write to RX_ADDR_P2 ... _P5
            break;
        }

    }
    readReg(EN_RXADDR,&reg);
    reg |= (1<<pipe);
    writeReg( EN_RXADDR,reg ); //Enable the pipe
    return status;

}

/**
* Sets the address of pipe (<=5), uses addess width set (either 3,4 or 5)
*/
char NRF2401P::setRxAddress( long long address, char pipe )
{
    char buff[ 5 ];
    buff[ 0 ] = address & 0xff;
    buff[ 1 ] = ( address >> 8 ) & 0xFF;
    buff[ 2 ] = ( address >> 16 ) & 0xFF;
    buff[ 3 ] = ( address >> 24 ) & 0xFF;
    buff[ 4 ] = ( address >> 32 ) & 0xFF;
    return setRxAddress( buff, pipe );
}
/**
*checks the status flag
*/
char NRF2401P::checkStatus()
{
    readReg(0x07,&status);
    return status;
}
/**
* checks if Ack data available.
*/
bool NRF2401P::isAckData()
{
    char fifo;
    readReg(0x17,&fifo);
    bool isData = !(fifo&0x01);
    return isData;
}

/**
* checks if RX data available.
*/
bool NRF2401P::isRxData()
{
    checkStatus();
    bool isData = (status>>6)&0x01;
    return isData;
}
/**
* returns the width of the dynamic payload
*/
char NRF2401P::getRxWidth()
{
    char width;
    if (dynamic) {
        csn = 0;
        status = spi->write( 0x60 );
        width = spi->write(0x00);
        csn = 1;

        if (width>32) {
            flushRx();
            width=0;
        }
    } else {
        status = readReg(0x12,&width); // width of p1

    }
    // width=18;
    return width;
}
/**
* return message in buffer, mem for buffer must have been allocated.
* Return value is number of bytes of buffer
*/
char  NRF2401P::getRxData(char * buffer)
{
    char address = 0x61;
    char width;
    width = getRxWidth();
    bool isData = (status>>6)&0x01;
    if (isData) {
        csn = 0;
        int i;
        // set up for reading
        status = spi->write( address );
        for ( i = 0; i <= width; i++ ) {
            buffer[i]=spi->write(0x00 );
        }
        csn = 1;
        sprintf(logMsg, "Receive data %d bytes", width );
        log(logMsg);
        clearStatus();
        return width;

    } else {
        sprintf(logMsg, "Receive NO data %d bytes", width );
        log(logMsg);
        clearStatus();
        return 0;
    }

}

/**
* Sets all the receive pipes to dynamic payload length
*/
void NRF2401P::setDynamicPayload()
{
    dynamic = true;

    writeReg(FEATURE,0x07);  // Enable Dyn payload, Payload with Ack and w_tx_noack command
    writeReg(EN_AA,0x3f); // EN_AA regi for P1 and P0
    writeReg(DYNPD, 0x1F);
}

/**
* Sets PWR_UP = 1;
*/
bool NRF2401P::setPwrUp()
{
    char data;
    char bit;
    ce=1;
    readReg( CONFIG, &data );
    if ((data>>1) &0x01) {
        return true; // Already powered up
    };
    data |= ( 0x02 );
    writeReg( 0x00, data );
    // check
    readReg( 0x00, &data );
    bit = ( data >> 1 ) & 1;

    wait(0.005); // wait 5ms
    if(debug) {
        sprintf(logMsg, "Set PWR_UP to %x", bit);
        log(logMsg);
    }

    return ( bit == 1 );
}
/**
* Sets PRIM_RX = 0;
*/
bool NRF2401P::setRxMode()
{
    char data;
    char bit;
    ce=1;
    readReg( 0x00, &data );
    data |= ( 0x01 );

    writeReg( 0x00, data );
    if (pipe0Add[0]|pipe0Add[1]|pipe0Add[2]|pipe0Add[3]|pipe0Add[4] >0) {
        setRxAddress(pipe0Add,0);
    }
    // check
    readReg( 0x00, &data );
    bit = ( data >> 0 ) & 1;

    wait (0.001);
    flushRx();
    flushTx();
    if (debug) {
        sprintf(logMsg, " set PRIM_RX to %x", bit);
        log(logMsg);
    }
    return ( bit == 1 );
}
/**
* Prints status string
*/
char * NRF2401P::statusString()
{
    char *msg;
    msg = statusS;
    if (((status>>1) & 0x07)==0x07) {
        sprintf(msg,"RX empty");
    } else {
        sprintf(msg,"pipe %02x",(status>>1) & 0x07);
    }

    if ((status>>6)&0x01) strcat(msg," RX_DR,");
    if ((status>>5)&0x01) strcat(msg," TX_DS,");
    if ((status>>4)&0x01) strcat(msg," MAX_RT,");
    if ((status>>0)&0x01) strcat(msg," TX_FLL,");

    return msg;
}