Add support for RAK6421 with RAK13300x radios and enhance configuration options

- Introduced `en_pin` and `en_pins` parameters in radio configuration.
- Updated `get_radio_for_board` to handle new configuration options.
- Added unit tests to verify correct handling of `en_pins`.
This commit is contained in:
Rightup
2026-04-26 15:52:27 +01:00
parent 8ab7bad173
commit d780afa56a
6 changed files with 123 additions and 6 deletions
+4
View File
@@ -251,6 +251,10 @@ sx1262:
txen_pin: -1
rxen_pin: -1
# Optional radio power-enable pin(s) driven HIGH during init
en_pin: -1
# en_pins: [26, 23]
# LED pins for TX/RX indication (-1 to disable)
txled_pin: -1
rxled_pin: -1
+2 -2
View File
@@ -30,7 +30,7 @@ keywords = ["mesh", "networking", "lora", "repeater", "daemon", "iot"]
dependencies = [
"pymc_core[hardware]==1.0.10",
"pymc_core[hardware] @ git+https://github.com/rightup/pyMC_core.git@dev",
"pyyaml>=6.0.0",
"cherrypy>=18.0.0",
"paho-mqtt>=1.6.0",
@@ -45,7 +45,7 @@ dependencies = [
[project.optional-dependencies]
# SX1262/SPI support (Linux only; required for Raspberry Pi HATs)
hardware = [
"pymc_core[hardware]",
"pymc_core[hardware] @ git+https://github.com/rightup/pyMC_core.git@dev",
]
# RRD metrics (Performance Metrics chart); system librrd required (e.g. apt install rrdtool)
rrd = [
+45 -3
View File
@@ -206,7 +206,7 @@
"preamble_length": 17,
"is_waveshare": false
},
"ultrapeater-e22": {
"ultrapeater-e22": {
"name": "Zindello Industries UltraPeater E22",
"bus_id": 0,
"cs_id": 0,
@@ -225,7 +225,7 @@
"use_gpiod_backend": true,
"gpio_chip": 1
},
"ultrapeater-e22p": {
"ultrapeater-e22p": {
"name": "Zindello Industries UltraPeater E22P",
"bus_id": 0,
"cs_id": 0,
@@ -244,6 +244,48 @@
"preamble_length": 17,
"use_gpiod_backend": true,
"gpio_chip": 1
},
"rak6421-13300x-slot1": {
"name": "Rak Wireless RAK6421 with RAK1330x on IO Slot 1",
"bus_id": 0,
"cs_id": 0,
"cs_pin": -1,
"reset_pin": 16,
"busy_pin": 24,
"irq_pin": 22,
"txen_pin": -1,
"rxen_pin": -1,
"en_pins": [12, 13],
"txled_pin": -1,
"rxled_pin": -1,
"tx_power": 22,
"use_dio2_rf": true,
"use_dio3_tcxo": true,
"dio3_tcxo_voltage": 1.8,
"preamble_length": 17,
"use_gpiod_backend": true,
"gpio_chip": 1
},
"rak6421-13300x-slot2": {
"name": "Rak Wireless RAK6421 with RAK1330x on IO Slot 2",
"bus_id": 0,
"cs_id": 1,
"cs_pin": -1,
"reset_pin": 24,
"busy_pin": 19,
"irq_pin": 18,
"txen_pin": -1,
"rxen_pin": -1,
"en_pins": [26, 23],
"txled_pin": -1,
"rxled_pin": -1,
"tx_power": 22,
"use_dio2_rf": true,
"use_dio3_tcxo": true,
"dio3_tcxo_voltage": 1.8,
"preamble_length": 17,
"use_gpiod_backend": true,
"gpio_chip": 1
}
}
}
}
+21 -1
View File
@@ -243,6 +243,20 @@ def get_radio_for_board(board_config: dict):
return int(value.strip().rstrip(','), 0)
raise ValueError(f"Invalid int value type: {type(value)}")
def _parse_int_list(value):
if value is None:
return None
if isinstance(value, (list, tuple)):
return [_parse_int(item) for item in value]
if isinstance(value, str):
stripped = value.strip()
if not stripped:
return []
if stripped[0] == "[" and stripped[-1] == "]":
stripped = stripped[1:-1]
return [_parse_int(item) for item in stripped.split(",") if item.strip()]
raise ValueError(f"Invalid int list value type: {type(value)}")
radio_type = board_config.get("radio_type", "sx1262").lower().strip()
if radio_type == "kiss-modem":
radio_type = "kiss"
@@ -286,7 +300,6 @@ def get_radio_for_board(board_config: dict):
"rxen_pin": _parse_int(spi_config["rxen_pin"]),
"txled_pin": _parse_int(spi_config.get("txled_pin", -1), default=-1),
"rxled_pin": _parse_int(spi_config.get("rxled_pin", -1), default=-1),
"en_pin": _parse_int(spi_config.get("en_pin", -1), default=-1),
"use_dio3_tcxo": spi_config.get("use_dio3_tcxo", False),
"dio3_tcxo_voltage": float(spi_config.get("dio3_tcxo_voltage", 1.8)),
"use_dio2_rf": spi_config.get("use_dio2_rf", False),
@@ -300,6 +313,13 @@ def get_radio_for_board(board_config: dict):
"sync_word": radio_config["sync_word"],
}
en_pin = _parse_int(spi_config.get("en_pin"), default=None)
en_pins = _parse_int_list(spi_config.get("en_pins"))
if en_pin is not None:
combined_config["en_pin"] = en_pin
if en_pins is not None:
combined_config["en_pins"] = en_pins
# Add optional GPIO parameters if specified in config
# These wont be supported by older versions of pymc_core
if "gpio_chip" in spi_config:
+2
View File
@@ -543,6 +543,8 @@ class APIEndpoints:
config_yaml["sx1262"]["rxen_pin"] = hw_config.get("rxen_pin", -1)
if "en_pin" in hw_config:
config_yaml["sx1262"]["en_pin"] = hw_config.get("en_pin", -1)
if "en_pins" in hw_config:
config_yaml["sx1262"]["en_pins"] = hw_config.get("en_pins", [])
if "cs_pin" in hw_config:
config_yaml["sx1262"]["cs_pin"] = hw_config.get("cs_pin", -1)
if "txled_pin" in hw_config:
+49
View File
@@ -0,0 +1,49 @@
from repeater.config import get_radio_for_board
class _DummyRadio:
_initialized = True
def test_get_radio_for_board_passes_en_pins(monkeypatch):
captured_kwargs = {}
class _DummySX1262Radio:
@classmethod
def get_instance(cls, **kwargs):
captured_kwargs.update(kwargs)
return _DummyRadio()
monkeypatch.setattr(
"pymc_core.hardware.sx1262_wrapper.SX1262Radio",
_DummySX1262Radio,
)
board_config = {
"radio_type": "sx1262",
"sx1262": {
"bus_id": 0,
"cs_id": 0,
"cs_pin": -1,
"reset_pin": 18,
"busy_pin": 5,
"irq_pin": 6,
"txen_pin": -1,
"rxen_pin": -1,
"en_pins": [26, 23],
},
"radio": {
"frequency": 915000000,
"tx_power": 22,
"spreading_factor": 9,
"bandwidth": 125000,
"coding_rate": 5,
"preamble_length": 17,
"sync_word": 0x3444,
},
}
get_radio_for_board(board_config)
assert captured_kwargs["en_pins"] == [26, 23]
assert "en_pin" not in captured_kwargs