From 07637feef6ab2944e52b805b8a1d59a532238e23 Mon Sep 17 00:00:00 2001 From: jgromes Date: Sat, 1 Jun 2019 20:30:16 +0200 Subject: [PATCH] [nRF24] Implemented basic functions --- .../nRF24/nRF24_Receive/nRF24_Receive.ino | 82 ++++ .../nRF24/nRF24_Transmit/nRF24_Transmit.ino | 75 ++- keywords.txt | 5 +- src/TypeDef.h | 7 +- src/modules/nRF24.cpp | 444 ++++++++++++++---- src/modules/nRF24.h | 257 +++++++++- 6 files changed, 723 insertions(+), 147 deletions(-) create mode 100644 examples/nRF24/nRF24_Receive/nRF24_Receive.ino diff --git a/examples/nRF24/nRF24_Receive/nRF24_Receive.ino b/examples/nRF24/nRF24_Receive/nRF24_Receive.ino new file mode 100644 index 00000000..1ea09fb9 --- /dev/null +++ b/examples/nRF24/nRF24_Receive/nRF24_Receive.ino @@ -0,0 +1,82 @@ +/* + RadioLib nRF24 Receive Example + + This example listens for FSK transmissions using nRF24 2.4 GHz radio module. + To successfully receive data, the following settings have to be the same + on both transmitter and receiver: + - carrier frequency + - data rate + - transmit pipe on transmitter must match receive pipe + on receiver +*/ + +// include the library +#include + +// nRF24 is in slot A on the shield +nRF24 nrf = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize nRF24 + Serial.print(F("[nRF24] Initializing ... ")); + // carrier frequency: 2400 MHz + // data rate: 1000 kbps + // output power: -12 dBm + // address width: 5 bytes + int state = nrf.begin(); + if(state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while(true); + } + + // set receive pipe 0 address + // NOTE: address width in bytes MUST be equal to the + // width set in begin() or setAddressWidth() + // methods (5 by default) + Serial.print(F("[nRF24] Setting address for receive pipe 0 ... ")); + byte addr[] = {0x01, 0x23, 0x45, 0x67, 0x89}; + state = nrf.setReceivePipe(0, addr); + if(state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while(true); + } +} + +void loop() { + Serial.print(F("[nRF24] Waiting for incoming transmission ... ")); + + // you can receive data as an Arduino String + // NOTE: receive() is a blocking method! + // See example ReceiveInterrupt for details + // on non-blocking reception method. + String str; + int state = nrf.receive(str); + + // you can also receive data as byte array + /* + byte byteArr[8]; + int state = nrf.receive(byteArr, 8); + */ + + if (state == ERR_NONE) { + // packet was successfully received + Serial.println(F("success!")); + + // print the data of the packet + Serial.print(F("[nRF24] Data:\t\t")); + Serial.println(str); + + } else if (state == ERR_RX_TIMEOUT) { + // timeout occurred while waiting for a packet + Serial.println(F("timeout!")); + + } +} diff --git a/examples/nRF24/nRF24_Transmit/nRF24_Transmit.ino b/examples/nRF24/nRF24_Transmit/nRF24_Transmit.ino index bb55b595..304ab6b1 100644 --- a/examples/nRF24/nRF24_Transmit/nRF24_Transmit.ino +++ b/examples/nRF24/nRF24_Transmit/nRF24_Transmit.ino @@ -1,5 +1,13 @@ /* RadioLib nRF24 Transmit Example + + This example transmits packets using nRF24 2.4 GHz radio module. + Each packet contains up to 32 bytes of data, in the form of: + - Arduino String + - null-terminated char array (C-string) + - arbitrary binary data (byte array) + + Packet delivery is automatically acknowledged by the receiver. */ // include the library @@ -15,6 +23,7 @@ void setup() { Serial.print(F("[nRF24] Initializing ... ")); // carrier frequency: 2400 MHz // data rate: 1000 kbps + // output power: -12 dBm // address width: 5 bytes int state = nrf.begin(); if(state == ERR_NONE) { @@ -25,46 +34,13 @@ void setup() { while(true); } - // set receive pipe 0 address - // NOTE: address width in bytes MUST be equal to the + // set transmit address + // NOTE: address width in bytes MUST be equal to the // width set in begin() or setAddressWidth() // methods (5 by default) - Serial.print(F("[nRF24] Setting address for receive pipe 0 ... ")); - byte receiveAddr0[] = {0x05, 0x06, 0x07, 0x08, 0x09}; - state = nrf.setReceivePipe(0, receiveAddr0); - if(state == ERR_NONE) { - Serial.println(F("success!")); - } else { - Serial.print(F("failed, code ")); - Serial.println(state); - while(true); - } - - // set receive pipe 1 - 5 address - // NOTE: unlike receive pipe 0, pipes 1 - 5 are only - // distinguished by their least significant byte, - // the upper bytes will be the same! - Serial.print(F("[nRF24] Setting addresses for receive pipes 1 - 5 ... ")); - byte receiveAddr1[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xE1}; - // set pipe 1 address - state = nrf.setReceivePipe(1, receiveAddr1); - // set the addresses for rest of pipes - state |= nrf.setReceivePipe(2, 0xE2); - state |= nrf.setReceivePipe(3, 0xE3); - state |= nrf.setReceivePipe(4, 0xE4); - state |= nrf.setReceivePipe(5, 0xE5); - if(state == ERR_NONE) { - Serial.println(F("success!")); - } else { - Serial.print(F("failed, code ")); - Serial.println(state); - while(true); - } - - // pipes 1 - 5 are automatically enabled upon address - // change, but can be disabled manually - Serial.print(F("[nRF24] Disabling pipes 2 - 5 ... ")); - state = nrf.disablePipe(2); + byte addr[] = {0x01, 0x23, 0x45, 0x67, 0x89}; + Serial.print(F("[nRF24] Setting transmit pipe ... ")); + state = nrf.setTransmitPipe(addr); if(state == ERR_NONE) { Serial.println(F("success!")); } else { @@ -77,23 +53,26 @@ void setup() { void loop() { Serial.print(F("[nRF24] Transmitting packet ... ")); - // set transmit address - // NOTE: address width in bytes MUST be equal to the - // width set in begin() or setAddressWidth() - // methods (5 by default) - byte addr[] = {0x00, 0x01, 0x02, 0x03, 0x04}; - // you can transmit C-string or Arduino string up to // 32 characters long - int state = nrf.transmit("Hello World!", addr); + int state = nrf.transmit("Hello World!"); if (state == ERR_NONE) { // the packet was successfully transmitted - Serial.println(" success!"); + Serial.println(F("success!")); } else if (state == ERR_PACKET_TOO_LONG) { - // the supplied packet was longer than 256 bytes - Serial.println(" too long!"); + // the supplied packet was longer than 32 bytes + Serial.println(F("too long!")); + + } else if (state == ERR_ACK_NOT_RECEIVED) { + // acknowledge from destination module + // was not received within 15 retries + Serial.println(F("ACK not received!")); + + } else if (state == ERR_TX_TIMEOUT) { + // timed out while transmitting + Serial.println(F("timeout!")); } diff --git a/keywords.txt b/keywords.txt index 49e7c600..7621a797 100644 --- a/keywords.txt +++ b/keywords.txt @@ -116,9 +116,11 @@ getPacketSource KEYWORD2 getPacketData KEYWORD2 # nRF24 -setTransmitAddress KEYWORD2 +setAddressWidth KEYWORD2 +setTransmitPipe KEYWORD2 setReceivePipe KEYWORD2 disablePipe KEYWORD2 +getStatus KEYWORD2 # HTTP get KEYWORD2 @@ -218,6 +220,7 @@ ERR_UNSUPPORTED_ENCODING LITERAL1 ERR_INVALID_DATA_RATE LITERAL1 ERR_INVALID_ADDRESS_WIDTH LITERAL1 ERR_INVALID_PIPE_NUMBER LITERAL1 +ERR_ACK_NOT_RECEIVED LITERAL1 ERR_INVALID_NUM_BROAD_ADDRS LITERAL1 diff --git a/src/TypeDef.h b/src/TypeDef.h index 4e567cfd..b007f408 100644 --- a/src/TypeDef.h +++ b/src/TypeDef.h @@ -395,7 +395,7 @@ */ #define ERR_UNSUPPORTED_ENCODING -402 -// nRF24 status codes +// nRF24-specific status codes /*! \brief Supplied data rate is invalid. @@ -412,6 +412,11 @@ */ #define ERR_INVALID_PIPE_NUMBER -503 +/*! + \brief ACK packet from destination module was not received within 15 retries. +*/ +#define ERR_ACK_NOT_RECEIVED -504 + // CC1101-specific status codes /*! diff --git a/src/modules/nRF24.cpp b/src/modules/nRF24.cpp index cdd87f4d..59e13a04 100644 --- a/src/modules/nRF24.cpp +++ b/src/modules/nRF24.cpp @@ -1,36 +1,66 @@ #include "nRF24.h" -nRF24::nRF24(Module* mod) { +nRF24::nRF24(Module* mod) : PhysicalLayer(NRF24_CRYSTAL_FREQ, NRF24_DIV_EXPONENT) { _mod = mod; } -int16_t nRF24::begin(int16_t freq, int16_t dataRate, uint8_t addrWidth) { +int16_t nRF24::begin(int16_t freq, int16_t dataRate, int8_t power, uint8_t addrWidth) { // set module properties _mod->SPIreadCommand = NRF24_CMD_READ; _mod->SPIwriteCommand = NRF24_CMD_WRITE; _mod->init(USE_SPI, INT_BOTH); - + // override pin mode on INT0 (connected to nRF24 CE pin) pinMode(_mod->getInt0(), OUTPUT); - - // set frequency - int16_t state = setFrequency(freq); + digitalWrite(_mod->getInt0(), LOW); + + // wait for minimum power-on reset duration + delay(100); + + // check SPI connection + int16_t val = _mod->SPIgetRegValue(NRF24_REG_SETUP_AW); + if(!((val >= 1) && (val <= 3))) { + DEBUG_PRINTLN(F("No nRF24 found!")); + _mod->term(); + return(ERR_CHIP_NOT_FOUND); + } + + // configure settings inaccessible by public API + int16_t state = config(); if(state != ERR_NONE) { return(state); } - + + // set mode to standby + state = standby(); + if(state != ERR_NONE) { + return(state); + } + + // set frequency + state = setFrequency(freq); + if(state != ERR_NONE) { + return(state); + } + // set data rate state = setDataRate(dataRate); if(state != ERR_NONE) { return(state); } - + + // set output power + state = setOutputPower(power); + if(state != ERR_NONE) { + return(state); + } + // set address width state = setAddressWidth(addrWidth); if(state != ERR_NONE) { return(state); } - + return(state); } @@ -39,60 +69,198 @@ int16_t nRF24::sleep() { } int16_t nRF24::standby() { + // make sure carrier output is disabled + _mod->SPIsetRegValue(NRF24_REG_RF_SETUP, NRF24_CONT_WAVE_OFF, 7, 7); + _mod->SPIsetRegValue(NRF24_REG_RF_SETUP, NRF24_PLL_LOCK_OFF, 4, 4); + digitalWrite(_mod->getInt0(), LOW); + + // use standby-1 mode return(_mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_POWER_UP, 1, 1)); } -int16_t nRF24::transmit(String& str, uint8_t* addr) { - return(nRF24::transmit(str.c_str(), addr)); +int16_t nRF24::transmit(uint8_t* data, size_t len, uint8_t addr) { + // start transmission + int16_t state = startTransmit(data, len, addr); + if(state != ERR_NONE) { + return(state); + } + + // wait until transmission is finished + uint32_t start = micros(); + while(digitalRead(_mod->getInt1())) { + // check maximum number of retransmits + if(getStatus(NRF24_MAX_RT)) { + standby(); + clearIRQ(); + return(ERR_ACK_NOT_RECEIVED); + } + + // check timeout: 15 retries * 4ms (max Tx time as per datasheet) + if(micros() - start >= 60000) { + standby(); + clearIRQ(); + return(ERR_TX_TIMEOUT); + } + } + + // clear interrupts + clearIRQ(); + + return(state); } -int16_t nRF24::transmit(const char* str, uint8_t* addr) { - return(nRF24::transmit((uint8_t*)str, strlen(str), addr)); +int16_t nRF24::receive(uint8_t* data, size_t len) { + // start reception + int16_t state = startReceive(); + if(state != ERR_NONE) { + return(state); + } + + // wait for Rx_DataReady or timeout + uint32_t start = micros(); + while(digitalRead(_mod->getInt1())) { + // check timeout: 15 retries * 4ms (max Tx time as per datasheet) + if(micros() - start >= 60000) { + standby(); + clearIRQ(); + return(ERR_RX_TIMEOUT); + } + } + + // read the received data + return(readData(data, len)); } -int16_t nRF24::transmit(uint8_t* data, size_t len, uint8_t* addr) { +int16_t nRF24::transmitDirect(uint32_t frf) { + // set raw frequency value + if(frf != 0) { + uint8_t freqRaw = frf - 2400; + _mod->SPIwriteRegister(NRF24_REG_RF_CH, freqRaw & 0b01111111); + } + + // output carrier + int16_t state = _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_PTX, 0, 0); + state |= _mod->SPIsetRegValue(NRF24_REG_RF_SETUP, NRF24_CONT_WAVE_ON, 7, 7); + state |= _mod->SPIsetRegValue(NRF24_REG_RF_SETUP, NRF24_PLL_LOCK_ON, 4, 4); + digitalWrite(_mod->getInt0(), HIGH); + return(state); +} + +int16_t nRF24::receiveDirect() { + // nRF24 is unable to directly output demodulated data + // this method is implemented only for PhysicalLayer compatibility + return(ERR_NONE); +} + +void nRF24::setIrqAction(void (*func)(void)) { + attachInterrupt(digitalPinToInterrupt(_mod->getInt1()), func, FALLING); +} + +int16_t nRF24::startTransmit(uint8_t* data, size_t len, uint8_t addr) { + // suppress unused variable warning + (void)addr; + + // check packet length + if(len > 32) { + return(ERR_PACKET_TOO_LONG); + } + // set mode to standby int16_t state = standby(); if(state != ERR_NONE) { return(state); } - - // reverse address byte order (LSB must be written first) - uint8_t* addrReversed = new uint8_t[_addrWidth]; - for(uint8_t i = 0; i < _addrWidth; i++) { - addrReversed[i] = addr[_addrWidth - 1 - i]; - } - - // set transmit address - _mod->SPIwriteRegisterBurst(NRF24_REG_TX_ADDR, addrReversed, _addrWidth); - - // check packet length - if(len > 32) { - return(ERR_PACKET_TOO_LONG); - } - - // enable Tx_DataSent interrupt - state = _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_MASK_RX_DR_IRQ_OFF | NRF24_MASK_TX_DS_IRQ_ON | NRF24_MASK_MAX_RT_IRQ_OFF, 6, 4); - - // fill Tx FIFO - SPIwriteTxPayload(data, len); - + // enable primary Tx mode - state |= _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_PTX, 0, 0); - + state = _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_PTX, 0, 0); + + // clear interrupts + clearIRQ(); + + // enable Tx_DataSent interrupt + state |= _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_MASK_TX_DS_IRQ_ON, 5, 5); + if(state != ERR_NONE) { + return(state); + } + + // flush Tx FIFO + SPItransfer(NRF24_CMD_FLUSH_TX); + + // fill Tx FIFO + uint8_t buff[32]; + memset(buff, 0x00, 32); + memcpy(buff, data, len); + SPIwriteTxPayload(data, len); + // CE high to start transmitting digitalWrite(_mod->getInt0(), HIGH); - - // wait until transmission is finished - while(digitalRead(_mod->getInt1())); - - // CE low + delayMicroseconds(10); digitalWrite(_mod->getInt0(), LOW); - + + return(state); +} + +int16_t nRF24::startReceive() { + // set mode to standby + int16_t state = standby(); + if(state != ERR_NONE) { + return(state); + } + + // enable primary Rx mode + state = _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_PRX, 0, 0); + if(state != ERR_NONE) { + return(state); + } + + // enable Rx_DataReady interrupt + clearIRQ(); + state |= _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_MASK_RX_DR_IRQ_ON, 6, 6); + if(state != ERR_NONE) { + return(state); + } + + // flush Rx FIFO + SPItransfer(NRF24_CMD_FLUSH_RX); + + // CE high to start receiving + digitalWrite(_mod->getInt0(), HIGH); + + // wait to enter Rx state + delayMicroseconds(130); + + return(state); +} + +int16_t nRF24::readData(uint8_t* data, size_t len) { + // set mode to standby + int16_t state = standby(); + if(state != ERR_NONE) { + return(state); + } + + // read payload length + uint8_t buff[1]; + SPItransfer(NRF24_CMD_READ_RX_PAYLOAD_WIDTH, false, NULL, buff, 1); + + size_t length = buff[0]; + + // read packet data + if(len == 0) { + // argument 'len' equal to zero indicates String call, which means dynamically allocated data array + // dispose of the original and create a new one + delete[] data; + data = new uint8_t[length + 1]; + } + SPIreadRxPayload(data, length); + + // add terminating null + data[length] = 0; + // clear interrupt clearIRQ(); - - return(state); + + return(ERR_NONE); } int16_t nRF24::setFrequency(int16_t freq) { @@ -100,13 +268,13 @@ int16_t nRF24::setFrequency(int16_t freq) { if(!((freq >= 2400) && (freq <= 2525))) { return(ERR_INVALID_FREQUENCY); } - + // set mode to standby int16_t state = standby(); if(state != ERR_NONE) { return(state); } - + // set frequency uint8_t freqRaw = freq - 2400; state = _mod->SPIsetRegValue(NRF24_REG_RF_CH, freqRaw, 6, 0); @@ -119,7 +287,7 @@ int16_t nRF24::setDataRate(int16_t dataRate) { if(state != ERR_NONE) { return(state); } - + // set data rate if(dataRate == 250) { state = _mod->SPIsetRegValue(NRF24_REG_RF_SETUP, NRF24_DR_250_KBPS, 5, 5); @@ -133,7 +301,38 @@ int16_t nRF24::setDataRate(int16_t dataRate) { } else { return(ERR_INVALID_DATA_RATE); } - + + return(state); +} + +int16_t nRF24::setOutputPower(int8_t power) { + // set mode to standby + int16_t state = standby(); + if(state != ERR_NONE) { + return(state); + } + + // check allowed values + uint8_t powerRaw = 0; + switch(power) { + case -18: + powerRaw = NRF24_RF_PWR_18_DBM; + break; + case -12: + powerRaw = NRF24_RF_PWR_12_DBM; + break; + case -6: + powerRaw = NRF24_RF_PWR_6_DBM; + break; + case 0: + powerRaw = NRF24_RF_PWR_0_DBM; + break; + default: + return(ERR_INVALID_OUTPUT_POWER); + } + + // write new register value + state = _mod->SPIsetRegValue(NRF24_REG_RF_SETUP, powerRaw, 2, 1); return(state); } @@ -143,7 +342,7 @@ int16_t nRF24::setAddressWidth(uint8_t addrWidth) { if(state != ERR_NONE) { return(state); } - + // set address width switch(addrWidth) { case 3: @@ -158,10 +357,26 @@ int16_t nRF24::setAddressWidth(uint8_t addrWidth) { default: return(ERR_INVALID_ADDRESS_WIDTH); } - + // save address width _addrWidth = addrWidth; - + + return(state); +} + +int16_t nRF24::setTransmitPipe(uint8_t* addr) { + // set mode to standby + int16_t state = standby(); + if(state != ERR_NONE) { + return(state); + } + + // set transmit address + _mod->SPIwriteRegisterBurst(NRF24_REG_TX_ADDR, addr, _addrWidth); + + // set Rx pipe 0 address (for ACK) + _mod->SPIwriteRegisterBurst(NRF24_REG_RX_ADDR_P0, addr, _addrWidth); + return(state); } @@ -171,26 +386,20 @@ int16_t nRF24::setReceivePipe(uint8_t pipeNum, uint8_t* addr) { if(state != ERR_NONE) { return(state); } - - // reverse byte order (LSB must be written first) - uint8_t* addrReversed = new uint8_t[_addrWidth]; - for(uint8_t i = 0; i < _addrWidth; i++) { - addrReversed[i] = addr[_addrWidth - 1 - i]; - } - + // write full pipe 0 - 1 address and enable the pipe switch(pipeNum) { case 0: - _mod->SPIwriteRegisterBurst(NRF24_REG_RX_ADDR_P0, addrReversed, _addrWidth); + _mod->SPIwriteRegisterBurst(NRF24_REG_RX_ADDR_P0, addr, _addrWidth); state |= _mod->SPIsetRegValue(NRF24_REG_EN_RXADDR, NRF24_P0_ON, 0, 0); case 1: - _mod->SPIwriteRegisterBurst(NRF24_REG_RX_ADDR_P1, addrReversed, _addrWidth); + _mod->SPIwriteRegisterBurst(NRF24_REG_RX_ADDR_P1, addr, _addrWidth); state |= _mod->SPIsetRegValue(NRF24_REG_EN_RXADDR, NRF24_P1_ON, 1, 1); break; default: return(ERR_INVALID_PIPE_NUMBER); } - + return(state); } @@ -200,7 +409,7 @@ int16_t nRF24::setReceivePipe(uint8_t pipeNum, uint8_t addrByte) { if(state != ERR_NONE) { return(state); } - + // write unique pipe 2 - 5 address and enable the pipe switch(pipeNum) { case 2: @@ -222,7 +431,7 @@ int16_t nRF24::setReceivePipe(uint8_t pipeNum, uint8_t addrByte) { default: return(ERR_INVALID_PIPE_NUMBER); } - + return(state); } @@ -232,7 +441,7 @@ int16_t nRF24::disablePipe(uint8_t pipeNum) { if(state != ERR_NONE) { return(state); } - + switch(pipeNum) { case 0: state = _mod->SPIsetRegValue(NRF24_REG_EN_RXADDR, NRF24_P0_OFF, 0, 0); @@ -255,28 +464,103 @@ int16_t nRF24::disablePipe(uint8_t pipeNum) { default: return(ERR_INVALID_PIPE_NUMBER); } - + return(state); } -void nRF24::SPIreadRxPayload(uint8_t numBytes, uint8_t* inBytes) { - digitalWrite(_mod->getCs(), LOW); - SPI.transfer(NRF24_CMD_READ_RX_PAYLOAD); - for(uint8_t i = 0; i < numBytes; i++) { - inBytes[i] = SPI.transfer(0x00); - } - digitalWrite(_mod->getCs(), HIGH); +int16_t nRF24::getStatus(uint8_t mask) { + return(_mod->SPIgetRegValue(NRF24_REG_STATUS) & mask); } -void nRF24::SPIwriteTxPayload(uint8_t* data, uint8_t numBytes) { - digitalWrite(_mod->getCs(), LOW); - SPI.transfer(NRF24_CMD_WRITE_TX_PAYLOAD); - for(uint8_t i = 0; i < numBytes; i++) { - SPI.transfer(data[i]); - } - digitalWrite(_mod->getCs(), HIGH); +int16_t nRF24::setFrequencyDeviation(float freqDev) { + // nRF24 is unable to set frequency deviation + // this method is implemented only for PhysicalLayer compatibility + (void)freqDev; + return(ERR_NONE); } void nRF24::clearIRQ() { + // clear status bits + _mod->SPIsetRegValue(NRF24_REG_STATUS, NRF24_RX_DR | NRF24_TX_DS | NRF24_MAX_RT, 6, 4); + + // disable interrupts _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_MASK_RX_DR_IRQ_OFF | NRF24_MASK_TX_DS_IRQ_OFF | NRF24_MASK_MAX_RT_IRQ_OFF, 6, 4); } + +int16_t nRF24::config() { + // enable 16-bit CRC + int16_t state = _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_CRC_ON | NRF24_CRC_16, 3, 2); + if(state != ERR_NONE) { + return(state); + } + + // set 15 retries and delay 1500 (5*250) us + _mod->SPIsetRegValue(NRF24_REG_SETUP_RETR, (5 << 4) | 5); + if(state != ERR_NONE) { + return(state); + } + + // set features: dynamic payload on, payload with ACK packets off, dynamic ACK off + state = _mod->SPIsetRegValue(NRF24_REG_FEATURE, NRF24_DPL_ON | NRF24_ACK_PAY_OFF | NRF24_DYN_ACK_OFF, 2, 0); + if(state != ERR_NONE) { + return(state); + } + + // enable dynamic payloads + state = _mod->SPIsetRegValue(NRF24_REG_DYNPD, NRF24_DPL_ALL_ON, 5, 0); + if(state != ERR_NONE) { + return(state); + } + + // reset IRQ + clearIRQ(); + + // clear status + _mod->SPIsetRegValue(NRF24_REG_STATUS, NRF24_RX_DR | NRF24_TX_DS | NRF24_MAX_RT, 6, 4); + + // flush FIFOs + SPItransfer(NRF24_CMD_FLUSH_TX); + SPItransfer(NRF24_CMD_FLUSH_RX); + + // power up + _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_POWER_UP, 1, 1); + delay(5); + + return(state); +} + +void nRF24::SPIreadRxPayload(uint8_t* data, uint8_t numBytes) { + SPItransfer(NRF24_CMD_READ_RX_PAYLOAD, false, NULL, data, numBytes); +} + +void nRF24::SPIwriteTxPayload(uint8_t* data, uint8_t numBytes) { + SPItransfer(NRF24_CMD_WRITE_TX_PAYLOAD, true, data, NULL, numBytes); +} + +void nRF24::SPItransfer(uint8_t cmd, bool write, uint8_t* dataOut, uint8_t* dataIn, uint8_t numBytes) { + // get pointer to used SPI interface and the settings + SPIClass* spi = _mod->getSpi(); + SPISettings spiSettings = _mod->getSpiSettings(); + + // start transfer + digitalWrite(_mod->getCs(), LOW); + spi->beginTransaction(spiSettings); + + // send command + spi->transfer(cmd); + + // send data + if(write) { + for(uint8_t i = 0; i < numBytes; i++) { + spi->transfer(dataOut[i]); + } + } else { + for(uint8_t i = 0; i < numBytes; i++) { + dataIn[i] = spi->transfer(0x00); + } + } + + // stop transfer + spi->endTransaction(); + digitalWrite(_mod->getCs(), HIGH); +} diff --git a/src/modules/nRF24.h b/src/modules/nRF24.h index e85e3437..dfbded20 100644 --- a/src/modules/nRF24.h +++ b/src/modules/nRF24.h @@ -2,6 +2,13 @@ #define _RADIOLIB_NRF24_H #include "Module.h" +#include "TypeDef.h" + +#include "../protocols/PhysicalLayer.h" + +// nRF24 physical layer properties (dummy only) +#define NRF24_CRYSTAL_FREQ 1.0 +#define NRF24_DIV_EXPONENT 0 // nRF24 SPI commands #define NRF24_CMD_READ 0b00000000 @@ -107,6 +114,8 @@ #define NRF24_DR_250_KBPS 0b00100000 // 5 5 data rate: 250 kbps #define NRF24_DR_1_MBPS 0b00000000 // 3 3 1 Mbps (default) #define NRF24_DR_2_MBPS 0b00001000 // 3 3 2 Mbps +#define NRF24_PLL_LOCK_ON 0b00010000 // 4 4 force PLL lock: enabled +#define NRF24_PLL_LOCK_OFF 0b00000000 // 4 4 disabled (default) #define NRF24_RF_PWR_18_DBM 0b00000000 // 2 1 output power: -18 dBm #define NRF24_RF_PWR_12_DBM 0b00000010 // 2 1 -12 dBm #define NRF24_RF_PWR_6_DBM 0b00000100 // 2 1 -6 dBm @@ -132,8 +141,8 @@ #define NRF24_TX_REUSE 0b01000000 // 6 6 reusing last transmitted payload #define NRF24_TX_FIFO_FULL_FLAG 0b00100000 // 5 5 Tx FIFO is full #define NRF24_TX_FIFO_EMPTY_FLAG 0b00010000 // 4 4 Tx FIFO is empty -#define NRF24_RX_FIFO_FULL_FLAG 0b00000010 // 5 5 Rx FIFO is full -#define NRF24_RX_FIFO_EMPTY_FLAG 0b00000001 // 4 4 Rx FIFO is empty +#define NRF24_RX_FIFO_FULL_FLAG 0b00000010 // 1 1 Rx FIFO is full +#define NRF24_RX_FIFO_EMPTY_FLAG 0b00000001 // 0 0 Rx FIFO is empty // NRF24_REG_DYNPD #define NRF24_DPL_P5_OFF 0b00000000 // 5 5 dynamic payload length on pipe 5: disabled (default) @@ -148,44 +157,258 @@ #define NRF24_DPL_P1_ON 0b00000010 // 1 1 enabled #define NRF24_DPL_P0_OFF 0b00000000 // 0 0 dynamic payload length on pipe 0: disabled (default) #define NRF24_DPL_P0_ON 0b00000001 // 0 0 enabled +#define NRF24_DPL_ALL_OFF 0b00000000 // 5 0 disable all dynamic payloads +#define NRF24_DPL_ALL_ON 0b00111111 // 5 0 enable all dynamic payloads // NRF24_REG_FEATURE #define NRF24_DPL_OFF 0b00000000 // 2 2 dynamic payload length: disabled (default) #define NRF24_DPL_ON 0b00000100 // 2 2 enabled #define NRF24_ACK_PAY_OFF 0b00000000 // 1 1 payload with ACK packets: disabled (default) #define NRF24_ACK_PAY_ON 0b00000010 // 1 1 enabled -#define NRF24_DYN_ACK_OFF 0b00000000 // 0 0 payloads without ACK packets: disabled (default) -#define NRF24_DYN_ACK_ON 0b00000001 // 0 0 enabled +#define NRF24_DYN_ACK_OFF 0b00000000 // 0 0 payloads without ACK: disabled (default) +#define NRF24_DYN_ACK_ON 0b00000001 // 0 0 enabled -class nRF24 { +/*! + \class nRF24 + + \brief Control class for %nRF24 module. +*/ +class nRF24: public PhysicalLayer { public: - // constructor + // introduce PhysicalLayer overloads + using PhysicalLayer::transmit; + using PhysicalLayer::receive; + using PhysicalLayer::startTransmit; + using PhysicalLayer::readData; + + /*! + \brief Default constructor. + + \param mod Instance of Module that will be used to communicate with the radio. + */ nRF24(Module* module); - + // basic methods - int16_t begin(int16_t freq = 2400, int16_t dataRate = 1000, uint8_t addrWidth = 5); + + /*! + \brief Initialization method. + + \param freq Carrier frequency in MHz. Defaults to 2400 MHz. + + \param dataRate Data rate to be used in kbps. Defaults to 1000 kbps. + + \param power Output power in dBm. Defaults to -12 dBm. + + \param addrWidth Address width in bytes. Defaults to 5 bytes. + + \returns \ref status_codes + */ + int16_t begin(int16_t freq = 2400, int16_t dataRate = 1000, int8_t power = -12, uint8_t addrWidth = 5); + + /*! + \brief Sets the module to sleep mode. + + \returns \ref status_codes + */ int16_t sleep(); + + /*! + \brief Sets the module to standby mode. + + \returns \ref status_codes + */ int16_t standby(); - int16_t transmit(String& str, uint8_t* addr); - int16_t transmit(const char* str, uint8_t* addr); - int16_t transmit(uint8_t* data, size_t len, uint8_t* addr); - + + /*! + \brief Blocking binary transmit method. + Overloads for string-based transmissions are implemented in PhysicalLayer. + + \param data Binary data to be sent. + + \param len Number of bytes to send. + + \param addr Dummy address parameter, to ensure PhysicalLayer compatibility. + + \returns \ref status_codes + */ + int16_t transmit(uint8_t* data, size_t len, uint8_t addr); + + /*! + \brief Blocking binary receive method. + Overloads for string-based transmissions are implemented in PhysicalLayer. + + \param data Binary data to be sent. + + \param len Number of bytes to send. + + \returns \ref status_codes + */ + int16_t receive(uint8_t* data, size_t len); + + /*! + \brief Starts direct mode transmission. + + \param frf Raw RF frequency value. Defaults to 0, required for quick frequency shifts in RTTY. + + \returns \ref status_codes + */ + int16_t transmitDirect(uint32_t frf = 0); + + /*! + \brief Dummy direct mode reception method, to ensure PhysicalLayer compatibility. + + \returns \ref status_codes + */ + int16_t receiveDirect(); + + // interrupt methods + + /*! + \brief Sets interrupt service routine to call when IRQ activates. + + \param func ISR to call. + */ + void setIrqAction(void (*func)(void)); + + /*! + \brief Interrupt-driven binary transmit method. IRQ will be activated when full packet is transmitted. + Overloads for string-based transmissions are implemented in PhysicalLayer. + + \param data Binary data to be sent. + + \param len Number of bytes to send. + + \param addr Dummy address parameter, to ensure PhysicalLayer compatibility. + + \returns \ref status_codes + */ + int16_t startTransmit(uint8_t* data, size_t len, uint8_t addr); + + /*! + \brief Interrupt-driven receive method. IRQ will be activated when full packet is received. + + \returns \ref status_codes + */ + int16_t startReceive(); + + /*! + \brief Reads data received after calling startReceive method. + + \param data Pointer to array to save the received binary data. + + \param len Number of bytes that will be received. Must be known in advance for binary transmissions. + + \returns \ref status_codes + */ + int16_t readData(uint8_t* data, size_t len); + // configuration methods + + /*! + \brief Sets carrier frequency. Allowed values range from 2400 MHz to 2525 MHz. + + \param freq Carrier frequency to be set in MHz. + + \returns \ref status_codes + */ int16_t setFrequency(int16_t freq); + + /*! + \brief Sets data rate. Allowed values are 2000, 1000 or 250 kbps. + + \param dataRate Data rate to be set in kbps. + + \returns \ref status_codes + */ int16_t setDataRate(int16_t dataRate); + + /*! + \brief Sets output power. Allowed values are -18, -12, -6 or 0 dBm. + + \param power Output power to be set in dBm. + + \returns \ref status_codes + */ + int16_t setOutputPower(int8_t power); + + /*! + \brief Sets address width of transmit and receive pipes in bytes. Allowed values are 3, 4 or 5 bytes. + + \param addrWidth Address width to be set in bytes. + + \returns \ref status_codes + */ int16_t setAddressWidth(uint8_t addrWidth); + + /*! + \brief Sets address of transmit pipe. The address width must be the same as the same as the configured in setAddressWidth. + + \param addr Address to which the next packet shall be transmitted. + + \returns \ref status_codes + */ + int16_t setTransmitPipe(uint8_t* addr); + + /*! + \brief Sets address of receive pipes 0 or 1. The address width must be the same as the same as the configured in setAddressWidth. + + \param pipeNum Number of pipe to which the address shall be set. Either 0 or 1, other pipes are handled using overloaded method. + + \param addr Address from which %nRF24 shall receive new packets on the specified pipe. + + \returns \ref status_codes + */ int16_t setReceivePipe(uint8_t pipeNum, uint8_t* addr); + + /*! + \brief Sets address of receive pipes 2 - 5. The first 2 - 4 address bytes for these pipes are the same as for address pipe 1, only the last byte can be set. + + \param pipeNum Number of pipe to which the address shall be set. Allowed values range from 2 to 5. + + \param addrByte LSB of address from which %nRF24 shall receive new packets on the specified pipe. + + \returns \ref status_codes + */ int16_t setReceivePipe(uint8_t pipeNum, uint8_t addrByte); + + /*! + \brief Disables specified receive pipe. + + \param pipeNum Receive pipe to be disabled. + + \returns \ref status_codes + */ int16_t disablePipe(uint8_t pipeNum); - + + /*! + \brief Gets nRF24 status register. + + \param mask Bit mask to be used on the returned register value. + + \returns Status register value or \ref status_codes + */ + int16_t getStatus(uint8_t mask = 0xFF); + + /*! + \brief Dummy configuration method, to ensure PhysicalLayer compatibility. + + \param freqDev Dummy frequency deviation parameter, no configuration will be changed. + + \returns \ref status_codes + */ + int16_t setFrequencyDeviation(float freqDev); + private: Module* _mod; - + uint8_t _addrWidth; - - void SPIreadRxPayload(uint8_t numBytes, uint8_t* inBytes); - void SPIwriteTxPayload(uint8_t* data, uint8_t numBytes); + + int16_t config(); void clearIRQ(); + + void SPIreadRxPayload(uint8_t* data, uint8_t numBytes); + void SPIwriteTxPayload(uint8_t* data, uint8_t numBytes); + void SPItransfer(uint8_t cmd, bool write = false, uint8_t* dataOut = NULL, uint8_t* dataIn = NULL, uint8_t numBytes = 0); }; #endif