From 1de98370ebfd2c17b39a7bb2bf889766850fc2a9 Mon Sep 17 00:00:00 2001 From: jgromes Date: Tue, 21 Oct 2025 20:16:22 +0100 Subject: [PATCH] [SX126x] Add experimental BPSK support --- keywords.txt | 1 + src/modules/SX126x/SX1262.cpp | 18 ++++++++++++ src/modules/SX126x/SX1262.h | 14 +++++++++ src/modules/SX126x/SX1268.cpp | 18 ++++++++++++ src/modules/SX126x/SX1268.h | 16 ++++++++++- src/modules/SX126x/SX126x.cpp | 40 +++++++++++++++++++++++++- src/modules/SX126x/SX126x.h | 11 +++++++ src/modules/SX126x/SX126x_commands.cpp | 18 ++++++++++++ src/modules/SX126x/SX126x_commands.h | 8 ++++++ src/modules/SX126x/SX126x_config.cpp | 13 +++++++-- src/modules/SX126x/SX126x_registers.h | 1 + 11 files changed, 154 insertions(+), 4 deletions(-) diff --git a/keywords.txt b/keywords.txt index 8f92c34e..819952f8 100644 --- a/keywords.txt +++ b/keywords.txt @@ -227,6 +227,7 @@ setCrcFiltering KEYWORD2 beginFSK4 KEYWORD2 # SX126x-specific +beginBPSK KEYWORD2 setTCXO KEYWORD2 setDio2AsRfSwitch KEYWORD2 getTimeOnAir KEYWORD2 diff --git a/src/modules/SX126x/SX1262.cpp b/src/modules/SX126x/SX1262.cpp index 20bd4367..a72163f0 100644 --- a/src/modules/SX126x/SX1262.cpp +++ b/src/modules/SX126x/SX1262.cpp @@ -49,6 +49,24 @@ int16_t SX1262::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t return(state); } +int16_t SX1262::beginBPSK(float freq, float br, int8_t power, float tcxoVoltage, bool useRegulatorLDO) { + // execute common part + int16_t state = SX126x::beginBPSK(br, tcxoVoltage, useRegulatorLDO); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + RADIOLIB_ASSERT(state); + + state = SX126x::fixPaClamping(); + RADIOLIB_ASSERT(state); + + state = setOutputPower(power); + RADIOLIB_ASSERT(state); + + return(state); +} + int16_t SX1262::beginLRFHSS(float freq, uint8_t bw, uint8_t cr, bool narrowGrid, int8_t power, float tcxoVoltage, bool useRegulatorLDO) { // execute common part int16_t state = SX126x::beginLRFHSS(bw, cr, narrowGrid, tcxoVoltage, useRegulatorLDO); diff --git a/src/modules/SX126x/SX1262.h b/src/modules/SX126x/SX1262.h index d9668ba8..da2c31bf 100644 --- a/src/modules/SX126x/SX1262.h +++ b/src/modules/SX126x/SX1262.h @@ -63,6 +63,20 @@ class SX1262: public SX126x { */ virtual int16_t beginFSK(float freq = 434.0, float br = 4.8, float freqDev = 5.0, float rxBw = 156.2, int8_t power = 10, uint16_t preambleLength = 16, float tcxoVoltage = 1.6, bool useRegulatorLDO = false); + /*! + \brief Initialization method for BPSK modem. + NOTE: Proceed with caution! BPSK support in SX126x is epxerimental and poorly documented! + \param freq Carrier frequency in MHz. Defaults to 434.0 MHz. + \param br FSK bit rate in kbps. Defaults to 600 bps, only 100 and 600 bps is supported + \param power Output power in dBm. Defaults to 10 dBm. + \param tcxoVoltage TCXO reference voltage to be set on DIO3. Defaults to 1.6 V. + If you are seeing -706/-707 error codes, it likely means you are using non-0 value for module with XTAL. + To use XTAL, either set this value to 0, or set SX126x::XTAL to true. + \param useRegulatorLDO Whether to use only LDO regulator (true) or DC-DC regulator (false). Defaults to false. + \returns \ref status_codes + */ + virtual int16_t beginBPSK(float freq = 434.0, float br = 0.6, int8_t power = 10, float tcxoVoltage = 1.6, bool useRegulatorLDO = false); + /*! \brief Initialization method for LR-FHSS modem. This modem only supports transmission! \param freq Carrier frequency in MHz. Defaults to 434.0 MHz. diff --git a/src/modules/SX126x/SX1268.cpp b/src/modules/SX126x/SX1268.cpp index f9ef606a..018ffb6e 100644 --- a/src/modules/SX126x/SX1268.cpp +++ b/src/modules/SX126x/SX1268.cpp @@ -49,6 +49,24 @@ int16_t SX1268::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t return(state); } +int16_t SX1268::beginBPSK(float freq, float br, int8_t power, float tcxoVoltage, bool useRegulatorLDO) { + // execute common part + int16_t state = SX126x::beginBPSK(br, tcxoVoltage, useRegulatorLDO); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + RADIOLIB_ASSERT(state); + + state = SX126x::fixPaClamping(); + RADIOLIB_ASSERT(state); + + state = setOutputPower(power); + RADIOLIB_ASSERT(state); + + return(state); +} + int16_t SX1268::beginLRFHSS(float freq, uint8_t bw, uint8_t cr, bool narrowGrid, int8_t power, float tcxoVoltage, bool useRegulatorLDO) { // execute common part int16_t state = SX126x::beginLRFHSS(bw, cr, narrowGrid, tcxoVoltage, useRegulatorLDO); diff --git a/src/modules/SX126x/SX1268.h b/src/modules/SX126x/SX1268.h index 28b246e3..911d2e7a 100644 --- a/src/modules/SX126x/SX1268.h +++ b/src/modules/SX126x/SX1268.h @@ -61,7 +61,21 @@ class SX1268: public SX126x { \returns \ref status_codes */ int16_t beginFSK(float freq = 434.0, float br = 4.8, float freqDev = 5.0, float rxBw = 156.2, int8_t power = 10, uint16_t preambleLength = 16, float tcxoVoltage = 1.6, bool useRegulatorLDO = false); - + + /*! + \brief Initialization method for BPSK modem. + NOTE: Proceed with caution! BPSK support in SX126x is epxerimental and poorly documented! + \param freq Carrier frequency in MHz. Defaults to 434.0 MHz. + \param br FSK bit rate in kbps. Defaults to 600 bps, only 100 and 600 bps is supported. + \param power Output power in dBm. Defaults to 10 dBm. + \param tcxoVoltage TCXO reference voltage to be set on DIO3. Defaults to 1.6 V. + If you are seeing -706/-707 error codes, it likely means you are using non-0 value for module with XTAL. + To use XTAL, either set this value to 0, or set SX126x::XTAL to true. + \param useRegulatorLDO Whether to use only LDO regulator (true) or DC-DC regulator (false). Defaults to false. + \returns \ref status_codes + */ + virtual int16_t beginBPSK(float freq = 434.0, float br = 0.6, int8_t power = 10, float tcxoVoltage = 1.6, bool useRegulatorLDO = false); + /*! \brief Initialization method for LR-FHSS modem. This modem only supports transmission! \param freq Carrier frequency in MHz. Defaults to 434.0 MHz. diff --git a/src/modules/SX126x/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index 660b0395..9d096438 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -120,6 +120,22 @@ int16_t SX126x::beginFSK(float br, float freqDev, float rxBw, uint16_t preambleL return(state); } +int16_t SX126x::beginBPSK(float br, float tcxoVoltage, bool useRegulatorLDO) { + // set module properties and perform initial setup + int16_t state = this->modSetup(tcxoVoltage, useRegulatorLDO, RADIOLIB_SX126X_PACKET_TYPE_BPSK); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setBitRate(br); + RADIOLIB_ASSERT(state); + + // set publicly accessible settings that are not a part of begin method + state = setDio2AsRfSwitch(true); + RADIOLIB_ASSERT(state); + + return(state); +} + int16_t SX126x::beginLRFHSS(uint8_t bw, uint8_t cr, bool narrowGrid, float tcxoVoltage, bool useRegulatorLDO) { this->lrFhssGridNonFcc = narrowGrid; @@ -898,8 +914,21 @@ RadioLibTime_t SX126x::getTimeOnAir(size_t len) { dataRate.lrFhss.narrowGrid = this->lrFhssGridNonFcc; packetConfig.lrFhss.hdrCount = this->lrFhssHdrCount; + } else if(type == RADIOLIB_SX126X_PACKET_TYPE_BPSK) { + // BPSK is so experimental it does not have a specific data rate structure + // so just reuse FSK + modem = RADIOLIB_MODEM_FSK; + + dataRate.fsk.bitRate = RADIOLIB_SX126X_CRYSTAL_FREQ * 32.0f * 1000.0f / (float)this->bitRate; + dataRate.fsk.freqDev = 0; + + packetConfig.fsk.preambleLength = 0; + packetConfig.fsk.syncWordLength = 0; + packetConfig.fsk.crcLength = 0; + } else { return(RADIOLIB_ERR_WRONG_MODEM); + } return(calculateTimeOnAir(modem, dataRate, packetConfig, len)); @@ -1015,7 +1044,16 @@ int16_t SX126x::stageMode(RadioModeType_t mode, RadioModeConfig_t* cfg) { } else if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) { state = setPacketParamsFSK(this->preambleLengthFSK, this->preambleDetLength, this->crcTypeFSK, this->syncWordLength, RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF, this->whitening, this->packetType, cfg->transmit.len); - + + } else if(modem == RADIOLIB_SX126X_PACKET_TYPE_BPSK) { + uint16_t rampUp = RADIOLIB_SX126X_BPSK_RAMP_UP_TIME_600_BPS; + uint16_t rampDown = RADIOLIB_SX126X_BPSK_RAMP_DOWN_TIME_600_BPS; + if(this->bitRate == 100) { + rampUp = RADIOLIB_SX126X_BPSK_RAMP_UP_TIME_100_BPS; + rampDown = RADIOLIB_SX126X_BPSK_RAMP_DOWN_TIME_100_BPS; + } + state = setPacketParamsBPSK(cfg->transmit.len, rampUp, rampDown, 8*cfg->transmit.len); + } else if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) { return(RADIOLIB_ERR_UNKNOWN); diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index 63d1bb08..adb91808 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -86,6 +86,15 @@ class SX126x: public PhysicalLayer { */ int16_t beginFSK(float br, float freqDev, float rxBw, uint16_t preambleLength, float tcxoVoltage, bool useRegulatorLDO = false); + /*! + \brief Initialization method for BPSK modem. + \param br FSK bit rate in kbps. Only 100 and 600 bps is supported. + \param tcxoVoltage TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip. + \param useRegulatorLDO Whether to use only LDO regulator (true) or DC-DC regulator (false). Defaults to false. + \returns \ref status_codes + */ + int16_t beginBPSK(float br, float tcxoVoltage, bool useRegulatorLDO = false); + /*! \brief Initialization method for LR-FHSS modem. This modem only supports transmission! \param bw LR-FHSS bandwidth, one of RADIOLIB_SX126X_LR_FHSS_BW_* values. @@ -817,8 +826,10 @@ class SX126x: public PhysicalLayer { int16_t setTxParams(uint8_t power, uint8_t rampTime); int16_t setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, uint8_t ldro); int16_t setModulationParamsFSK(uint32_t br, uint8_t sh, uint8_t rxBw, uint32_t freqDev); + int16_t setModulationParamsBPSK(uint32_t br, uint8_t sh = RADIOLIB_SX126X_BPSK_PULSE_SHAPE); int16_t setPacketParams(uint16_t preambleLen, uint8_t crcType, uint8_t payloadLen, uint8_t hdrType, uint8_t invertIQ); int16_t setPacketParamsFSK(uint16_t preambleLen, uint8_t preambleDetectorLen, uint8_t crcType, uint8_t syncWordLen, uint8_t addrCmp, uint8_t whiten, uint8_t packType = RADIOLIB_SX126X_GFSK_PACKET_VARIABLE, uint8_t payloadLen = 0xFF); + int16_t setPacketParamsBPSK(uint8_t payloadLen, uint16_t rampUpDelay, uint16_t rampDownDelay, uint16_t payloadLenBits); int16_t setBufferBaseAddress(uint8_t txBaseAddress = 0x00, uint8_t rxBaseAddress = 0x00); int16_t setRegulatorMode(uint8_t mode); uint8_t getStatus(); diff --git a/src/modules/SX126x/SX126x_commands.cpp b/src/modules/SX126x/SX126x_commands.cpp index f8833468..114c38ac 100644 --- a/src/modules/SX126x/SX126x_commands.cpp +++ b/src/modules/SX126x/SX126x_commands.cpp @@ -202,6 +202,11 @@ int16_t SX126x::setModulationParamsFSK(uint32_t br, uint8_t sh, uint8_t rxBw, ui return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_MODULATION_PARAMS, data, 8)); } +int16_t SX126x::setModulationParamsBPSK(uint32_t br, uint8_t sh) { + const uint8_t data[] = {(uint8_t)((br >> 16) & 0xFF), (uint8_t)((br >> 8) & 0xFF), (uint8_t)(br & 0xFF), sh}; + return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_MODULATION_PARAMS, data, sizeof(data))); +} + int16_t SX126x::setPacketParams(uint16_t preambleLen, uint8_t crcType, uint8_t payloadLen, uint8_t hdrType, uint8_t invertIQ) { int16_t state = fixInvertedIQ(invertIQ); RADIOLIB_ASSERT(state); @@ -216,6 +221,19 @@ int16_t SX126x::setPacketParamsFSK(uint16_t preambleLen, uint8_t preambleDetecto return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_PACKET_PARAMS, data, 9)); } +int16_t SX126x::setPacketParamsBPSK(uint8_t payloadLen, uint16_t rampUpDelay, uint16_t rampDownDelay, uint16_t payloadLenBits) { + const uint8_t data[] = { payloadLen, + (uint8_t)((rampUpDelay >> 8) & 0xFF), (uint8_t)(rampUpDelay & 0xFF), + (uint8_t)((rampDownDelay >> 8) & 0xFF), (uint8_t)(rampDownDelay & 0xFF), + (uint8_t)((payloadLenBits >> 8) & 0xFF), (uint8_t)(payloadLenBits & 0xFF) + }; + + // this one is a bit different, it seems to be split into command transaction and then a register write + int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_PACKET_PARAMS, data, sizeof(uint8_t)); + RADIOLIB_ASSERT(state); + return(this->writeRegister(RADIOLIB_SX126X_REG_BPSK_PACKET_PARAMS, &data[1], sizeof(data) - sizeof(uint8_t))); +} + int16_t SX126x::setBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress) { const uint8_t data[2] = {txBaseAddress, rxBaseAddress}; return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_BUFFER_BASE_ADDRESS, data, 2)); diff --git a/src/modules/SX126x/SX126x_commands.h b/src/modules/SX126x/SX126x_commands.h index 2ce7f3ac..b66124b7 100644 --- a/src/modules/SX126x/SX126x_commands.h +++ b/src/modules/SX126x/SX126x_commands.h @@ -160,6 +160,7 @@ //RADIOLIB_SX126X_CMD_SET_PACKET_TYPE #define RADIOLIB_SX126X_PACKET_TYPE_GFSK 0x00 // 7 0 packet type: GFSK #define RADIOLIB_SX126X_PACKET_TYPE_LORA 0x01 // 7 0 LoRa +#define RADIOLIB_SX126X_PACKET_TYPE_BPSK 0x02 // 7 0 BPSK #define RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS 0x03 // 7 0 LR-FHSS //RADIOLIB_SX126X_CMD_SET_TX_PARAMS @@ -218,6 +219,7 @@ #define RADIOLIB_SX126X_LORA_CR_4_8_LI 0x07 // 7 0 4/8, long interleaver #define RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF 0x00 // 7 0 LoRa low data rate optimization: disabled #define RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON 0x01 // 7 0 enabled +#define RADIOLIB_SX126X_BPSK_PULSE_SHAPE 0x16 // 7 0 BSPK pulse shape double OSR, RRC, BT=0.7 //RADIOLIB_SX126X_CMD_SET_PACKET_PARAMS #define RADIOLIB_SX126X_GFSK_PREAMBLE_DETECT_OFF 0x00 // 7 0 GFSK minimum preamble length before reception starts: detector disabled @@ -243,6 +245,12 @@ #define RADIOLIB_SX126X_LORA_CRC_ON 0x01 // 7 0 enabled #define RADIOLIB_SX126X_LORA_IQ_STANDARD 0x00 // 7 0 LoRa IQ setup: standard #define RADIOLIB_SX126X_LORA_IQ_INVERTED 0x01 // 7 0 inverted +#define RADIOLIB_SX126X_BPSK_RAMP_UP_TIME_NONE 0x0000 // 15 0 BPSK ramp-up time optimization: none +#define RADIOLIB_SX126X_BPSK_RAMP_UP_TIME_100_BPS 0x370F // 15 0 for 100 bps +#define RADIOLIB_SX126X_BPSK_RAMP_UP_TIME_600_BPS 0x092F // 15 0 for 600 bps +#define RADIOLIB_SX126X_BPSK_RAMP_DOWN_TIME_NONE 0x0000 // 15 0 BPSK ramp-down time optimization: none +#define RADIOLIB_SX126X_BPSK_RAMP_DOWN_TIME_100_BPS 0x1D70 // 15 0 for 100 bps +#define RADIOLIB_SX126X_BPSK_RAMP_DOWN_TIME_600_BPS 0x04E1 // 15 0 for 600 bps //RADIOLIB_SX126X_CMD_SET_CAD_PARAMS #define RADIOLIB_SX126X_CAD_ON_1_SYMB 0x00 // 7 0 number of symbols used for CAD: 1 diff --git a/src/modules/SX126x/SX126x_config.cpp b/src/modules/SX126x/SX126x_config.cpp index a4715aed..88336f39 100644 --- a/src/modules/SX126x/SX126x_config.cpp +++ b/src/modules/SX126x/SX126x_config.cpp @@ -217,12 +217,18 @@ int16_t SX126x::setFrequencyDeviation(float freqDev) { int16_t SX126x::setBitRate(float br) { // check active modem uint8_t modem = getPacketType(); - if((modem != RADIOLIB_SX126X_PACKET_TYPE_GFSK) && (modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS)) { + if((modem != RADIOLIB_SX126X_PACKET_TYPE_GFSK) && + (modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) && + (modem != RADIOLIB_SX126X_PACKET_TYPE_BPSK)) { return(RADIOLIB_ERR_WRONG_MODEM); } - if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) { + if(modem == RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) { RADIOLIB_CHECK_RANGE(br, 0.6f, 300.0f, RADIOLIB_ERR_INVALID_BIT_RATE); + } else if(modem == RADIOLIB_SX126X_PACKET_TYPE_BPSK) { + // this should be just either 100 or 600 bps, not the range + // but the BPSK support is so experimental it probably does not matter + RADIOLIB_CHECK_RANGE(br, 0.1f, 0.6f, RADIOLIB_ERR_INVALID_BIT_RATE); } // calculate raw bit rate value @@ -232,6 +238,9 @@ int16_t SX126x::setBitRate(float br) { this->bitRate = brRaw; // update modulation parameters + if(modem == RADIOLIB_SX126X_PACKET_TYPE_BPSK) { + return(setModulationParamsBPSK(this->bitRate)); + } return(setModulationParamsFSK(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev)); } diff --git a/src/modules/SX126x/SX126x_registers.h b/src/modules/SX126x/SX126x_registers.h index a20cc56c..86ee5d96 100644 --- a/src/modules/SX126x/SX126x_registers.h +++ b/src/modules/SX126x/SX126x_registers.h @@ -6,6 +6,7 @@ #if !RADIOLIB_EXCLUDE_SX126X // SX126X register map +#define RADIOLIB_SX126X_REG_BPSK_PACKET_PARAMS 0x00F0 #define RADIOLIB_SX126X_REG_RX_GAIN_RETENTION_0 0x029F // SX1268 datasheet v1.1, section 9.6 #define RADIOLIB_SX126X_REG_RX_GAIN_RETENTION_1 0x02A0 // SX1268 datasheet v1.1, section 9.6 #define RADIOLIB_SX126X_REG_RX_GAIN_RETENTION_2 0x02A1 // SX1268 datasheet v1.1, section 9.6