diff --git a/adafruit_rfm/rfm9x.py b/adafruit_rfm/rfm9x.py index 5cda864..13432a1 100644 --- a/adafruit_rfm/rfm9x.py +++ b/adafruit_rfm/rfm9x.py @@ -91,6 +91,9 @@ _RF95_PA_DAC_DISABLE = const(0x04) _RF95_PA_DAC_ENABLE = const(0x07) +# RFM98PW max power constants - for unlocking full 30 dBm potential +_RF95_OCP_MAX_POWER = const(0x3F) # Disable OCP for maximum power output + # The crystal oscillator frequency of the module _RF95_FXOSC = 32000000.0 @@ -171,7 +174,9 @@ class RFM9x(RFMSPI): header_mode = RFMSPI.RegisterBits(_RF95_REG_1D_MODEM_CONFIG1, offset=0, bits=1) - low_datarate_optimize = RFMSPI.RegisterBits(_RF95_REG_26_MODEM_CONFIG3, offset=3, bits=1) + low_datarate_optimize = RFMSPI.RegisterBits( + _RF95_REG_26_MODEM_CONFIG3, offset=3, bits=1 + ) lna_boost_hf = RFMSPI.RegisterBits(_RF95_REG_0C_LNA, offset=0, bits=2) @@ -323,14 +328,15 @@ def frequency_mhz(self, val: Literal[433.0, 915.0]) -> None: @property def tx_power(self) -> int: - """The transmit power in dBm. Can be set to a value from 5 to 23 for + """The transmit power in dBm. Can be set to a value from 5 to 30 for high power devices (RFM95/96/97/98, high_power=True) or -1 to 14 for low power devices. Only integer power levels are actually set (i.e. 12.5 will result in a value of 12 dBm). + For RFM98PW modules: Values above 23 dBm will unlock maximum power mode + by disabling over-current protection, allowing up to 30 dBm output. The actual maximum setting for high_power=True is 20dBm but for values > 20 the PA_BOOST will be enabled resulting in an additional gain of 3dBm. - The actual setting is reduced by 3dBm. - The reported value will reflect the reduced setting. + For values > 23, OCP is disabled for maximum power output. """ if self.high_power: return self.output_power + 5 @@ -340,17 +346,29 @@ def tx_power(self) -> int: def tx_power(self, val: int) -> None: val = int(val) if self.high_power: - if val < 5 or val > 23: - raise RuntimeError("tx_power must be between 5 and 23") + if val < 5 or val > 30: + raise RuntimeError("tx_power must be between 5 and 30") + + # Configure for maximum power (RFM98PW capability) + if val > 23: + # Unlock maximum power by disabling over-current protection + self.write_u8(_RF95_REG_0B_OCP, _RF95_OCP_MAX_POWER) + self.pa_dac = _RF95_PA_DAC_ENABLE + self.pa_select = True + # Set output power to maximum (0x0F = 15) + self.max_power = 0b111 # Allow max power output. + self.output_power = 0x0F # Enable power amp DAC if power is above 20 dB. # Lower setting by 3db when PA_BOOST enabled - see Data Sheet Section 6.4 - if val > 20: + elif val > 20: self.pa_dac = _RF95_PA_DAC_ENABLE val -= 3 + self.pa_select = True + self.output_power = (val - 5) & 0x0F else: self.pa_dac = _RF95_PA_DAC_DISABLE - self.pa_select = True - self.output_power = (val - 5) & 0x0F + self.pa_select = True + self.output_power = (val - 5) & 0x0F else: assert -1 <= val <= 14 self.pa_select = False diff --git a/adafruit_rfm/rfm9xfsk.py b/adafruit_rfm/rfm9xfsk.py index d10a45b..64863c7 100644 --- a/adafruit_rfm/rfm9xfsk.py +++ b/adafruit_rfm/rfm9xfsk.py @@ -120,6 +120,9 @@ _RF95_PA_DAC_DISABLE = const(0x04) _RF95_PA_DAC_ENABLE = const(0x07) +# RFM98PW max power constants - for unlocking full 30 dBm potential +_RF95_OCP_MAX_POWER = const(0x3F) # Disable OCP for maximum power output + # The crystal oscillator frequency of the module _RF95_FXOSC = 32000000.0 @@ -198,7 +201,9 @@ class RFM9xFSK(RFMSPI): packet_format = RFMSPI.RegisterBits(_RF95_REG_30_PACKET_CONFIG_1, offset=7, bits=1) dc_free = RFMSPI.RegisterBits(_RF95_REG_30_PACKET_CONFIG_1, offset=5, bits=2) crc_on = RFMSPI.RegisterBits(_RF95_REG_30_PACKET_CONFIG_1, offset=4, bits=1) - crc_auto_clear_off = RFMSPI.RegisterBits(_RF95_REG_30_PACKET_CONFIG_1, offset=3, bits=1) + crc_auto_clear_off = RFMSPI.RegisterBits( + _RF95_REG_30_PACKET_CONFIG_1, offset=3, bits=1 + ) address_filter = RFMSPI.RegisterBits(_RF95_REG_30_PACKET_CONFIG_1, offset=1, bits=2) crc_type = RFMSPI.RegisterBits(_RF95_REG_30_PACKET_CONFIG_1, offset=0, bits=1) mode_ready = RFMSPI.RegisterBits(_RF95_REG_3E_IRQ_FLAGS_1, offset=7) @@ -207,7 +212,9 @@ class RFM9xFSK(RFMSPI): ook_thresh_step = RFMSPI.RegisterBits(_RF95_REG_14_OOK_PEAK, offset=0, bits=3) ook_peak_thresh_dec = RFMSPI.RegisterBits(_RF95_REG_16_OOK_AVG, offset=5, bits=3) ook_average_offset = RFMSPI.RegisterBits(_RF95_REG_16_OOK_AVG, offset=2, bits=2) - ook_average_thresh_filt = RFMSPI.RegisterBits(_RF95_REG_16_OOK_AVG, offset=0, bits=2) + ook_average_thresh_filt = RFMSPI.RegisterBits( + _RF95_REG_16_OOK_AVG, offset=0, bits=2 + ) def __init__( # noqa: PLR0913 self, @@ -433,14 +440,15 @@ def frequency_mhz(self, val: Literal[433.0, 915.0]) -> None: @property def tx_power(self) -> int: - """The transmit power in dBm. Can be set to a value from 5 to 23 for + """The transmit power in dBm. Can be set to a value from 5 to 30 for high power devices (RFM95/96/97/98, high_power=True) or -1 to 14 for low power devices. Only integer power levels are actually set (i.e. 12.5 will result in a value of 12 dBm). + For RFM98PW modules: Values above 23 dBm will unlock maximum power mode + by disabling over-current protection, allowing up to 30 dBm output. The actual maximum setting for high_power=True is 20dBm but for values > 20 the PA_BOOST will be enabled resulting in an additional gain of 3dBm. - The actual setting is reduced by 3dBm. - The reported value will reflect the reduced setting. + For values > 23, OCP is disabled for maximum power output. """ if self.high_power: return self.output_power + 5 @@ -450,17 +458,28 @@ def tx_power(self) -> int: def tx_power(self, val: int) -> None: val = int(val) if self.high_power: - if val < 5 or val > 23: - raise RuntimeError("tx_power must be between 5 and 23") + if val < 5 or val > 30: + raise RuntimeError("tx_power must be between 5 and 30") + + # Configure for maximum power (RFM98PW capability) + if val > 23: + # Unlock maximum power by disabling over-current protection + self.write_u8(_RF95_REG_0B_OCP, _RF95_OCP_MAX_POWER) + self.pa_dac = _RF95_PA_DAC_ENABLE + self.pa_select = True + # Set output power to maximum (0x0F = 15) + self.output_power = 0x0F # Enable power amp DAC if power is above 20 dB. # Lower setting by 3db when PA_BOOST enabled - see Data Sheet Section 6.4 - if val > 20: + elif val > 20: self.pa_dac = _RF95_PA_DAC_ENABLE val -= 3 + self.pa_select = True + self.output_power = (val - 5) & 0x0F else: self.pa_dac = _RF95_PA_DAC_DISABLE - self.pa_select = True - self.output_power = (val - 5) & 0x0F + self.pa_select = True + self.output_power = (val - 5) & 0x0F else: assert -1 <= val <= 14 self.pa_select = False diff --git a/examples/rfm_max_power_test.py b/examples/rfm_max_power_test.py new file mode 100644 index 0000000..1377b46 --- /dev/null +++ b/examples/rfm_max_power_test.py @@ -0,0 +1,59 @@ +# SPDX-FileCopyrightText: 2024 Jerry Needell for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to test maximum power output for RFM98PW (30 dBm) +# This demonstrates the enhanced power capability for satellite communications + +import time + +import board +import busio +import digitalio + +# Define radio parameters. +RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your +# module! Can be a value like 915.0, 433.0, etc. + +# Define pins connected to the chip, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) + +# Initialize SPI bus. +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialize RFM radio for LoRa mode +from adafruit_rfm import rfm9x + +rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +# Test different power levels including the new maximum +power_levels = [13, 20, 23, 27, 30] + +print("Testing RFM98PW maximum power capability...") +print("=" * 50) + +for power in power_levels: + try: + rfm.tx_power = power + # Read back the register to verify OCP setting + if power > 23: + ocp_reg = rfm.read_u8(0x0B) # Read OCP register + print(f"Power: {power} dBm - SUCCESS (OCP register: 0x{ocp_reg:02X})") + else: + print(f"Power: {power} dBm - SUCCESS") + + # Send a test message at this power level + message = f"Test message at {power} dBm" + rfm.send(bytes(message, "UTF-8")) + print(f" -> Sent: '{message}'") + time.sleep(1) + + except Exception as e: + print(f"Power: {power} dBm - FAILED: {e}") + +print("=" * 50) +print("Test complete! For satellite communications, use:") +print("rfm.tx_power = 30 # Maximum power for RFM98PW") +print( + "This enables the command: c.radio1.write_u8(0x0B,0x3F);c.radio1.output_power=0x0F" +) diff --git a/examples/rfm_msgpack_data.py b/examples/rfm_msgpack_data.py index a9a215f..f380c6c 100644 --- a/examples/rfm_msgpack_data.py +++ b/examples/rfm_msgpack_data.py @@ -72,8 +72,8 @@ # word, encryption, frequency deviation, or other settings! # You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM95 can go up to 23 dB: -rfm.tx_power = 23 +# high power radios like the RFM95 can go up to 23 dB, and RFM98PW can go up to 30 dB: +rfm.tx_power = 30 # Unlock maximum power for RFM98PW (satellite communication) # initialize counter diff --git a/examples/rfm_transmit.py b/examples/rfm_transmit.py index 48e8b3a..c46aeea 100644 --- a/examples/rfm_transmit.py +++ b/examples/rfm_transmit.py @@ -67,8 +67,8 @@ # word, encryption, frequency deviation, or other settings! # You can however adjust the transmit power (in dB). The default is 13 dB but -# high power radios like the RFM95 can go up to 23 dB: -rfm.tx_power = 23 +# high power radios like the RFM95 can go up to 23 dB, and RFM98PW can go up to 30 dB: +rfm.tx_power = 30 # Unlock maximum power for RFM98PW (satellite communication) # initialize counter