// NRF24.cpp // // Copyright (C) 2012 Mike McCauley // $Id: RH_NRF24.cpp,v 1.22 2016/04/04 01:40:12 mikem Exp $ #include RH_NRF24::RH_NRF24(uint8_t chipEnablePin, uint8_t slaveSelectPin, RHGenericSPI& spi) : RHNRFSPIDriver(slaveSelectPin, spi), _rxBufValid(0) { _configuration = RH_NRF24_EN_CRC | RH_NRF24_CRCO; // Default: 2 byte CRC enabled _chipEnablePin = chipEnablePin; } bool RH_NRF24::init() { // Teensy with nRF24 is unreliable at 8MHz: // so is Arduino with RF73 _spi.setFrequency(RHGenericSPI::Frequency1MHz); if (!RHNRFSPIDriver::init()) return false; // Initialise the slave select pin pinMode(_chipEnablePin, OUTPUT); digitalWrite(_chipEnablePin, LOW); // Clear interrupts spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_RX_DR | RH_NRF24_TX_DS | RH_NRF24_MAX_RT); // Enable dynamic payload length on all pipes spiWriteRegister(RH_NRF24_REG_1C_DYNPD, RH_NRF24_DPL_ALL); // Enable dynamic payload length, disable payload-with-ack, enable noack spiWriteRegister(RH_NRF24_REG_1D_FEATURE, RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK); // Test if there is actually a device connected and responding // CAUTION: RFM73 and version 2.0 silicon may require ACTIVATE if (spiReadRegister(RH_NRF24_REG_1D_FEATURE) != (RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK)) { spiWrite(RH_NRF24_COMMAND_ACTIVATE, 0x73); // Enable dynamic payload length, disable payload-with-ack, enable noack spiWriteRegister(RH_NRF24_REG_1D_FEATURE, RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK); if (spiReadRegister(RH_NRF24_REG_1D_FEATURE) != (RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK)) return false; } // Make sure we are powered down setModeIdle(); // Flush FIFOs flushTx(); flushRx(); setChannel(2); // The default, in case it was set by another app without powering down setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm); return true; } // Use the register commands to read and write the registers uint8_t RH_NRF24::spiReadRegister(uint8_t reg) { return spiRead((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_R_REGISTER); } uint8_t RH_NRF24::spiWriteRegister(uint8_t reg, uint8_t val) { return spiWrite((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_W_REGISTER, val); } uint8_t RH_NRF24::spiBurstReadRegister(uint8_t reg, uint8_t* dest, uint8_t len) { return spiBurstRead((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_R_REGISTER, dest, len); } uint8_t RH_NRF24::spiBurstWriteRegister(uint8_t reg, uint8_t* src, uint8_t len) { return spiBurstWrite((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_W_REGISTER, src, len); } uint8_t RH_NRF24::statusRead() { // status is a side-effect of NOP, faster than reading reg 07 return spiCommand(RH_NRF24_COMMAND_NOP); } uint8_t RH_NRF24::flushTx() { return spiCommand(RH_NRF24_COMMAND_FLUSH_TX); } uint8_t RH_NRF24::flushRx() { return spiCommand(RH_NRF24_COMMAND_FLUSH_RX); } bool RH_NRF24::setChannel(uint8_t channel) { spiWriteRegister(RH_NRF24_REG_05_RF_CH, channel & RH_NRF24_RF_CH); return true; } bool RH_NRF24::setOpMode(uint8_t mode) { _configuration = mode; return true; } bool RH_NRF24::setNetworkAddress(uint8_t* address, uint8_t len) { if (len < 3 || len > 5) return false; // Set both TX_ADDR and RX_ADDR_P0 for auto-ack with Enhanced shockwave spiWriteRegister(RH_NRF24_REG_03_SETUP_AW, len-2); // Mapping [3..5] = [1..3] spiBurstWriteRegister(RH_NRF24_REG_0A_RX_ADDR_P0, address, len); spiBurstWriteRegister(RH_NRF24_REG_10_TX_ADDR, address, len); return true; } bool RH_NRF24::setRF(DataRate data_rate, TransmitPower power) { uint8_t value = (power << 1) & RH_NRF24_PWR; // Ugly mapping of data rates to noncontiguous 2 bits: if (data_rate == DataRate250kbps) value |= RH_NRF24_RF_DR_LOW; else if (data_rate == DataRate2Mbps) value |= RH_NRF24_RF_DR_HIGH; // else DataRate1Mbps, 00 // RFM73 needs this: value |= RH_NRF24_LNA_HCURR; spiWriteRegister(RH_NRF24_REG_06_RF_SETUP, value); // If we were using auto-ack, we would have to set the appropriate timeout in reg 4 here // see NRF24::setRF() return true; } void RH_NRF24::setModeIdle() { if (_mode != RHModeIdle) { spiWriteRegister(RH_NRF24_REG_00_CONFIG, _configuration); digitalWrite(_chipEnablePin, LOW); _mode = RHModeIdle; } } bool RH_NRF24::sleep() { if (_mode != RHModeSleep) { spiWriteRegister(RH_NRF24_REG_00_CONFIG, 0); // Power Down mode digitalWrite(_chipEnablePin, LOW); _mode = RHModeSleep; return true; } return false; // Already there? } void RH_NRF24::setModeRx() { if (_mode != RHModeRx) { spiWriteRegister(RH_NRF24_REG_00_CONFIG, _configuration | RH_NRF24_PWR_UP | RH_NRF24_PRIM_RX); digitalWrite(_chipEnablePin, HIGH); _mode = RHModeRx; } } void RH_NRF24::setModeTx() { if (_mode != RHModeTx) { // Its the CE rising edge that puts us into TX mode // CE staying high makes us go to standby-II when the packet is sent digitalWrite(_chipEnablePin, LOW); // Ensure DS is not set spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_TX_DS | RH_NRF24_MAX_RT); spiWriteRegister(RH_NRF24_REG_00_CONFIG, _configuration | RH_NRF24_PWR_UP); digitalWrite(_chipEnablePin, HIGH); _mode = RHModeTx; } } bool RH_NRF24::send(const uint8_t* data, uint8_t len) { if (len > RH_NRF24_MAX_MESSAGE_LEN) return false; // Set up the headers _buf[0] = _txHeaderTo; _buf[1] = _txHeaderFrom; _buf[2] = _txHeaderId; _buf[3] = _txHeaderFlags; memcpy(_buf+RH_NRF24_HEADER_LEN, data, len); spiBurstWrite(RH_NRF24_COMMAND_W_TX_PAYLOAD_NOACK, _buf, len + RH_NRF24_HEADER_LEN); setModeTx(); // Radio will return to Standby II mode after transmission is complete _txGood++; return true; } bool RH_NRF24::waitPacketSent() { // If we are not currently in transmit mode, there is no packet to wait for if (_mode != RHModeTx) return false; // Wait for either the Data Sent or Max ReTries flag, signalling the // end of transmission // We dont actually use auto-ack, so prob dont expect to see RH_NRF24_MAX_RT uint8_t status; while (!((status = statusRead()) & (RH_NRF24_TX_DS | RH_NRF24_MAX_RT))) YIELD; // Must clear RH_NRF24_MAX_RT if it is set, else no further comm if (status & RH_NRF24_MAX_RT) flushTx(); setModeIdle(); spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_TX_DS | RH_NRF24_MAX_RT); // Return true if data sent, false if MAX_RT return status & RH_NRF24_TX_DS; } bool RH_NRF24::isSending() { return !(spiReadRegister(RH_NRF24_REG_00_CONFIG) & RH_NRF24_PRIM_RX) && !(statusRead() & (RH_NRF24_TX_DS | RH_NRF24_MAX_RT)); } bool RH_NRF24::printRegisters() { #ifdef RH_HAVE_SERIAL // Iterate over register range, but don't process registers not in use. for (uint8_t r = RH_NRF24_REG_00_CONFIG; r <= RH_NRF24_REG_1D_FEATURE; r++) { if ((r <= RH_NRF24_REG_17_FIFO_STATUS) || (r >= RH_NRF24_REG_1C_DYNPD)) { Serial.print(r, HEX); Serial.print(": "); uint8_t len = 1; // Address registers are 5 bytes in size if ( (RH_NRF24_REG_0A_RX_ADDR_P0 == r) || (RH_NRF24_REG_0B_RX_ADDR_P1 == r) || (RH_NRF24_REG_10_TX_ADDR == r) ) { len = 5; } uint8_t buf[5]; spiBurstReadRegister(r, buf, len); for (uint8_t j = 0; j < len; ++j) { Serial.print(buf[j], HEX); Serial.print(" "); } Serial.println(""); } } #endif return true; } // Check whether the latest received message is complete and uncorrupted void RH_NRF24::validateRxBuf() { if (_bufLen < 4) return; // Too short to be a real message // Extract the 4 headers _rxHeaderTo = _buf[0]; _rxHeaderFrom = _buf[1]; _rxHeaderId = _buf[2]; _rxHeaderFlags = _buf[3]; if (_promiscuous || _rxHeaderTo == _thisAddress || _rxHeaderTo == RH_BROADCAST_ADDRESS) { _rxGood++; _rxBufValid = true; } } bool RH_NRF24::available() { if (!_rxBufValid) { if (_mode == RHModeTx) return false; setModeRx(); if (spiReadRegister(RH_NRF24_REG_17_FIFO_STATUS) & RH_NRF24_RX_EMPTY) return false; // Manual says that messages > 32 octets should be discarded uint8_t len = spiRead(RH_NRF24_COMMAND_R_RX_PL_WID); if (len > 32) { flushRx(); clearRxBuf(); setModeIdle(); return false; } // Clear read interrupt spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_RX_DR); // Get the message into the RX buffer, so we can inspect the headers spiBurstRead(RH_NRF24_COMMAND_R_RX_PAYLOAD, _buf, len); _bufLen = len; // 140 microsecs (32 octet payload) validateRxBuf(); if (_rxBufValid) setModeIdle(); // Got one } return _rxBufValid; } void RH_NRF24::clearRxBuf() { _rxBufValid = false; _bufLen = 0; } bool RH_NRF24::recv(uint8_t* buf, uint8_t* len) { if (!available()) return false; if (buf && len) { // Skip the 4 headers that are at the beginning of the rxBuf if (*len > _bufLen-RH_NRF24_HEADER_LEN) *len = _bufLen-RH_NRF24_HEADER_LEN; memcpy(buf, _buf+RH_NRF24_HEADER_LEN, *len); } clearRxBuf(); // This message accepted and cleared return true; } uint8_t RH_NRF24::maxMessageLength() { return RH_NRF24_MAX_MESSAGE_LEN; }