249 lines
7.0 KiB
C++
249 lines
7.0 KiB
C++
#ifndef RADIO_NRF24_H
|
|
#define RADIO_NRF24_H
|
|
|
|
#include <SPI.h>
|
|
#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
|