Merge ListenMode branch code
This commit is contained in:
parent
dfffbba7b8
commit
e1145c9543
287
RFM69.cpp
287
RFM69.cpp
|
|
@ -47,7 +47,7 @@ RFM69::RFM69(uint8_t slaveSelectPin, uint8_t interruptPin, bool isRFM69HW)
|
|||
_promiscuousMode = false;
|
||||
_powerLevel = 31;
|
||||
_isRFM69HW = isRFM69HW;
|
||||
#if defined(RF69_LISTENMODE_ENABLE)
|
||||
#if defined(RF69_LISTENMODE_ENABLE)
|
||||
_isHighSpeed = true;
|
||||
_haveEncryptKey = false;
|
||||
uint32_t rxDuration = DEFAULT_LISTEN_RX_US;
|
||||
|
|
@ -132,6 +132,10 @@ bool RFM69::initialize(uint8_t freqBand, uint8_t nodeID, uint8_t networkID)
|
|||
|
||||
selfPointer = this;
|
||||
_address = nodeID;
|
||||
#if defined(RF69_LISTENMODE_ENABLE)
|
||||
_freqBand = freqBand;
|
||||
_networkID = networkID;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -382,6 +386,9 @@ void RFM69::receiveBegin() {
|
|||
PAYLOADLEN = 0;
|
||||
ACK_REQUESTED = 0;
|
||||
ACK_RECEIVED = 0;
|
||||
#if defined(RF69_LISTENMODE_ENABLE)
|
||||
RF69_LISTEN_BURST_REMAINING_MS = 0;
|
||||
#endif
|
||||
RSSI = 0;
|
||||
if (readReg(REG_IRQFLAGS2) & RF_IRQFLAGS2_PAYLOADREADY)
|
||||
writeReg(REG_PACKETCONFIG2, (readReg(REG_PACKETCONFIG2) & 0xFB) | RF_PACKET2_RXRESTART); // avoid RX deadlocks
|
||||
|
|
@ -413,9 +420,15 @@ bool RFM69::receiveDone() {
|
|||
// To disable encryption: radio.encrypt(null) or radio.encrypt(0)
|
||||
// KEY HAS TO BE 16 bytes !!!
|
||||
void RFM69::encrypt(const char* key) {
|
||||
#if defined(RF69_LISTENMODE_ENABLE)
|
||||
_haveEncryptKey = key;
|
||||
#endif
|
||||
setMode(RF69_MODE_STANDBY);
|
||||
if (key != 0)
|
||||
{
|
||||
#if defined(RF69_LISTENMODE_ENABLE)
|
||||
memcpy(_encryptKey, key, 16);
|
||||
#endif
|
||||
select();
|
||||
SPI.transfer(REG_AESKEY1 | 0x80);
|
||||
for (uint8_t i = 0; i < 16; i++)
|
||||
|
|
@ -843,3 +856,275 @@ inline void RFM69::maybeInterrupts()
|
|||
// Only reenable interrupts if we're not being called from the ISR
|
||||
if (!_inISR) interrupts();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// ListenMode specific functions
|
||||
//=============================================================================
|
||||
#if defined(RF69_LISTENMODE_ENABLE)
|
||||
volatile uint16_t RFM69::RF69_LISTEN_BURST_REMAINING_MS = 0;
|
||||
|
||||
//=============================================================================
|
||||
// reinitRadio() - use base class initialization with saved values
|
||||
//=============================================================================
|
||||
bool RFM69::reinitRadio()
|
||||
{
|
||||
if (!initialize(_freqBand, _address, _networkID)) return false;
|
||||
if (_haveEncryptKey) RFM69::encrypt(_encryptKey); // Restore the encryption key if necessary
|
||||
if (_isHighSpeed) writeReg(REG_LNA, (readReg(REG_LNA) & ~0x3) | RF_LNA_GAINSELECT_AUTO);
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t getUsForResolution(uint8_t resolution)
|
||||
{
|
||||
switch (resolution) {
|
||||
case RF_LISTEN1_RESOL_RX_64:
|
||||
case RF_LISTEN1_RESOL_IDLE_64:
|
||||
return 64;
|
||||
case RF_LISTEN1_RESOL_RX_4100:
|
||||
case RF_LISTEN1_RESOL_IDLE_4100:
|
||||
return 4100;
|
||||
case RF_LISTEN1_RESOL_RX_262000:
|
||||
case RF_LISTEN1_RESOL_IDLE_262000:
|
||||
return 262000;
|
||||
default:
|
||||
// Whoops
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t getCoefForResolution(uint8_t resolution, uint32_t duration)
|
||||
{
|
||||
uint32_t resolDuration = getUsForResolution(resolution);
|
||||
uint32_t result = duration / resolDuration;
|
||||
|
||||
// If the next-higher coefficient is closer, use that
|
||||
if (abs(duration - ((result + 1) * resolDuration)) < abs(duration - (result * resolDuration)))
|
||||
return result + 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool chooseResolutionAndCoef(uint8_t *resolutions, uint32_t duration, uint8_t& resolOut, uint8_t& coefOut)
|
||||
{
|
||||
for (int i = 0; resolutions[i]; i++) {
|
||||
uint32_t coef = getCoefForResolution(resolutions[i], duration);
|
||||
if (coef <= 255) {
|
||||
coefOut = coef;
|
||||
resolOut = resolutions[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// out of range
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RFM69::listenModeSetDurations(uint32_t& rxDuration, uint32_t& idleDuration)
|
||||
{
|
||||
uint8_t rxResolutions[] = { RF_LISTEN1_RESOL_RX_64, RF_LISTEN1_RESOL_RX_4100, RF_LISTEN1_RESOL_RX_262000, 0 };
|
||||
uint8_t idleResolutions[] = { RF_LISTEN1_RESOL_IDLE_64, RF_LISTEN1_RESOL_IDLE_4100, RF_LISTEN1_RESOL_IDLE_262000, 0 };
|
||||
|
||||
if (!chooseResolutionAndCoef(rxResolutions, rxDuration, _rxListenResolution, _rxListenCoef))
|
||||
return false;
|
||||
|
||||
if (!chooseResolutionAndCoef(idleResolutions, idleDuration, _idleListenResolution, _idleListenCoef))
|
||||
return false;
|
||||
|
||||
rxDuration = getUsForResolution(_rxListenResolution) * _rxListenCoef;
|
||||
idleDuration = getUsForResolution(_idleListenResolution) * _idleListenCoef;
|
||||
_listenCycleDurationUs = rxDuration + idleDuration;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RFM69::listenModeGetDurations(uint32_t &rxDuration, uint32_t &idleDuration)
|
||||
{
|
||||
rxDuration = getUsForResolution(_rxListenResolution) * _rxListenCoef;
|
||||
idleDuration = getUsForResolution(_idleListenResolution) * _idleListenCoef;
|
||||
}
|
||||
|
||||
void RFM69::listenModeReset(void)
|
||||
{
|
||||
DATALEN = 0;
|
||||
SENDERID = 0;
|
||||
TARGETID = 0;
|
||||
PAYLOADLEN = 0;
|
||||
ACK_REQUESTED = 0;
|
||||
ACK_RECEIVED = 0;
|
||||
RF69_LISTEN_BURST_REMAINING_MS = 0;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// irq handler, simply calls listenModeInterruptHandler method so internal methods can be accessed easily
|
||||
//=============================================================================
|
||||
void RFM69::listenModeIrq() { selfPointer->listenModeInterruptHandler(); }
|
||||
|
||||
//=============================================================================
|
||||
// listenModeInterruptHandler() - only called by listen irq handler
|
||||
//=============================================================================
|
||||
void RFM69::listenModeInterruptHandler(void)
|
||||
{
|
||||
if (DATALEN != 0) return;
|
||||
|
||||
listenModeReset();
|
||||
noInterrupts();
|
||||
select();
|
||||
|
||||
union // union to simplify addressing of long and short parts of time offset
|
||||
{
|
||||
uint32_t l;
|
||||
uint8_t b[4];
|
||||
} burstRemaining;
|
||||
|
||||
burstRemaining.l = 0;
|
||||
|
||||
SPI.transfer(REG_FIFO & 0x7F);
|
||||
PAYLOADLEN = SPI.transfer(0);
|
||||
PAYLOADLEN = PAYLOADLEN > 64 ? 64 : PAYLOADLEN; // precaution
|
||||
TARGETID = SPI.transfer(0);
|
||||
if(!(_promiscuousMode || TARGETID == _address || TARGETID == RF69_BROADCAST_ADDR) // match this node's address, or broadcast address or anything in promiscuous mode
|
||||
|| PAYLOADLEN < 3) // address situation could receive packets that are malformed and don't fit this library's extra fields
|
||||
{
|
||||
listenModeReset();
|
||||
goto out;
|
||||
}
|
||||
|
||||
// We've read the target, and will read the sender id and two time offset bytes for a total of 4 bytes
|
||||
DATALEN = PAYLOADLEN - 4;
|
||||
SENDERID = SPI.transfer(0);
|
||||
burstRemaining.b[0] = SPI.transfer(0); // and get the time remaining
|
||||
burstRemaining.b[1] = SPI.transfer(0);
|
||||
RF69_LISTEN_BURST_REMAINING_MS = burstRemaining.l;
|
||||
|
||||
for (uint8_t i = 0; i < DATALEN; i++)
|
||||
DATA[i] = SPI.transfer(0);
|
||||
|
||||
if (DATALEN < RF69_MAX_DATA_LEN)
|
||||
DATA[DATALEN] = 0; // add null at end of string
|
||||
|
||||
out:
|
||||
unselect();
|
||||
interrupts();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// listenModeStart() - switch radio to Listen Mode in prep for sleep until burst
|
||||
//=============================================================================
|
||||
void RFM69::listenModeStart(void)
|
||||
{
|
||||
//pRadio = this;
|
||||
while (readReg(REG_IRQFLAGS2) & RF_IRQFLAGS2_PACKETSENT == 0x00); // wait for ModeReady
|
||||
listenModeReset();
|
||||
|
||||
detachInterrupt(_interruptNum);
|
||||
attachInterrupt(_interruptNum, listenModeIrq, RISING);
|
||||
setMode(RF69_MODE_STANDBY);
|
||||
writeReg(REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_01);
|
||||
writeReg(REG_FRFMSB, readReg(REG_FRFMSB) + 1);
|
||||
writeReg(REG_FRFLSB, readReg(REG_FRFLSB)); // MUST write to LSB to affect change!
|
||||
|
||||
listenModeApplyHighSpeedSettings();
|
||||
|
||||
writeReg(REG_PACKETCONFIG1, RF_PACKET1_FORMAT_VARIABLE | RF_PACKET1_DCFREE_WHITENING | RF_PACKET1_CRC_ON | RF_PACKET1_CRCAUTOCLEAR_ON);
|
||||
writeReg(REG_PACKETCONFIG2, RF_PACKET2_RXRESTARTDELAY_NONE | RF_PACKET2_AUTORXRESTART_ON | RF_PACKET2_AES_OFF);
|
||||
writeReg(REG_SYNCVALUE1, 0x5A);
|
||||
writeReg(REG_SYNCVALUE2, 0x5A);
|
||||
writeReg(REG_LISTEN1, _rxListenResolution | _idleListenResolution | RF_LISTEN1_CRITERIA_RSSI | RF_LISTEN1_END_10);
|
||||
writeReg(REG_LISTEN2, _idleListenCoef);
|
||||
writeReg(REG_LISTEN3, _rxListenCoef);
|
||||
writeReg(REG_RSSITHRESH, 180);
|
||||
writeReg(REG_RXTIMEOUT2, 75);
|
||||
writeReg(REG_OPMODE, RF_OPMODE_SEQUENCER_ON | RF_OPMODE_STANDBY);
|
||||
writeReg(REG_OPMODE, RF_OPMODE_SEQUENCER_ON | RF_OPMODE_LISTEN_ON | RF_OPMODE_STANDBY);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// listenModeEnd() - exit listen mode and reinit the radio
|
||||
//=============================================================================
|
||||
void RFM69::listenModeEnd(void)
|
||||
{
|
||||
detachInterrupt(_interruptNum);
|
||||
writeReg(REG_OPMODE, RF_OPMODE_SEQUENCER_ON | RF_OPMODE_LISTENABORT | RF_OPMODE_STANDBY);
|
||||
writeReg(REG_OPMODE, RF_OPMODE_SEQUENCER_ON | RF_OPMODE_STANDBY);
|
||||
writeReg(REG_RXTIMEOUT2, 0);
|
||||
setMode(RF69_MODE_STANDBY);
|
||||
while ((readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00); // wait for ModeReady
|
||||
listenModeReset();
|
||||
reinitRadio();
|
||||
}
|
||||
|
||||
void RFM69::listenModeApplyHighSpeedSettings()
|
||||
{
|
||||
if (!_isHighSpeed) return;
|
||||
writeReg(REG_BITRATEMSB, RF_BITRATEMSB_200000);
|
||||
writeReg(REG_BITRATELSB, RF_BITRATELSB_200000);
|
||||
writeReg(REG_FDEVMSB, RF_FDEVMSB_100000);
|
||||
writeReg(REG_FDEVLSB, RF_FDEVLSB_100000);
|
||||
writeReg( REG_RXBW, RF_RXBW_DCCFREQ_000 | RF_RXBW_MANT_20 | RF_RXBW_EXP_0 );
|
||||
|
||||
// Force LNA to the highest gain
|
||||
//writeReg(REG_LNA, (readReg(REG_LNA) << 2) | RF_LNA_GAINSELECT_MAX);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// sendBurst() - send a burst of packets to a sleeping listening node (or all)
|
||||
//=============================================================================
|
||||
void RFM69::listenModeSendBurst( uint8_t targetNode, void* buffer, uint8_t size )
|
||||
{
|
||||
detachInterrupt(_interruptNum);
|
||||
setMode(RF69_MODE_STANDBY);
|
||||
writeReg(REG_PACKETCONFIG1, RF_PACKET1_FORMAT_VARIABLE | RF_PACKET1_DCFREE_WHITENING | RF_PACKET1_CRC_ON | RF_PACKET1_CRCAUTOCLEAR_ON );
|
||||
writeReg(REG_PACKETCONFIG2, RF_PACKET2_RXRESTARTDELAY_NONE | RF_PACKET2_AUTORXRESTART_ON | RF_PACKET2_AES_OFF);
|
||||
writeReg(REG_SYNCVALUE1, 0x5A);
|
||||
writeReg(REG_SYNCVALUE2, 0x5A);
|
||||
listenModeApplyHighSpeedSettings();
|
||||
writeReg(REG_FRFMSB, readReg(REG_FRFMSB) + 1);
|
||||
writeReg(REG_FRFLSB, readReg(REG_FRFLSB)); // MUST write to LSB to affect change!
|
||||
|
||||
union // union to simplify addressing of long and short parts of time offset
|
||||
{
|
||||
int32_t l;
|
||||
uint8_t b[4];
|
||||
} timeRemaining;
|
||||
|
||||
uint16_t cycleDurationMs = _listenCycleDurationUs / 1000;
|
||||
timeRemaining.l = cycleDurationMs;
|
||||
|
||||
#ifdef RF69_WL_DEBUG
|
||||
Serial.print("Sending burst for ");
|
||||
Serial.print(cycleDurationMs, DEC);
|
||||
Serial.println(" ms");
|
||||
#endif
|
||||
|
||||
setMode(RF69_MODE_TX);
|
||||
uint32_t numSent = 0;
|
||||
uint32_t startTime = millis();
|
||||
|
||||
while(timeRemaining.l > 0) {
|
||||
noInterrupts();
|
||||
// write to FIFO
|
||||
select();
|
||||
SPI.transfer(REG_FIFO | 0x80);
|
||||
SPI.transfer(size + 4); // two bytes for target and sender node, two bytes for the burst time remaining
|
||||
SPI.transfer(targetNode);
|
||||
SPI.transfer(_address);
|
||||
|
||||
// We send the burst time remaining with the packet so the receiver knows how long to wait before trying to reply
|
||||
SPI.transfer(timeRemaining.b[0]);
|
||||
SPI.transfer(timeRemaining.b[1]);
|
||||
|
||||
for (uint8_t i = 0; i < size; i++) {
|
||||
SPI.transfer(((uint8_t*) buffer)[i]);
|
||||
}
|
||||
|
||||
unselect();
|
||||
interrupts();
|
||||
|
||||
while ((readReg(REG_IRQFLAGS2) & RF_IRQFLAGS2_FIFONOTEMPTY) != 0x00); // make sure packet is sent before putting more into the FIFO
|
||||
timeRemaining.l = cycleDurationMs - (millis() - startTime);
|
||||
}
|
||||
|
||||
setMode(RF69_MODE_STANDBY);
|
||||
reinitRadio();
|
||||
}
|
||||
#endif
|
||||
59
RFM69.h
59
RFM69.h
|
|
@ -150,6 +150,14 @@
|
|||
#define RFM69_CTL_SENDACK 0x80
|
||||
#define RFM69_CTL_REQACK 0x40
|
||||
|
||||
//#define RF69_LISTENMODE_ENABLE //comment this line out to compile sketches without the ListenMode (saves ~2k)
|
||||
|
||||
#if defined(RF69_LISTENMODE_ENABLE)
|
||||
// By default, receive for 256uS in listen mode and idle for ~1s
|
||||
#define DEFAULT_LISTEN_RX_US 256
|
||||
#define DEFAULT_LISTEN_IDLE_US 1000000
|
||||
#endif
|
||||
|
||||
class RFM69 {
|
||||
public:
|
||||
static volatile uint8_t DATA[RF69_MAX_DATA_LEN]; // recv/xmit buf, including header & crc bytes
|
||||
|
|
@ -221,6 +229,55 @@ class RFM69 {
|
|||
virtual void select();
|
||||
virtual void unselect();
|
||||
inline void maybeInterrupts();
|
||||
|
||||
#if defined(RF69_LISTENMODE_ENABLE)
|
||||
//=============================================================================
|
||||
// ListenMode specific declarations
|
||||
//=============================================================================
|
||||
public:
|
||||
// When we receive a packet in listen mode, this is the time left in the sender's burst.
|
||||
// You need to wait at least this long before trying to reply.
|
||||
static volatile uint16_t RF69_LISTEN_BURST_REMAINING_MS;
|
||||
|
||||
void listenModeStart(void);
|
||||
void listenModeEnd(void);
|
||||
void listenModeHighSpeed(bool highSpeed) { _isHighSpeed = highSpeed; }
|
||||
|
||||
// rx and idle duration in microseconds
|
||||
bool listenModeSetDurations(uint32_t& rxDuration, uint32_t& idleDuration);
|
||||
|
||||
// The values passed to listenModeSetDurations() may be slightly different to accomodate
|
||||
// what is allowed by the radio. This function returns the actual values used.
|
||||
void listenModeGetDurations(uint32_t& rxDuration, uint32_t& idleDuration);
|
||||
|
||||
// This repeatedly sends the message to the target node for the duration
|
||||
// of an entire listen cycle. The amount of time remaining in the burst
|
||||
// is transmitted to the receiver, and it is expected that the receiver
|
||||
// wait for the burst to end before attempting a reply.
|
||||
// See RF69_LISTEN_BURST_REMAINING_MS above.
|
||||
void listenModeSendBurst(uint8_t targetNode, void* buffer, uint8_t size);
|
||||
|
||||
protected:
|
||||
void listenModeInterruptHandler(void);
|
||||
void listenModeApplyHighSpeedSettings();
|
||||
void listenModeReset(); //resets variables used on the receiving end
|
||||
bool reinitRadio(void);
|
||||
static void listenModeIrq();
|
||||
|
||||
bool _isHighSpeed;
|
||||
bool _haveEncryptKey;
|
||||
char _encryptKey[16];
|
||||
|
||||
// Save these so we can reinitialize the radio after sending a burst
|
||||
// or exiting listen mode.
|
||||
uint8_t _freqBand;
|
||||
uint8_t _networkID;
|
||||
uint8_t _rxListenCoef;
|
||||
uint8_t _rxListenResolution;
|
||||
uint8_t _idleListenCoef;
|
||||
uint8_t _idleListenResolution;
|
||||
uint32_t _listenCycleDurationUs;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -77,3 +77,4 @@ PAYLOADLEN LITERAL2
|
|||
ACK_REQUESTED LITERAL2
|
||||
ACK_RECEIVED LITERAL2
|
||||
RSSI LITERAL2
|
||||
RF69_LISTEN_BURST_REMAINING_MS LITERAL2
|
||||
Loading…
Reference in New Issue