diff --git a/keywords.txt b/keywords.txt index dcec71c7..1bb6c6f8 100644 --- a/keywords.txt +++ b/keywords.txt @@ -90,6 +90,10 @@ setDataShapingOOK KEYWORD2 setCRC KEYWORD2 variablePacketLengthMode KEYWORD2 fixedPacketLengthMode KEYWORD2 +setCrcFiltering KEYWORD2 +enableSyncWordFiltering KEYWORD2 +disableSyncWordFiltering KEYWORD2 +setPromiscuous KEYWORD2 # RF69-specific setAESKey KEYWORD2 diff --git a/src/modules/CC1101.cpp b/src/modules/CC1101.cpp index 657400a0..658a3ba2 100644 --- a/src/modules/CC1101.cpp +++ b/src/modules/CC1101.cpp @@ -4,6 +4,8 @@ CC1101::CC1101(Module* module) : PhysicalLayer(CC1101_CRYSTAL_FREQ, CC1101_DIV_E _mod = module; _packetLengthQueried = false; _packetLengthConfig = CC1101_LENGTH_CONFIG_VARIABLE; + + _syncWordLength = CC1101_DEFAULT_SYNC_WORD_LENGTH; } int16_t CC1101::begin(float freq, float br, float rxBw, float freqDev, int8_t power) { @@ -441,11 +443,35 @@ int16_t CC1101::setOutputPower(int8_t power) { return(SPIsetRegValue(CC1101_REG_PATABLE, powerRaw)); } -int16_t CC1101::setSyncWord(uint8_t syncH, uint8_t syncL) { - // set sync word - int16_t state = SPIsetRegValue(CC1101_REG_SYNC1, syncH); - state |= SPIsetRegValue(CC1101_REG_SYNC0, syncL); - return(state); +int16_t CC1101::setSyncWord(uint8_t* syncWord, uint8_t len, uint8_t maxErrBits) { + if((maxErrBits > 1) || (len > CC1101_MAX_SYNC_WORD_LENGTH)) { + return(ERR_INVALID_SYNC_WORD); + } + + // sync word must not contain value 0x00 + for(uint8_t i = 0; i < len; i++) { + if(syncWord[i] == 0x00) { + return(ERR_INVALID_SYNC_WORD); + } + } + + _syncWordLength = len; + + // enable sync word filtering + int16_t state = enableSyncWordFiltering(maxErrBits); + if (state != ERR_NONE) { + return(state); + } + + // set sync word register + _mod->SPIwriteRegisterBurst(CC1101_REG_SYNC1, syncWord, len); + + return(ERR_NONE); +} + +int16_t CC1101::setSyncWord(uint8_t syncH, uint8_t syncL, uint8_t maxErrBits) { + uint8_t syncWord[] = { syncH, syncL }; + return(setSyncWord(syncWord, sizeof(syncWord), maxErrBits)); } int16_t CC1101::setNodeAddress(uint8_t nodeAddr, uint8_t numBroadcastAddrs) { @@ -547,6 +573,76 @@ int16_t CC1101::variablePacketLengthMode(uint8_t maxLen) { return(state); } +int16_t CC1101::enableSyncWordFiltering(uint8_t maxErrBits) { + if (maxErrBits > 1) { + return(ERR_INVALID_SYNC_WORD); + } + + if (maxErrBits == 0) { + if (_syncWordLength == 1) { + // in 16 bit sync word, expect all 16 bits + return(SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_SYNC_MODE_16_16, 2, 0)); + } else { + // there's no 32 of 32 case, so we resort to 30 of 32 bits required + return(SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_SYNC_MODE_30_32, 2, 0)); + } + } + + if (maxErrBits == 1) { + if (_syncWordLength == 1) { + // in 16 bit sync word, expect at least 15 bits + return(SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_SYNC_MODE_15_16, 2, 0)); + } else { + // in 32 bits sync word (16 + 16), expect 30 of 32 to match + return(SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_SYNC_MODE_30_32, 2, 0)); + } + } + + return(ERR_UNKNOWN); +} + +int16_t CC1101::disableSyncWordFiltering() { + return(SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_SYNC_MODE_NONE, 2, 0)); +} + +int16_t CC1101::setCrcFiltering(bool crcOn) { + if (crcOn == true) { + return(SPIsetRegValue(CC1101_REG_PKTCTRL0, CC1101_CRC_ON, 2, 2)); + } else { + return(SPIsetRegValue(CC1101_REG_PKTCTRL0, CC1101_CRC_OFF, 2, 2)); + } +} + +int16_t CC1101::setPromiscuousMode(bool promiscuous) { + int16_t state = ERR_NONE; + + if (_promiscuous == promiscuous) { + return(state); + } + + if (promiscuous == true) { + // disable preamble and sync word filtering and insertion + state = disableSyncWordFiltering(); + if (state != ERR_NONE) { + return(state); + } + + // disable CRC filtering + state = setCrcFiltering(false); + } else { + // enable preamble and sync word filtering and insertion + state = enableSyncWordFiltering(); + if (state != ERR_NONE) { + return(state); + } + + // enable CRC filtering + state = setCrcFiltering(true); + } + + return(state); +} + int16_t CC1101::config() { // enable automatic frequency synthesizer calibration int16_t state = SPIsetRegValue(CC1101_REG_MCSM0, CC1101_FS_AUTOCAL_IDLE_TO_RXTX, 5, 4); diff --git a/src/modules/CC1101.h b/src/modules/CC1101.h index c57a7e6d..92440449 100644 --- a/src/modules/CC1101.h +++ b/src/modules/CC1101.h @@ -10,6 +10,9 @@ #define CC1101_CRYSTAL_FREQ 26.0 #define CC1101_DIV_EXPONENT 16 #define CC1101_MAX_PACKET_LENGTH 63 +#define CC1101_MAX_SYNC_WORD_LENGTH 2 +#define CC1101_DEFAULT_SYNC_WORD_LENGTH 2 +#define CC1101_DEFAULT_SYNC_WORD { 0xD3, 0x91 } // CC1101 SPI commands #define CC1101_CMD_READ 0b10000000 @@ -237,7 +240,7 @@ #define CC1101_MOD_FORMAT_MFSK 0b01110000 // 6 4 MFSK - only for data rates above 26 kBaud #define CC1101_MANCHESTER_EN_OFF 0b00000000 // 3 3 Manchester encoding: disabled (default) #define CC1101_MANCHESTER_EN_ON 0b00001000 // 3 3 enabled -#define CC1101_SYNC_MODE_NONE 0b00000000 // 2 0 synchronization: no preamble sync +#define CC1101_SYNC_MODE_NONE 0b00000000 // 2 0 synchronization: no preamble/sync #define CC1101_SYNC_MODE_15_16 0b00000001 // 2 0 15/16 sync word bits #define CC1101_SYNC_MODE_16_16 0b00000010 // 2 0 16/16 sync word bits (default) #define CC1101_SYNC_MODE_30_32 0b00000011 // 2 0 30/32 sync word bits @@ -694,9 +697,24 @@ class CC1101: public PhysicalLayer { \param syncL LSB of the sync word. + \param maxErrBits Maximum allowed number of bit errors in received sync word. Defaults to 0. + \returns \ref status_codes */ - int16_t setSyncWord(uint8_t syncH, uint8_t syncL); + int16_t setSyncWord(uint8_t syncH, uint8_t syncL, uint8_t maxErrBits = 0); + + /*! + \brief Sets 1 or 2 bytes of sync word. + + \param syncWord Pointer to the array of sync word bytes. + + \param len Sync word length in bytes. + + \param maxErrBits Maximum allowed number of bit errors in received sync word. Defaults to 0. + + \returns \ref status_codes + */ + int16_t setSyncWord(uint8_t* syncWord, uint8_t len, uint8_t maxErrBits = 0); /*! \brief Sets node and broadcast addresses. Calling this method will also enable address filtering. @@ -757,6 +775,40 @@ class CC1101: public PhysicalLayer { */ int16_t variablePacketLengthMode(uint8_t maxLen = CC1101_MAX_PACKET_LENGTH); + /*! + \brief Enable sync word filtering and generation. + + \param numBits Sync word length in bits. + + \returns \ref status_codes + */ + int16_t enableSyncWordFiltering(uint8_t maxErrBits = 0); + + /*! + \brief Disable preamble and sync word filtering and generation. + + \returns \ref status_codes + */ + int16_t disableSyncWordFiltering(); + + /*! + \brief Enable CRC filtering and generation. + + \param crcOn Set or unset promiscuous mode. + + \returns \ref status_codes + */ + int16_t setCrcFiltering(bool crcOn = true); + + /*! + \brief Set modem in "sniff" mode: no packet filtering (e.g., no preamble, sync word, address, CRC). + + \param promiscuous Set or unset promiscuous mode. + + \returns \ref status_codes + */ + int16_t setPromiscuousMode(bool promiscuous = true); + private: Module* _mod; @@ -768,6 +820,10 @@ class CC1101: public PhysicalLayer { bool _packetLengthQueried; uint8_t _packetLengthConfig; + bool _promiscuous; + + uint8_t _syncWordLength; + int16_t config(); int16_t directMode(); void getExpMant(float target, uint16_t mantOffset, uint8_t divExp, uint8_t expMax, uint8_t& exp, uint8_t& mant); diff --git a/src/modules/RF69.cpp b/src/modules/RF69.cpp index a0febe19..882ca15d 100644 --- a/src/modules/RF69.cpp +++ b/src/modules/RF69.cpp @@ -3,8 +3,13 @@ RF69::RF69(Module* module) : PhysicalLayer(RF69_CRYSTAL_FREQ, RF69_DIV_EXPONENT, RF69_MAX_PACKET_LENGTH) { _mod = module; _tempOffset = 0; + _packetLengthQueried = false; _packetLengthConfig = RF69_PACKET_FORMAT_VARIABLE; + + _promiscuous = false; + + _syncWordLength = RF69_DEFAULT_SYNC_WORD_LENGTH; } int16_t RF69::begin(float freq, float br, float rxBw, float freqDev, int8_t power) { @@ -87,8 +92,8 @@ int16_t RF69::begin(float freq, float br, float rxBw, float freqDev, int8_t powe } // default sync word values 0x2D01 is the same as the default in LowPowerLab RFM69 library - uint8_t syncWord[] = {0x2D, 0x01}; - state = setSyncWord(syncWord, 2); + uint8_t syncWord[] = RF69_DEFAULT_SYNC_WORD; + state = setSyncWord(syncWord, sizeof(syncWord)); if(state != ERR_NONE) { return(state); } @@ -495,7 +500,7 @@ int16_t RF69::setOutputPower(int8_t power) { int16_t RF69::setSyncWord(uint8_t* syncWord, size_t len, uint8_t maxErrBits) { // check constraints - if((maxErrBits > 7) || (len > 8)) { + if((maxErrBits > 7) || (len > RF69_MAX_SYNC_WORD_LENGTH)) { return(ERR_INVALID_SYNC_WORD); } @@ -506,13 +511,14 @@ int16_t RF69::setSyncWord(uint8_t* syncWord, size_t len, uint8_t maxErrBits) { } } - // enable sync word recognition - int16_t state = _mod->SPIsetRegValue(RF69_REG_SYNC_CONFIG, RF69_SYNC_ON | RF69_FIFO_FILL_CONDITION_SYNC | (len - 1) << 3 | maxErrBits, 7, 0); - if(state != ERR_NONE) { + _syncWordLength = len; + + int16_t state = enableSyncWordFiltering(maxErrBits); + if (state != ERR_NONE) { return(state); } - // set sync word + // set sync word register _mod->SPIwriteRegisterBurst(RF69_REG_SYNC_VALUE_1, syncWord, len); return(ERR_NONE); } @@ -636,6 +642,71 @@ int16_t RF69::variablePacketLengthMode(uint8_t maxLen) { return(state); } +int16_t RF69::enableSyncWordFiltering(uint8_t maxErrBits) { + // enable sync word recognition + int16_t state = _mod->SPIsetRegValue(RF69_REG_SYNC_CONFIG, RF69_SYNC_ON | RF69_FIFO_FILL_CONDITION_SYNC | (_syncWordLength - 1) << 3 | maxErrBits, 7, 0); + if(state != ERR_NONE) { + return(state); + } + + return(state); +} + +int16_t RF69::disableSyncWordFiltering() { + // disable preamble detection and generation + int16_t state = _mod->SPIsetRegValue(RF69_REG_PREAMBLE_LSB, 0, 7, 0); + state |= _mod->SPIsetRegValue(RF69_REG_PREAMBLE_MSB, 0, 7, 0); + if (state != ERR_NONE) { + return(state); + } + + // disable sync word detection and generation + state = _mod->SPIsetRegValue(RF69_REG_SYNC_CONFIG, RF69_SYNC_OFF | RF69_FIFO_FILL_CONDITION, 7, 6); + if (state != ERR_NONE) { + return(state); + } + + return(state); +} + +int16_t RF69::setCrcFiltering(bool crcOn) { + if (crcOn == true) { + return(_mod->SPIsetRegValue(RF69_REG_PACKET_CONFIG_1, RF69_CRC_ON, 4, 4)); + } else { + return(_mod->SPIsetRegValue(RF69_REG_PACKET_CONFIG_1, RF69_CRC_OFF, 4, 4)); + } +} + +int16_t RF69::setPromiscuousMode(bool promiscuous) { + int16_t state = ERR_NONE; + + if (_promiscuous == promiscuous) { + return(state); + } + + if (promiscuous == true) { + // disable preamble and sync word filtering and insertion + state = disableSyncWordFiltering(); + if (state != ERR_NONE) { + return(state); + } + + // disable CRC filtering + state = setCrcFiltering(false); + } else { + // enable preamble and sync word filtering and insertion + state = enableSyncWordFiltering(); + if (state != ERR_NONE) { + return(state); + } + + // enable CRC filtering + state = setCrcFiltering(true); + } + + return(state); +} + int16_t RF69::config() { int16_t state = ERR_NONE; diff --git a/src/modules/RF69.h b/src/modules/RF69.h index 9820d539..5318b7e8 100644 --- a/src/modules/RF69.h +++ b/src/modules/RF69.h @@ -10,6 +10,10 @@ #define RF69_CRYSTAL_FREQ 32.0 #define RF69_DIV_EXPONENT 19 #define RF69_MAX_PACKET_LENGTH 64 +#define RF69_MAX_PREAMBLE_LENGTH 4 +#define RF69_MAX_SYNC_WORD_LENGTH 8 +#define RF69_DEFAULT_SYNC_WORD_LENGTH 2 +#define RF69_DEFAULT_SYNC_WORD { 0x2D, 0x01 } // RF69 register map #define RF69_REG_FIFO 0x00 @@ -646,7 +650,7 @@ class RF69: public PhysicalLayer { int16_t setOutputPower(int8_t power); /*! - \brief Sets sync word. Up to 8 bytes can be set as snyc word. + \brief Sets sync word. Up to 8 bytes can be set as sync word. \param syncWord Pointer to the array of sync word bytes. @@ -724,6 +728,40 @@ class RF69: public PhysicalLayer { */ int16_t variablePacketLengthMode(uint8_t maxLen = RF69_MAX_PACKET_LENGTH); + /*! + \brief Enable sync word filtering and generation. + + \param numBits Sync word length in bits. + + \returns \ref status_codes + */ + int16_t enableSyncWordFiltering(uint8_t maxErrBits = 0); + + /*! + \brief Disable preamble and sync word filtering and generation. + + \returns \ref status_codes + */ + int16_t disableSyncWordFiltering(); + + /*! + \brief Enable CRC filtering and generation. + + \param crcOn Set or unset promiscuous mode. + + \returns \ref status_codes + */ + int16_t setCrcFiltering(bool crcOn = true); + + /*! + \brief Set modem in "sniff" mode: no packet filtering (e.g., no preamble, sync word, address, CRC). + + \param promiscuous Set or unset promiscuous mode. + + \returns \ref status_codes + */ + int16_t setPromiscuousMode(bool promiscuous = true); + protected: Module* _mod; @@ -735,6 +773,10 @@ class RF69: public PhysicalLayer { bool _packetLengthQueried; uint8_t _packetLengthConfig; + bool _promiscuous; + + uint8_t _syncWordLength; + int16_t config(); int16_t directMode();