Merge ListenMode branch code

This commit is contained in:
Felix Rusu 2018-04-05 16:20:12 -04:00
parent dfffbba7b8
commit e1145c9543
3 changed files with 345 additions and 2 deletions

287
RFM69.cpp
View File

@ -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
View File

@ -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

View File

@ -77,3 +77,4 @@ PAYLOADLEN LITERAL2
ACK_REQUESTED LITERAL2
ACK_RECEIVED LITERAL2
RSSI LITERAL2
RF69_LISTEN_BURST_REMAINING_MS LITERAL2