Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 27 additions & 9 deletions adafruit_rfm/rfm9x.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
39 changes: 29 additions & 10 deletions adafruit_rfm/rfm9xfsk.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
59 changes: 59 additions & 0 deletions examples/rfm_max_power_test.py
Original file line number Diff line number Diff line change
@@ -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"
)
4 changes: 2 additions & 2 deletions examples/rfm_msgpack_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions examples/rfm_transmit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down