#ifndef RADIO_NRF24_H #define RADIO_NRF24_H #include #include "board_config.h" // NRF24L01+ 2.4GHz Radio Driver // PA+LNA module for range, all TX at +20dBm class RadioNRF24 { public: // Initialize NRF24 static bool begin() { pinMode(NRF24_CSN, OUTPUT); pinMode(NRF24_CE, OUTPUT); pinMode(NRF24_IRQ, INPUT); digitalWrite(NRF24_CSN, HIGH); digitalWrite(NRF24_CE, LOW); // Start SPI SPI.begin(RADIO_SPI_CLK, RADIO_SPI_MISO, RADIO_SPI_MOSI); SPI.setFrequency(10000000); SPI.setDataMode(SPI_MODE0); delay(100); // Verify chip uint8_t id = readReg(0x00); // CONFIG if ((id & 0x0F) == 0x00) { return false; // Not responding } // Reset config writeReg(0x00, 0x08); // PWR_UP, CRC enabled delay(150); // Setup for max power 2.4GHz writeReg(0x01, 0x03); // EN_AA = 0x03 (pipe 0,1) writeReg(0x02, 0x03); // EN_RXADDR = 0x03 (pipe 0,1) writeReg(0x03, 0x03); // SETUP_AW = 5 bytes writeReg(0x04, 0x2F); // SETUP_RETR = ARD=750us, ARC=15 writeReg(0x05, 0x76); // RF_CH = 118 (2476 MHz center) writeReg(0x06, 0x0F); // RF_SETUP = PA_MAX, 2Mbps, LNA on // Set RX addresses setRxAddress(0, 0x6E6B6C6D6ELL); setRxAddress(1, 0x6B6C6D6E6FLL); // Set TX address setTxAddress(0x6E6B6C6D6ELL); // Enable RX writeReg(0x00, 0x0B); // PWR_UP + PRIM_RX digitalWrite(NRF24_CE, HIGH); return true; } // Set RX channel (0-125, maps to 2400-3525 MHz) static void setChannel(uint8_t ch) { if (ch > 125) ch = 125; writeReg(0x05, ch); } // Set TX power: 0x0F=+20dBm, 0x07=0dBm, 0x03=-6dBm, 0x00=-18dBm static void setTxPower(uint8_t pwr) { uint8_t rf_setup = readReg(0x06); rf_setup = (rf_setup & 0xF9) | ((pwr & 0x03) << 1); writeReg(0x06, rf_setup); } // Set max TX power static void setMaxPower() { setTxPower(0x03); // PA_MAX on NRF24L01+PA+LNA } // Set data rate: 0x00=1Mbps, 0x08=2Mbps, 0x20=250kbps static void setDataRate(uint8_t rate) { uint8_t rf_setup = readReg(0x06); rf_setup = (rf_setup & 0xD7) | (rate & 0x28); writeReg(0x06, rf_setup); } // Transmit packet static bool transmit(const uint8_t* data, uint8_t len) { if (len > 32) return false; // Go to TX mode uint8_t config = readReg(0x00); writeReg(0x00, config & 0xFE); // Clear PRIM_RX digitalWrite(NRF24_CE, LOW); // Load TX FIFO digitalWrite(NRF24_CSN, LOW); SPI.transfer(0xA0); // W_TX_PAYLOAD for (uint8_t i = 0; i < len; i++) { SPI.transfer(data[i]); } digitalWrite(NRF24_CSN, HIGH); // Start transmission digitalWrite(NRF24_CE, HIGH); delayMicroseconds(15); digitalWrite(NRF24_CE, LOW); // Wait for completion or timeout uint32_t start = millis(); while (millis() - start < 5000) { uint8_t status = getStatus(); if (status & 0x20) { // TX_DS = transmission complete // Clear interrupt writeReg(0x07, 0x20); return true; } if (status & 0x10) { // MAX_RT = max retries reached // Clear interrupt writeReg(0x07, 0x10); return false; } delay(1); } return false; } // Receive packet (non-blocking) // Returns length of packet or 0 if none static uint8_t receive(uint8_t* buffer, uint8_t max_len) { uint8_t status = getStatus(); if (!(status & 0x40)) { // RX_DR = data ready return 0; } // Read payload uint8_t len = readReg(0x60); // RX_PL_WID if (len > max_len) len = max_len; digitalWrite(NRF24_CSN, LOW); SPI.transfer(0x61); // R_RX_PAYLOAD for (uint8_t i = 0; i < len; i++) { buffer[i] = SPI.transfer(0x00); } digitalWrite(NRF24_CSN, HIGH); // Clear RX FIFO writeReg(0x07, 0x40); // Clear RX_DR flag return len; } // Get signal strength (RSSI estimation) // No built-in RSSI on NRF24, so estimate from carrier detect static int8_t getRSSI() { uint8_t cd = readReg(0x09); // CD (carrier detect) if (cd & 0x01) { return -50; // Signal present } return -90; // No signal } // Enter listening (RX) mode static void listenMode() { digitalWrite(NRF24_CE, LOW); uint8_t config = readReg(0x00); writeReg(0x00, config | 0x01); // PRIM_RX digitalWrite(NRF24_CE, HIGH); } // Sleep mode (low power) static void sleep() { digitalWrite(NRF24_CE, LOW); uint8_t config = readReg(0x00); writeReg(0x00, config & 0xFD); // PWR_UP = 0 } static void wakeup() { uint8_t config = readReg(0x00); writeReg(0x00, config | 0x02); // PWR_UP delay(5); digitalWrite(NRF24_CE, HIGH); } // Promiscuous mode (Goodspeed) - capture all 2.4GHz packets static void enablePromiscuous() { // Disable address matching writeReg(0x02, 0x01); // EN_RXADDR = pipe 0 only writeReg(0x03, 0x03); // SETUP_AW = 5 bytes // Set minimal RX address (all zeros) uint8_t addr[5] = {0x00, 0x00, 0x00, 0x00, 0x00}; setRxAddress(0, 0x0000000000LL); listenMode(); } // Spectrum scanner - sweep channels and measure signal static uint8_t scanChannel(uint8_t channel) { setChannel(channel); delay(40); return readReg(0x09) & 0x01; // CD bit = carrier detect } private: static uint8_t getStatus() { digitalWrite(NRF24_CSN, LOW); uint8_t status = SPI.transfer(0xFF); digitalWrite(NRF24_CSN, HIGH); return status; } static uint8_t readReg(uint8_t reg) { digitalWrite(NRF24_CSN, LOW); SPI.transfer(reg & 0x1F); // Max 5 bits uint8_t val = SPI.transfer(0x00); digitalWrite(NRF24_CSN, HIGH); return val; } static void writeReg(uint8_t reg, uint8_t val) { digitalWrite(NRF24_CSN, LOW); SPI.transfer((reg & 0x1F) | 0x20); // Write flag SPI.transfer(val); digitalWrite(NRF24_CSN, HIGH); } static void setRxAddress(uint8_t pipe, uint64_t addr) { uint8_t reg = 0x0A + pipe; // RX_ADDR_P0-P5 digitalWrite(NRF24_CSN, LOW); SPI.transfer(reg | 0x20); for (int i = 0; i < 5; i++) { SPI.transfer((addr >> (i * 8)) & 0xFF); } digitalWrite(NRF24_CSN, HIGH); } static void setTxAddress(uint64_t addr) { uint8_t reg = 0x10; // TX_ADDR digitalWrite(NRF24_CSN, LOW); SPI.transfer(reg | 0x20); for (int i = 0; i < 5; i++) { SPI.transfer((addr >> (i * 8)) & 0xFF); } digitalWrite(NRF24_CSN, HIGH); } }; #endif // RADIO_NRF24_H